首先要明確一點,編譯只是一個統稱,編譯的整個過程有預處理、編譯、彙編和連結的過程
我們給出一個特別簡單的程式
//test.c#include <stdio.h>#define max 5int main(){ printf("max = %d\n", max); return 0;}
1、預處理
預處理階段的指令一般都是以#來開頭的,替換#include包含的標頭檔案,替換#define定義的宏,刪除註釋,去掉#ifdef不符合條件的那一部分,所有#開頭的程式碼都會在預處理階段完成處理。
預處理命令:gcc -E test.c -o test.i
這裡-E的作用是讓程式在預處理完成之後就停止,為了方便我們後面的觀察。我們在當前目錄下ls,就可以看見多了一個test.i的檔案,開啟它可以看到很多變數、函式等等的宣告,這些都是stdio.h這個標頭檔案展開的結果,拉到最後,可以看到我們定義的宏max被替換成5了。
2、編譯大學如果學的是計算機專業的童鞋一定會學過一門《編譯原理》的課,這門課幾乎會把很多大學生折騰得死去活來。而這個編譯的過程也正式編譯原理裡面介紹的內容,包括詞法分析、語法分析、語義分析、程式最佳化等等一系列的過程,這些都是編譯器的核心內容,如果你想開發編譯器,這個過程你要非常非常的精通!這個過程就是把程式編譯成更接近機器語言的組合語言。平時我們用IDE編譯的時候,經常看見的錯誤和警告,一般都是在過程發出的。
編譯命令:gcc -S test.i -o test.s
這裡-S的作用是讓程式在編譯完成之後就停止,為了方便我們後面的觀察。我們在當前目錄下ls,就可以看見多了一個test.s的檔案,開啟它看到的一大堆彙編指令。這些指令,我根本看不懂,說實話,沒有接觸過組合語言的人,幾乎都是看不懂的。但是如果你是想在編譯器這個底層領域翻江倒海的話,組合語言是必須要懂的。
3、彙編組合語言有些專業人員看得懂,但是計算機是根本就看不懂的。計算機看得懂的僅僅只有010101這種機器語言,所以我們還要將組合語言轉換成機器語言,至於這個過程怎麼轉的,不在本文的討論範圍,也討論不了,因為我也不知道。這些都是那些非常厲害的大神的研究領域,真不是我誇大這個難度,能開發出商用編譯器的人,至少在計算機領域絕對都是逆天的天選之子。
彙編命令:gcc -C test.i -o test.o
我們在當前目錄下ls,就可以看見多了一個test.o的檔案,開啟它看到的一大堆亂碼,實際上這些都是二進位制命令,而這些命令才是計算機能看得懂的。
4、連結二進位制檔案雖然計算機可以看懂了,但是如果你的原始檔中用到了其他自己寫的標頭檔案的函式,或者是第三方靜態庫動態庫,這時候還需要進行把它們連結起來生成可執行檔案,才可以正確的被執行。
連結命令:gcc test.o -o test
/opt/rh/devtoolset-9/root/usr/libexec/gcc/x86_64-redhat-linux/9/ld: error in test.o(.eh_frame); no .eh_frame_hdr table will be created
以上就是編譯的幾個步驟,只有比較清晰地掌握好每個步驟,才能真正地把編譯的整個流程搞清楚。當然,你也可以用一步到位的方式進行編譯:
gcc test.c -o test
這樣可以直接生成可執行檔案。
相關編譯的文章請閱讀: