開發(fā)者應(yīng)知道的編譯原理和語言基礎(chǔ)知識

一把跨、以 Hello World開篇

Hello World對程序員而言肯定是如雷貫耳。但是簡單的事物背后往往包含這個復(fù)雜的機(jī)制字逗,如果深入思考Hello world就會發(fā)現(xiàn)很多問題。C語言中的Hello World往往是這樣寫的:

#include <stdio.h>
int main(){
    printf("Hello World");
    return 0;
}

但是你是否想過以下問題:
1、程序?yàn)槭裁匆痪幾g之后才能運(yùn)行躏吊?
2、編譯器在把C語言程序轉(zhuǎn)換成可以執(zhí)行的機(jī)器碼的過程中做了什么帐萎?
3比伏、最后編譯出來的可執(zhí)行文件里面是什么?除了機(jī)器碼還有什么疆导?如何存放的赁项?
4、#include <stdio.h>的包含意味著什么澈段?又是如何實(shí)現(xiàn)的悠菜?
5、什么是編譯器败富,它以什么為分界線分為前端和后端悔醋?編譯器和解釋器有什么區(qū)別,為什么會有解釋型語言一說兽叮?
6芬骄、以及由此延伸出的一些相關(guān)問題:Swift 是靜態(tài)語言,為什么還有運(yùn)行時庫鹦聪?OC中的Runtime和運(yùn)行時庫是什么關(guān)系账阻?
7、什么是ABI 椎麦?ABI穩(wěn)定對一門語言的發(fā)展有何影響 宰僧?為什么 Swift 打包的 App 會平白無故的多出幾Mb ?
8观挎、........
等等琴儿,還有很多問題,這些問題實(shí)際上和編譯都脫離不了干系嘁捷。讀完本篇文章造成,你的這些疑惑都能得到解答。除此之外雄嚣,你還將掌握一些主流語言的基本知識晒屎。另外喘蟆,繼該篇文章之后,筆者打算后期再寫一篇文章動手試試LLVM鼓鲁。歡迎關(guān)注蕴轨。。骇吭。橙弱。。燥狰。

二棘脐、編譯總過程預(yù)覽

相信讀者對編譯的整個流程組成部分應(yīng)該相對比較熟悉。整個流程包括預(yù)處理(Prepressing)龙致、編譯(Compilation)蛀缝、匯編(Assembly)和鏈接(Linking)。

GCC編譯hello world程序過程分解

預(yù)編譯

首先是源代碼文件hello.c和相關(guān)頭文件目代,如 stdio.h 被編譯器 cpp預(yù)編譯到一個 .i 文件屈梁。預(yù)編譯過程主要是處理那些源代碼文件中以 # 開始的預(yù)編譯指令。比如#include 榛了、#include 等俘闯。經(jīng)預(yù)編譯后的 .i 文件不包含任何宏定義,因?yàn)樗械暮暌呀?jīng)被展開忽冻,并且包含的文件已經(jīng)被插入到 .i 文件中。

編譯

編譯過程就是把預(yù)處理的文件經(jīng)過一系列的詞法分析此疹、語法分析僧诚、語義分析生成中間代碼蝗碎、生成目標(biāo)代碼 優(yōu)化后生產(chǎn)相應(yīng)的匯編文件代碼湖笨。

編譯器以中間代碼為界限,又可以分前端和后端蹦骑。比如 clang 就是一個前端工具慈省,而 LLVM 則負(fù)責(zé)后端處理。另一個知名工具 GCC(GNU Compile Collection)則是一個套裝眠菇,包攬了前后端的所有任務(wù)边败。

前端主要負(fù)責(zé)預(yù)處理、詞法分析捎废、語法分析笑窜,最終生成語言無關(guān)的中間代碼。后端主要負(fù)責(zé)目標(biāo)代碼的生成和優(yōu)化登疗。后面我會重點(diǎn)介紹編譯的整個過程的每一步排截。這里暫時簡單提一下嫌蚤。

