久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

編譯和鏈接那點(diǎn)事<上> | 淺墨的部落格

 astrotycoon 2018-06-05

有位學(xué)弟想讓我說說編譯和鏈接的簡單過程,,我覺得幾句話簡單說的話也沒什么意思,索性寫篇博文稍微詳細(xì)的解釋一下吧,。其實(shí)詳細(xì)的流程在經(jīng)典的《Linkers and Loaders》和《深入理解計(jì)算機(jī)系統(tǒng)》中均有描述,,也有國產(chǎn)的諸如《程序員的自我修養(yǎng)——鏈接、裝載與庫》等大牛著作,。不過,,我想大家恐怕很難有足夠的時(shí)間去研讀這些厚如詞典的書籍。正巧我大致翻閱過其中的部分章節(jié),,干脆也融入這篇文章作為補(bǔ)充吧,。

我的環(huán)境:Fedora 16 i686 kernel-3.6.11-4 gcc 4.6.3

其實(shí)MSVC的編譯器在編譯過程中的流程是差不多的,只是具體調(diào)用的程序和使用的參數(shù)不同罷了,。不過為了描述的流暢性,,我在行文中不會(huì)涉及MSVC的具體操作,使用Windows的同學(xué)可以自行搜索相關(guān)指令和參數(shù),。但是作為Linuxer,,我還是歡迎大家使用Linux系統(tǒng)。如果大家確實(shí)需要,,我會(huì)擠時(shí)間在附言中給出MSVC中相對(duì)應(yīng)的試驗(yàn)方法,。

閑話不多說了,我們進(jìn)入正題,。在正式開始我們的描述前,,我們先來引出幾個(gè)問題:

  1. C語言代碼為什么要編譯后才能執(zhí)行?整個(gè)過程中編譯器都做了什么,?
  2. C代碼中經(jīng)常會(huì)包含頭文件,,那頭文件是什么?C語言庫又是什么,?
  3. 有人說main函數(shù)是C語言程序的入口,,是這樣嗎?難道就不能把其它函數(shù)當(dāng)入口,?
  4. 不同的操作系統(tǒng)上編譯好的程序可以直接拷貝過去運(yùn)行嗎,?

如果上面的問題你都能回答的話,那么后文就不用再看下去了,。因?yàn)楸疚氖羌兇獾拿嫦蛐率?,所以注定了不?huì)寫的多么詳細(xì)和深刻。如果你不知道或者不是很清楚,,那么我們就一起繼續(xù)研究吧,。

我們就以最經(jīng)典的HelloWorld程序?yàn)槔_始吧,。我們先使用vim等文本編輯器寫好代碼,,接著在終端執(zhí)行命令 gcc HelloWorld.c -o HelloWorld 輸出了可執(zhí)行文件HelloWorld,,最后我們?cè)诮K端執(zhí)行 ./HelloWorld,順利地顯示了輸出結(jié)果,。

可是,,簡單的命令背后經(jīng)過了什么樣的處理過程呢?gcc真的就“直接”生成了最后的可執(zhí)行文件了嗎,?當(dāng)然不是,,我們?cè)趃cc編譯命令行加上參數(shù) –verbose要求gcc輸出完整的處理過程(命令行加上 -v 也行),我們看到了一段較長的過程輸出,。

輸出結(jié)果我們就不完整截圖了,,大家有興趣可以自己試驗(yàn)然后試著分析整個(gè)流程。

一圖勝千言,,我們先上一張圖吧,。這是gcc編譯過程的分解圖,我在網(wǎng)上找不到滿意的,,就自己畫了一張簡單的,,大家將就著看吧。

從圖中我們大致可以看出gcc處理HelloWorld.c的大致過程:

預(yù)處理(Prepressing)—>編譯(Compilation)—>匯編(Assembly)—>鏈接(Linking)

括號(hào)中我注明了各個(gè)過程中實(shí)際執(zhí)行任務(wù)的程序名稱:預(yù)處理器cpp,、編譯器cc1,、匯編器as以及最后的鏈接器ld。

我們一步一步來看,,首先是預(yù)處理,,我們看看預(yù)處理階段對(duì)代碼進(jìn)行了哪些處理。

