C+程序的編譯包括三個步驟:
預(yù)處理:預(yù)處理程序采用C+源代碼文件,并處理#include
S,#define
S和其他預(yù)處理指令。這個步驟的輸出是一個“純”C+文件,沒有預(yù)處理指令。
編譯:編譯器獲取預(yù)處理器的輸出并從中生成一個對象文件.
鏈接:鏈接器獲取編譯器生成的對象文件,并生成庫或可執(zhí)行文件。
預(yù)處理
預(yù)處理程序處理預(yù)處理指令,就像#include
和#define
。它不知道C+的語法,這就是為什么必須謹(jǐn)慎使用它的原因。
它一次工作在一個C+源文件上,方法是#include
具有相應(yīng)文件內(nèi)容的指令(通常只是聲明),替換宏(#define
),并根據(jù)以下內(nèi)容選擇文本的不同部分:#if
, #ifdef
和#ifndef
指令。
預(yù)處理器工作在預(yù)處理令牌流上。宏替換定義為用其他令牌替換令牌(操作符)。##
當(dāng)有意義時,啟用合并兩個令牌)。
在所有這些之后,預(yù)處理器產(chǎn)生一個單一的輸出,這是由上面描述的轉(zhuǎn)換產(chǎn)生的令牌流。它還添加了一些特殊的標(biāo)記,告訴編譯器每一行來自哪里,這樣它就可以使用這些標(biāo)記來生成合理的錯誤消息。
在此階段,可以通過巧妙地使用#if
和#error
指令。
編撰
編譯步驟對預(yù)處理程序的每個輸出執(zhí)行。編譯器解析純C+源代碼(現(xiàn)在沒有任何預(yù)處理器指令)并將其轉(zhuǎn)換為程序集代碼。然后調(diào)用底層后端(工具鏈中的匯編程序),將該代碼組裝成生成某種格式的實際二進制文件的機器代碼(ELF,COFF,a.out,.)。此對象文件包含輸入中定義的符號的編譯代碼(二進制形式)。對象文件中的符號按名稱引用。
對象文件可以引用未定義的符號。如果使用聲明,而不提供聲明的定義,則會出現(xiàn)這種情況。編譯器不介意這一點,只要源代碼格式良好,編譯器就會很高興地生成對象文件。
編譯器通常允許您在此時停止編譯。這非常有用,因為使用它,您可以分別編譯每個源代碼文件。它提供的優(yōu)點是您不需要重新編譯一切如果您只更改一個文件。
生成的對象文件可以放在稱為靜態(tài)庫的特殊檔案中,以便以后更容易重用。
在這個階段,報告了“常規(guī)”編譯器錯誤,比如語法錯誤或失敗的過載解析錯誤。
鏈接
鏈接器是編譯器生成的對象文件的最終編譯輸出。這個輸出可以是一個共享的(或者是動態(tài)的)庫(雖然名稱相似,但它們與前面提到的靜態(tài)庫沒有多少共同點),也可以是一個可執(zhí)行文件。
它通過用正確的地址替換對未定義符號的引用來鏈接所有對象文件。這些符號中的每一個都可以在其他對象文件或庫中定義。如果它們是在標(biāo)準(zhǔn)庫以外的庫中定義的,則需要將它們告知鏈接器。
在這個階段,最常見的錯誤是缺少定義或重復(fù)定義。前者意味著定義不存在(即它們不被寫入),或者它們所在的對象文件或庫沒有提供給鏈接器。后者是顯而易見的:在兩個不同的對象文件或庫中定義了相同的符號。