匯編

匯編器將匯編代碼轉(zhuǎn)變成機(jī)器可以執(zhí)行的指令,每一個匯編語句幾乎都對應(yīng)一條機(jī)器指令断傲。所以匯編起的過程相對于編譯器而言是比較簡單的脱吱。因?yàn)闆]有復(fù)雜的語法,也沒有予以认罩,所以就不需要做指令優(yōu)化箱蝠,只是根據(jù)匯編指令和機(jī)器指令的對照便一一翻譯就可以了。到這一步猜年,經(jīng)過預(yù)編譯抡锈、編譯和匯編就可直接輸出目標(biāo)文件

鏈接

在一個目標(biāo)文件中乔外,不可能所有變量和函數(shù)都定義在同一個文件內(nèi)部床三。不同文件之間要做相應(yīng)的鏈接處理。

三杨幼、編譯器做了些什么

最直白的來說撇簿,編譯器就是將高級語言翻譯成機(jī)器語言的一個工具。先來看一下編譯器的整個流程差购。從該流程圖我們可以看到編譯器被分為前端和后端四瘫,在前端和后端之間的過度是中間代碼。其中編譯器前端包含詞法分析欲逃、語法分析找蜜、語義分析中間代碼生成(嚴(yán)格意義來說在此四個步驟之前還有預(yù)編譯操作)稳析;編譯器后端主要是代碼優(yōu)化洗做、目標(biāo)代碼生成以及目標(biāo)代碼優(yōu)化,編譯器的大致自責(zé)就是如此彰居。編譯器在整個編譯過程中輸入源是源代碼诚纸,輸出的是中間代碼。

3.1 詞法分析

首先是源代碼程序被輸入到掃描器陈惰,掃描器的任務(wù)很簡單畦徘,它只是簡單的進(jìn)行詞法分析。運(yùn)用有限狀態(tài)機(jī)的算法可以很輕松的將源代碼的字符序列分割成一系列的記號抬闯。記號一般分為如下幾類:關(guān)鍵字井辆、標(biāo)識符、字面量(數(shù)字和字符串等)以及特殊符號(如+溶握,= .....)掘剪。

lex程序可以實(shí)現(xiàn)詞法分析,它會按照用戶之前掃描害的詞法規(guī)則將輸入的字符串分割成一個個記號奈虾。因?yàn)檫@樣一個程序的存在夺谁,編譯器開發(fā)者就無需為每一個編譯器開發(fā)一個獨(dú)立的詞法掃描器廉赔,而是根據(jù)需要改變詞法規(guī)則就可以了。

3.2 語法分析

這一步驟語法分析器將由掃描器產(chǎn)生的記號進(jìn)行語法分析匾鸥,從而產(chǎn)生語法樹蜡塌。簡單的講,由語法分析器生成的語法樹就是以表達(dá)式為節(jié)點(diǎn)的樹勿负。

int fun(int a, int b) {
    int c = 0;
    c = a + b;
    return c;
}

以如上代碼為例馏艾,它的語法樹形式如下:


語法樹將字符串格式的源代碼轉(zhuǎn)化為樹狀的數(shù)據(jù)結(jié)構(gòu),更容易被計算機(jī)理解和處理奴愉。如前面詞法分析的 lex 一樣琅摩,語法分析同樣有現(xiàn)成的工具,其中有一個叫做Yet Another Compiler Compiler 簡稱 yacc 的工具锭硼。它可以根據(jù)用戶給定的語法規(guī)則對輸入的記號序列進(jìn)行解析房资,從而構(gòu)建出一棵語法樹。針對不同的語言檀头,一般編譯器開發(fā)者只需要改變語法規(guī)則轰异,根本不需要為每個編譯器重新寫一個語法分析器,所以它又被稱為編譯器的編譯器暑始。

3.3 語義分析