我們?cè)诮K端輸入指令 gcc -E HelloWorld.c -o HelloWorld.i,,然后我們打開輸出文件,。

首先是大段大段的變量和函數(shù)的聲明,汗..我們的代碼哪里去了,?我們?cè)趘im的普通模式中按下shift+g(大寫G)來到最后,,終于在幾千行以后看到了我們可憐兮兮的幾行代碼。

前面幾千行是什么呢,?其實(shí)它就是 /usr/include/stdio.h 文件的所有內(nèi)容,,預(yù)處理器把所有的#include替換為實(shí)際文件的內(nèi)容了。這個(gè)過程是遞歸進(jìn)行的,,所以stdio.h里面的#include也被實(shí)際內(nèi)容所替換了,。

而且我在HelloWorld.c里面的所有注釋被預(yù)處理器全部刪除了。就連printf語句前的Tab縮進(jìn)也被替換為一個(gè)空格了,,顯得代碼都不美觀了,。

時(shí)間關(guān)系,,我們就不一一試驗(yàn)處理的內(nèi)容了,我直接給出預(yù)處理器處理的大致范圍吧,。

  • 展開所有的宏定義并刪除 #define
  • 處理所有的條件編譯指令,,例如 #if #else #endif #ifndef …
  • 把所有的 #include 替換為頭文件實(shí)際內(nèi)容,遞歸進(jìn)行
  • 把所有的注釋 // 和 / / 替換為空格
  • 添加行號(hào)和文件名標(biāo)識(shí)以供編譯器使用
  • 保留所有的 #pragma 指令,,因?yàn)榫幾g器要使用
    ……

基本上就是這些了,。在這里我順便插播一個(gè)小技巧,在代碼中有時(shí)候宏定義比較復(fù)雜的時(shí)候我們很難判斷其處理后的結(jié)構(gòu)是否正確,。這個(gè)時(shí)候我們呢就可以使用gcc的-E參數(shù)輸出處理結(jié)果來判斷了,。

前文中我們提到了頭文件中放置的是變量定義和函數(shù)聲明等等內(nèi)容。這些到底是什么東西呢,?其實(shí)在比較早的時(shí)候調(diào)用函數(shù)并不需要聲明,,后來因?yàn)椤肮P誤”之類的錯(cuò)誤實(shí)在太多,造成了鏈接期間的錯(cuò)誤過多,,所有編譯器開始要求對(duì)所有使用的變量或者函數(shù)給出聲明,,以支持編譯器進(jìn)行參數(shù)檢查和類型匹配。頭文件包含的基本上就是這些東西和一些預(yù)先的宏定義來方便程序員編程,。其實(shí)對(duì)于我們的HelloWorld.c程序來說不需要這個(gè)龐大的頭文件,,只需要在main函數(shù)前聲明printf函數(shù),不需要#include即可通過編譯,。

聲明如下:

1
int printf(const char *format, ...);

這個(gè)大家就自行測(cè)試吧,。另外再補(bǔ)充一點(diǎn),gcc其實(shí)并不要求函數(shù)一定要在被調(diào)用之前定義或者聲明(MSVC不允許),,因?yàn)間cc在處理到某個(gè)未知類型的函數(shù)時(shí),,會(huì)為其創(chuàng)建一個(gè)隱式聲明,并假設(shè)該函數(shù)返回值類型為int,。但gcc此時(shí)無法檢查傳遞給該函數(shù)的實(shí)參類型和個(gè)數(shù)是否正確,,不利于編譯器為我們排除錯(cuò)誤(而且如果該函數(shù)的返回值不是int的話也會(huì)出錯(cuò))。所以還是建議大家在函數(shù)調(diào)用前,,先對(duì)其定義或聲明,。

預(yù)處理部分說完了,我們接著看編譯和匯編,。那么什么是編譯,?一句話描述:編譯就是把預(yù)處理之后的文件進(jìn)行一系列詞法分析、語法分析,、語義分析以及優(yōu)化后生成的相應(yīng)匯編代碼文件,。這一部分我們不能展開說了,一來我沒有系統(tǒng)學(xué)習(xí)過編譯原理的內(nèi)容不敢信口開河,二來這部分要是展開去說需要很厚很厚的一本書了,,細(xì)節(jié)大家就自己學(xué)習(xí)《編譯原理》吧,,相關(guān)的資料自然就是經(jīng)典的龍書、虎書和鯨書了,。

