GCC常用編譯選項

在使用GCC編譯程序時,編譯過程可以被細分爲四個階段:
預處理(Pre-Processing)
編譯(Compiling)
彙編(Assembling)
鏈接(Linking)

假如有一個hello.c文件,使用以下命令即可:
gcc hello.c -o hello
./hello

分階段編譯:
預處理:gcc -E hello.c -o hello.i
編譯:gcc -c hello.i -o hello.o
鏈接:gcc hello.o -o hello

使用參數:
-Wall可以使gcc產生儘可能多警告信息,建議在用GCC編譯源代碼時始終帶上-Wall選項,它們不僅可以幫助程序員寫出更加健壯的程序,而且還是跟蹤和調試程序的有力工具。
gcc -Wall hello.c -o hello
-Werror,它要求GCC將所有的警告當成錯誤進行處理,這在使用自動編譯工具(如Make等)時非常有用。
gcc -Wall -Werror hello.c -o hello
-On,n指0到2或3的整數,表示代碼優化。數字越大優化約好。-O等價於-O1。
gcc -Wall -O -Werror hello.c -o hello

庫依賴:
在Linux下開發軟件時,完全不使用第三方函數庫的情況是比較少見的,通常來講都需要藉助一個或多個函數庫的支持才能夠完成相應的功能。從程序員的角度看,函數庫實際上就是一些頭文件(.h)和庫文件(.so或者.a)的集合。雖然Linux下的大多數函數都默認將頭文件放到/usr/include/目錄下,而庫文件則放到/usr/lib/目錄下,但並不是所有的情況都是這樣。正因如此,GCC在編譯時必須有自己的辦法來查找所需要的頭文件和庫文件。

GCC採用搜索目錄的辦法來查找所需要的文件,-I選項可以向GCC的頭文件搜索路徑中添加新的目錄。例如,如果在/home/test/include/目錄下有編譯時所需要的頭文件,爲了讓GCC能夠順利地找到它們,就可以使用-I選項:

# gcc foo.c -I /home/test/include -o foo

同樣,如果使用了不在標準位置的庫文件,那麼可以通過-L選項向GCC的庫文件搜索路徑中添加新的目錄。例如,如果在/home/test/lib/目錄下有鏈接時所需要的庫文件libfoo.so,爲了讓GCC能夠順利地找到它,可以使用下面的命令:

# gcc foo.c -L /home/test/lib -lfoo -o foo

值得好好解釋一下的是-l選項,它指示GCC去連接庫文件libfoo.so。Linux下的庫文件在命名時有一個約定,那就是應該以lib三個字母開頭,由於所有的庫文件都遵循了同樣的規範,因此在用-l選項指定鏈接的庫文件名時可以省去lib三個字母,也就是說GCC在對-lfoo進行處理時,會自動去鏈接名爲libfoo.so的文件。

Linux下的庫文件分爲兩大類分別是動態鏈接庫(通常以.so結尾)和靜態鏈接庫(通常以.a結尾),兩者的差別僅在程序執行時所需的代碼是在運行時動態加載的,還是在編譯時靜態加載的。默認情況下,GCC在鏈接時優先使用動態鏈接庫,只有當動態鏈接庫不存在時才考慮使用靜態鏈接庫,如果需要的話可以在編譯時加上-static選項,強制使用靜態鏈接庫。例如,如果在/home/xiaowp/lib/目錄下有鏈接時所需要的庫文件libfoo.so和libfoo.a,爲了讓GCC在鏈接時只用到靜態鏈接庫,可以使用下面的命令:

# gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo

調試
一個功能強大的調試器不僅爲程序員提供了跟蹤程序執行的手段,而且還可以幫助程序員找到解決問題的方法。對於Linux程序員來講,GDB(GNU Debugger)通過與GCC的配合使用,爲基於Linux的軟件開發提供了一個完善的調試環境。

默認情況下,GCC在編譯時不會將調試符號插入到生成的二進制代碼中,因爲這樣會增加可執行文件的大小。如果需要在編譯時生成調試符號信息,可以使用GCC的-g或者-ggdb選項。GCC在產生調試符號時,同樣採用了分級的思路,開發人員可以通過在-g選項後附加數字1、2或3來指定在代碼中加入調試信息的多少。默認的級別是2(-g2),此時產生的調試信息包括擴展的符號表、行號、局部或外部變量信息。級別3(-g3)包含級別2中的所有調試信息,以及源代碼中定義的宏。級別1(-g1)不包含局部變量和與行號有關的調試信息,因此只能夠用於回溯跟蹤和堆棧轉儲之用。回溯跟蹤指的是監視程序在運行過程中的函數調用歷史,堆棧轉儲則是一種以原始的十六進制格式保存程序執行環境的方法,兩者都是經常用到的調試手段。

