1. 編譯的四個步驟
預(yù)處理(Propressing)
編譯(Compilation)
匯編(Assembly)
鏈接(Linking)
1.1 預(yù)處理(Propressing)
預(yù)處理過程主要是處理源代碼中以“#”開始的預(yù)編譯指令蜒灰,生成.i文件,主要處理規(guī)則如下:
- 刪除所有“#define”,展開所有的宏定義。
- 處理所有條件預(yù)編譯指令羹唠,比如“#ifdef”。
- 處理“#include”預(yù)編譯指令娄昆,將被包含的文件插入到指令所在的位置。
- 刪除注釋缝彬。
- 添加行號和文件名標識萌焰,用于產(chǎn)生調(diào)試信息。
1.2 編譯(Compilation)
編譯過程是把預(yù)處理之后的文件經(jīng)過詞法分析谷浅、語法分析扒俯、語義分析、中間代碼生成一疯、目標代碼生成與優(yōu)化這些步驟撼玄,生成相應(yīng)的匯編代碼.s文件。
1.2.1 詞法分析
預(yù)處理之后的源代碼被輸入到掃描器(Scanner)墩邀,運用算法將代碼的字符序列分割成一系列的Token掌猛,并對Token分類:關(guān)鍵字、標識符眉睹、字面量(數(shù)字荔茬、字符串等)和特殊符號(運算符等)。
1.2.2 語法分析
對詞法分析得到的Token進行語法分析竹海,生成語法樹(如下)慕蔚。語法樹是以表達式(Expression)為結(jié)點的樹。符號和數(shù)字是最小的表達式斋配,作為葉節(jié)點孔飒。語法分析的同時灌闺,運算符的優(yōu)先級和含義也被確定。如果表達式不合法坏瞄,編譯器會報語法錯誤菩鲜。
1.2.3 語義分析
語法分析只完成了表達式語法層面的分析,并不了解表達式是否真正有意義惦积,比如指針乘法在語法上是合法的接校。編譯器所能分析的是靜態(tài)語義,包括類型匹配和轉(zhuǎn)換狮崩。
語義分析之后蛛勉,語法樹的表達式會被標識類型,如果有類型隱式轉(zhuǎn)換睦柴,會插入相應(yīng)的轉(zhuǎn)換節(jié)點诽凌。
1.2.4 中間代碼生成
現(xiàn)代編譯器往往在源代碼級別會有一個優(yōu)化,比如上面的2+6坦敌,會被優(yōu)化成8侣诵。直接在語法樹上做優(yōu)化比較困難,因此往往會把語法樹轉(zhuǎn)換成中間代碼(Intermediate Code)狱窘,它是語法樹的順序表示杜顺。
中間代碼很接近目標代碼,但是跟平臺無關(guān)蘸炸,比如不包含數(shù)據(jù)的尺寸躬络、變量地址和寄存器名字等等。中間代碼的表示形式常見的有三地址碼(Three_address Code)搭儒,里面對三個變量地址操作。
1.3 匯編(Assembly)
將匯編代碼轉(zhuǎn)換成機器指令馁菜,由于每一條匯編語句幾乎都對應(yīng)一條機器指令铃岔,只需根據(jù)對照表一一翻譯就可以了汪疮。
主要包括代碼生成器(Code Generator)和目標代碼優(yōu)化器(Target Code Optimizer)。代碼生成器將中間代碼轉(zhuǎn)換成目標機器代碼德撬,優(yōu)化器對目標代碼進行優(yōu)化铲咨,比如選擇合適的尋址方式、刪除多余的指令等等蜓洪。
1.4 鏈接(Linking)
目標代碼中有變量定義在其他模塊怎么辦纤勒?這是編譯無法解決的,需要在鏈接的時候才能確定隆檀。所以現(xiàn)代編譯器是將源代碼文件變成從未連接的目標文件摇天,有鏈接器最終將目標文件鏈接成可執(zhí)行文件粹湃。
從原理上來講,鏈接就是修正外部變量的地址泉坐。(靜態(tài)鏈接)