語義分析有語義分析器完成谈截。語義分析之前的語法分析僅僅只是完成了對表達(dá)式成眠的語法層面分析摊聋,但是它并不能確定這個語句是否真正有意義徙硅。如OC中兩個 Person 對象實(shí)例直接做加減乘除運(yùn)算男应,實(shí)際上是沒有意義的,但是在語法分析上確實(shí)合情合理嗤朴。這里要說明一下編譯器分析的語義都是靜態(tài)語義惊奇,靜態(tài)語義是指在編譯器件可以確定的語義,與之對應(yīng)的動態(tài)語義只能在運(yùn)行期間才能被確定播赁。

靜態(tài)語義分析通常包括聲明、類型匹配吼渡、類型轉(zhuǎn)換等容为。如一個浮點(diǎn)類型賦值給整形變量,其中就隱含了浮點(diǎn)類型轉(zhuǎn)換為整型的語義寺酪;動態(tài)語義分析是指運(yùn)行期間出現(xiàn)的相關(guān)語義問題坎背。

經(jīng)過語義分析之后,在語法分析生成的語法樹的基礎(chǔ)上進(jìn)一步對表達(dá)式做一些標(biāo)識寄雀。如:有些某些類型需要做隱式轉(zhuǎn)化得滤,語義分析器會在之前的語法樹中插入相應(yīng)的轉(zhuǎn)換節(jié)點(diǎn)。

3.4 生成中間代碼

3.4.1 生成中間代碼的意義

理論上來說盒犹,中間代碼是可以直接被省略的懂更,因?yàn)槌橄笳Z法樹可以直接轉(zhuǎn)為目標(biāo)代碼(匯編代碼)眨业。然而不同的 CPU 架構(gòu)采用的匯編語法并不一樣,如: Intel 架構(gòu)和 AT&T 架構(gòu)的匯編碼中沮协,源操作數(shù)和目標(biāo)操作數(shù)位置恰好相反參考鏈接龄捡。中間代碼可以理解為抽象的代碼,一方面它和語言無關(guān)慷暂,同時也和 CPU 無關(guān)聘殖,它僅僅只是描述了代碼要做的事情,可以將其理解為是全世界通用的語言行瑞,任何語言都可以轉(zhuǎn)換為世界語言奸腺,而世界語言又能被任何人翻譯理解。要知道血久,中間代碼的存在使得編譯器被分為前端后端突照。其中編譯器前端主要負(fù)責(zé)產(chǎn)生與機(jī)器無關(guān)的中間代碼,編譯器后端主要是將中間代碼轉(zhuǎn)換成目標(biāo)機(jī)器代碼洋魂。因?yàn)檫@意味著針對那些跨平臺的編譯器而言绷旗,可以針對不同的平臺使用同一個前端和針對不同機(jī)器平臺的多個后端。

3.4.2 生成中間代碼的過程

生成中間代碼主要包含以下步驟副砍,以下是用 GCC 編譯器為實(shí)例說明衔肢。

  • 1、語法樹轉(zhuǎn)高端 gimple(簡化樹)
    該步驟主要是處理寄存器和棧豁翎,比如拿c = a + b來說通常顯示執(zhí)行a + b,然后將該結(jié)果保存到寄存器中角骤,最后再將其賦值給c。此外心剥,調(diào)用函數(shù)時會進(jìn)入到此函數(shù)自己的棧中邦尊,建棧操作需要在gimple中聲明。
  • 2优烧、高端 gimple 轉(zhuǎn)低端 gimple
    該步驟主要是把變量定義蝉揍、語句執(zhí)行和返回語句區(qū)分開來,分別存儲畦娄,這樣可以很好的計算一個函數(shù)所需要的椨终矗空間。同時在這一步驟中熙卡,return語句會被統(tǒng)一處理轉(zhuǎn)換成goto語句杖刷,返回值同意放在最后處理。
