對於日常的應用開發,咱們不多關注編譯和連接過程,由於 Xcode 在 build 的時候將編譯和連接合併到了一塊兒一步完成。以mian.m爲例:前端
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
NSLog(@"Hellow World!");
}
return 0;
}
複製代碼
它的編譯和連接過程以下: 程序員
以上過程能夠被成 4 個步驟,分別是預處理,編譯,彙編和連接。後端
預處理主要處理那些源代碼中以「#」開頭的預編譯指令:xcode
clang -E main.m -F /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks
複製代碼
主要處理規則以下:markdown
clang -fmodules -E -Xclang -dump-tokens main.m
複製代碼
編譯過程就是將預處理完的文件進行一系列詞法分析、語法分析、語義分析及優化後生成相應的彙編代碼文件。app
clang -fmodules -E -Xclang -dump-tokens main.m
複製代碼
將源代碼的字符序列分割成一系列的符號(Token)。通常能夠分爲一下幾類:關鍵字、標識符、字面量(數字、字符串)和特殊符號(如加號、等號)。函數
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
複製代碼
生成以表達式爲節點的抽象語法樹AST,運算符號的優先級和含義被肯定,若是出現表達式不合法,好比各類括號不匹配、表達式中缺乏操做符,編譯器就會報錯。post
一般包括聲明和類型的匹配,類型的轉換,好比一個浮點型到整形的轉換。將浮點型賦值給指針類型不匹配編譯器會報錯。優化
生成和目標機器與運行時環境無關代碼。中間代碼使得編譯器能夠被分爲前端和後端,xcode 的編譯器前端爲 Clang,後端爲 LLVM,其中前端負責產生機器無關的中間代碼,後端將中間代碼轉換成目標機器代碼。ui
這個過程屬於編譯器後端,主要包括代碼生成器和目標代碼優化器。代碼生成器將中間代碼轉換成目標機器的彙編代碼,代碼優化器對目標代碼進行優化,選擇合適的尋址方式,使用位移來代替乘法運算、刪除多餘的指令等。這個階段若是變量跟源代碼定義在了同一個編譯單元裏面,那麼編譯器能夠爲它們分配空間和地址,定義在其餘模塊的全局變量和函數在最終運行時的絕對地址都要在最終連接的時候肯定。
clang -c main.s -o main.o
複製代碼
彙編是將彙編代碼轉換成機器能夠執行的指令,輸出目標文件.o。
編譯器將源代碼文件編譯成了未連接的目標文件,連接器將這些目標文件連接成可執行文件,能夠是靜態庫或者動態庫。這個過程主要包括地址和空間分配、符號決議和重定位。好比說有個全局變量 var 定義在目標文件 A 裏面,目標問文件 B 裏面要訪問這個全局變量,因爲在編譯目標文件 B 的時候,編譯器並不知道 var 變量的目標地址,因此將目標地址設置爲 0,等待連接器將目標文件 A 和目標文件 B 連接起來的時候再將其修正,這個地址修正的過程也叫作重定位。
《程序員的自我修養》