1. 程式人生 > 其它 >gcc簡介和命令列引數說明

gcc簡介和命令列引數說明

文章目錄


更多的相關資料見: GCC常用引數詳解



(一) gcc 基本用法

使用gcc編譯器時,必須給出一系列必要的呼叫引數和檔名稱。不同引數的先後順序對執行結果沒有影響,只有在使用同類引數時的先後順序才需要考慮。 如果使用了多個 -L 的引數來定義庫目錄,gcc會根據多個 -L 引數的先後順序來執行相應的庫目錄。

因為很多gcc引數都由多個字母組成,所以gcc引數不支援單字母的組合,Linux中常被叫短引數(short options),如 -dr 與 -d -r 的含義不一樣。gcc編譯器的呼叫引數大約有100多個,其中多數引數我們可能根本就用不到,這裡只介紹其中最基本、最常用的引數。

gcc最基本的用法是:gcc [options] [filenames]

其中,options就是編譯器所需要的引數,filenames給出相關的檔名稱,最常用的有以下引數:

引數說明
− c -c c只編譯,不連結成為可執行檔案。 編譯器只是由輸入的 .c 等原始碼檔案生成 .o 為字尾的目標檔案,通常用於編譯不包含主程式的子程式檔案。
− o o u t p u t _ f i l e n a m e -o \quad output\_filename ooutput_filename確定輸出檔案的名稱為-o output_filename。 同時這個名稱不能和原始檔同名。如果不給出這個選項,gcc就給出預設的可執行檔案 a.out 。
− g -g g產生符號除錯工具(GNU的 gdb)所必要的符號資訊。 想要對原始碼進行除錯,就必須加入這個選項。
− O -O O對程式進行優化編譯、連結。 採用這個選項,整個原始碼會在編譯、連結過程中進行優化處理,這樣產生的可執行檔案的執行效率可以提高,但是編譯、連結的速度就相應地要慢一些,而且對執行檔案的除錯會產生一定的影響,造成一些執行效果與對應原始檔程式碼不一致等一些令人“困惑”的情況。因此,一般在編譯輸出軟體發行版時使用此選項。
− O 2 -O2 O2比 -O 更好的優化編譯、連結。當然整個編譯連結過程會更慢。
− I d i r -Idir Idir即 Include dir,將 dirname 所指出的目錄加入到程式標頭檔案目錄列表中,是在預編譯過程中使用的引數。


說明:C程式中的標頭檔案包含兩種情況:
#include <stdio.h>
#include "stdio.h"
其中,使用尖括號(<>),預處理程式 cpp 在系統預設包含檔案目錄(如/usr/include)中搜索相應的檔案;使用雙引號,預處理程式 cpp 首先在當前目錄中搜尋標頭檔案,如果沒有找到,就到指定的 dirname 目錄中去尋找。在程式設計中,如果需要的這種包含檔案分別分佈在不同的目錄中,就需要逐個使用 -I 選項給出搜尋路徑。
− L d i r -Ldir Ldir即 Link dir,將dirname所指出的目錄加入到程式函式庫檔案的目錄列表中,是在連結過程中使用的引數。 在預設狀態下,連結程式 ld 在系統預設路徑中(如 /usr/lib)尋找所需要的庫檔案。這個選項告訴連結程式,首先到 -L 指定的目錄中去尋找,然後到系統預設路徑中尋找;如果函式庫存放在多個目錄下,就需要依次使用這個選項,給出相應的存放目錄。
− l n a m e -lname lname連結時裝載名為 libname.a 的函式庫。 該函式庫位於系統預設的目錄或者由 -L 選項確定的目錄下。 Linux下的庫檔案在命名時有一個約定,就是應該以 lib 這3個字母開頭,由於所有的庫檔案都遵循了同樣的規範,因此在用 -l 選項指定連結的庫檔名時可以省去 lib 這3個字母。例如,gcc 在對 -lfoo 進行處理時,會自動去連結名為 libfoo.so 的檔案,又如,-lm 表示連結名為 libm.a 的數學函式庫。

gcc使用案例:

假定有一個程式名為 test.c 的C語言原始碼檔案,要生成一個可執行檔案。

#include <stdio.h>
int main(void)
{
    printf("Hello world/n");
    return 0;
}

最簡單的辦法:gcc test.c -o test

首先給出gcc將原始碼轉換為機器碼的過程(以C語言為例)。預設情況下,預編譯、編譯連結一次完成:
在這裡插入圖片描述
相關內容可見:計算機系統漫遊:1. 程式由原始檔轉換至機器碼的過程

編譯過程的分步執行:
為了更好地理解gcc的工作過程,我們可以讓在gcc工作的4個階段中的任何一個階段中停止下來。相關的引數有:

  • ① -E:預編譯後停下來,生成字尾為 .i 的預編譯檔案。gcc -E test.c -o test.i,檢視 test.i 檔案中的內容,會發現 stdio.h 的內容確實都插到檔案裡去了,而其他應當被預處理的巨集定義也都做了相應的處理;
  • ② -S:彙編後停下來,生成字尾為 .s 的彙編原始檔。生成彙編原始檔,gcc -S test.c -o test.s
  • ③ -c:編譯後停下來,生成字尾為 .o 的目標檔案。gcc -c test.c -o test.o
  • ④ -o:將生成的目標檔案連結成可執行檔案,gcc test.o - o test

對於稍微複雜的情況,比如有多個原始碼檔案、需要連結庫或有其他比較特別的要求,就要給定適當的呼叫選項引數。

例子:整個原始碼程式由兩個檔案 testmain.c 和 testsub.c 組成,程式中使用了系統提供的數學庫(所有與浮點相關的數學運算都必須使用數學庫)。gcc testmain.c testsub.c -lm -o test。其中,-lm 表示連結系統的數學庫 libm.a


說明: 在編譯一個包含許多原始檔的工程時,若只用一條gcc命令來完成編譯是非常浪費時間的。假如專案中有100個原始檔需要編譯,並且每個原始檔中都包含一萬行程式碼,如果像上面那樣僅用一條gcc命令來完成編譯工作,那麼gcc需要將每個原始檔都重新編譯一遍,然後再全部連結起來。很顯然,這樣浪費的時間相當多,尤其是當用戶只是修改了其中某個檔案的時候,完全沒有必要將每個檔案都重新編譯一遍,因為很多已經生成的目標檔案是不會發生改變的。要解決這個問題,需要藉助像make這樣的工具。



(二) 警告提示功能選項

gcc包含完整的出錯檢查和警告提示功能,它們可以幫助Linux程式設計師寫出更加專業的程式碼。

引數說明
− v -v v輸出 gcc 工作的詳細過程
–target-help顯示目前所用的gcc支援CPU型別
− Q -Q Q顯示編譯過程的統計資料和每一個函式名
− p e d a n t i c -pedantic pedantic當gcc在編譯不符合ANSI/ISO C 語言標準的原始碼時,將產生相應的警告資訊。
− W a l l -Wall Wall除了 -pedantic 之外,gcc 還有一些其他編譯選項,也能夠產生有用的警告資訊。這些選項大多以 -W 開頭。其中最有價值的當數 -Wall 了,使用它能夠使 gcc 產生儘可能多的警告資訊。
− W e r r o r -Werror Werror它要求 gcc 將所有的警告當成錯誤進行處理,這在使用自動編譯工具(如 Make 等)時非常有用。如果編譯時帶上 -Werror 選項,那麼 gcc 會在所有產生警告的地方停止編譯,迫使程式設計師對自己的程式碼進行修改。只有當相應的警告資訊消除時,才可能將編譯過程繼續朝前推進。
− W c a s t − a l i g n -Wcast-align Wcastalign當源程式中地址不需要對齊的指標指向一個地址需要對齊的變數地址時,則產生一個警告。例如,char * 指向一個 int * 地址,而通常在機器中 int 變數型別是需要地址能被2或4整除的對齊地址。

對於如下程式碼,使用-pedantic引數與不使用有下圖的區別:

#include <stdio.h>  
 void main(void)  
 {  
     long long int var = 1;  
     printf("It is not standard C code!/n");  
 }  
