計(jì)算機(jī)工作原理(一)編譯和鏈接

對(duì)于平常的應(yīng)用程序開發(fā)夏块,我們很少需要關(guān)注編譯和鏈接過程羞福,因?yàn)橥ǔ5拈_發(fā)環(huán)境都是流行的集成開發(fā)環(huán)境(IDE)伺通,這樣的IDE一般都將編譯和鏈接的過程一步完成唆迁,通常將這種編譯和鏈接合并到一起的過程稱為構(gòu)建(Build)。

一后频、那些被隱藏了的過程

當(dāng)我們運(yùn)行一個(gè)程序的之前梳庆,通常要經(jīng)過4個(gè)步驟,分別是:

1卑惜、預(yù)處理(Prepressing)

2膏执、編譯(Compilation)

3、匯編(Assembly)

4露久、鏈接(Linking)

(一)預(yù)處理

預(yù)處理又稱預(yù)編譯更米,預(yù)編譯過程主要處理那些源代碼文件中的以"#"開始的預(yù)編譯指令。比如#include毫痕、#define等征峦,主要處理規(guī)則如下:

  • 將所有的#define刪除,并且展開所有宏定義消请。
  • 處理所有條件預(yù)編譯指令栏笆,比如#if#ifdef臊泰、#elif蛉加、#else#endif缸逃。
  • 處理#include預(yù)編譯指令针饥,將被包含的文件插入到該預(yù)編譯指令的位置。注意需频,這個(gè)過程是遞歸進(jìn)行的丁眼,也就是說被包含的文件可能還包含其他文件。
  • 刪除所有的注釋贺辰。
  • 添加行號(hào)和文件名標(biāo)識(shí)户盯,以便于編譯時(shí)編譯期產(chǎn)生調(diào)試用的行號(hào)信息及用于編譯時(shí)產(chǎn)生編譯錯(cuò)誤或警告時(shí)能夠顯示行號(hào)嵌施。
  • 保留所有的#pragma編譯器指令,因?yàn)榫幾g器須要使用它們莽鸭。

經(jīng)過預(yù)編譯后的文件(.i文件)不包含任何宏定義吗伤,因?yàn)樗械暮暌呀?jīng)被展開,并且包含的文件也已經(jīng)插入到.i文件中硫眨,所以當(dāng)我們無法判斷宏定義是否正確或頭文件包含是否正確時(shí)足淆,可以查看預(yù)編譯后的文件來確定問題。

(二)編譯

編譯過程就是把預(yù)處理完的文件進(jìn)行一系列詞法分析礁阁、語(yǔ)法分析巧号、語(yǔ)義分析及優(yōu)化后生產(chǎn)相應(yīng)的匯編代碼文件,這個(gè)過程往往是我們所說的整個(gè)程序構(gòu)建的核心部分姥闭。

(三)匯編

匯編器是將匯編代碼轉(zhuǎn)變成機(jī)器可以執(zhí)行的指令丹鸿,每一個(gè)匯編語(yǔ)句幾乎都對(duì)應(yīng)一條機(jī)器指令。所以匯編器的匯編過程相對(duì)于編譯器來說比較簡(jiǎn)單棚品,它沒有復(fù)雜的語(yǔ)法靠欢,也沒有語(yǔ)義,也不需要做指令優(yōu)化铜跑,只是根據(jù)匯編指令和機(jī)器指令的對(duì)照表一一翻譯就可以了门怪。

(四)鏈接

鏈接通常是一個(gè)讓人比較費(fèi)解的過程,為什么匯編器不直接輸出可執(zhí)行文件而是輸出一個(gè)目標(biāo)文件呢锅纺?鏈接過程到底包含了什么內(nèi)容掷空?為什么要鏈接?這是很多人的疑惑囤锉,所以我們會(huì)在本篇文章具體的分析靜態(tài)鏈接坦弟。

二、編譯器做了什么

從最直觀的角度來講嚼锄,編譯器就是將高級(jí)語(yǔ)言翻譯成機(jī)器語(yǔ)言的一個(gè)工具减拭。編譯過程一般可以分為6步:掃描、語(yǔ)法分析区丑、語(yǔ)義分析、源代碼優(yōu)化修陡、代碼生成和目標(biāo)代碼優(yōu)化沧侥。

我們將結(jié)合這個(gè)過程來簡(jiǎn)單描述從源代碼到最終目標(biāo)代碼的過程,以一段很簡(jiǎn)單的C語(yǔ)言的代碼為例子來講述這個(gè)過程魄鸦,比如我們有一行C語(yǔ)言的源代碼如下:

array[index] = (index + 4) * (2 + 6)

(一)詞法分析

