鏈接器把多個二進制的目標文件(object file)鏈接成一個單獨的可執行文件。在鏈接過程中,它必須把符號(變量名、函數名等一些列標識符)用對應的數據的內存地址(變量地址、函數地址等)替代,以完成程序中多個模塊的外部引用。
而且,鏈接器也必須將程序中所用到的所有C標准庫函數加入其中。對於鏈接器而言,鏈接庫不過是一個具有許多目標文件的集合,它們在一個文件中以方便處理。
當把程序鏈接到一個鏈接庫時,只會鏈接程序所用到的函數的目標文件。在已編譯的目標文件之外,如果創建自己的鏈接庫,可以使用 ar 命令。
標准庫的大部分函數通常放在文件 libc.a 中(文件名后綴.a代表“achieve”,譯為“獲取”),或者放在用於共享的動態鏈接文件 libc.so 中(文件名后綴.so代表“share object”,譯為“共享對象”)。這些鏈接庫一般位於 /lib/ 或 /usr/lib/,或者位於 GCC 默認搜索的其他目錄。
當使用 GCC 編譯和鏈接程序時,GCC 默認會鏈接 libc.a 或者 libc.so,但是對於其他的庫(例如非標准庫、第三方庫等),就需要手動添加。
令人驚訝的是,標准頭文件 <math.h> 對應的數學庫默認也不會被鏈接,如果沒有手動將它添加進來,就會發生函數未定義錯誤。
GCC 的-l選項可以讓我們手動添加鏈接庫。下面我們編寫一個數學程序 main.c,並使用到了 cos() 函數,它位於 <math.h> 頭文件。
#include <stdio.h> /* printf */
#include <math.h> /* cos */
#define PI 3.14159265
int main ()
{
double param, result;
param = 60.0;
result = cos ( param * PI / 180.0 );
printf ("The cosine of %f degrees is %f.\n", param, result );
return 0;
}
為了編譯這個 main.c,必須使用-l選項,以鏈接數學庫:
$ gcc main.c -o main.out -lm
數學庫的文件名是 libm.a。前綴lib和后綴.a是標准的,m是基本名稱,GCC 會在-l選項后緊跟着的基本名稱的基礎上自動添加這些前綴、后綴,本例中,基本名稱為 m。
在支持動態鏈接的系統上,GCC 自動使用在 Darwin 上的共享鏈接庫 libm.so 或 libm.dylib。
鏈接其它目錄中的庫
通常,GCC 會自動在標准庫目錄中搜索文件,例如 /usr/lib,如果想鏈接其它目錄中的庫,就得特別指明。有三種方式可以鏈接在 GCC 搜索路徑以外的鏈接庫,下面我們分別講解。
1) 把鏈接庫作為一般的目標文件,為 GCC 指定該鏈接庫的完整路徑與文件名。
例如,如果鏈接庫名為 libm.a,並且位於 /usr/lib 目錄,那么下面的命令會讓 GCC 編譯 main.c,然后將 libm.a 鏈接到 main.o:
$ gcc main.c -o main.out /usr/lib/libm.a
2) 使用-L選項,為 GCC 增加另一個搜索鏈接庫的目錄:
$ gcc main.c -o main.out -L/usr/lib -lm
可以使用多個-L選項,或者在一個-L選項內使用冒號分割的路徑列表。
3) 把包括所需鏈接庫的目錄加到環境變量 LIBRARYPATH 中。