/*它有以下問題:
> main 函式的返回值被宣告為 void,但實際上應該是 int。
> 使用了 GNU 語法擴充套件,即使用 long long 來宣告64位整數,不符合 ANSI/ISO C 語言標準。
> main 函式在終止前沒有呼叫 return 語句。*/

此外還有-Wall-Werror引數的使用效果也見下圖:

-pedantic 命令列引數效果
-Wall 命令列引數效果
-Werror 命令列引數效果

建議:gcc 給出的警告資訊雖然從嚴格意義上說不能算作錯誤,但卻和可能成為錯誤來源。一個優秀的程式設計師應該儘量避免產生警告資訊,使自己的程式碼始終保持簡潔、優美和健壯的特性。gcc 給出的警告資訊是很有價值的,它們不僅可以幫助程式設計師寫出更加健壯的程式,而且還是跟蹤和除錯程式的有力工具。建議在用 gcc 編譯原始碼時始終帶上 -Wall 選項,並把它逐漸培養成一種習慣,這對找出常見的隱式程式設計錯誤很有幫助。



(三) 庫操作選項

在Linux下開發軟體時,完全不使用第三方函式庫的情況是比較少見的,通常來講都需要藉助一個或多個函式庫的支援才能夠完成相應的功能。

從程式設計師的角度看,函式庫實際上就是一些標頭檔案(.h)和庫檔案(.so 或 .a)的集合。雖然Linux下的大多數函式都預設將標頭檔案放到 /usr/include/ 目錄下,而庫檔案則放到 /usr/lib/ 目錄下,但並不是所有的情況都是這樣。正因如此,gcc 在編譯時必須有自己的辦法來查詢所需要的標頭檔案和庫檔案。常用的方法有:

引數說明
− I -I I可以向 gcc 的標頭檔案搜尋路徑中新增新的目錄。同(一)gcc基本用法中的-Idir命令列引數
− L -L L如果使用了不在標準位置的庫檔案,那麼可以通過 -L 選項向 gcc 的庫檔案搜尋路徑中新增新的目錄。同(一)gcc基本用法中的-Ldir命令列引數
− l -l lLinux下的庫檔案在命名時有一個約定,就是應該以 lib 這3個字母開頭,由於所有的庫檔案都遵循了同樣的規範,因此在用 -l 選項指定連結的庫檔名時可以省去 lib 這3個字母。同(一)gcc基本用法中的-lname命令列引數。例如,gcc 在對 -lfoo 進行處理時,會自動去連結名為 libfoo.so 的檔案。
− s t a t i c -static staticLinux下的庫檔案分為兩大類,分別是:動態連結庫(通常以 .so 結尾)和靜態連結庫(通常以 .a 結尾)。兩者的差別僅在程式執行時所需的程式碼是在執行時動態載入的,還是在編譯時靜態載入的。預設情況下,gcc 在連結時優先使用動態連結庫,只有當動態連結庫不存在時才考慮使用靜態連結庫。如果需要的話,可以在編譯時加上 -static 選項,強制使用靜態連結庫
− s h a r e d -shared shared生成一個共享的目標檔案,它能夠與其他的目標一起連結生成一個可執行的檔案。


(四) 除錯選項

對於Linux程式設計師來講,gdb(GNU Debugger)通過與 gcc 的配合使用,為基於Linux的軟體開發提供了一個完善的除錯環境。常用的有:

引數說明
− g \quad-g g
\quad
− g g d b -ggdb ggdb
預設情況下,gcc 在編譯時不會將除錯符號插入到生成的二進位制程式碼中,因為這樣會增加可執行檔案的大小。如果需要在編譯時生成除錯符號資訊,可以使用 gcc 的 -g 或 -ggdb 選項。

