gcc的編譯過程分為四步,分別為:
(1)先行編譯 (Pre-Processing)
(2)編譯 (Compiling)
(3)彙編 (Assembling)
(4)連結 (Linking)
以hello.c為例說明:
#include<stdio.h>
int main(void)
{
printf("Hello World!");
return 0;
}
(1)先行編譯階段 (Pre-Processing)
在該階段,編譯器將上述代碼中的stdio.h編譯進來,並且使用gcc -E進行查看(-E讓gcc在預先處理結束後停止編譯)
gcc -E hello.c -o hello.i
查看hello.i可見,gcc確實進行了預先處理,它把"stdio.h"的內容插入到hello.i檔案中
(2)編譯階段
在這個階段,gcc首先檢查代碼的規範性,是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤後
gcc把代碼編譯成組合語言,可以使用-S選項進行查看,該選項之進行編譯而不進行彙編,產生彙編代碼
gcc -S hello.i -o hello.s
(3)彙編階段
就是把編譯階段產生的.s檔案轉成目標檔案,可以使用選項-c ,就是把彙編代碼轉化成.o的二進位目標代碼了
gcc -c hello.s -o hello.o
(4)連結階段
在成功編譯之後就進入了連結階段,在這裡涉及到一個重要的概念:函數庫。
讀者重新查看這個小程式,在該小程式中沒有並沒有定義"printf"的函數實現,且在先行編譯中包含進的"stdio.h"
中也只有該函數的聲明,而沒有定義該函數的實現,那麼在哪實現"printf"函數哪?,最後答案是:系統把這些函數實現
都被做到名為libc.so.6的庫檔案中去了,在沒有特別指定時gcc會到系統預設的搜尋路徑"/usr/lib"下去尋找
也就是連結到libc.so.6庫函數中去,這樣就實現了"printf"了,而這也是連結的作用
函數庫一般分為靜態庫和動態庫兩種,靜態庫是指在編譯連結時,把庫檔案的代碼全部加到可執行程式中去,因此產生的
可執行檔比較大,在運行時就不再需要庫檔案了,尾碼名為.a。動態庫與之相反,在編譯連結時並沒有把庫檔案的代碼
加入到可執行檔中,而是在程式執行時由運行時連結檔案載入庫,這樣可以節省系統的開銷,動態庫一般尾碼名為.so。
完成連結後gcc就可以產生可執行檔
gcc hello.o -o hello