天天看點

編譯--連結--被執行

一串代碼從寫入編譯器到最後被執行,到底中途經曆了什麼?

c語言中有一個經典的例子helloworld,這是每一個程式員踏入程式設計之路的第一步,哈哈,一入佛門深似海,從此節操是路人。

剛開始我們很懵,不知道什麼,也不敢多問,可是學習了很久之後,你發現你的問題變得多了,為什麼就會被編譯器給列印出來hello world?為什麼必須這樣子寫才能被執行出來hello world?怎麼存放,怎麼執行?等等,你就是一個十萬個為什麼了。

#include<stdio.h>
int main()
{
    printf("hello world\n");
    return ;
}
           

好簡單的代碼,相信你是閉着眼睛都能寫出來的。這不是重點,重點是怎麼運作出來的,解決你的為什麼。

這個代碼被寫出來,然後被gcc -o hello hello.c 就可以執行了,哇!真的執行出來了helloworld。剛開始你也會覺得很神奇,但是見多了就覺得沒那麼新奇了。

其實看着很簡單的代碼,可是對于編譯器來說它是怎麼處理的呢?

事實上,上述的過程被分解成了4個部分,分别是預處理、編譯、彙編和連結。

那就一步一步來,反正一口也是吃不了一個胖子的。

預處理:

剛開始#include是被當成了一種慣性來寫出來的。它是什麼,為什麼用?預處理可是專門為它量身打造。

預處理階段stdio.h被預編譯成一個.i的檔案,對于c++程式來說,源代碼是.cpp結尾,頭檔案是.hpp結尾的,那麼被預編譯以後成了.ii。

linux經常使用的指令是:gcc -E hello -o hello.i

預編譯主要的工作(處理以#開頭的預編譯指令):

1.展開所有#define定義的宏;

2.處理所有的條件預編譯指令,如:”if“,”ifdef”,”elif”,”else”,”endif”.

3.#include包含的檔案按插入到檔案指定的位置。

4.删除注釋

5.添加行号

6.保留#pragme的預編譯指令。

編譯:

編譯就是就接着預處理的檔案,進行詞法分析、文法分析、語義分析、優化代碼、檢查錯誤、彙總所有的符号。

linux的指令 :gcc -S hello.i -o hello.s

彙編:

根據編譯完之後的.s檔案,将.s檔案轉變成彙編指令。

linux指令:gcc -c hello.c -o hello.o

經過彙編後變成了.o檔案。

大緻畫圖說明一下,前面發生的事情。

編譯--連結--被執行

那麼這個二進制可重定向的二進制檔案,是什麼樣子感覺很神奇呀!沒事神秘還是要有的,一會在說。

連結:

連結可是一個橋梁,一個很重要的角色。

他主要負責:

1.合并個段

2.給符号配置設定位址(重定向)

3.符号解析

從此之後hello變成了可執行檔案。

剛才前面不是留下了一個疑問,現在來看一下,目标檔案經過一個連結之後,變成了一個可執行檔案,他們兩個有什麼不一樣嗎?(不就是有了位址有什麼了不起的)。

二進制可重定向目标檔案

編譯--連結--被執行

目标檔案:

紅色部分是和二進制可重定向目标檔案的差別:

編譯--連結--被執行

回歸到原來的主幹道上吧!不要迷路了!

将目标問價加載到虛拟位址空間之後,将main函數的入口位址寫入pc寄存器。

還沒有執行,目标檔案被加載都了自己的虛拟位址空間。但是作業系統不認識這個虛拟位址呀!

我們得大費周章的加載到實體記憶體,讓作業系統認識它。怎麼加載呢,大緻是這樣子的。

編譯--連結--被執行

這下子作業系統才認識,hello才會被執行,現在看來,是不是一個helloworld要被執行,也是很費勁的,沒你想得那麼簡單。

是以好好愛惜你的作業系統吧!

繼續閱讀