GCC簡介
1、GCC經(jīng)過那么多年的發(fā)展,已經(jīng)從最初的C編譯器轉(zhuǎn)變成了編譯器的集合,官方定義是GNU Complier Collection,現(xiàn)在的GCC不僅支持C還支持C++、Java等語言。
2、GCC是一個編譯系統(tǒng)的驅(qū)動程序,負責解析輸入的參數(shù),依次調(diào)用預(yù)處理器(cpp)、編譯器(ccl/cclplus)、匯編器(as)、鏈接器(ld)生成可執(zhí)行文件。
3、GCC 和 G++ 的區(qū)別并不是前者用來編譯C代碼,后者用來編譯 C++ 代碼。它們的區(qū)別是GCC把后綴為c的文件當C代碼處理(ccl編譯),而 G++ 則當作 C++ 處理(cclplus編譯),對于后綴為cpp的文件gcc和 g++ 處理過程沒有什么區(qū)別。GCC默認是不能編譯 C++ 程序的,需要加上-lstdc++選項,G++ 是可以直接編譯的,G++相當于是對GCC的封裝。
GCC編譯過程
在介紹GCC編譯步驟之前,首先需要了解GCC支持的后綴文件類型。
| 后綴名 | 對應(yīng)語言 |
|---|---|
| .c | C程序 |
| .C/.cc/.cxx | C++程序 |
| .i | 預(yù)處理后的C程序 |
| .ii | 預(yù)處理后的C++程序 |
| .s/.S | 匯編語言程序 |
| .h | 頭文件 |
| .o | 目標文件 |
| .a/.so | 編譯后的庫文件 |
GCC編譯流程分為四個步驟:預(yù)處理(生成.i/.ii文件)、編譯(生成.s/.S文件)、匯編(生成.o文件)、鏈接(生成可執(zhí)行文件)。
gcc指令的一般格式為:
gcc [選項] 要編譯的文件 [選項] [目標文件]
其中,目標文件可缺省,gcc默認生成可執(zhí)行的文件為a.out
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
1、預(yù)處理
對于該階段,gcc將stdio.h文件中的代碼包含進這段程序,我們可以利用gcc -E test.c -o test.i命令來生成預(yù)處理過的.i文件。-E選項代表讓gcc在預(yù)處理階段后停止編譯。test.i文件中的內(nèi)容如下所示,可以看出stdio.h文件中的內(nèi)容被展開。
extern int fprintf (FILE *__restrict __stream,
__const char *__restrict __format, ...);
......
# 8 "test.c" 2
int main()
{
printf("Hello World\n");
return 0;
}
2、編譯
該階段主要是對預(yù)編譯后的.i文件編譯,生成匯編代碼的.s文件。gcc首先要檢查代碼的規(guī)范性、是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤后,gcc把代碼翻譯成匯編語言。我們可以使用-S選項來進行查看,該選項只進行編譯而不進行匯編過程,生成匯編代碼。
我們可以利用gcc -S test.i -o test.s命令進行編譯過程。test.s文件中的內(nèi)容如下所示。
.file "test.c"
.section .rodata
.LC0:
.string "Hello World"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
call puts
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)"
.section .note.GNU-stack,"",@progbits
3、匯編
該階段是將編譯后的.s文件轉(zhuǎn)化成二進制文件.o的過程,利用-c選項就可以生成二進制.o文件。我們可以利用gcc -c test.s -o test.o生成二進制代碼。
4、鏈接
該階段主要將成功編譯的二進制文件進行鏈接操作,生成可執(zhí)行文件。利用gcc test.o -o test生成可執(zhí)行文件test,運行./test即可打印出hello world。
總結(jié)一下:
gcc -E test.c -o test.i //把原代碼交給cpp預(yù)處理器生成經(jīng)過預(yù)處理后的中間文件test.i
gcc -S test.i -o test.s //把經(jīng)過預(yù)處理之后的test.i文件交給編譯器cc1生成test.s文件
gcc -c test.s -o test.o //把經(jīng)過編譯后匯編文件test.s交給as進行匯編,生成目標test.o文件
gcc test.o -o test //將as匯編之后的目標文件交給ld鏈接成一個可執(zhí)行的文件test
GCC常用編譯選項
1、總體編譯選項
| 選項名稱 | 作用 |
|---|---|
| -c | 只是編譯不鏈接,生成目標文件.o |
| -S | 只是編譯不匯編,生成匯編代碼 |
| -E | 只進行預(yù)編譯,不做其他處理 |
| -g | 在可執(zhí)行程序中包含標準調(diào)試信息 |
| -o file | 把輸出文件輸出到file里 |
| -v | 打印出編譯器內(nèi)部編譯各過程的命令行信息和編譯器的版本 |
| -I dir | 在頭文件的搜索路徑列表中添加dir目錄 |
| -L dir | 在庫文件的搜索路徑列表中添加dir目錄 |
| -static | 鏈接靜態(tài)庫 |
| -llibrary | 連接名為library的庫文件 |
表中前邊幾個選項在GCC編譯步驟中已經(jīng)有所了解,主要對表中后四個選項進行介紹。
當我們進行程序開發(fā)時,基本都會用到很多函數(shù)庫,從程序員的角度看,函數(shù)庫就是封裝的一些頭文件(.h)和庫文件(.a/.so),Linux系統(tǒng)下頭文件一般默認放到/usr/include/目錄下,庫文件放在/usr/lib/目錄下,如果我們使用的庫不在標準路徑下,我們就需要指定頭文件和庫文件的路徑以便讓gcc找到它們。
舉個例子,假設(shè)上一節(jié)test.c主要實現(xiàn)C語言連接mysql數(shù)據(jù)庫的功能,我們從官網(wǎng)下載的mysql connectors的頭文件在/usr/local/mysq/include/路徑下,庫函數(shù)在/usr/local/mysql/lib/路徑下。
我們可以利用
gcc -c -I /usr/local/mysql/include test.c -o test.o
生成test.o二進制文件,再利用
gcc -L /usr/local/mysql/lib -lmysqlclient test.o -o test
進行鏈接操作產(chǎn)生可執(zhí)行的test文件。這兩步也可以合成一步,直接生成可執(zhí)行的test文件
gcc -I /usr/local/mysql/include -L /usr/local/mysql/lib -lmysqlclient test.c -o test
Linux下的庫文件分為兩大類分別是動態(tài)鏈接庫(通常以.so結(jié)尾)和靜態(tài)鏈接庫(通常以.a結(jié)尾),二者的區(qū)別僅在于程序執(zhí)行時所需的代碼是在運行時動態(tài)加載的,還是在編譯時靜態(tài)加載的。
默認情況下, GCC在鏈接時優(yōu)先使用動態(tài)鏈接庫,只有當動態(tài)鏈接庫不存在時才考慮使用靜態(tài)鏈接庫,如果需要使用靜態(tài)鏈接庫可以在編譯時加上-static選項,強制使用靜態(tài)鏈接庫。
比如在/usr/local/mysql/lib目錄下有鏈接時所需要的庫文件libmysqlclient.so和libmysqlclient.a,為了讓GCC在鏈接時只用到靜態(tài)鏈接庫,可以使用下面的命令:
gcc –L /usr/local/mysql/lib -static –lmysqlclient test.o –o test
需要注意的是:
-
-I dir和-L dir都只是指定了路徑,而沒有指定文件,因此不能在路徑中包含文件名。 - 另外值得詳細解釋一下的是
-l選項, 它指示gcc去連接庫文件libXXX.so。由于在Linux下的庫文件命名時有一個規(guī)定:必須以lib三個字母開頭。因此在用-l選項指定鏈接的庫文件名時可以省去lib三個字母。也就是說gcc在對”-lXXX”進行處理時,會自動去鏈接名為libXXX.so的文件。
2、其他常用編譯選項
| 選項名稱 | 作用 |
|---|---|
| -ansi | 支持符合ANSI標準的C程序 |
| -pedantic | 允許發(fā)出ANSI C標準所列的全部警告信息 |
| -pedantic-error | 允許發(fā)出ANSI C標準所列的全部錯誤信息 |
| -Wall | 允許發(fā)出gcc提供的所有有用的報警信息 |
| -Werror | 把所有的告警信息轉(zhuǎn)化為錯誤信息,并在告警發(fā)生時終止編譯過程 |
| -O | 主要進行線程跳轉(zhuǎn)(Thread Jump)和延遲退棧(Deferred Stack Pops)兩種優(yōu)化 |
| -O2 | 除了完成所有“-O1”級別的優(yōu)化之外,同時還要進行一些額外的調(diào)整工作,如處理器指令調(diào)度等 |
| -O3 | 還包括循環(huán)展開和其他一些與處理器特性相關(guān)的優(yōu)化工作 |
| -static | 指示鏈接器構(gòu)建一個完全鏈接的可執(zhí)行程序,即鏈接靜態(tài)庫而不鏈接動態(tài)庫 |
| -fPIC | 指示鏈接器創(chuàng)建一個共享的目標文件,即so文件 |
| -shared | 生成動態(tài)庫,一般和上面的-fPIC一起使用 |