一個程式的源代碼到變成可執行程式,要經過 --預處理—編譯—彙編–連結四個過程。
1. 預處理都做了啥?
指令: gcc -E hello.c -o hello.i
預處理主要處理源代碼中以“#”開頭的預編譯指令,主要包括:
- 将所有的“#define”删除并展開所有的宏定義。 處理所有條件預編譯指令,比如“#if”"#ifdef""#else"“endif”
- 處理#include預編譯指令,将被包含的檔案插入到該編譯指令的位置。 删除所有的注釋 “//” “”
- 添加行号和檔案名辨別,比如#2 “hello.c” 2, 以便編譯時編譯器産生調試用的行号資訊*,及用于編譯時,*産生編譯錯誤或警告時能夠顯示行号
- 保留所有的#pragma編譯器指令,因為編譯器需要使用它們
2. 編譯都做了啥?
指令:gcc -S hello.i -o hello.s
把預處理完的檔案進行一系列詞法分析,文法分析,語義分析,及優化後産生相應的彙編代碼(目标代碼)檔案。
2.1 詞法分析—将源代碼的字元序列掃描分割成一系列的記号。例如:
code: arry[index] = (index + 4) * (2 + 6)
上面語句會被拆分為:
記号 | 類型 |
---|---|
arry | 辨別符 |
[ | 左方括号 |
index | 辨別符 |
] | 右方括号 |
…略
2.2 文法分析
将掃描器産生的記号進行文法分析,進而産生文法樹(syntax tree),文法樹是以表達式(expression)為節點的樹。例如:arry[index] = (index + 4) * (2 + 6)
這是一個包含指派表達式,加法表達式,乘法表達式,數組表達式,括号表達式組成的複雜表達式。
符号和數字是最小的表達式,是以他們是末端子節點。
文法分析過程中會确定運算符号的優先級。
如果發現表達式不合法,比如括号不配對,表達式中缺少操作符,變編譯器将報錯。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TP35UNnpnT5FEROBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLzMjMyQzMzUTM2ITNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
2.3 語義分析
分析整條語句是否有意義,包括:
靜态語義:編譯期間可以确定的語義。通常包括聲明和類型的比對,類型轉換
動态語義:運作期間可以确定的語義。如,0 作為除數。
語義分析後,文法樹的表達式都被标示了類型
2.4 代碼優化
将文法樹轉換成中間代碼(常見的有三位址碼—(例如 x = y op z)和P-代碼)
中間代碼使得編譯器被分為前端和後端。前端負責産生機器無關的中間代碼。後端負責将中間代碼轉換成目标機器代碼(跨平台編譯發生在後端)
3. 彙編都做了啥?
指令: gcc -c hello.s -o hello.o
彙編是将彙編代碼轉變成機器可以執行的指令,彙編過程沒有複雜的文法,隻是根據彙編指令和機器指令的對照表一一翻譯過來。輸出的是目标檔案(object file)。
4. 連結都做了啥?
連結過程主要包括 位址和空間配置設定,符号決議和重定位。
連接配接器的主要功能是重定位,每個需要修正的地方叫做一個重定位入口(relocation entry),重定位就是給程式中每個這樣的位址引用的位置“打更新檔”,使它們指向正确的位址。