概述
C和C++編譯器是集成的,編譯一般分為如下四個(gè)步驟:
a. 預(yù)處理(preprocessing) ------------ cpp/gcc -E
b. 編譯(compilation) ------------------ cc1 / gcc -S
c. 匯編(assembly) --------------------- as
d. 鏈接(linking) ------------------------ ld
下面就以一個(gè)經(jīng)典的hello程序?yàn)槔私庖幌律鲜龅乃牟?/h3>
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
1. 預(yù)處理階段:預(yù)處理器(cpp)根據(jù)字符#開(kāi)頭的命令,修改原始的C程序。例如,hello.c中第一行的#include <stdio.h>命令告訴預(yù)處理器讀取系統(tǒng)頭文件stdio.h的內(nèi)容,并把它直接插入到程序中。結(jié)果得到另一個(gè)C程序,通常以.i作為文件擴(kuò)展名。
2. 編譯階段:編譯器(ccl)將文本文件hello.i翻譯成文件本文件hello.s,它包含一個(gè)匯編語(yǔ)言程序。該程序包含函數(shù)main的定義,如下所示:
1 main:
2 subq $8, %rsp
3 movl $.LCO, %edi
4 call puts
5 movl $0, %eax
6 addq $8, %rsp
7 ret
定義中2~7行的的每條語(yǔ)句都以一種文本格式描述了一條低級(jí)機(jī)器語(yǔ)言指令。匯編語(yǔ)言是非常有用的,因?yàn)樗鼮椴煌呒?jí)語(yǔ)言的不同編譯器提供了通用的輸出語(yǔ)言。例如,C編譯器和Frotran編譯器產(chǎn)出的輸出文件用的都是一樣的匯編語(yǔ)言
3. 匯編階段:接下來(lái),匯編器(as)將hello.s翻譯成機(jī)器語(yǔ)言指令,并把這些指令打包成一種叫做可重定位目標(biāo)程序的格式,并將結(jié)果保存到目標(biāo)文件hello.o中,hello.o是一個(gè)二進(jìn)制文件,它包含的17個(gè)字節(jié)是函數(shù)main的指令編碼。如果我們?cè)谖谋揪庉嬈骼锩娲蜷_(kāi)hello.o文件,看到的將是一堆亂碼。
4. 鏈接階段:hello程序調(diào)用了printf函數(shù),他是每個(gè)C編譯器都提供的標(biāo)準(zhǔn)C庫(kù)的一個(gè)函數(shù)。printf函數(shù)存在于一個(gè)名為printf.o的單獨(dú)的預(yù)編譯好了的目標(biāo)文件中,而這個(gè)文件必須以某種方式合并到我們的hello.o程序中,鏈接器(ld)就負(fù)責(zé)處理這種合并。結(jié)果就得到hello文件,他是一個(gè)可執(zhí)行目標(biāo)文件或簡(jiǎn)稱為可執(zhí)行文件,可以被加載到內(nèi)存中,有系統(tǒng)執(zhí)行。
-
為了更好的理解可以參考下圖:
函數(shù)庫(kù)
- 函數(shù)庫(kù)一般分為靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)兩種
- 靜態(tài)庫(kù)是指編譯鏈接時(shí),把庫(kù)文件的代碼全部加入到可執(zhí)行文件中,因此生成的文件比較大,但在運(yùn)行時(shí)也就不再需要庫(kù)文件了。其后綴名一般為”.a“。
- 動(dòng)態(tài)庫(kù)與之相反,在編譯鏈接時(shí)并沒(méi)有把庫(kù)文件的代碼加入到可執(zhí)行文件中,而是在程序執(zhí)行時(shí)由運(yùn)行時(shí)鏈接文件加載庫(kù),這樣可以節(jié)省系統(tǒng)的開(kāi)銷。動(dòng)態(tài)庫(kù)一般后綴名為”.so”,如前面所述的libc.so.6就是動(dòng)態(tài)庫(kù)。Gcc在編譯時(shí)默認(rèn)使用動(dòng)態(tài)庫(kù)。
靜態(tài)庫(kù)生成方法:
ar cr libxxx.a file1.o file2.o
就是把file1.o和file2.o打包生成libxxx.a靜態(tài)庫(kù)
使用方法
gcc test.c -L/path -lxxx -o test
動(dòng)態(tài)庫(kù)生成方法:
gcc -fPIC -shared file1.c -o libxxx.so
也可以分成兩部來(lái)寫(xiě)
//這一步生成file1.o
gcc -fPIC file1.c -c
gcc -shared file1.o -o libtest.so
使用方法
gcc test.c -L/path -lxxx -o test
- 靜態(tài)庫(kù)鏈接時(shí)搜索路徑順序:
- ld會(huì)去找GCC命令中的參數(shù)-L
- 再找gcc的環(huán)境變量LIBRARY_PATH
- 再找內(nèi)定目錄 /lib /usr/lib /usr/local/lib 這是當(dāng)初compile gcc時(shí)寫(xiě)在程序內(nèi)的
- 動(dòng)態(tài)鏈接時(shí)、執(zhí)行時(shí)搜索路徑順序
- 編譯目標(biāo)代碼時(shí)指定的動(dòng)態(tài)庫(kù)搜索路徑
- 環(huán)境變量LD_LIBRARY_PATH指定的動(dòng)態(tài)庫(kù)搜索路徑
- 配置文件/etc/ld.so.conf中指定的動(dòng)態(tài)庫(kù)搜索路徑
- 默認(rèn)的動(dòng)態(tài)庫(kù)搜索路徑/lib
- 默認(rèn)的動(dòng)態(tài)庫(kù)搜索路徑/usr/lib
環(huán)境變量
- LIBRARY_PATH環(huán)境變量:指定程序靜態(tài)鏈接庫(kù)文件搜索路徑
- LD_LIBRARY_PATH環(huán)境變量:指定程序動(dòng)態(tài)鏈接庫(kù)文件搜索路徑
#include <stdio.h>
int main()
{
printf("hello, world\n");
return 0;
}
#include <stdio.h>命令告訴預(yù)處理器讀取系統(tǒng)頭文件stdio.h的內(nèi)容,并把它直接插入到程序中。結(jié)果得到另一個(gè)C程序,通常以.i作為文件擴(kuò)展名。匯編語(yǔ)言程序。該程序包含函數(shù)main的定義,如下所示:1 main:
2 subq $8, %rsp
3 movl $.LCO, %edi
4 call puts
5 movl $0, %eax
6 addq $8, %rsp
7 ret
定義中2~7行的的每條語(yǔ)句都以一種文本格式描述了一條低級(jí)機(jī)器語(yǔ)言指令。匯編語(yǔ)言是非常有用的,因?yàn)樗鼮椴煌呒?jí)語(yǔ)言的不同編譯器提供了通用的輸出語(yǔ)言。例如,C編譯器和Frotran編譯器產(chǎn)出的輸出文件用的都是一樣的匯編語(yǔ)言
main的指令編碼。如果我們?cè)谖谋揪庉嬈骼锩娲蜷_(kāi)hello.o文件,看到的將是一堆亂碼。printf函數(shù),他是每個(gè)C編譯器都提供的標(biāo)準(zhǔn)C庫(kù)的一個(gè)函數(shù)。printf函數(shù)存在于一個(gè)名為printf.o的單獨(dú)的預(yù)編譯好了的目標(biāo)文件中,而這個(gè)文件必須以某種方式合并到我們的hello.o程序中,鏈接器(ld)就負(fù)責(zé)處理這種合并。結(jié)果就得到hello文件,他是一個(gè)可執(zhí)行目標(biāo)文件或簡(jiǎn)稱為可執(zhí)行文件,可以被加載到內(nèi)存中,有系統(tǒng)執(zhí)行。為了更好的理解可以參考下圖:
靜態(tài)庫(kù)生成方法:
ar cr libxxx.a file1.o file2.o
就是把file1.o和file2.o打包生成libxxx.a靜態(tài)庫(kù)
使用方法
gcc test.c -L/path -lxxx -o test
動(dòng)態(tài)庫(kù)生成方法:
gcc -fPIC -shared file1.c -o libxxx.so
也可以分成兩部來(lái)寫(xiě)
//這一步生成file1.o
gcc -fPIC file1.c -c
gcc -shared file1.o -o libtest.so
使用方法
gcc test.c -L/path -lxxx -o test