if (1 > 0) {
    return 1;
}
else {
    return 0;
}
//上述代碼將會被轉(zhuǎn)換成如下形式:
if (1 > 0) {
    goto a;
}
else {
    goto b;
}
a:
    return 1;
b:
    return 0;
  • 3驳癌、低端 gimple 經(jīng)過 cfa 轉(zhuǎn) ssa 再轉(zhuǎn)中間代碼滑燃。
    該步驟主要是進(jìn)行各種相關(guān)的優(yōu)化。

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

經(jīng)過上面生成中間代碼步驟之后颓鲜,這一步驟屬于編譯器后端表窘。該步驟主要的任務(wù)是生成并優(yōu)化目標(biāo)代碼典予,目標(biāo)代碼亦稱為匯編代碼(其實(shí)和匯編代碼非常接近)。編譯器后端主要包括目標(biāo)代碼生成器和目標(biāo)代碼優(yōu)化去蚊丐。

代碼生成器將中間代碼轉(zhuǎn)換成目標(biāo)機(jī)器代碼熙参,此過程依賴目標(biāo)機(jī)器,應(yīng)為不同的機(jī)器有不同的寄存器麦备、整數(shù)數(shù)據(jù)類型和浮點(diǎn)數(shù)據(jù)類型等孽椰。

目標(biāo)代碼優(yōu)化器主要是對目標(biāo)代碼進(jìn)行優(yōu)化,如:選擇合適的尋址方式凛篙、使用位移代替乘法運(yùn)算黍匾、刪除多余的指令等。

3.6 編譯過程小結(jié)

編譯器的結(jié)構(gòu)實(shí)際上是異常復(fù)雜的呛梆,主要在于三個因素锐涯。

  • 1、高級編程語言本身就異常復(fù)雜填物。
    就拿C++來說纹腌,至今沒有一個編譯器能夠比較完整的支持C++標(biāo)準(zhǔn)語言所規(guī)定的所有語言特性。
  • 2滞磺、計算機(jī)的 CPU 也同樣異常復(fù)雜升薯。
  • 3、要求編譯器要支持多種硬件平臺击困,即要求編譯器能生成與多種 CPU 匹配的代碼涎劈。

四、匯編

匯編過程中輸入源是匯編代碼阅茶,輸出是二進(jìn)制機(jī)器碼(后綴為 .o 的目標(biāo)文件)蛛枚。輸出的二進(jìn)制機(jī)器碼可以直接被 CPU 識別并執(zhí)行。匯編過程相對于編譯器過程而言相對簡單些脸哀,因?yàn)闆]有復(fù)雜的語法蹦浦、沒有語義、不需要做指令優(yōu)化撞蜂,根據(jù)匯編指令和機(jī)器指令的對照表一一翻譯即可盲镶。

由于匯編更接近機(jī)器語言,能夠直接對硬件進(jìn)行操作谅摄,生成的程序與其他的語言相比具有更高的運(yùn)行速度,占用更小的內(nèi)存系馆,因此在一些對于時效性要求很高的程序送漠、許多大型程序的核心模塊以及工業(yè)控制方面大量應(yīng)用。

五由蘑、鏈接

5.1 鏈接的簡單介紹

大型軟件往往有成千上萬的模塊闽寡,模塊之前相互依賴但又獨(dú)立代兵。一個程序被分割成多個模塊之后,這些模塊又是通過何種形式組合成一個完整的程序爷狈?模塊之間如何組合的問題實(shí)際上就是模塊之間的通信問題植影。

鏈接過程主要包括了:

  • 地址和空間的分配(Address and Storage Alloction)
  • 符號決議(Symbol Resolution)Ps:"決議"更傾向于靜態(tài)鏈接,而"綁定"更傾向于動態(tài)鏈接涎永。
  • 重定位(Relocation)
鏈接的過程