首先宴杀,源代碼程序被輸入到掃描器,通過特定算法輕松地將源代碼的字符序列分割成一系列的記號(hào)拾因。比如上面的那行代碼旺罢,總共包含了28個(gè)非空字符旷余,經(jīng)過掃描以后,產(chǎn)生了16個(gè)記號(hào)扁达,比如:

記號(hào) 類型
array 標(biāo)識(shí)符
[ 左方括號(hào)
index 標(biāo)識(shí)符
] 右方括號(hào)

(二)語(yǔ)法分析

接下來的語(yǔ)法分析器將對(duì)由掃描器產(chǎn)生的記號(hào)進(jìn)行語(yǔ)法分析正卧,從而產(chǎn)生語(yǔ)法樹。簡(jiǎn)單的講跪解,由語(yǔ)法分析器生成的語(yǔ)法樹就是以表達(dá)式為結(jié)點(diǎn)的樹炉旷,我們知道。C語(yǔ)言的一個(gè)語(yǔ)句是一個(gè)表達(dá)式叉讥,而復(fù)雜的語(yǔ)句是很多表達(dá)式的組合窘行。上面例子中的語(yǔ)句就是一個(gè)由賦值表達(dá)式、加法表達(dá)式图仓、乘法表達(dá)式罐盔、數(shù)組表達(dá)式、括號(hào)表達(dá)式組成的復(fù)雜語(yǔ)句救崔。

(三)語(yǔ)義分析

(四)中間語(yǔ)言生成

現(xiàn)代的編譯期有著很多層次的優(yōu)化惶看,往往在源代碼級(jí)別會(huì)有一個(gè)優(yōu)化過程。我們這里所描述的源碼級(jí)優(yōu)化器在不同編譯器中可能會(huì)有不同的定義活有一些其他的差異帚豪。源代碼級(jí)優(yōu)化器會(huì)在源代碼級(jí)別進(jìn)行優(yōu)化碳竟,在上面那段代碼中,(2+6)這個(gè)表達(dá)式可以被優(yōu)化掉狸臣,因?yàn)樗闹翟诰幾g期就可以被確定莹桅。

(五)目標(biāo)代碼生成與優(yōu)化

源代碼優(yōu)化器產(chǎn)生中間代碼標(biāo)志著下面的過程都屬于編譯器后端。編譯器后端主要包括代碼生成器和目標(biāo)代碼優(yōu)化器烛亦。

代碼生成器:將中間代碼轉(zhuǎn)換成目標(biāo)機(jī)器代碼诈泼。

目標(biāo)代碼優(yōu)化器:對(duì)上述的目標(biāo)代碼進(jìn)行優(yōu)化,比如選擇合適的尋址方式煤禽、使用位移來代替乘法運(yùn)算铐达、刪除多余的指令等。

經(jīng)過這些掃描檬果、語(yǔ)法分析瓮孙、語(yǔ)義分析、源代碼優(yōu)化选脊、代碼生成和目標(biāo)代碼優(yōu)化杭抠,編譯器忙活了這么多個(gè)步驟以后,源代碼終于被編譯成了目標(biāo)代碼恳啥,但是這個(gè)目標(biāo)代碼中有一個(gè)問題是:index和array的地址還沒有確定偏灿。如果我們要把目標(biāo)代碼使用匯編器編譯成真正能夠在機(jī)器上執(zhí)行的指令,那么index和array的地址應(yīng)該從哪得到呢钝的?如果index和array定義在跟上面的源代碼同一個(gè)編譯單元里面翁垂,那么編譯器可以為index和array分配空間铆遭,確定他們的地址,那如果是定義在其他的程序模塊呢沿猜?

這個(gè)問題就涉及到了鏈接的過程枚荣。

三、鏈接器年齡比編譯期長(zhǎng)

我們都知道邢疙,上古時(shí)期沒有高級(jí)語(yǔ)言棍弄,用的都是機(jī)器語(yǔ)言,甚至連匯編語(yǔ)言都沒有疟游,當(dāng)程序需要被運(yùn)行時(shí)呼畸,程序員人工將他所寫的程序?qū)懭氲酱鎯?chǔ)設(shè)備上,最原始的存儲(chǔ)設(shè)備就是紙帶颁虐,即在紙帶上打孔蛮原。

假設(shè)有一種計(jì)算機(jī),它的每條指令是1個(gè)字節(jié)另绩,也就是8位儒陨,我們假設(shè)有一種跳轉(zhuǎn)指令,它的高4位是0001笋籽,表示這是一條跳轉(zhuǎn)指令蹦漠,低4位存放的是跳轉(zhuǎn)目的地的絕對(duì)地址。當(dāng)程序修改的時(shí)候笛园,這些位置都要重新計(jì)算,絕對(duì)地址都會(huì)改變侍芝,重新計(jì)算的過程十分繁瑣又耗時(shí)研铆,并且很容易出錯(cuò),這種重新計(jì)算各個(gè)目標(biāo)的地址過程被叫做重定位州叠。

