C/C++語言預處理,編譯,彙編,鏈接的詳細過程講解

一、問題背景

最近博主在複習各類編程語言知識時,在類比Java解釋型高級語言的運行機制時,第一時間想到了C/C++的運行機制,於是博主我查閱相關資料後對C/C++的運行機制做一個詳細但易懂的總結歸納。

二、C/C++的運行機制

程序要運行起來,必須要經過四個步驟:預處理、編譯、彙編和鏈接。

預處理
編譯
彙編
鏈接
hello.c, hello.h
hello.i
hello.s
hello.o或hello.obj
可執行文件Windows的.exe文件或Linux的.out文件等

1.預處理

C/C++語言最常見的預處理就是將所有的“#define”刪除,並且展開所有的宏定義。而預處理其實還包括:處理所有的條件編譯指令,比如“#if”、處理“#include”預編譯指令、刪除所有的註釋、添加行號和文件名標識等。

注意:GCC命令來自於Linux 系統C/C++的GCC編譯器,而Windows 下常用的編譯器是微軟開發的 Visual C++,它被集成在 Visual Studio 中。

在這裏插入圖片描述
上圖是CSDN博主douguailove在他的博客編譯和鏈接的過程中使用Linux系統下的gcc -E命令把hello.c文件預處理成hello.i文件後的對比圖,圖左側爲hello.c文件,圖右側爲hello.i文件。

預處理後的.i被稱爲擴展後的c源碼文件。爲什麼還叫c源碼文件呢?
因爲預處理後,只是宏定義等東西不見了,但是C源碼依然還在,比如main函數,各種自己寫的子函數,依然存在,所以還是被稱爲c源碼文件。打開.i文件後,我們是能夠看的懂的,所以.c文件、.h文件和.i文件都是ascii文件。

2.編譯

2.1編譯詞彙的爭議性

對於C/C++這種編譯性語言,我們平時編譯時,不管是通過IDE圖形界面,還是通過命令行,總感覺編譯一下就完成了,然後就得到了針對某操作系統和某CPU的二進制可執行文件(機器指令的文件)。但是實際上在源碼到可執行文件中間隱藏了四個過程,這四個過程被操作系統默默的處理了。

編譯四個過程:預處理、編譯、彙編、鏈接

四個過程中的“編譯”,特指其中的某個過程,這四個過程合在一起,我們也統稱爲編譯。所以編譯二字到底指的是第二個過程,還是全部過程的統稱,這個就要看說話的“語境”了。其實統稱的編譯,完整的稱法應該叫編譯鏈接,只是簡稱爲編譯而已。

如果這四個過程是一次性編譯完成的,這個四個過程分別會產生相應的文件,只不過中間產生的文件都是過渡性的臨時文件,使用完成後就會被刪除。

2.2編譯的定義

編譯會將源代碼由文本形式轉換成機器語言,編譯過程就是把預處理完的文件進行一系列詞法分析、語法分析、語義分析以及優化後生成相應的彙編代碼文件。

編譯後的.s也是ascii碼文件。因爲彙編也是人能看懂的文字編碼形式,所以.s彙編文件也是ASCII碼文件。

3.彙編

彙編過程調用匯編器AS來完成,是用於將彙編代碼轉換成機器可以執行的指令,每一個彙編語句幾乎都對應一條機器指令。(非底層的程序員不需要考慮)

彙編後的.o文件是純二進制文件。因爲.o中放的是純二進制的機器指令,所以我們打開後看不懂。
ASCII的源碼被彙編爲能被CPU執行的機器指令,.o文件中放的就是機器指令。但是.o文件還無法運行,需要鏈接後才能運行。

4.鏈接

鏈接是將所有的.o文件和庫(動態庫、靜態庫)鏈接在一起,得到可以運行的可執行文件(Windows的.exe文件或Linux的.out文件)等。它的工作就是把一些指令對其他符號地址的引用加以修正。鏈接過程主要包括了地址和空間分配、符號決議和重定向。

最基本的鏈接叫做靜態鏈接,就是將每個模塊的源代碼文件編譯、彙編成目標文件(Linux:.o 文件;Windows:.obj文件),然後將目標文件和庫一起鏈接形成最後的可執行文件(.exe或.out等)。庫其實就是一組目標文件的包,就是一些最常用的代碼變異成目標文件後打包存放。最常見的庫就是運行時庫,它是支持程序運行的基本函數的集合。

5.可執行文件(Windows的.exe文件或Linux的.out文件)

你是否曾疑惑“a.out”這個名字是怎樣確定的?把所有的輸出文件都缺省的使用同一個名字a.out可能會帶來不便,可能會忘了它來自哪一個源文件,對任何文件進行下一次編譯時都有可能覆蓋它。

a.out是“assembler output”(彙編程序輸出)的縮寫形式。這裏有一個問題:它不是彙編程序輸出,而是鏈接器輸出。“彙編程序輸出”這個名字的產生純屬歷史原因。在早期的語言中並不存在鏈接器,程序是這樣創建的:先把所有源文件連接在一起,然後進行彙編,彙編產生的彙編程序輸出保存在a.out中。即使最後有了鏈接器之後,最後一個環節的輸出文件依然沿用了這個命名習慣。

UNIX中的可執行文件是以一種特殊的方式加上標籤,這樣系統就能確認它們的特殊屬性。爲重要的數據定義標籤,用獨特的數字唯一的標識該數據是一種普遍採用的編程技巧。可執行文件用文件的第一個字節來標註,文件以十六進制數7F開頭,緊跟在後面的第二至第四個字節爲“ELF”(Executable and Linking Format)可執行文件和鏈接格式。

參考文獻:
[1]C語言編譯和鏈接詳解(通俗易懂,深入本質)
[2]c語言編譯過程詳解,預處理,編譯,彙編,鏈接(乾貨滿滿)
[3]瞭解“預編譯、編譯、彙編、鏈接”這四個過程對你有很大幫助
[4]編譯和鏈接的過程
[5]理解a.out
[6]預處理、編譯、彙編、鏈接、啓動代碼、相關command

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章