GCC編譯過程(預處理 編譯 彙編 鏈結)

2021-10-09 22:25:49 字數 3013 閱讀 4301

gcc編譯過程(預處理->編譯->彙編->鏈結)

這裡gcc編譯器 是指在linux類作業系統下,windows編譯器 mingw(相當於windows版gcc)

乙個c/c++檔案要經過預處理(preprocessing)、編譯(compilation)、彙編(assembly)和鏈結(linking)等4步才能變成可執行檔案。

生成可執行程式過程為成四個步驟:

1、 由.c檔案到.i檔案,這個過程叫預處理。

以「#」號開頭的預處理指令如包含#include,巨集定義制定#define等。在源程式中這些指令都放在函式之外,而且一般放在原始檔的前面。

c/c++原始檔中,以「#」開頭的命令被稱為預處理命令,如包含命令「#include」、巨集定義命令「#define」、條件編譯命令「#if」、「#ifdef」等。預處理就是將要包含(include)的檔案插入原檔案中、將巨集定義展開、根據條件編譯命令選擇要使用的**,最後將這些東西輸出到乙個「.i」檔案中等待進一步處理。

2、由.i檔案到.s檔案,這個過程叫編譯。

這裡的編譯不是指程式從原始檔到二進位制程式的全部過程,而是指將經過預處理檔案(test.i)之後的程式轉換成特定彙編(test.s)**的過程。

彙編是機器語言的直接翻譯形式

人們便嘗試用英文助記符代替晦澀的機器指令,例如add代表一條機器加法指令,助記符和機器指令一一對應,就這樣組合語言出現了,它極大的提高了人的工作效率。但是計算機並不認識彙編指令,必須將它替換為對應的機器指令,計算機才能執行它,這個過程叫彙編!

***人的追求永無止境,後來出現了各種高階語言,它接近人類自然語言的表達方式,便於人理解和使用。***其中c語言脫穎而出,它不可撼動的成為了系統程式語言,眾多作業系統例如unix,linux,ios,andriod等底層全部由c語言實現。c語言雖然是一種高階語言,但是它的硬體親和性決定了適合於底層,驅動,作業系統等領域,在嵌入式領域,首選的也是c語言。計算機無法執行高階語言程式,需要通過編譯程式把它翻譯成彙編**,然後通過匯程式設計序翻譯成二進位制序列(機器語言),計算機才能執行它!

c語言封裝了很多彙編變成需要考慮的事情,比如函式傳參和呼叫棧管理

編譯就是把c/c++**(比如上述的「.i」檔案)「翻譯」成彙編**,所用到的工具為cc1(它的名字就是cc1,x86有自己的cc1命令,arm板也有自己的cc1命令)。

3、由.s檔案到.o檔案,這個過程叫彙編。

彙編過程將上一步的彙編**轉換成機器碼,這一步產生的檔案叫做目標檔案,是二進位制格式。

有兩種方式:

使用 gcc直接從源**生成目標** gcc -c .s -o .o

使用彙編器從彙編**生成目標** as *.s -o *.o

到編譯階段,**還都是人類可以讀懂的。彙編這一階段,正式將彙編**生成機器可以執行的目標**,也就是二進位製碼。

彙編就是將第二步輸出的彙編**翻譯成符合一定格式的機器**,在linux系統上一般表現為elf目標檔案(obj檔案),用到的工具為as。x86有自己的as命令,arm版也有自己的as命令,也可能是***x-as(比如arm-linux-as)。

「反彙編」是指將機器**轉換為彙編**,這在除錯程式時常常用到。

4、由.o檔案到可執行檔案,這個過程叫鏈結。

鏈結過程使用鏈結器將該目標檔案與其他目標檔案、庫檔案、啟動檔案等鏈結起來生成可執行檔案。附加的目標檔案包括靜態連線庫和動態連線庫。

-l 與 -l 鏈結器引數,就是指定鏈結時去(**)找(什麼)庫。

• -l,代表鏈結哪個庫,會自動檢索lib開頭的對應庫名。 例如-lpthread,-lqt5core。會自動檢索libpthread.so,libpthread.a,libqt5core.so,libqt5core.a

o 如果靜態庫動態庫同時存在,優先鏈結動態庫

• -l,指定去**找庫檔案。例如指定:-l/home/threedog/test,則在編譯時會優先檢索/home/threedog/test/libpthread.so等檔案。

• 鏈結庫最直接的辦法是不用任何引數,直接寫庫的路徑載入編譯引數裡。

• 查詢順序 :

o 如果直接寫的庫的全路徑,則會直接去找到庫,不走下面的順序檢索。

o -l,優先順序最高

o 然後是系統的環境變數library_path

o 最後再找內定目錄 /lib/usr/lib/usr/local/lib 這是當初編譯 gcc時寫在程式內的

o 如果都找不到,會報錯找不到檔案或找不到-l***x

編譯程式時,加上-v選項就可以看到這幾個步驟。比如:

gcc -o hello hello.c -v

可以看到很多輸出結果,我們把其中的主要資訊摘出來:

cc1 hello.c -o /tmp/cctetob7.s

as -o /tmp/ccvv2kbl.o /tmp/cctetob7.s

collect2 -o hello crt1.o crti.o crtbegin.o /tmp/ccvv2kbl.o crtend.o crtn.o

以上3個命令分別對應於編譯步驟中的預處理+編譯、彙編和鏈結,ld被collect2呼叫來鏈結程式。預處理和編譯被放在了乙個命令(cc1)中進行的,可以把它再次拆分為以下兩步:

cpp -o hello.i hello.c

cc1 hello.i -o /tmp/cctetob7.s

我們不需要手工去執行cpp、cc1、collect2等命令,我們直接執行gcc並指定不同的引數就可以了。

可以通過各種選項來控制gcc的動作,下面介紹一些常用的選項。

常用選項 描述

-e 預處理,開發過程中想快速確定某個巨集可以使用「-e -dm」

-c 把預處理、編譯、彙編都做了,但是不鏈結

-o 指定輸出檔案

-i 指定頭檔案目錄

-l 指定鏈結時庫檔案目錄

-l 指定鏈結哪乙個庫檔案

gcc 預處理(預編譯),編譯,彙編,鏈結

一,預編譯 操作步驟 gcc e hello.c o hello.i 主要作用 處理關於 的指令 刪除 define,展開所有巨集定義。例 define portnumber 3333 處理條件預編譯 if,ifdef,if,elif,endif 處理 include 預編譯指令,將包含的 h 檔案...

gcc 預處理(預編譯),編譯,彙編,鏈結

一,預編譯 操作步驟 gcc e hello.c o hello.i 主要作用 處理關於 的指令 刪除 define,展開所有巨集定義。例 define portnumber 3333 處理條件預編譯 if,ifdef,if,elif,endif 處理 include 預編譯指令,將包含的 h 檔案...

gcc預處理 編譯 彙編 鏈結詳解

讓我們來用最簡單的程式理解一下我們的編譯系統 include int main hello程式的生命週期是從乙個高階語言c語言程式開始,因為這種形式能夠人讀懂。然而,為了在系統上執行hello.c程式,每條c語句都必須被其他程式轉化為一系列的低階機器語言指令。然後這些指令按照一種稱為可執行目標程式的...