gcc a.c 究竟經(jīng)歷了什么?
掃描二維碼
隨時(shí)隨地手機(jī)看文章
你知道一次gcc命令究竟經(jīng)歷了什么嗎?
我們先來(lái)看一段C語(yǔ)言示例源代碼:
// test.cc
int main() {
printf("Hello 程序喵\n");
return 0;
}
編譯運(yùn)行
gcc test.cc
./a.out
Hello 程序喵
如圖一,
gcc構(gòu)建過(guò)程分解
我們平時(shí)都會(huì)使用gcc來(lái)編譯程序,這一行簡(jiǎn)單的命令其實(shí)經(jīng)歷了很多復(fù)雜的過(guò)程:
預(yù)處理
編譯
匯編
鏈接
首先使用file看一下test.cc文件類型:
$ file test.cc
test.cc: C source, UTF-8 Unicode text, with CRLF line terminators
我們接下來(lái)看看這每個(gè)過(guò)程都做了什么?
預(yù)處理
命令:
gcc -E test.cc -o test.i
或者
cpp test.cc -o test.i
再看下test.i的文件類型
$ file test.i
test.i: C source, UTF-8 Unicode text
這里可以看出預(yù)處理后的文件和預(yù)處理前的文件類型是相同的,都是文本文件,也可以直接查看test.i的內(nèi)容,里面代碼較多,就不貼上來(lái)了。
其實(shí)預(yù)處理主要操作有這幾個(gè):
展開(kāi)所有#define宏定義,進(jìn)行文本替換
刪除程序中所有的注釋
處理所有的條件編譯,#if、#ifdef、#elif等
處理所有的#include指令,把這些頭文件的內(nèi)容都復(fù)制到引用的源文件中
添加行號(hào)和文件名標(biāo)識(shí),方便編譯器產(chǎn)生警告及調(diào)試信息
保留所有的#pragma編譯器指令,因?yàn)榫幾g器會(huì)使用他們
編譯
命令:
gcc -S test.cc -o test.s
再查看文件類型
$ file test.s
test.s: assembler source, ASCII text
編譯過(guò)程分解
如圖二,編譯過(guò)程就是把預(yù)處理后的文件進(jìn)行一系列操作生成相應(yīng)的匯編文件:
詞法分析:又稱詞法掃描,通過(guò)掃描器,利用有限狀態(tài)機(jī)的算法將源碼中的字符串分割成一系列記號(hào),如加減乘除數(shù)字括號(hào)等。
語(yǔ)法分析:使用語(yǔ)法分析器對(duì)詞法分析產(chǎn)生的記號(hào)運(yùn)用上下文無(wú)關(guān)語(yǔ)法的手段進(jìn)行語(yǔ)法分析,產(chǎn)生語(yǔ)法分析樹(shù)。這期間如果表達(dá)式不合法(括號(hào)不匹配等),就會(huì)報(bào)錯(cuò)。
語(yǔ)義分析:語(yǔ)法分析檢查表達(dá)式是否合法,語(yǔ)義分析檢查表達(dá)式是否有意義,如浮點(diǎn)型整數(shù)賦值給指針,編譯器就會(huì)報(bào)錯(cuò)。
中間語(yǔ)言生成:做一些語(yǔ)法樹(shù)優(yōu)化,如6+2=8。
目標(biāo)代碼生成及優(yōu)化:將中間代碼生成目標(biāo)匯編代碼。
匯編
命令:
gcc -c test.s -o test.o
或
as test.s -o test.o
查看文件類型:
$ file test.o
testt.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
使用匯編器將匯編代碼轉(zhuǎn)成機(jī)器可以執(zhí)行的指令,其實(shí)就是將匯編指令和機(jī)器指令按照對(duì)照表一一翻譯。
鏈接
為什么匯編器不直接生成可執(zhí)行文件而是生成一個(gè)目標(biāo)文件呢,因?yàn)橐粋€(gè)文件需要依賴其它好多個(gè)庫(kù),這些庫(kù)的符號(hào)需要通過(guò)鏈接過(guò)程才可以互相配合生成一個(gè)可執(zhí)行文件,需要經(jīng)歷地址和空間分配、符號(hào)決議、重定位等步驟,這塊內(nèi)容較多,后續(xù)會(huì)詳細(xì)介紹,現(xiàn)在我們可以簡(jiǎn)單的通過(guò)ldd查看一下可執(zhí)行程序需要依賴的庫(kù),這些庫(kù)都需要在鏈接過(guò)程中被鏈接才可以使用。
$ ldd a.out
linux-vdso.so.1 (0x00007ffff5b4a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa1fc660000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa1fce00000)
參考
程序員的自我修養(yǎng)——鏈接、裝載與庫(kù)
c++11新特性,所有知識(shí)點(diǎn)都在這了!
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!