讓我們來看看什么是重定位思币。假設(shè)有個全局變量叫做 var ,它在目標(biāo)文件A里面羡微。我們在目標(biāo)文件B里面要訪問這個全局變量谷饿。由于在編譯目標(biāo)文件B的時候,編譯器并不知道變量var的目標(biāo)地址妈倔,所以編譯器在沒法確定的情況下博投,將目標(biāo)地址設(shè)置為0,等待鏈接器在目標(biāo)文件A和B連接起來的時候?qū)⑵湫拚⒑_@個地址修正的過程被叫做重定位毅哗,每個被修正的地方叫一個重定位入口。

鏈接器就是靠著重定位表來知道哪些地方需要被重定位的捧挺。每個可能存在重定位的段都會有對應(yīng)的重定位表虑绵。在鏈接階段,鏈接器會根據(jù)重定位表中松忍,需要重定位的內(nèi)容蒸殿,去別的目標(biāo)文件中找到地址并進(jìn)行重定位。

5.2靜態(tài)鏈接的缺點(diǎn)

  • 靜態(tài)鏈接這種方法的確很簡單鸣峭,原理上很容易理解宏所,實(shí)踐上很難實(shí)現(xiàn),但是靜態(tài)鏈接對于計算機(jī)內(nèi)存和磁盤的空間浪費(fèi)非常嚴(yán)重摊溶。特別是多進(jìn)程操作系統(tǒng)的情況下爬骤,靜態(tài)鏈接極大的浪費(fèi)了內(nèi)存和空間。想象一下每個程序內(nèi)部除了都保留著print()函數(shù)莫换、scanf()函數(shù)霞玄、strlen()等這樣的公用函數(shù)庫,還有數(shù)量相當(dāng)可觀的其他庫以及它們所需要的輔助數(shù)據(jù)結(jié)構(gòu)拉岁。

  • 空間浪費(fèi)是靜態(tài)鏈接的一個問題坷剧,另一個問題是靜態(tài)鏈接對程序的更新、部署和發(fā)布也會帶來很多麻煩喊暖。比如Program1所使用的Lib.o是由一個第三方廠商提供的惫企,當(dāng)該廠商更新了Lib.o的時候(比如修復(fù)了lib.o里面包含的一個bug),那么Program1的廠商就需要拿到最新版的Lib.o,然后將其與Program1.o鏈接后將新的Program1整個發(fā)布給用戶。這樣做的缺點(diǎn)很明顯狞尔,即一旦程序中有任何模塊更新丛版,整個程序就要重新鏈接、發(fā)布給用戶偏序。

基于上述兩個問題页畦,就引出了一個名詞,動態(tài)鏈接研儒。

5.3 動態(tài)鏈接

要解決上述兩個問題豫缨,就是不對哪些組成程序的目標(biāo)文件進(jìn)行鏈接,等到程序要運(yùn)行時才進(jìn)行鏈接殉摔。也就是說州胳,把鏈接這個過程推遲到了運(yùn)行時再進(jìn)行,這就是動態(tài)鏈接(Dynamic Linking)的基本思想逸月。所謂的動態(tài)鏈接表示重定位發(fā)生在運(yùn)行時而非編譯后栓撞。

雖然動態(tài)鏈接可以解決上述的兩個問題,但是在性能上要略微比靜態(tài)鏈接差一些碗硬。筆者之前也寫過一篇Swift性能分析的文章瓤湘,其中也涉及到一些關(guān)于OC和Swift語言動態(tài)鏈接相關(guān)的點(diǎn)。

六恩尾、編譯器和解釋器

6.1 解釋型語言

編譯器和解釋器

解釋器是一條一條的解釋執(zhí)行源語言弛说,不需要編譯直接由解釋器執(zhí)行,對應(yīng)的語言稱為解釋型語言也稱作腳本語言翰意。比如 Php木人,Ruby,JavaScript冀偶、Python 等就是典型的解釋性語言醒第。

