連結器
連結器的工作是將一堆目標檔案(.o
檔案)連結到一個二進位制可執行檔案中。連結過程主要涉及將符號地址解析為數字地址。連結過程的結果通常是可執行程式。
在連結過程中,連結器將獲取命令列中指定的所有物件模組,在前面新增一些特定於系統的啟動程式碼,並嘗試使用其他物件檔案中的外部定義來解析物件模組中的所有外部引用 (目標檔案)可以直接在命令列上指定,也可以通過庫隱式新增。然後,它將為目標檔案分配載入地址,也就是說,它指定程式碼和資料最終將在完成的程式的地址空間中的位置。一旦獲得了載入地址,它就可以用目標地址空間中的真實數字地址替換目的碼中的所有符號地址。該程式現在可以執行了。 ** **
這包括編譯器從原始碼檔案建立的目標檔案以及為你預編譯並收集到庫檔案中的目標檔案。這些檔案的名稱以 .a
或 .so
結尾,你通常不需要了解它們,因為連結器知道大多數檔案所在的位置,並根據需要自動連結它們。
隱式呼叫連結器
與前處理器一樣,連結器是一個單獨的程式,通常稱為 ld
(但 Linux 使用 collect2
,例如)。與前處理器一樣,當你使用編譯器時,會自動為你呼叫連結器。因此,使用連結器的常規方法如下:
% gcc foo.o bar.o baz.o -o myprog
該行告訴編譯器將三個目標檔案(foo.o
,bar.o
和 baz.o
)連結到一個名為 myprog
的二進位制可執行檔案中。現在你有了一個名為 myprog
的檔案,你可以執行它,希望能做一些很酷和/或有用的東西。
顯式呼叫連結器
可以直接呼叫連結器,但這很少是可取的,並且通常是特定於平臺的。也就是說,在 Linux 上執行的選項不一定適用於 Solaris,AIX,macOS,Windows,對於任何其他平臺也是如此。如果你使用 GCC,你可以使用 gcc -v
檢視代表你執行的操作。
連結器的選項
連結器還需要一些引數來修改它的行為。以下命令將告訴 gcc 連結 foo.o
和 bar.o
,但也包括 ncurses
庫。
% gcc foo.o bar.o -o foo -lncurses
這實際上(或多或少)相當於
% gcc foo.o bar.o /usr/lib/libncurses.so -o foo
(雖然 libncurses.so
可能是 libncurses.a
,這只是一個用 ar
建立的檔案)。請注意,你應該在目標檔案之後列出庫(通過路徑名或通過 -lname
選項)。對於靜態庫,它們的指定順序很重要; 通常,對於共享庫,順序無關緊要。
請注意,在許多系統上,如果使用數學函式(來自 <math.h>
),則需要指定 -lm
來載入數學庫 - 但 Mac OS X 和 macOS Sierra 不需要這樣。在 Linux 和其他 Unix 系統上還有其他庫是獨立的庫,但不是在 macOS 上 - POSIX 執行緒,POSIX 實時和網路庫就是例子。因此,連結過程因平臺而異。
其他編譯選項
這是你開始編譯自己的 C 程式時需要知道的全部內容。通常,我們還建議你使用 -Wall
命令列選項:
% gcc -Wall -c foo.cc
-Wall
選項會使編譯器警告你合法但可疑的程式碼構造,並且會幫助你儘早捕獲大量錯誤。
如果你希望編譯器向你發出更多警告(包括宣告但未使用的變數,忘記返回一個值等),你可以使用這組選項,如 -Wall
,儘管有名稱,但不會全部可能的警告 :
% gcc -Wall -Wextra -Wfloat-equal -Wundef -Wcast-align -Wwrite-strings -Wlogical-op \
> -Wmissing-declarations -Wredundant-decls -Wshadow …
請注意,clang
有一個選項 -Weverything
,它確實開啟了 clang
中的所有警告。