寫了這么多程式,那么程式到底是如何編譯的呢?
每一個源檔案都會經過編譯與鏈接的程序,最終才會展示出來,
程序大致如下:
源檔案先經過編譯器編譯為目標檔案,最后由聯結器將庫檔案和目標檔案結合,成為可執行檔案,

接下來,我們來探討一下編譯與鏈接的具體程序,
目錄
一、編譯
1.1 預編譯
1.2 編譯
1.3 匯編
二、鏈接
三、運行
一、編譯
編譯可以分為三個階段:預編譯、編譯、匯編,
為了方便演示,這里使用Linux環境演示,
1.1 預編譯
這個程序主要由三個步驟:
1. 將頭檔案的宣告匯入源檔案中
2. #define 的替換
3. 洗掉注釋
這些都屬于文本操作,
在Linux 環境下,對應代碼為: gcc -E test.c -o test.i
這里寫一串代碼示例:
#include <stdio.h>
#define MAX 10
//This is a note.
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 1;
int b = 2;
printf("%d\n", Add(a, b));
printf("%d\n", MAX);
return 0;
}
輸入命令,會生成 .i 檔案,即預編譯好的檔案,再打開 .i 檔案,如下:

1.2 編譯
這個程序主要將C語言代碼轉為匯編代碼,主要是四個程序:
1. 語法分析
2. 詞法分析
3. 語意分析
4. 符號匯總
在Linux 環境下,對應代碼為: gcc -S test.c
還是以上面那串帶碼為例,編譯后的結果是 .s 檔案,打開它如下:

一堆看不懂的代碼哈哈,其實它們就是古老的匯編語言,
轉換為匯編的四個程序中涉及到編譯原理,這里不多闡述,
其中的符號匯總需要講一下,就是將全域變數以及函式形成一個符號,
1.3 匯編
這個步驟主要將匯編代碼轉為二進制檔案,
這個程序會生成符號表,
剛剛講到,每個全域變數與函式會形成一個符號,
這個程序就是將每一個符號結合生成一個符號表,
一個符號是一個標識,此程序還會給符號一個地址,最終將所有符號及其地址組合,形成符號表,
對應 Linux 的代碼是:gcc -c test.c
會生成 .o 檔案,打開檔案:

這個相信沒人能看懂了,這是只有機器能看懂的二進制檔案,
二、鏈接
這個程序主要干兩件事:
1. 合并段表
2. 符號表的合并和重定位
那么段表是什么意思呢?
一個匯編后的 .o 檔案都有一個段,
比如我們將一個函式寫入 Add.c 的檔案中,在 test.c 中呼叫,
那么這兩個檔案都會經過編譯,最后有一個自己的段,
比如這個test.c呼叫了Add.c檔案,所以這兩個段會結合在一起,即.o檔案的結合,最終形成段表,

那么符號表的合并是什么意思呢?
之前我們講到全域變數與函式都會生成符號,在匯編階段生成符號表,
在main函式中呼叫時,其實也會有這樣一個符號表,
但是main函式內的只能算一個標識,沒有自己的意義,
靠著這個標識去找到main函式外的函式或者全域變數,
一旦找到,就是將他們的符號表合并,

三、運行
最后我們來看看運行,
程式執行的程序:
1. 程式先載入記憶體中,一般這個由作業系統完成,在獨立的環境中,程式的載入必須由手工安排,也可能是通過可執行代碼置入只讀記憶體來完成,
2. 程式的執行便開始,接著便呼叫main函式,
3. 開始執行程式代碼,這個時候程式將使用一個運行時堆疊(stack),存盤函式的區域變數和回傳地址,程式同時也可以使用靜態(static)記憶體,存盤于靜態記憶體中的變數在程式的整個執行程序并一直保留他們的值,
4. 終止程式,正常終止main函式;也有可能是意外終止,
感謝你能看到這,如有幫助,希望點個贊👍,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/316634.html
標籤:其他