四棵红、模塊拼裝 - 靜態(tài)鏈接

程序設(shè)計(jì)的模塊化是人們一直在追求的目標(biāo),因?yàn)楫?dāng)一個(gè)系統(tǒng)十分復(fù)雜的時(shí)候咧栗,我們不得不將一個(gè)復(fù)雜的系統(tǒng)逐步分割成小的系統(tǒng)以達(dá)到各個(gè)突破的目的逆甜。一個(gè)復(fù)雜的軟件也如此。人們把每個(gè)源代碼模塊獨(dú)立地編譯致板,然后按照須要將他們“組裝”起來忆绰,這個(gè)組裝模塊的過程就是鏈接。鏈接的主要內(nèi)容就是把各個(gè)模塊之間相互引用的部分都處理好可岂,使得各個(gè)模塊之間能夠正確的銜接。鏈接器所要做的工作其實(shí)跟前面所描述的“程序員人工調(diào)整地址”本質(zhì)上沒什么兩樣翰灾,只不過現(xiàn)代的高級(jí)語(yǔ)言的諸多特性和功能缕粹,使得編譯器稚茅、鏈接器更為復(fù)雜,功能更為強(qiáng)大平斩,但從原理上講亚享,它的工作無非就是把一些指令對(duì)其他符號(hào)地址的引用加以修正。鏈接過程主要包括了地址和空間分配绘面、符號(hào)決議和重定位等這些步驟欺税。

對(duì)于最基本的靜態(tài)鏈接過程,每個(gè)模塊的源代碼文件經(jīng)過編譯器編譯成目標(biāo)文件(Object File揭璃,一般擴(kuò)展名為.o活.obj)晚凿,目標(biāo)文件和庫(kù)(Library)一起鏈接形成最終可執(zhí)行文件。而最常見的庫(kù)就是運(yùn)行時(shí)庫(kù)(Runtime Library)瘦馍,它是支持程序運(yùn)行的基本函數(shù)的集合歼秽。庫(kù)其實(shí)是一組目標(biāo)文件的包,就是一些最常用的代碼編譯成目標(biāo)文件后打包存放情组。

比如我們?cè)诔绦蚰Kmain.c中使用另外一個(gè)func.c中的函數(shù)foo()燥筷,我們?cè)趍ain.c模塊中每一處調(diào)用foo的時(shí)候都必須確切知道foo這個(gè)函數(shù)的地址,但是由于每個(gè)模塊都是單獨(dú)編譯的院崇,在編譯器編譯main.c的時(shí)候它并不知道foo函數(shù)的地址肆氓,所以它暫時(shí)把這些調(diào)用foo的指令的目標(biāo)地址擱置,等待最后鏈接的時(shí)候由鏈接器去將這些指令的目標(biāo)地址修正底瓣,這個(gè)地址修正的過程也被叫做重定位谢揪,每個(gè)要被修正的地方叫一個(gè)重定位入口,重定位所做的就是給程序中每個(gè)這樣的絕對(duì)地址引用的位置“打補(bǔ)丁”濒持,使他們指向正確的地址键耕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市柑营,隨后出現(xiàn)的幾起案子屈雄,更是在濱河造成了極大的恐慌,老刑警劉巖官套,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酒奶,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡奶赔,警方通過查閱死者的電腦和手機(jī)惋嚎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來站刑,“玉大人另伍,你說我怎么就攤上這事。” “怎么了摆尝?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵温艇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我堕汞,道長(zhǎng)勺爱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任讯检,我火速辦了婚禮琐鲁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘人灼。我一直安慰自己围段,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布挡毅。 她就那樣靜靜地躺著蒜撮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跪呈。 梳的紋絲不亂的頭發(fā)上段磨,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音耗绿,去河邊找鬼苹支。 笑死,一個(gè)胖子當(dāng)著我的面吹牛误阻,可吹牛的內(nèi)容都是我干的债蜜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼究反,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼寻定!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起精耐,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤狼速,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后卦停,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體向胡,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年惊完,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了僵芹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡小槐,死狀恐怖拇派,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤攀痊,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布桐腌,位于F島的核電站,受9級(jí)特大地震影響苟径,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜躬审,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一棘街、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧承边,春花似錦遭殉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至富岳,卻和暖如春蛔糯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背窖式。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工蚁飒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萝喘。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓淮逻,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親阁簸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子爬早,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容