源代碼  --->  解釋器  ---> 執(zhí)行

解釋型語言同編譯型語言相比,編譯器是把源代碼整個編譯成目標(biāo)代碼进鸠,執(zhí)行時不在需要再去編譯器稠曼,直接在支持目標(biāo)代碼的平臺上運(yùn)行,所以執(zhí)行效率比解釋執(zhí)行快很多客年。比如C語言代碼被編譯成二進(jìn)制代碼(exe程序)霞幅,在windows平臺上執(zhí)行。

6.2 解釋型語言和編譯型語言的共同點(diǎn)

兩者的共同點(diǎn)很簡單量瓜,一句話總結(jié):都需要轉(zhuǎn)換成二進(jìn)制才能執(zhí)行司恳。

6.3 解釋型語言和編譯型語言的不同點(diǎn)

  • 1、運(yùn)行的時候是否需要編譯器
    編譯型語言運(yùn)行的是最終的二進(jìn)制代碼了绍傲,所以不再需要編譯器扔傅;但是解釋型語言邊解釋、邊運(yùn)行,所以運(yùn)行時候還有部分代碼沒有解釋好舉個例子:在瀏覽器里铅鲤,要看 html 效果,要通過帶有內(nèi)置編譯工具的軟件去查看(如:瀏覽器或者模擬瀏覽器的工具)枫弟。
  • 2邢享、執(zhí)行速度
    毫無疑問邊翻譯邊執(zhí)行的解釋型語言的速度會比編譯型語言運(yùn)行速度要慢。但是CPU的運(yùn)行速度如果很快淡诗,你可能看不出來骇塘,偶爾會看到“有點(diǎn)卡”的效果。
  • 3韩容、可移植性對比
    編譯型語言運(yùn)行二進(jìn)制內(nèi)容款违,一旦 CPU 指令改變,之前的二進(jìn)制文件可能運(yùn)行不了了群凶。即在其他硬件平臺上運(yùn)行插爹,可能出錯,如果想在其他平臺運(yùn)行就需要編譯出新的二進(jìn)制文件请梢,所以編譯型語言可移植性差赠尾;解釋型語言在需要的時候才開始編譯、運(yùn)行毅弧,所以具有可移植性气嫁,在很多平臺都能運(yùn)行起來。
  • 4够坐、升級上對比
    編譯型語言的二進(jìn)制文件如果要升級寸宵,需要重新下載一個新的二進(jìn)制文件了。如QQ的升級元咙,就是要重新下載梯影、安裝、覆蓋蛾坯;
    而解釋型的語言光酣,只要重新寫好源代碼即可。如網(wǎng)站平臺升級脉课,用戶只要重新刷新即可救军。
  • 5、 應(yīng)用領(lǐng)域
    編譯型語言應(yīng)用領(lǐng)域通常是安裝軟件倘零,如:桌面或手機(jī)上安裝軟件唱遭;
    解釋型的語言的應(yīng)用領(lǐng)域通常是互聯(lián)網(wǎng),網(wǎng)站等呈驶,刷新就可以看到最新效果拷泽。

七、額外擴(kuò)充(運(yùn)行時、ABI )

7.1 關(guān)于運(yùn)行時

7.1.1 Runtime

如果你是一個 iOS 開發(fā)者司致,想必都聽過并用過 runtime拆吆。但其實(shí) runtime 并非是 Objective-C 的專利,絕大多數(shù)語言都有這個概念脂矫。所以說 runtime 讓 Objective-C 具有動態(tài)性這句話是錯誤的枣耀。如果要認(rèn)清楚這一點(diǎn),感覺有必要先認(rèn)清楚運(yùn)行時庫庭再,要知道 runtime 就是運(yùn)行時庫的一部分捞奕。

7.1.2 運(yùn)行時庫概念

