解密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,或者用其他編譯器,原理都是一樣的,感興趣的同學可以去學習一下編譯原理,會對編譯有更深入的理解。