一. 編譯器
編譯器也是一種程序渔肩,其作用是將一種語(yǔ)言翻譯為另一種語(yǔ)言周偎,通常是將高級(jí)語(yǔ)言翻譯為低級(jí)語(yǔ)言,或者說(shuō)是將源代碼翻譯成能被計(jì)算機(jī)或虛擬機(jī)執(zhí)行的目標(biāo)代碼吧兔。
編譯器的主要工作流程是:源代碼-預(yù)處理器-編譯器-目標(biāo)代碼-鏈接器-可執(zhí)行文件
另一個(gè)角度的工作流程:詞法分析-語(yǔ)法分析-語(yǔ)義分析-中間代碼生成-代碼優(yōu)化-目標(biāo)代碼生成-目標(biāo)代碼優(yōu)化
編譯器的種類(lèi)
“本地”編譯器
用來(lái)生成與編譯器本身所在環(huán)境操作系統(tǒng)(平臺(tái))相同的環(huán)境運(yùn)行的目標(biāo)代碼的編譯器叫“本地”編譯器。
“交叉”編譯器
生成用來(lái)在其他平臺(tái)上運(yùn)行的目標(biāo)代碼伺通,這種編譯器叫做“交叉”編譯器。這個(gè)過(guò)程也叫交叉編譯吴藻。
例如:在 Mac 上編譯能在 Android 上運(yùn)行的 so 弓柱, 這個(gè)過(guò)程就屬于交叉編譯。
編譯器的前后端
以中間代碼生成步驟為中心劃分:
- 與源語(yǔ)言有關(guān)航罗,與目標(biāo)語(yǔ)言無(wú)關(guān)的部分叫做編譯器前端
- 和目標(biāo)語(yǔ)言有關(guān)粥血,和源語(yǔ)言無(wú)關(guān)的部分叫做編譯器后端
將編譯器分為前端和后端酿箭,對(duì)編譯技術(shù)的起到了一定作用缭嫡。
二. GNU & GCC & Clang & llvm
1. GNU
GNU, Gnu's Not Unix 的縮寫(xiě)。由于一開(kāi)始 Unix 系統(tǒng)是商業(yè)收費(fèi)的耕突,理查德·斯托曼提出 GNU 計(jì)劃讥耗,希望發(fā)展出一套完整的開(kāi)放源代碼操作系統(tǒng)來(lái)取代 Unix古程,名為 GNU 蔼卡。
1989 年,GNU 項(xiàng)目中編輯器挣磨、編譯器雇逞、shell 等都已完成,唯獨(dú)缺了操作系統(tǒng)核心茁裙,所以開(kāi)始正式發(fā)展 Hurd 來(lái)作為 GNU 計(jì)劃的操作系統(tǒng)塘砸。
1991 年,Linux 出現(xiàn)晤锥, GNU 項(xiàng)目的軟件可在 Linux 上運(yùn)行掉蔬。
1992 年廊宪,Linux 和 GNU 結(jié)合,形成完全自由的操作系統(tǒng)箭启,稱(chēng)為 GNU/Linux 簡(jiǎn)稱(chēng) Linux 。此時(shí) Hurd 還沒(méi)完善蛉迹,被拋棄傅寡。
2. GCC
gcc, GNU C Compiler 的縮寫(xiě),是 GNU 項(xiàng)目的編譯器部分北救,也是類(lèi) Unix 和 Mac OS X 操作系統(tǒng)的標(biāo)準(zhǔn)編譯器荐操。
gcc 原本只處理 C 語(yǔ)言,后來(lái)也發(fā)展成可處理 Object-c珍策、Java 托启、C++。
g++
gcc 和 g++ 都是 GNU 的編譯器膛壹。他們的區(qū)別如下:
- 對(duì)于 .c 驾中,gcc 把它當(dāng)作 C 程序,而 g++ 當(dāng)作 C++ 程序模聋;對(duì)于 .cpp , gcc 和 g++ 都會(huì)當(dāng)作 c++ 程序肩民。
對(duì)于 .cpp 的編譯鏈接
gcc 和 g++ 都可以編譯,而鏈接可以用 g++ 或者gcc -lstdc++链方。因?yàn)?gcc 命令不能自動(dòng)和 C++ 程序使用的庫(kù)聯(lián)接持痰,所以通常使用 -lstdc++ 來(lái)完成聯(lián)接。
3. Clang
Clang 是一個(gè) C祟蚀、C++工窍、Objective-C 和 Objective-C++ 編程語(yǔ)言的編譯器前端。它采用了底層虛擬機(jī)(LLVM)作為其后端前酿。這個(gè)軟件項(xiàng)目是由蘋(píng)果發(fā)起的患雏,目標(biāo)是替代 GNU 的 gcc 編譯器套裝。
因?yàn)?gcc 的編譯器慢慢無(wú)法滿(mǎn)足蘋(píng)果的需求罢维,因此淹仑,蘋(píng)果開(kāi)發(fā)了 Clang 與LLVM來(lái)完全取代 gcc,Xcode4 之后肺孵,蘋(píng)果的默認(rèn)編譯器已經(jīng)是 Clang/LLVM 匀借。Clang 作為編譯器前端,LLVM 作為編譯器后端平窘。
4. MinGw
MinGw 是 Minimalist GNU for Windows 的縮寫(xiě)吓肋。它是一個(gè)可自由使用和自由發(fā)布的 Windows 特定頭文件和使用 GNU 工具集導(dǎo)入庫(kù)的集合,允許在 Windows 平臺(tái)生成本地的 Windows 程序而不需要第三方 C 運(yùn)行時(shí)(C Runtime)庫(kù)瑰艘。
三. C/C++ 編譯過(guò)程
C/C++ 編譯過(guò)程可以分為四個(gè)步驟:
- 預(yù)處理
- 編譯
- 匯編
- 鏈接
1.預(yù)處理
預(yù)處理是處理文件中的預(yù)處理命令是鬼,通常是 # 開(kāi)頭肤舞,預(yù)處理通常包含如下步驟:
- 替換宏定義 #define
- 處理?xiàng)l件預(yù)編譯指令如 #if
- 處理 #include 指令,將需要包含的文件遞歸包含進(jìn)來(lái)
- 等等
可以使用 -E 命令執(zhí)行預(yù)處理操作屑咳,-o 表示生成的文件萨赁,最終生成 .i 文件
gcc -E -o test.i test.c
例如:有 test.h test.c 文件
>>test.h
int func(int a,int b) {
return a + b;
}
>>test.c
#include "test.h"
#define A 1
#define B 2
int main() {
int c = func(A,B);
}
執(zhí)行 gcc -E -o test.i test.c
>> 生成的 test.i
# 1 "test.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 363 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "test.c" 2
# 1 "./test.h" 1
int func(int a,int b) {
return a + b;
}
# 3 "test.c" 2
int main() {
int c = func(1,2);
}
2. 編譯
編譯的過(guò)程是將經(jīng)過(guò)處理之后的程序轉(zhuǎn)換成特定匯編語(yǔ)言代碼的過(guò)程,可以使用 -E 命令執(zhí)行編譯操作兆龙,其表示讓編譯器編譯之后停止,不進(jìn)行后續(xù)步驟敲董。
gcc -S -o test.s test.c
>> 匯編代碼:
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 10, 15 sdk_version 10, 15, 4
.globl _func ## -- Begin function func
.p2align 4, 0x90
_func: ## @func
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
addl -8(%rbp), %eax
popq %rbp
retq
.cfi_endproc
## -- End function
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $1, %edi
movl $2, %esi
callq _func
xorl %ecx, %ecx
movl %eax, -4(%rbp)
movl %ecx, %eax
addq $16, %rsp
popq %rbp
retq
.cfi_endproc
## -- End function
.subsections_via_symbols
3.匯編
匯編是將上述匯編代碼轉(zhuǎn)換為機(jī)器碼紫皇,這一步產(chǎn)生的文件叫做目標(biāo)文件,是二進(jìn)制格式腋寨。每一個(gè)源文件都會(huì)產(chǎn)生一個(gè) .o 的目標(biāo)文件聪铺。
as test.s -o test.o
等價(jià)于
gcc –c test.c –o test.o
>> 這里就不貼
4.鏈接
鏈接過(guò)程是將目標(biāo)文件以及所需的庫(kù)文件,鏈接成最終的 .out 可執(zhí)行文件萄窜。鏈接程序的主要工作就是將有關(guān)的 .o 的目標(biāo)文件彼此相連接铃剔,使得所有的這些目標(biāo)文件成為一個(gè)能夠被操作系統(tǒng)裝入執(zhí)行的統(tǒng)一整體。
>> 執(zhí)行
gcc test.c
生成 a.out 的可執(zhí)行文件
在 mac 上執(zhí)行 ./a.out 可運(yùn)行文件
四. 靜態(tài)鏈接和動(dòng)態(tài)鏈接
靜態(tài)鏈接
靜態(tài)鏈接:在鏈接階段查刻,將匯編生成的目標(biāo)文件 .o 與引用到的庫(kù)一起鏈接打包進(jìn)可執(zhí)行文件中键兜。就是在編譯鏈接時(shí)直接將需要的執(zhí)行代碼拷貝到調(diào)用處,因此允許的時(shí)候存在多份內(nèi)存拷貝穗泵。
靜態(tài)鏈接庫(kù):一組目標(biāo)文件的的集合普气,即很多目標(biāo)文件經(jīng)過(guò)壓縮后打包形成一個(gè)文件 .a
靜態(tài)鏈接庫(kù)的特點(diǎn):
- 靜態(tài)庫(kù)對(duì)函數(shù)庫(kù)的鏈接是放在編譯時(shí)期完成的
- 程序在運(yùn)行的時(shí)候與函數(shù)庫(kù)無(wú)關(guān)
- 浪費(fèi)內(nèi)存,多個(gè)地方調(diào)用某個(gè)函數(shù)就存在多次拷貝
動(dòng)態(tài)鏈接
就是在編譯的時(shí)候不直接拷貝可執(zhí)行的代碼佃延,而是通過(guò)記錄一系列符號(hào)和參數(shù)现诀,在程序運(yùn)行或加載時(shí)將這些信息傳遞給操作系統(tǒng),操作系統(tǒng)負(fù)責(zé)將需要的動(dòng)態(tài)庫(kù)加載到內(nèi)存中履肃。
然后程序在運(yùn)行到指定的代碼時(shí)仔沿,去共享執(zhí)行內(nèi)存中已經(jīng)加載的動(dòng)態(tài)庫(kù)可執(zhí)行代碼,最終達(dá)到運(yùn)行時(shí)連接的目的尺棋。
動(dòng)態(tài)鏈接庫(kù)的特點(diǎn):
- 動(dòng)態(tài)庫(kù)把對(duì)一些庫(kù)函數(shù)的鏈接載入推遲到程序運(yùn)行的時(shí)期封锉。
- 多個(gè)程序可以共享同一段代碼,而不需要在內(nèi)存上存儲(chǔ)多個(gè)拷貝陡鹃,
- 缺點(diǎn)是由于是運(yùn)行時(shí)加載烘浦,可能會(huì)影響程序的前期執(zhí)行性能。
不同操作系統(tǒng)下編譯過(guò)程文件的后綴
系統(tǒng) | 源文件 | 目標(biāo)文件 | 動(dòng)態(tài)鏈接庫(kù) | 靜態(tài)鏈接庫(kù) | 可執(zhí)行文件 |
---|---|---|---|---|---|
windows | .c/.cpp | .obj | .dll | .lib | .exe |
Linux | .c/.cpp | .o | .so | .a | .out/coff/elf (沒(méi)有后綴) |
Mac OS X | .c/.cpp | .o | .dylib | .a | .out/coff/elf (沒(méi)有后綴) |
參考文章
編譯器
編譯器(GNU & GCC & clang & llvm)
C語(yǔ)言編譯過(guò)程詳解
Linux 中的動(dòng)態(tài)鏈接庫(kù)和靜態(tài)鏈接庫(kù)是干什么的