以 C 語言為例說明運(yùn)行時庫的概念。在 C 語言中 glibc 這個動態(tài)鏈接庫通常會被很多操作依賴拄轻,包括字符串處理(strlen颅围、strcpy)、信號處理恨搓、socket院促、線程、IO斧抱、動態(tài)內(nèi)存分配等等一疯。由于每個程序都依賴于運(yùn)行時庫,這些庫一般都是動態(tài)鏈接的夺姑。這樣一來墩邀,運(yùn)行時庫可以存儲在操作系統(tǒng)中,很多程序共享一個動態(tài)庫盏浙,這樣就可以節(jié)省內(nèi)存占用空間和應(yīng)用程序大小眉睹。

7.1.3 Swift運(yùn)行時庫

參照上述 C 語言的運(yùn)行石庫,就很容易理解 Swift運(yùn)行時庫的概念了废膘。一方面竹海,swift 是絕對的靜態(tài)語言,另一方面丐黄,swift 毫無疑問的帶有自己的運(yùn)行時庫斋配。按照常理來說類似字符串、數(shù)組灌闺、print 函數(shù)都應(yīng)該是運(yùn)行時庫中的一部分艰争。然而,Swift 依然沒有穩(wěn)定自己的 ABI 桂对,導(dǎo)致每個程序都必須自帶運(yùn)行時庫甩卓,這也就是為什么目前 swift 開發(fā)的 app 普遍會增加幾 Mb 包大小的原因。

7.2 ABI 簡單概念

7.2.1 什么是ABI蕉斜?

ABI 是 Application Binary Interface的縮寫逾柿,它是一個規(guī)范缀棍。簡單的說它就是編譯后的 API (API 描述了在應(yīng)用程序級別,模塊之間的調(diào)用約定)机错。 通過這個規(guī)范爬范,所有被獨(dú)立編譯的二進(jìn)制實(shí)體才能被鏈接在一起并執(zhí)行。這些二進(jìn)制實(shí)體必須在一些很低層的細(xì)節(jié)上達(dá)成一致弱匪,例如:如何調(diào)用函數(shù)坦敌,如何在內(nèi)存中表示數(shù)據(jù),甚至是如何存儲以及訪問數(shù)據(jù)痢法。要重點(diǎn)知道,ABI 是平臺相關(guān)的杜顺,因?yàn)樗P(guān)注的這些底層細(xì)節(jié)會受到不同的硬件架構(gòu)以及操作系統(tǒng)的的影響财搁。

為了更好的理解什么是ABI,如下舉個詳細(xì)的列子說明躬络。
比如模塊 A 有兩個整數(shù) a 和 b尖奔,它們的內(nèi)存布局如下:

其他模塊調(diào)用 A 模塊的 b 變量,可以通過初始地址加偏移量的方式獲取b變量穷当。如果后來模塊 A 新增了一個整數(shù) c (該過程可以看做是手機(jī)系統(tǒng)更新(伴隨著運(yùn)行時庫更新))提茁,它的內(nèi)存布局可能又會變成如下這種形式。如果還是通過初始地址加偏移量的方式獲取變量馁菜,那么此時獲取的是 a 變量茴扁,而不再是之前的 b 變量。如果把模塊 A 看做是 Swift 運(yùn)行時庫汪疮,假設(shè)現(xiàn)在該運(yùn)行時庫已經(jīng)內(nèi)置于操作系統(tǒng)中并與手機(jī)上不同的應(yīng)用程序動態(tài)鏈接在一些峭火。如果每次更新系統(tǒng),就會出現(xiàn)某些 App 崩潰的情況智嚷。如何定義好 A 模塊獲取變量的規(guī)則卖丸,其中的規(guī)則就是所謂的 ABI 。


7.2.2 什么是ABI穩(wěn)定盏道?

