我們大部分程序員可能都是從C語(yǔ)言學(xué)起的,寫(xiě)過(guò)幾萬(wàn)行、幾十萬(wàn)行、甚至上百萬(wàn)行的代碼,但是大家是否都清楚C語(yǔ)言編譯的完整過(guò)程呢,如果不清楚的話,我今天就帶著大家一起來(lái)做個(gè)解密吧。
?
C語(yǔ)言相對(duì)于匯編語(yǔ)言是一種高級(jí)語(yǔ)言,要想在系統(tǒng)上運(yùn)行,需要通過(guò)編譯器把它轉(zhuǎn)換成機(jī)器能夠讀懂的可執(zhí)行的代碼。
?
以Linux系統(tǒng)上的gcc為例,通常我們編譯一個(gè)源文件都是用下面的命令:
$gcc hello.c –o hello
?
?編譯成功后,目錄里會(huì)生成hello這個(gè)程序,直接運(yùn)行它可以看到結(jié)果。
$./hello
Hello World!
?
但hello這個(gè)程序是怎么生成的呢,其實(shí)中間還是有好幾步的。用下面這個(gè)命令重新編譯一下,你可以看到所有的中間文件。
$gcc -save-temps hello.c –o hello
$ls
hello hello.c hello.i hello.o hello.s
?
C編譯器的編譯過(guò)程主要分成四步:
(1) 預(yù)處理
(2) 編譯
(3) 匯編
(4) 連接
?1) 預(yù)處理 Pre-prosssing
?預(yù)處理生成了hello.i 的中間文件,主要完成了下面幾步:
*
去掉所有的注釋
*
展開(kāi)所有的宏定義(也就是做字符替換)
*
插入#include文件的內(nèi)容
*
處理所有的條件編譯
hello.i 文件內(nèi)容如下(文件較大,只展示了最下面的一塊):
??
可以發(fā)現(xiàn)源代碼中所有的注釋被刪除了,并且插入了stdio.h頭文件的內(nèi)容。
??
2)編譯 Compiling
?編譯將 hello.i 文件編譯生成一個(gè)中間文件 hello.s,打開(kāi)可以看到里邊都是匯編語(yǔ)言,所以編譯的作用就是把源代碼轉(zhuǎn)換成匯編語(yǔ)言。
?
?3)匯編 Assembly
?匯編器將 hello.s 匯編成 hello.o 文件。hello.o是二進(jìn)制文件,里邊都是機(jī)器可以執(zhí)行的代碼。
??
4)連接 Linking
?連接顧名思義起到了一個(gè)連接作用,雖然 hello.o 已經(jīng)是二進(jìn)制文件了,但是里邊用到的比如 printf
函數(shù)需要調(diào)用別的庫(kù)。連接器將我們的二進(jìn)制文件和其他庫(kù)做了一個(gè)綁定??梢钥吹竭B接后生成的 hello 文件要比 hello.o 大的多。
??
到這里 C的完整編譯流程就結(jié)束了,本文的示例用的是Linux操作系統(tǒng),編譯器用的是 gcc,但在其他操作系統(tǒng),比如
Unix、Windows,或者用其他編譯器,原理都是一樣的,感興趣的同學(xué)可以去學(xué)習(xí)一下編譯原理,會(huì)對(duì)編譯有更深入的理解。
?
?
熱門工具 換一換
