解密C語言編譯背後的過程
我們大部分程式設計師可能都是從C語言學起的,寫過幾萬行、幾十萬行、甚至上百萬行的程式碼,但是大家是否都清楚C語言編譯的完整過程呢,如果不清楚的話,我今天就帶著大家一起來做個解密吧。
C語言相對於彙編語言是一種高級語言,要想在系統上運行,需要通過編譯器把它轉換成機器能夠讀懂的可執行的程式碼。
以Linux系統上的gcc為例,通常我們編譯一個源文件都是用下面的命令:
$gcc hello.c –o hello
編譯成功後,目錄里會生成hello這個程式,直接運行它可以看到結果。
$./hello
Hello World!
但hello這個程式是怎麼生成的呢,其實中間還是有好幾步的。用下面這個命令重新編譯一下,你可以看到所有的中間文件。
$gcc -save-temps hello.c –o hello
$ls
hello hello.c hello.i hello.o hello.s
C編譯器的編譯過程主要分成四步:
(1) 預處理
(2) 編譯
(3) 彙編
(4) 連接
1) 預處理 Pre-prosssing
預處理生成了hello.i 的中間文件,主要完成了下面幾步:
-
去掉所有的注釋
-
展開所有的宏定義(也就是做字元替換)
-
插入#include文件的內容
-
處理所有的條件編譯
hello.i 文件內容如下(文件較大,只展示了最下面的一塊):
可以發現源程式碼中所有的注釋被刪除了,並且插入了stdio.h頭文件的內容。
2)編譯 Compiling
編譯將 hello.i 文件編譯生成一個中間文件 hello.s,打開可以看到裡邊都是彙編語言,所以編譯的作用就是把源程式碼轉換成彙編語言。
3)彙編 Assembly
彙編器將 hello.s 彙編成 hello.o 文件。hello.o是二進位文件,裡邊都是機器可以執行的程式碼。
4)連接 Linking
連接顧名思義起到了一個連接作用,雖然 hello.o 已經是二進位文件了,但是裡邊用到的比如 printf 函數需要調用別的庫。連接器將我們的二進位文件和其他庫做了一個綁定。可以看到連接後生成的 hello 文件要比 hello.o 大的多。
到這裡 C的完整編譯流程就結束了,本文的示例用的是Linux作業系統,編譯器用的是 gcc,但在其他作業系統,比如 Unix、Windows,或者用其他編譯器,原理都是一樣的,感興趣的同學可以去學習一下編譯原理,會對編譯有更深入的理解。