.m --> .i前端
主要處理那些源代碼文件中的以「#」開始的預編譯指令。好比 「#include」、「#define 」等
複製代碼
1.將全部的 「#define 」刪除,而且展開全部的宏定義。程序員
2.處理全部條件預編譯指令,好比 「#if」、「#ifdef」、「#elif」、「#else」、「#endif 」。」swift
3.處理 「#include 」預編譯指令,將被包含的文件插入到該預編譯指令的位置。注意,這個過程是遞歸進行的,也就是說被包含的文件可能還包含其餘文件。後端
4.刪除全部的註釋「//」和「/* */」。markdown
5.添加行號和文件名標識,好比#2「hello.c」2,以便於編譯時編譯器產生調試用的行號信息及用於編譯時產生編譯錯誤或警告時可以顯示行號。函數
6.保留全部的 #pragma 編譯器指令,由於編譯器需要使用它們。post
.i --> .s優化
Objective C/C/C++
使用的編譯器前端是Clang,Swift是swift,後端都是LLVM.spa
編譯過程通常能夠分爲6步:詞法分析(掃描)、語法分析、語義分析、源代碼優化、代碼生成和目標代碼優化
複製代碼
這樣對於一些能夠跨平臺的編譯器而言,它們能夠針對不一樣的平臺使用同一個前端和針對不一樣機器平臺的數個後端。
複製代碼
負責生成機器無關的中間代碼
複製代碼
詞法掃描器 將代碼分割生成記號token,好比關鍵字、標識符、字面量,特殊符號(+、-運算符)
複製代碼
使用上下文無關語法,生成語法樹,以表達式爲節點的樹,處理表達式中括號不匹配,缺乏操做符,可是並不瞭解整個語句的意義翻譯
編譯期能作的就是靜態語義分析,運行時作的是動態語義分析,靜態語義分析主要作的是聲明與類型的匹配 ,類型的轉換
好比一些簡單運算 加減表達式能夠直接計算肯定 也叫作 三地址碼 x = y op z
負責將中間代碼轉換成目標機器代碼
複製代碼
這個階段若是變量跟源代碼定義在了同一個編譯單元裏面,那麼編譯器能夠爲它們分配空間和地址,定義在其餘模塊的全局變量和函數在最終運行時的絕對地址都要在最終連接的時候肯定。
clang -c main.s -o main.o
複製代碼
彙編器是將彙編代碼轉變成機器能夠執行的指令,每個彙編語句幾乎都對應一條機器指令。因此彙編器的彙編過程相對於編譯器來說比較簡單,它沒有複雜的語法,也沒有語義,也不須要作指令優化,只是根據彙編指令和機器指令的對照表一一翻譯就能夠了」
clang -c main.o -o main.out
複製代碼
編譯器將源代碼文件編譯成了未連接的目標文件,連接器將這些目標文件連接成可執行文件,能夠是靜態庫或者動態庫。這個過程主要包括地址和空間分配、符號決議和重定位。好比說有個全局變量 var 定義在目標文件 A 裏面,目標問文件 B 裏面要訪問這個全局變量,因爲在編譯目標文件 B 的時候,編譯器並不知道 var 變量的目標地址,因此將目標地址設置爲 0,等待連接器將目標文件 A 和目標文件 B 連接起來的時候再將其修正,這個地址修正的過程也叫作重定位
節選自《程序員的自我修養:連接、裝載與庫》 -- 俞甲子 石凡 潘愛民