gcc 在產生除錯符號時,同樣採用了分級的思路,開發人員可以通過在 -g 選項後附加數字1、2、3指定在程式碼中加入除錯資訊的多少:
級別2(-g2),預設的級別。此時產生的除錯資訊包括:擴充套件的符號表、行號、區域性或外部變數資訊;
級別3(-g3)包含級別2中的所有除錯資訊以及原始碼中定義的巨集;
級別1(-g1)不包含區域性變數和與行號有關的除錯資訊,因此只能夠用於回溯跟蹤和堆疊轉儲。
回溯追蹤:指的是監視程式在執行過程中函式呼叫歷史。
堆疊轉儲:則是一種以原始的十六進位制格式儲存程式執行環境的方法。
− p \quad-p p
\quad
− p g \quad-pg pg
會將剖析(Profiling)資訊加入到最終生成的二進位制程式碼中。剖析資訊對於找出程式的效能瓶頸很有幫助,是協助Linux程式設計師開發出高效能程式的有力工具。
− s a v e − t e m p s -save-temps savetemps儲存編譯過程中生成的一些列中間檔案。gcc test.c -o test -save-temps。除了生成執行檔案test之外,還儲存了test.i 和 test.s 中間檔案,供使用者查詢除錯。

注意:使用任何一個除錯選項都會使最終生成的二進位制檔案的大小急劇增加,同時增加程式在執行時的開銷,因此,除錯選項通常僅在軟體的開發和除錯階段使用。



(五) 交叉編譯選項

通常情況下使用 gcc 編譯的目的碼都與使用的機器是一致的,但 gcc 也支援交叉編譯的功能,能夠編譯其他不同CPU的目的碼

使用 gcc 開發嵌入式系統,我們幾乎都是以通用的PC機(X86)平臺來做宿主機,通過 gcc 的交叉編譯功能對其他嵌入式CPU的開發任務。

(具體的選項設定,此處省略)



(六) gcc 和 g++ 比較

gcc 和 g++現在是GNU中最主要和最流行的 c/c++編譯器。


gcc/g++在執行編譯工作的時候,總共需要以下幾步:

  • ①預處理,生成.i的檔案[前處理器cpp];
  • ②將預處理後的檔案不轉換成組合語言,生成檔案.s[編譯器egcs];
  • ③有彙編變為目的碼(機器程式碼)生成.o的檔案[彙編器as];
  • ④連線目的碼,生成可執行程式[連結器ld];

實際過程與上邊的流程圖並無二致。


gcc能夠處理的字尾有:

  • *.c *.C (C語言)
  • *.cxx *.cc (C++語言)
  • *.m (面向物件的C,objective C?)
  • *.i (預處理後的C語言原始檔)
  • *.ii (預處理後的C++語言原始檔)
  • *.s *.S (組合語言)
  • *.h (標頭檔案)

目標檔案可以是:

  • *.o 編譯連線後的目標檔案
  • *.a 庫檔案

gcc與g++有什麼區別?

gcc和g++都是GNU(組織)的一個編譯器。

誤區答案
gcc只能編譯c程式碼,g++只能編譯c++程式碼兩者都可以,但是請注意:
1.字尾為.c的,gcc把它當作是C程式,而g++當作是c++程式;字尾為.cpp的,兩者都會認為是c++程式,注意,雖然c++是c的超集,但是兩者對語法的要求是有區別的。C++的語法規則更加嚴謹一些。
2. 編譯階段,g++會呼叫gcc,對於c++程式碼,兩者是等價的,但是因為gcc命令不能自動和C++程式使用的庫聯接,所以通常用g++來完成連結,為了統一起見,乾脆編譯/連結統統用g++了,這就給人一種錯覺,好像cpp程式只能用g++似的。
gcc不會定義__cplusplus巨集,而g++會實際上,這個巨集只是標誌著編譯器將會把程式碼按C還是C++語法來解釋,如上所述,如果字尾為.c,並且採用gcc編譯器,則該巨集就是未定義的,否則,就是已定義。
編譯只能用gcc,連結只能用g++嚴格來說,這句話不算錯誤,但是它混淆了概念,應該這樣說:編譯可以用gcc/g++,而連結可以用g++或者gcc -lstdc++。因為gcc命令不能自動和C++程式使用的庫聯接,所以通常使用g++來完成聯接。但在編譯階段,g++會自動呼叫gcc,二者等價。