關(guān)于編譯代碼,你應(yīng)該知道的
源代碼必須要經(jīng)過(guò)編譯才能夠運(yùn)行程序,而對(duì)于開(kāi)源軟件,每個(gè)人都可以獲取源代碼。無(wú)論你是自己編寫(xiě)了代碼,想要編譯和運(yùn)行它,還是下載了某人的項(xiàng)目來(lái)嘗試它,了解如何通過(guò) ??編譯器?? 處理源代碼,以及編譯器如何處理這些代碼,這都很有用。
創(chuàng)建一個(gè)更好的捕鼠器
一般情況我們不會(huì)將一個(gè)捕鼠器比作電腦,但不管你信不信,它確實(shí)與你正在使用的設(shè)備(手機(jī)或電腦)的 CPU 有一些相似之處。經(jīng)典的捕鼠器(我說(shuō)的不是 ??)有兩種狀態(tài):打開(kāi)或者釋放。你可以認(rèn)為 打開(kāi) 是將捕鼠器設(shè)置好準(zhǔn)備捕獲老鼠,以及 釋放 是捕鼠器被老鼠觸發(fā)。某種意義上來(lái)說(shuō),捕鼠器就像是一臺(tái)有鼠標(biāo)的電腦。你可以想象一下這個(gè)代碼,用一種虛構(gòu)的語(yǔ)言來(lái)描述這個(gè)過(guò)程:
換句話說(shuō),你可以基于捕鼠器的狀態(tài)發(fā)現(xiàn)是否有老鼠(數(shù)據(jù))。當(dāng)然,捕鼠器不是萬(wàn)無(wú)一失的,有可能有一只老鼠在捕鼠器旁邊,由于老鼠還沒(méi)有觸發(fā)捕鼠器,所以它的狀態(tài)還是 打開(kāi) 的。因此該程序可以進(jìn)行改進(jìn),這都是非常典型的。
開(kāi)關(guān)
總的來(lái)說(shuō),捕鼠器就是一個(gè)開(kāi)關(guān)。你會(huì)在家里使用開(kāi)關(guān)打開(kāi)燈??梢詮拈_(kāi)關(guān)中獲得許多信息。比如,人們會(huì)從你家燈的狀態(tài)了解到你是否在家。
你可以根據(jù)鄰居家燈的狀態(tài)來(lái)改變行為。如果鄰居家所有的燈都熄滅了,那么請(qǐng)關(guān)掉你大聲的音樂(lè),因?yàn)槿藗兛赡芤呀?jīng)上床睡覺(jué)了。
CPU 也使用這樣的邏輯,只不過(guò)乘以幾個(gè)數(shù)量級(jí),縮小到了微觀級(jí)別。當(dāng) CPU 在特定寄存器上接收到電信號(hào)時(shí),可以觸發(fā)其他一些寄存器,然后觸發(fā)另一個(gè),以此類(lèi)推。如果這些寄存器有特定的意義,那么就可以通信。也許激活同一主板上某處的芯片,或者使 LED 亮起,或者改變屏幕上的像素顏色。
種瓜得瓜,種豆得豆。如果你真的想在多個(gè)位置而不是僅限于一處發(fā)現(xiàn)老鼠,但是你只有一個(gè)捕鼠器,那你應(yīng)該開(kāi)發(fā)一個(gè)應(yīng)用才行。使用網(wǎng)絡(luò)攝像頭和一些基本的圖像識(shí)別軟件,你可以建立空廚房的模型,然后掃描變化。當(dāng)老鼠進(jìn)入廚房,在原先沒(méi)有老鼠的圖像上會(huì)有像素的變化。記錄下這些數(shù)據(jù),如果有無(wú)人機(jī)可以追蹤老鼠并捕獲會(huì)更好,這樣就可以將老鼠趕出廚房了。這時(shí),你通過(guò)打開(kāi)和關(guān)閉信號(hào)的魔法,創(chuàng)造了一個(gè)更好的捕鼠器。
編譯器
代碼編譯器將人們可閱讀的代碼轉(zhuǎn)換成 CPU 可以理解的機(jī)器語(yǔ)言。這是非常復(fù)雜的過(guò)程,因?yàn)?CPU 非常復(fù)雜(甚至比捕鼠器更加復(fù)雜),同時(shí)因?yàn)樵撨^(guò)程比嚴(yán)格“需要”的更加靈活。并不是所有的編譯器都很靈活。有一些編譯器只有一個(gè)目標(biāo),它們只會(huì)處理特定格式的代碼文件,處理過(guò)程也因此而簡(jiǎn)單明了。
幸運(yùn)的是,現(xiàn)代的通用編譯器并不簡(jiǎn)單。它們?cè)试S你編寫(xiě)不同語(yǔ)言的代碼,也允許你用不同的方式鏈接庫(kù)文件,并且可以生成運(yùn)行在不同架構(gòu)上的文件。??GNU 編譯器集合???(GCC)的 ??gcc?
?? 編譯器 ??--help?
?? 會(huì)輸出超過(guò) 50 行的選項(xiàng),LLVM 的 ??clang?
?? 編譯器的 ??--help?
? 輸出超過(guò) 1000 行。GCC 指導(dǎo)手冊(cè)的字?jǐn)?shù)超過(guò) 10 萬(wàn)。
當(dāng)你在編譯代碼時(shí)會(huì)有很多選項(xiàng)。
當(dāng)然,大多數(shù)人并不需要知道所有的選項(xiàng)。我從未讀過(guò) GCC 的手冊(cè)頁(yè),因?yàn)樗鼈兪轻槍?duì) Objective-C、Fortran 以及我從未聽(tīng)說(shuō)過(guò)的芯片架構(gòu)的。不過(guò)我重視它將代碼編譯為不同的架構(gòu) —— 64 位或者 32 位 —— 的能力,以及在其他行業(yè)已經(jīng)落后的計(jì)算機(jī)上運(yùn)行開(kāi)源軟件的能力。
編譯生命周期
同樣重要的是,理解編譯代碼的不同階段。這是一個(gè)簡(jiǎn)單的 C 語(yǔ)言程序的生命周期:
- 帶有宏定義的 C 源代碼?
?.c?
?? 文件,用??cpp?
?? 預(yù)處理為??.i?
? 文件。 - 擴(kuò)展了宏定義的 C 源代碼?
?.i?
?? 文件,會(huì)被??gcc?
?? 轉(zhuǎn)譯成??.s?
? 文件。 - 以匯編語(yǔ)言寫(xiě)的文本文件?
?.s?
?? 文件被匯編為目標(biāo)??.o?
? 文件。 - 帶有 CPU 指令的二進(jìn)制目標(biāo)代碼,以及其他目標(biāo)文件和庫(kù)?
?*.o?
?? 文件,以?xún)?nèi)存區(qū)域無(wú)關(guān)的偏移量,使用??ld?
? 鏈接以生成可執(zhí)行文件。 - 最終的二進(jìn)制文件要么包含所有需要的目標(biāo),要么設(shè)置以動(dòng)態(tài)鏈接庫(kù)?
?*.so?
? 文件加載。
你可以試試這個(gè)簡(jiǎn)單示例(可能需要對(duì)庫(kù)路徑做一些調(diào)整):
可獲得的知識(shí)
計(jì)算機(jī)已經(jīng)變得非常強(qiáng)大,并且用戶(hù)友好。請(qǐng)不要走向這兩種可能的極端中的任何一種:計(jì)算機(jī)不像捕鼠器和電燈開(kāi)關(guān)那么簡(jiǎn)單,但它們也不是無(wú)法理解的。你可以了解編譯代碼、如何鏈接以及針對(duì)不同架構(gòu)進(jìn)行編譯。一旦你知道了,你就可以更好地調(diào)試代碼。你可以理解你下載的代碼,甚至可以修復(fù)其中的一兩個(gè)錯(cuò)誤。同時(shí)從理論上來(lái)講,你可以建造一個(gè)更好的捕鼠器,或者用捕鼠器造一個(gè) CPU。由你決定。