ABI 穩(wěn)定就是將 ABI 鎖定在某種形式下稍浆,使之后的相關(guān)編譯器可以遵守這種二進(jìn)制實(shí)體,這種二進(jìn)制實(shí)體可以是庫也可以是程序猜嘱。一旦穩(wěn)定了 ABI 衅枫,基本便是它會伴隨著這個平臺一生一世,甚至是走到滅忙朗伶。

對 ABI 做出的每一個決定都會對一門編程語言產(chǎn)生長遠(yuǎn)的影響为鳄,甚至可能會約束一門語言后期的發(fā)展和進(jìn)化。如:Swift 語言一直尚未申明ABI的穩(wěn)定腕让,但只要申明了某個平臺的 ABI 已經(jīng)穩(wěn)定孤钦,那么任何有缺陷的設(shè)計將永遠(yuǎn)伴隨著這個平臺歧斟。

7.2.3 ABI穩(wěn)定了會怎樣?

ABI 穩(wěn)定之后偏形,OS 發(fā)行商就可以把 Swift 標(biāo)準(zhǔn)庫和運(yùn)行時作為操作系統(tǒng)的一部分嵌入静袖,由于這些標(biāo)準(zhǔn)庫和運(yùn)行時可以支持用更老或更新版本 Swift 構(gòu)建的應(yīng)用程序,這樣俊扭,開發(fā)者就無需在分發(fā)應(yīng)用的同時队橙,還要帶上一份自己構(gòu)建應(yīng)用時使用的標(biāo)準(zhǔn)庫和運(yùn)行時拷貝。這使得工具和操作系統(tǒng)可以更好的進(jìn)行集成萨惑。 然而目前 Swift 還是一門年輕的語言捐康,ABI 尚未穩(wěn)定,暫時還未和 iOS 系統(tǒng)硬件綁定庸蔼,所以在開發(fā)移動端應(yīng)用的時候會發(fā)現(xiàn) app 普遍會增加幾 Mb 包大小解总。

八、總結(jié)

簡單的做個小結(jié)姐仅,本文顯示總的介紹的整個編譯過程花枫,之后針對編輯中的每個步驟做了進(jìn)一步的說明。最后相繼介紹了編譯型和解釋型語言的區(qū)別掏膏、runtime劳翰、運(yùn)行時庫、什么是 ABI 以及 ABI 穩(wěn)定的意義馒疹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末佳簸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颖变,更是在濱河造成了極大的恐慌溺蕉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悼做,死亡現(xiàn)場離奇詭異疯特,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)肛走,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門漓雅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人朽色,你說我怎么就攤上這事邻吞。” “怎么了葫男?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵抱冷,是天一觀的道長。 經(jīng)常有香客問我梢褐,道長旺遮,這世上最難降的妖魔是什么赵讯? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮耿眉,結(jié)果婚禮上边翼,老公的妹妹穿的比我還像新娘。我一直安慰自己鸣剪,他們只是感情好组底,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筐骇,像睡著了一般债鸡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上铛纬,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天厌均,我揣著相機(jī)與錄音,去河邊找鬼饺鹃。 笑死,一個胖子當(dāng)著我的面吹牛间雀,可吹牛的內(nèi)容都是我干的悔详。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼惹挟,長吁一口氣:“原來是場噩夢啊……” “哼茄螃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起连锯,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤归苍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后运怖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拼弃,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年摇展,在試婚紗的時候發(fā)現(xiàn)自己被綠了吻氧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡咏连,死狀恐怖盯孙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情祟滴,我是刑警寧澤振惰,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站垄懂,受9級特大地震影響骑晶,放射性物質(zhì)發(fā)生泄漏痛垛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一透罢、第九天 我趴在偏房一處隱蔽的房頂上張望榜晦。 院中可真熱鬧,春花似錦羽圃、人聲如沸乾胶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽识窿。三九已至,卻和暖如春脑融,著一層夾襖步出監(jiān)牢的瞬間喻频,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工肘迎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留甥温,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓妓布,卻偏偏與公主長得像姻蚓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子匣沼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

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