gcc怎么查看編譯后的匯編代碼呢,?命令是 gcc -S HelloWorld.c -o HelloWorld.s,這樣輸出了匯編代碼文件HelloWorld.s,,其實(shí)輸出的文件名可以隨意,,我是習(xí)慣使然。順便說一句,,這里生成的匯編是AT&T風(fēng)格的匯編代碼,如果大家更熟悉Intel風(fēng)格,,可以在命令行加上參數(shù) -masm=intel ,這樣gcc就會(huì)生成Intel風(fēng)格的匯編代碼了(如圖,,這個(gè)好多人不知道哦)。不過gcc的內(nèi)聯(lián)匯編只支持AT&T風(fēng)格,,大家還是找找資料學(xué)學(xué)AT&T風(fēng)格吧,。

再下來是匯編步驟,我們繼續(xù)用一句話來描述:匯編就是將編譯后的匯編代碼翻譯為機(jī)器碼,,幾乎每一條匯編指令對(duì)應(yīng)一句機(jī)器碼,。

這里其實(shí)也沒有什么好說的了,命令行 gcc -c HelloWorld.c 可以讓編譯器只進(jìn)行到生成目標(biāo)文件這一步,,這樣我們就能在目錄下看到HelloWorld.o文件了,。

Linux下的可執(zhí)行文件以及目標(biāo)文件的格式叫作ELF(Executable Linkable Format)。其實(shí)Windows下的PE(Portable Executable)也好,,ELF也罷,,都是COFF(Common file format)格式的一種變種,甚至Windows下的目標(biāo)文件就是以COFF格式去存儲(chǔ)的,。不同的操作系統(tǒng)之間的可執(zhí)行文件的格式通常是不一樣的,,所以造成了編譯好的HelloWorld沒有辦法直接復(fù)制執(zhí)行,而需要在相關(guān)平臺(tái)上重新編譯,。當(dāng)然了,,不能運(yùn)行的原因自然不是這一點(diǎn)點(diǎn),不同的操作系統(tǒng)接口(windows API和Linux的System Call)以及相關(guān)的類庫不同也是原因之一,。

由于本文的讀者定位,,我們不能詳細(xì)展開說了,有相關(guān)需求的同學(xué)可以去看《Windows PE權(quán)威指南》和《程序員的自我修養(yǎng)》去詳細(xì)了解,。

我們接下來看最后的鏈接過程,。這一步是將匯編產(chǎn)生的目標(biāo)文件和所使用的庫函數(shù)的目標(biāo)文件鏈接生成一個(gè)可執(zhí)行文件的過程。我想在這里稍微的擴(kuò)展一下篇幅,,稍微詳細(xì)的說一說鏈接,,一來這里造成的錯(cuò)誤通常難以理解和處理,,二來使用第三方庫在開發(fā)中越來越常見了,想著大家可能更需要稍微了解一些細(xì)節(jié)了,。

我們先介紹gnu binutils工具包,,這是一整套的二進(jìn)制分析處理工具包。詳細(xì)介紹請(qǐng)大家參考喂雞百科:http://zh./wiki/GNU_Binutils

我的fedora已經(jīng)自帶了這套工具包,,如果你的發(fā)行版沒有,,請(qǐng)自行搜索安裝方法。

這套工具包含了足夠多的工具,,我們甚至可以用來研究ELF文件的格式等內(nèi)容,。不過本文只是拋磚引玉,更多的使用方法和技巧還是需要大家自己去學(xué)習(xí)和研究,。

由于時(shí)間關(guān)系,,上篇到此就告一段落了,我們的問題2和3還沒有給出完整的答案,,而且鏈接還沒有詳細(xì)去解釋和說明,。這些內(nèi)容我們將在下篇中解決,當(dāng)然,,大家也可以先行研究,,到時(shí)候我們相互學(xué)習(xí)補(bǔ)充。

另外,,上文部分內(nèi)容因?yàn)榭紤]到讀者基礎(chǔ),,所以行文力求簡明易懂,部分描述并不嚴(yán)密且有部分刻意的簡化和保留,。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多