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