“Hello问裕,World”背后的故事

學(xué)習(xí)一門語言,經(jīng)常都是從打印“Hello孵坚,World”開始的,打過招呼后窥淆,你便可以進入程序的新世界卖宠。

就拿經(jīng)典的C語言舉例,基本上每個程序員在上學(xué)時就可以閉著眼睛寫下“Hello忧饭,World”扛伍,這也是檢測開發(fā)環(huán)境是否能正常工作常用的小程序,就像有的人看能不能上網(wǎng)就輸個百度試試(手動斜眼词裤,程序員應(yīng)該用谷歌).

//hello.c
#include <stdio.h>

int main()
{
    printf("Hello World\n");
    return 0;
}

我們使用gcc編譯并運行該文件:

$ gcc hello.c -o hello
$ ./hello

輸出結(jié)果:


helloworld

其實刺洒,輸出一行字符并沒有那么簡單,gcc幫我們處理了很多步吼砂,如果你用Visual Studio逆航,運行按鈕更是連編譯指令都不用敲了,IDE是簡化了很多步驟渔肩,但是深入探索背后的步驟是每個程序員必備的素養(yǎng)因俐,更何況很多成熟的大型項目都是需要自己構(gòu)建(Build)。

上述過程可以分為4個步驟:

  • 預(yù)處理 (Prepressing)
  • 編譯 (Compilation)
  • 匯編 (Assembly)
  • 鏈接 (Linking)
gcc編譯過程

下面我們詳述這一過程:

預(yù)處理##

預(yù)處理器cpphello.c及包含的頭文件周偎,這里就是stdio.h預(yù)編譯成為一個hello.i的文件抹剩。
我們可以用以下命令只對hello.c進行預(yù)處理:

$ gcc -E hello.c -o hello.i

或者:

$ cpp hello.c > hello.i

你沒看錯,預(yù)處理器就是cpp蓉坎,與C++擴展名.cpp沒有關(guān)系澳眷,具體可以man cpp查看手冊,其實gcc只是把預(yù)編譯器蛉艾,編譯器钳踊,匯編器,鏈接器這一系列工具集成在一起伺通,通過不同的參數(shù)去調(diào)用不同的部分或者全部調(diào)用

預(yù)處理做的工作:

  • 將所有的#define刪除箍土, 并且展開所有的宏定義,像#define MAX 1024,那么代碼文件中所有的MAX都會被1024代替罐监。
  • 處理所有的條件預(yù)編譯指令吴藻,包括#if#ifdef弓柱、#elif沟堡、#else侧但、#endif。至于這些指令到底干嘛的航罗,任何一本C語言教材都會有明確的解釋禀横。
  • 處理#include指令,將所有頭文件插入到預(yù)編譯指令的位置,這一過程是遞歸進行的粥血,也就是說丰榴,頭文件里包含的頭文件也會被插入頭文件里。良好的代碼規(guī)范都指導(dǎo)我們使用頭文件保護森渐,避免重復(fù)包含頭文件椅文。
  • 刪除所有注釋///* ··· */。注釋給人看的缔御,機器不需要看注釋抬闷。
  • 添加行號和文件名標(biāo)識,比如打開剛剛的hello.i,int main()之前插入了一句# 2 "hello.c" 2耕突,以便于編譯器產(chǎn)生調(diào)試用的行號信息笤成,這樣產(chǎn)生編譯錯誤或警告時,編譯器就會給出文件名和行號眷茁。
    -保留所有的#pragma指令炕泳,編譯器會使用它們。

經(jīng)過預(yù)處理后蔼卡,文件中所有的宏被展開喊崖,包含的文件也被插入,這時候就可以給編譯器使用雇逞。

編譯##

編譯過程是整個程序構(gòu)建的核心部分荤懂,包含了大量編譯原理的知識,注明的參考書有龍書塘砸。
編譯過程可以分為以下幾個部分节仿,每個部分深究起來都很耗費功夫,有機會可以自己實現(xiàn):

  • 詞法分析
  • 語法分析
  • 語義分析
  • 中間語言生成與優(yōu)化

現(xiàn)在版本的gcc把預(yù)處理和編譯兩個步驟合二為一掉蔬,使用一個叫cc1的程序完成這兩個步驟廊宪,在我的計算機里位于“/usr/lib/gcc/i686-linux-gnu/4.8/cc1”
我們可以通過以下命令生成編譯后的文件:

$ gcc -S hello.c -o hello.s

