流程概況:將源代碼轉(zhuǎn)換成機(jī)器可識(shí)別代碼的過程,編譯程序讀取源代碼,對(duì)他進(jìn)行詞法和語法的分析,將高級(jí)語言轉(zhuǎn)化為功能等效的匯編代碼,然后轉(zhuǎn)化為機(jī)器語言,按照操作系統(tǒng)對(duì)可執(zhí)行文件格式的要求連接生成可執(zhí)行程序
源程序->編譯預(yù)處理->編譯->優(yōu)化程序->匯編程序->鏈接程序->可執(zhí)行文件
編譯預(yù)處理
讀取源代碼,對(duì)其中的偽指令(#開頭)和特殊符號(hào)進(jìn)行處理.
- 宏定義
#define Name TokenString
:將所有Name
替換成TokenString
#undef
: 取消宏定義 - 條件編譯
#ifdef
,#ifudef
,#else
,#elif
,endif
等等
這些偽指令讓程序員通過不同的宏決定編譯程序?qū)δ切┐a進(jìn)行處理,從而把不必要的代碼過濾掉 - 頭文件包含指令
#include "FileName"
,#include <FileName>
- 頭文件中有大量的宏定義,還有外部符號(hào)的聲明
采用頭文件的目的是讓某些定義供不同的 C 源程序使用,因?yàn)橹灰由弦粭l#include
,就可以把這些定義和聲明加入到對(duì)應(yīng)的源程序文件中. - 系統(tǒng)提供的頭文件一般放在
/usr/include
下,需要使用<>
來包含他們,而開發(fā)人員自己的頭文件一般在同一目錄下,此時(shí)要用""
- 頭文件中有大量的宏定義,還有外部符號(hào)的聲明
- 特殊符號(hào)
預(yù)編譯可以處理一些特殊符號(hào),比如LINE
是當(dāng)前的行號(hào)(十進(jìn)制),FILE
是當(dāng)前源程序的名稱.預(yù)編譯會(huì)識(shí)別這些特殊符號(hào).
總結(jié):這個(gè)階段主要是對(duì)源程序的"替換"工作,替換后,上面的四個(gè)東西就消失了,得到一個(gè)沒有上述特性的輸出文件.
編譯階段
在預(yù)處理之后,編譯通過詞法和語法分析,將輸出文件翻譯陳等價(jià)的中間代碼或者匯編代碼.
優(yōu)化階段
比較復(fù)雜, 比如刪除公共表達(dá)式, 循環(huán)優(yōu)化, 無用賦值的刪除等等.
優(yōu)化階段放在編譯之后,是一種比較籠統(tǒng)的表達(dá)
匯編過程
把匯編翻譯成目標(biāo)機(jī)器指令,每一個(gè) C 語言源程序都會(huì)得到目標(biāo)文件
目標(biāo)文件由段組成:
- 代碼段:程序本身
- 數(shù)據(jù)段
鏈接程序
上面的目標(biāo)文件不能立即被執(zhí)行,還有一些問題, 比如,某個(gè)源程序中的函數(shù)引用了另一個(gè)源文件中的符號(hào), 調(diào)用了其他庫中的函數(shù),這些問題需要鏈接來實(shí)現(xiàn).
鏈接的主要工作就是將有關(guān)的目標(biāo)文件彼此鏈接,使得所有目標(biāo)文件成為一個(gè)統(tǒng)一整體.
- 靜態(tài)鏈接: 函數(shù)代碼從所在的靜態(tài)鏈接庫中被拷貝到最終可執(zhí)行程序中,在執(zhí)行的時(shí)候這些代碼被裝入這個(gè)進(jìn)程的虛擬地址空間中
靜態(tài)鏈接庫:目標(biāo)文件的集合,每個(gè)文件含有庫中一個(gè)或者一組相關(guān)函數(shù)的代碼
- 動(dòng)態(tài)鏈接: 這種方式下,函數(shù)的代碼被放在動(dòng)態(tài)鏈接庫或者共享對(duì)象的某個(gè)目標(biāo)文件中,鏈接只是在最終的可執(zhí)行程序中,記錄下共享對(duì)象的名字和一些登記信息,執(zhí)行的時(shí)候,動(dòng)態(tài)鏈接庫中的全部?jī)?nèi)容被映射到相應(yīng)進(jìn)程的虛地址空間,動(dòng)態(tài)鏈接程序通過可執(zhí)行程序中的信息找到相應(yīng)的代碼.
動(dòng)態(tài)鏈接能讓程序更加短小,多個(gè)進(jìn)程共享對(duì)象的時(shí)候能節(jié)省內(nèi)存,但是某些情況下可能會(huì)有性能的損傷