.h
檔案 、 單makefile的編譯方式.h
檔 、 單makefile的編譯方式在[Day 14] 的時候我們有引入靜態函示庫的概念
函式庫 (Library) 可以想像成是一組已經寫好、整理好的程式碼集合,提供了某些功能讓其他程式可以直接使用,而不必每次都重新實作
例如C語言中的 stdio.h, string.h, time.h, stdlib.h (標頭檔 header file) → 包含在 GNU C Library (glibc) 的 libc.a (靜態庫)/ libc.so (動態庫) 裡面
https://sourceware.org/glibc/manual/latest/pdf/libc.pdf
https://en.wikipedia.org/wiki/C_standard_librarylibc.a
包含什麼?
math.h
他的函示庫是libm.a
gcc main.c -o main -lm
後面的 main -lm
告訴編譯器要去連結 libm.a (或 libm.so)
.a
(archive).o
物件檔打包在一起 可以對照[Day 14] 的內容一起看就會有比較深刻的體會.a
裡面找哪個 .o
定義了這個符號,再從 .a
裡挑出需要的函式,複製進執行檔printf.o
scanf.o
malloc.o
time.o
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
.o
包起來的倉庫 的連結器只會從 libc.a 把 printf.o
拉進去,不會拉 malloc.o
、time.o
,所以執行檔裡面只包含需要的東西.a
裡的設計是 .o
包含很多函式(例如 stdio.o
同時有 printf
、scanf
、fopen
),只要用到其中一個函式,整個 stdio.o
都會被拉進來libc.a
(裡面是.o
的集成會根據妳的函示去選擇適配的.o
),生成一個完全獨立、不需要外部依賴的可執行檔
gcc main.c -static -o main
.so
(shared object)#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
gcc main.c -o main
預設會使用 動態連結,產生的執行檔會記錄需要 libc.so 的 printf這樣的資訊,而不是把 printf.o 搬進去, (.a
會把整個搬進去)printf
也就是要執行的系統裏頭本身要有libc.so.6
.so
可能會造成相容性問題linux-vdso.so.1 (0x00007ffd8a5d9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4d8b3a0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4d8b7a0000)
/usr/lib/x86_64-linux-gnu/libc.a
/lib/x86_64-linux-gnu/libc.so.6
glibc 提供兩種版本,讓你根據需求選用
.a
= 靜態庫,編譯時打包進去.so
= 動態庫,執行時載入.a
.so
特性 | 靜態庫 .a (archive) |
動態庫 .so (shared object) |
---|---|---|
連結時機 | 編譯時 (compile time) → 把需要的 .o 直接放進執行檔 |
執行時 (runtime) → 作業系統載入 .so |
執行檔大小 | 大,因為包含庫的程式碼 | 小,因為只記錄符號資訊 |
記憶體使用 | 每個程式各自擁有一份 | 多個程式共享同一份 .so |
外部依賴 | 無 → 執行檔獨立,不需額外檔案 | 有 → 執行檔必須能找到對應的 .so |
更新函式庫 | 需要重新編譯程式 | 不需重編,只要 .so 介面相容 |
部署情境 | 嵌入式系統、單一可攜執行檔 | 桌面應用程式、伺服器程式 |
常見檔案 | /usr/lib/x86_64-linux-gnu/libc.a |
/lib/x86_64-linux-gnu/libc.so.6 |