也可以直接使用cc1:

$ /usr/lib/gcc/i686-linux-gnu/4.8/cc1 hello.c

編譯后生成匯編文件hello.s

匯編##

匯編器就是將匯編代碼轉(zhuǎn)變成機器可以執(zhí)行的指令女轿,每一條匯編語句幾乎都對應(yīng)一條機器指令箭启。所以匯編器相對簡單,只需要一一翻譯就可以蛉迹。
我們使用匯編器as完成如上工作:

$ gcc -c hello.s -o hello.o

或者

$ as hello.s - o hello.o

也可以直接從hello.c直接得到目標(biāo)文件:

$ gcc -c hello.c -o hello.o

鏈接##

據(jù)說鏈接器的歷史比編譯器還長傅寡,像我們的“Hello,World”程序,生成的hello.o中包含了printf函數(shù)荐操,頭文件只包含了函數(shù)的申明芜抒,所以最后還需要鏈接到libc.a,其實需要鏈接的不僅僅是printf托启,我們用鏈接器ld鏈接以下這么多模塊才能生成最終的可執(zhí)行文件宅倒。

$ ld -static /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i686-linux-gnu/4.8/crtbeginT.o 
-L/usr/lib -L/lib hello.o --start-group -lgcc -lgcc_rh -lc --end-group 
/usr/lib/gcc/i686-linux-gnu/4.8/crtend.o /usr/lib/crtn.o

一個再復(fù)雜的軟件也是如此,將源代碼分別獨立編譯屯耸,再組裝起來拐迁,這個過程就叫做鏈接,鏈接的主要目的疗绣,一個是將模塊間依賴的函數(shù)調(diào)用打通唠亚,還有就是模塊間共通的變量打通。

鏈接器所做的工作主要就是“調(diào)整地址”持痰,寫匯編代碼時,有這么一句jmp foo,其實鏈接器就幫我們把foo翻譯成運行時的地址祟蚀。

鏈接的主要過程:

  • 地址和空間分配 (Address and Storage Allocation)
  • 符號決議 (Symbol Resolution)
  • 重定位 (Relocation)

舉個例子工窍,可以很清楚的解釋這個過程,我們在main.c調(diào)用了另外一個文件func.c中的函數(shù)test(),那么當(dāng)我們在main.c中每使用一次test()都必須知道test()的地址前酿,但文件都是單獨編譯的患雏,所以我們在main.c中的做法是暫時擱置test()的地址,當(dāng)鏈接的時候罢维,鏈接器會根據(jù)test符號淹仑,自動填入test()的地址,如果func.c重新編譯了肺孵,test()地址會變化匀借,但是編譯時,沒有改變的main.c并不會編譯了平窘,只是在鏈接時吓肋,會鏈接新的test()的地址。這個修正的過程也叫作重定位瑰艘。

鏈接還分為靜態(tài)鏈接動態(tài)鏈接是鬼,這個以后會專門說。

如果覺得還不錯紫新,請點個贊吧~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末均蜜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子芒率,更是在濱河造成了極大的恐慌囤耳,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異紫皇,居然都是意外死亡慰安,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門聪铺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來化焕,“玉大人,你說我怎么就攤上這事铃剔∪鼋埃” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵键兜,是天一觀的道長凤类。 經(jīng)常有香客問我,道長普气,這世上最難降的妖魔是什么谜疤? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮现诀,結(jié)果婚禮上夷磕,老公的妹妹穿的比我還像新娘。我一直安慰自己仔沿,他們只是感情好坐桩,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著封锉,像睡著了一般绵跷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上成福,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天碾局,我揣著相機與錄音,去河邊找鬼闷叉。 笑死擦俐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的握侧。 我是一名探鬼主播蚯瞧,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼品擎!你這毒婦竟也來了埋合?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤萄传,失蹤者是張志新(化名)和其女友劉穎甚颂,沒想到半個月后蜜猾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡振诬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年蹭睡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赶么。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡肩豁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辫呻,到底是詐尸還是另有隱情清钥,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布放闺,位于F島的核電站祟昭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏怖侦。R本人自食惡果不足惜篡悟,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匾寝。 院中可真熱鬧恰力,春花似錦、人聲如沸旗吁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽很钓。三九已至,卻和暖如春董栽,著一層夾襖步出監(jiān)牢的瞬間码倦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工锭碳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留袁稽,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓擒抛,卻偏偏與公主長得像推汽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子歧沪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

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