GCC產生的調試符號具有普遍的適應性,可以被許多調試器加以利用,但如果使用的是GDB,那麼還可以通過-ggdb選項在生成的二進制代碼中包含GDB專用的調試信息。這種做法的優點是可以方便GDB的調試工作,但缺點是可能導致其它調試器(如DBX)無法進行正常的調試。選項-ggdb能夠接受的調試級別和-g是完全一樣的,它們對輸出的調試符號有着相同的影響。

需要注意的是,使用任何一個調試選項都會使最終生成的二進制文件的大小急劇增加,同時增加程序在執行時的開銷,因此調試選項通常僅在軟件的開發和調試階段使用。調試選項對生成代碼大小的影響從下面的對比過程中可以看出來:

# gcc optimize.c -o optimize
# ls optimize -l
-rwxrwxr-x 1 test test 11649 Nov 20 08:53 optimize (未加調試選項)
# gcc -g optimize.c -o optimize
# ls optimize -l
-rwxrwxr-x 1 test test 15889 Nov 20 08:54 optimize (加入調試選項)

雖然調試選項會增加文件的大小,但事實上Linux中的許多軟件在測試版本甚至最終發行版本中仍然使用了調試選項來進行編譯,這樣做的目的是鼓勵用戶在發現問題時自己動手解決,是Linux的一個顯著特色。
雖然GCC允許在優化的同時加入調試符號信息,但優化後的代碼對於調試本身而言將是一個很大的挑戰。代碼在經過優化之後,在源程序中聲明和使用的變量很可能不再使用,控制流也可能會突然跳轉到意外的地方,循環語句有可能因爲循環展開而變得到處都有,所有這些對調試來講都將是一場噩夢。建議在調試的時候最好不使用任何優化選項,只有當程序在最終發行的時候才考慮對其進行優化。

加速

在將源代碼變成可執行文件的過程中,需要經過許多中間步驟,包含預處理、編譯、彙編和連接。這些過程實際上是由不同的程序負責完成的。大多數情況下GCC可以爲Linux程序員完成所有的後臺工作,自動調用相應程序進行處理。

這樣做有一個很明顯的缺點,就是GCC在處理每一個源文件時,最終都需要生成好幾個臨時文件才能完成相應的工作,從而無形中導致處理速度變慢。例如,GCC在處理一個源文件時,可能需要一個臨時文件來保存預處理的輸出、一個臨時文件來保存編譯器的輸出、一個臨時文件來保存彙編器的輸出,而讀寫這些臨時文件顯然需要耗費一定的時間。當軟件項目變得非常龐大的時候,花費在這上面的代價可能會變得很沉重。

解決的辦法是,使用Linux提供的一種更加高效的通信方式—管道。它可以用來同時連接兩個程序,其中一個程序的輸出將被直接作爲另一個程序的輸入,這樣就可以避免使用臨時文件,但編譯時卻需要消耗更多的內存。

在編譯過程中使用管道是由GCC的-pipe選項決定的。下面的這條命令就是藉助GCC的管道功能來提高編譯速度的:

# gcc -pipe foo.c -o foo

在編譯小型工程時使用管道,編譯時間上的差異可能還不是很明顯,但在源代碼非常多的大型工程中,差異將變得非常明顯。

文件擴展名

在使用GCC的過程中,用戶對一些常用的擴展名一定要熟悉,並知道其含義。爲了方便大家學習使用GCC,在此將這些擴展名羅列如下:

.c C原始程序;
.C C++原始程序;
.cc C++原始程序;
.cxx C++原始程序;
.m Objective-C原始程序;
.i 已經過預處理的C原始程序;
.ii 已經過預處理之C++原始程序;
.s 組合語言原始程序;
.S 組合語言原始程序;
.h 預處理文件(標頭文件);
.o 目標文件;
.a 存檔文件。

GCC常用選項

GCC作爲Linux下C/C++重要的編譯環境,功能強大,編譯選項繁多。爲了方便大家日後編譯方便,在此將常用的選項及說明羅列出來如下:

-c 通知GCC取消鏈接步驟,即編譯源碼並在最後生成目標文件;
-Dmacro 定義指定的宏,使它能夠通過源碼中的#ifdef進行檢驗;
-E 不經過編譯預處理程序的輸出而輸送至標準輸出;
-g3 獲得有關調試程序的詳細信息,它不能與-o選項聯合使用;
-Idirectory 在包含文件搜索路徑的起點處添加指定目錄;
-llibrary 提示鏈接程序在創建最終可執行文件時包含指定的庫;
-O、-O2、-O3 將優化狀態打開,該選項不能與-g選項聯合使用;
-S 要求編譯程序生成來自源代碼的彙編程序輸出;
-v 啓動所有警報;
-Wall 在發生警報時取消編譯操作,即將警報看作是錯誤;
-Werror 在發生警報時取消編譯操作,即把報警當作是錯誤;
-w 禁止所有的報警。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章