GCC編譯器總結(jié)

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一起使用

參考文獻:


GCC學(xué)習(xí)總結(jié)

Linux GCC常用命令

GCC 編譯詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容