編譯流程
預處理
- 完成宏替換、文件引入,去除空行、注釋等,為下一步編譯做準備
- 對各種預處理命令進行處理,包括頭文件的包含、宏定義的擴展、條件編譯的選擇等
test.c
#include <stdio.h>
int main(){
printf("hello world!\n");
return 0;
}
$ gcc -E test.c -o test.i
-E:讓gcc在預處理結束后停止編譯
-o:指定輸出文件
"test.i"文件為預處理后輸出的文件
編譯
- 將預處理后的代碼編譯成匯編代碼。在這個階段中,首先檢查代碼的規范性、是否有語法錯誤等,以確定代碼實際要做的工作,在檢查無誤后,再把代碼翻譯成匯編語言
- 匯編程序執行時,先分析,后綜合。分析,就是指詞法分析、語法分析、語義分析和中間代碼生成。綜合,就是指代碼優化和代碼生成
- 大多數的編譯程序直接產生機器語言的目標代碼,形成可執行的目標文件,也有的是先產生匯編語言一級的符號代碼文件,再調用匯編程序進行翻譯和加工處理,最后產生可執行的機器語言目標文件。
$ gcc -S test.i -o test.s
-S:讓gcc在編譯結束后停止編譯過程
"test.s"文件為編譯后生成的匯編代碼
匯編
- 就是把編譯階段生成的“.s”文件轉成二進制目標代碼,也就是機器代碼(0 1 序列)
$ gcc -c test.s -o test.o
-c:讓gcc在匯編結束后停止編譯過程
"test.o"文件為匯編后生成的機器碼目標文件
鏈接
- 就是將多個目標文件以及所需的庫文件鏈接生成可執行目標文件的過程
$ gcc test.o -o test
-o:本質上是一個重命名選項。不使用時,默認生成的是a.out文件。這里生成的是可執行文件test
$ ./test
執行后輸出hello world!
靜態庫和動態庫
靜態庫
- 靜態庫實際就是一些目標文件(一般是.o)的集合,靜態庫一般以.a結尾,只用于生成可執行文件階段
- 在連接步驟中,鏈接器將從庫文件取得所需代碼,復制到生成的可執行文件中。這種庫稱為靜態庫。
- 特點是,可執行文件中包含了庫代碼的一份完整拷貝,在編譯過程中被載入程序中。
- 缺點是,多次使用會有多份冗余拷貝,并且對程序的更新、部署和發布會帶來麻煩,如果靜態庫有更新,那么所有使用它的程序都需要重新編譯發布。
生成靜態庫
$ gcc -c test.c -o test.o
$ ar rcs libtest.a test.o
ar:該命令將目標文件打包成靜態庫。
r:更新或增加新文件到靜態庫
c:不管庫是否存在,都創建一個庫
s:創建文檔索引,對于文件較大的庫,能加快編譯時間
這條命令就是把test.o打包成libtest.a靜態庫
$ ar t libtest.a
使用該命令查看靜態庫內容
動態庫
- 動態庫在鏈接階段沒有被復制到程序中,而是在程序運行時由系統動態加載到內存中供程序調用
- 系統只需載入一次動態庫,不同的程序可以得到內存中相同動態庫的副本,因此節省了很多內存
生成動態庫
$ gcc -c test.c -o test.o
$ gcc -shared -fPIC -o libtest.so test.o