首先是預編譯,這一步可以粗略的認為隻做了一件事情,那就是“宏展開”,也就是對那些#***的指令的一種展開,例如define MAX 1000就是建立起MAX和1000之間的對等關系,好在編譯階段進行替換。例如ifdef/ifndef就是從一個檔案中有選擇性的挑出一些符合條件的代碼來交給下一步的編譯階段來處理。這裡面最複雜的莫過于include了,其實也很簡單,就是相當于把那個對應的檔案裡面的内容一下子替換到這條include***語句的地方來。
其次是編譯,這一步很重要,編譯是以一個個獨立的檔案作為單元的,一個檔案就會編譯出一個目标檔案。(這裡插入一點關于編譯的檔案的說明,編譯器通過字尾名來辨識是否編譯該檔案,是以“.h”的頭檔案一概不理會,而“.cpp”的源檔案一律都要被編譯,我實驗過把.h檔案的字尾名改為.cpp,然後在include的地方相應的改為***.cpp,這樣一來,編譯器就會編譯許多不必要的頭檔案,隻不過頭檔案裡我們通常隻放置聲明而不是定義,是以最後連結生成的可執行檔案的大小是不會改變的)清楚編譯是以一個個單獨的檔案為單元的,這一點很重要,是以編譯隻負責本單元的那些事,而對外部的事情一概不理會,在這一步裡,我們可以調用一個函數而不必給出這個函數的定義,但是要在調用前得到這個函數的聲明(其實這就是include的本質,不就是為了給你提前提供個聲明而好讓你使用嗎?至于那個函數到底是如何實作的,需要在連結這一步裡去找函數的入口位址。是以提供聲明的方式可以是用include把放在别的檔案中的聲明拿過來,也可以是在調用之前自己寫一句void
max(int,int);都行。),編譯階段剩下的事情就是分析文法的正确性之類的工作了。好啦,總結一下,可以粗略的認為編譯階段分兩步:第一步,檢驗函數或者變量是否存在它們的聲明;第二步,檢查語句是否符合C++文法。
最後一步是連結,它會把所有編譯好的單元全部連結為一個整體檔案,其實這一步可以比作一個“連線”的過程,比如A檔案用了B檔案中的函數,那麼連結的這一步會建立起這個關聯。連結時最重要的我認為是檢查全局空間裡面是不是有重複定義或者缺失定義。這也就解釋了為什麼我們一般不在頭檔案中出現定義,因為頭檔案有可能被釋放到多個源檔案中,每個源檔案都會單獨編譯,連結時就會發現全局空間中有多個定義了。
1、編譯預處理:
編譯器讀取C源程式,對其中的預處理指令(以#開頭)和特殊符号進行處理。預處理指令包括主要包括三種,一是宏定義指令,二是條件編譯指令,三是頭檔案包含指令。采用頭檔案的目的是使某些定義可以供多個不同的C源程式使用。在需要用到這些定義的C源程式中,隻需加上#include語句即可,而不必重新定義一遍。預編譯程式将頭檔案中的代碼統統加入到源檔案,進而産生輸出檔案。
除了以上三種預處理指令,還有特殊符号。預編譯程式可以識别一些特殊符号。例如在源程式中出現的LINE表示将被解釋為十進制表示的目前行号。FILE則被解釋為目前編譯的源程式的檔案名。
預編譯程式完成的工作,可以說成是對源程式的“替換”工作。經過這個過程,生成一個沒有宏定義、沒有條件編譯指令、沒有特殊符号的輸出檔案。
2、編譯、優化:
編譯程式的工作是,通過詞法分析、文法分析,在确認所有的指令都符合文法規則之後,将其翻譯成等價的中間代碼或彙編代碼。 在生成中間代碼或彙編代碼過程中,可能涉及到優化處理。優化有兩種:一種優化僅涉及代碼本身,主要是删除公共表達式、循環優化、代碼外提、無用 代碼指派等。另一種優化設計具體的計算機硬體,比如,如何根據機器硬體執行指令的特點對指令進行調整優化,減少目标代碼長度,提高執行效率。中間代碼或者彙編代碼生成以後,編譯程式将中間代碼轉換為目标機器指令的序列,得到對應于源程式的目标檔案。目标檔案中存放的也就是與源程式等效的目标機器的機器語言代碼。目标檔案一般至少包含2個段:代碼段和資料段。
3、連結:
由第二階段生成的若幹對應于多個源程式的目标檔案,并不能立即就被執行。其中存在一些問題,比如,某個源檔案中的函數可能引用了另一個源檔案中的某個符号(如變量或者函數等);在一個源檔案中可能調用了某個庫檔案中的函數,等等。這些問題,需要連接配接程式來解決。
連接配接程式的主要工作就是将有關的目标檔案彼此連接配接。也就是将在一個檔案中引用的符号同該符号在另一個檔案中的定義連接配接起來。使得所有這些目标檔案成為一個能夠被作業系統執行的一個整體。
補充:連結庫分為2種
靜态連結
在這種連接配接方式下,函數的代碼将直接拷貝到最終的可執行檔案中。該程式被執行時候,會被裝入該程序的虛拟位址空間中。靜态連結庫實際上是一個或若幹目标檔案。
動态連結
這種方式下,函數的代碼被放到稱作動态連接配接庫或共享對象的某個目标檔案中。連結程式此時的工作隻是在生成的可執行檔案中,記錄下共享對象的名字以及少量關鍵資訊。動态連接配接庫可以被多個程序共享,在運作時候記憶體中隻有一個執行個體。
二者比較
使用動态連結能夠使可執行檔案較小,并且當共享對象被多個程序使用時節省記憶體。但有時候系統運作改變或省級,不能保證動态連接配接庫一定可用、有效。