學硬件的同學第一件事肯定是GPIO點亮LED燈,而學軟件的同學,大部分做的第一件事就是Hello world!
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Hello World!\n");
return 0;
}
相信上述代碼很多同學閉着眼睛都能敲出來,然後使用下面的命令編譯執行一氣呵成!
很多時候我們都沒有仔細研究gcc main.c -o main這個命令到底做了什麼,今天我們就來分析下。
事實上,上述過程基本可以分爲4個步驟,分別是:
- 預處理(Prepressing)
- 編譯(Compilation)
- 彙編(Assembly)
- 鏈接(Linking)
1.預編譯:
首先是源代碼文件main.c的預處理,這個預處理主要處理以"#"開始的預編譯命令,主要包括:
- 對所有使用 #define 定義的語句進行展開
- 處理條件預編譯指令,如 #if 、 #ifdef 、 #elif 、#else 、#endif 等
- 處理#include 預編譯指令,將被包含的文件插入到預編譯指令的位置(這可能是個遞歸過程,因爲被包含的文件可能包含其他文件)
- 刪除註釋,如 // 、/**/
- 添加行號和文件名標識, 以便編譯時編譯器產生調試用的行號信息及用於編譯時產生編譯錯誤或警告能顯示行號
- 保留所有的#pragma編譯器指令,因爲編譯器須要使用它們
我們可以使用GCC參數-E表示只進行預編譯,預編譯生成的.i文件不包含任何宏定義,因爲所有的宏已經被展開,所以我們可以使用-E選項調試宏函數。
gcc -E main.c -o main.i
2. 編譯
編譯過程就是把預處理完成的文件進行一系列的詞法分析,語法分析,語義分析及優化後生成相應的彙編代碼文件。
這個過程往往是我們所說的整個程序構建的核心部分,也是最複雜的部分之一。
我們可以使用gcc 的 -S 參數進行編譯。
gcc -S main.i -o main.s
當然,你也可以使用gcc -S main.c -o main.s直接生成彙編文件.s
3. 彙編
彙編器就是將彙編文件代碼轉變成機器可以執行的指令,每一個彙編語句幾乎對應一條機器指令,所以彙編器的彙編過程相對於編譯器來講比較簡單,它沒有複雜的語法,也沒有語義,也不需要做指令優化,只是根據彙編指令和機器指令對照表一一翻譯就可以了。上面的彙編過程我們可以用匯編器as來完成
as main.s -o main.o
或
gcc -c main.c -o main.o
4. 鏈接
鏈接過程可以簡單理解爲“組裝”過程。鏈接的過程主要包括了地址和空間分配,符號決議,和重定位等步驟。
這些過程可以參考我後面的文章詳解。
附上一圖詳細直觀點: