參考:《GNU gcc嵌入式系統(tǒng)開(kāi)發(fā) 作者:董文軍》
(一) gcc的基本用法
(二) 警告提示功能選項(xiàng)
(三) 庫(kù)操作選項(xiàng)
(四) 調(diào)試選項(xiàng)
(五) 交叉編譯選項(xiàng)
(一) gcc的基本用法
使用gcc編譯器時(shí),必須給出一系列必要的調(diào)用參數(shù)和文件名稱(chēng)。不同參數(shù)的先后順序?qū)?zhí)行結(jié)果沒(méi)有影響,只有在使用同類(lèi)參數(shù)時(shí)的先后順序才需要考慮。如果使用了多個(gè) -L 的參數(shù)來(lái)定義庫(kù)目錄,gcc會(huì)根據(jù)多個(gè) -L 參數(shù)的先后順序來(lái)執(zhí)行相應(yīng)的庫(kù)目錄。
因?yàn)楹芏鄃cc參數(shù)都由多個(gè)字母組成,所以gcc參數(shù)不支持單字母的組合,Linux中常被叫短參數(shù)(short options),如 -dr 與 -d -r 的含義不一樣。gcc編譯器的調(diào)用參數(shù)大約有100多個(gè),其中多數(shù)參數(shù)我們可能根本就用不到,這里只介紹其中最基本、最常用的參數(shù)。
gcc最基本的用法是:gcc [options] [filenames]
其中,options就是編譯器所需要的參數(shù),filenames給出相關(guān)的文件名稱(chēng),最常用的有以下參數(shù):
-c
只編譯,不鏈接成為可執(zhí)行文件。編譯器只是由輸入的 .c 等源代碼文件生成 .o 為后綴的目標(biāo)文件,通常用于編譯不包含主程序的子程序文件。
-o output_filename
確定輸出文件的名稱(chēng)為output_filename。同時(shí)這個(gè)名稱(chēng)不能和源文件同名。如果不給出這個(gè)選項(xiàng),gcc就給出默認(rèn)的可執(zhí)行文件 a.out 。
-g
產(chǎn)生符號(hào)調(diào)試工具(GNU的 gdb)所必要的符號(hào)信息。想要對(duì)源代碼進(jìn)行調(diào)試,就必須加入這個(gè)選項(xiàng)。
-O
對(duì)程序進(jìn)行優(yōu)化編譯、鏈接。采用這個(gè)選項(xiàng),整個(gè)源代碼會(huì)在編譯、鏈接過(guò)程中進(jìn)行優(yōu)化處理,這樣產(chǎn)生的可執(zhí)行文件的執(zhí)行效率可以提高,但是編譯、鏈接的速度就相應(yīng)地要慢一些,而且對(duì)執(zhí)行文件的調(diào)試會(huì)產(chǎn)生一定的影響,造成一些執(zhí)行效果與對(duì)應(yīng)源文件代碼不一致等一些令人“困惑”的情況。因此,一般在編譯輸出軟件發(fā)行版時(shí)使用此選項(xiàng)。
-O2
比 -O 更好的優(yōu)化編譯、鏈接。當(dāng)然整個(gè)編譯鏈接過(guò)程會(huì)更慢。
-Idirname
將 dirname 所指出的目錄加入到程序頭文件目錄列表中,是在預(yù)編譯過(guò)程中使用的參數(shù)。
說(shuō)明:
C程序中的頭文件包含兩種情況:
#include
#include "stdio.h"
其中,使用尖括號(hào)(<>),預(yù)處理程序 cpp 在系統(tǒng)默認(rèn)包含文件目錄(如/usr/include)中搜索相應(yīng)的文件;使用雙引號(hào),預(yù)處理程序 cpp 首先在當(dāng)前目錄中搜尋頭文件,如果沒(méi)有找到,就到指定的 dirname 目錄中去尋找。
在程序設(shè)計(jì)中,如果需要的這種包含文件分別分布在不同的目錄中,就需要逐個(gè)使用 -I 選項(xiàng)給出搜索路徑。
-Ldirname
將dirname所指出的目錄加入到程序函數(shù)庫(kù)文件的目錄列表中,是在鏈接過(guò)程中使用的參數(shù)。在默認(rèn)狀態(tài)下,鏈接程序 ld 在系統(tǒng)默認(rèn)路徑中(如 /usr/lib)尋找所需要的庫(kù)文件。這個(gè)選項(xiàng)告訴鏈接程序,首先到 -L 指定的目錄中去尋找,然后到系統(tǒng)默認(rèn)路徑中尋找;如果函數(shù)庫(kù)存放在多個(gè)目錄下,就需要依次使用這個(gè)選項(xiàng),給出相應(yīng)的存放目錄。
-lname
鏈接時(shí)裝載名為 libname.a 的函數(shù)庫(kù)。該函數(shù)庫(kù)位于系統(tǒng)默認(rèn)的目錄或者由 -L 選項(xiàng)確定的目錄下。例如,-lm 表示鏈接名為 libm.a 的數(shù)學(xué)函數(shù)庫(kù)。
例子:假定有一個(gè)程序名為 test.c 的C語(yǔ)言源代碼文件,要生成一個(gè)可執(zhí)行文件。
#include
int main(void)
{
printf("Hello world/n");
return 0;
}
最簡(jiǎn)單的辦法:gcc test.c -o test
首先,gcc需要調(diào)用預(yù)處理程序 cpp,由它負(fù)責(zé)展開(kāi)在源文件中定義的宏,并向其中插入“#include”語(yǔ)句所包含的內(nèi)容;接著,gcc調(diào)用ccl 和 as,將處理后的源代碼編譯成目標(biāo)代碼;最后,gcc調(diào)用鏈接程序 ld,把生成的目標(biāo)代碼鏈接成一個(gè)可執(zhí)行程序。因此,默認(rèn)情況下,預(yù)編譯、編譯鏈接一次完成。
編譯過(guò)程的分步執(zhí)行:
為了更好地理解gcc的工作過(guò)程,我們可以讓在gcc工作的4個(gè)階段中的任何一個(gè)階段中停止下來(lái)。相關(guān)的參數(shù)有:
-E
預(yù)編譯后停下來(lái),生成后綴為 .i 的預(yù)編譯文件。
-c
編譯后停下來(lái),生成后綴為 .o 的目標(biāo)文件。
-S
匯編后停下來(lái),生成后綴為 .s 的匯編源文件。
第一步:進(jìn)行預(yù)編譯,使用 -E 參數(shù)
gcc -E test.c -o test.i
查看 test.i 文件中的內(nèi)容,會(huì)發(fā)現(xiàn) stdio.h 的內(nèi)容確實(shí)都插到文件里去了,而其他應(yīng)當(dāng)被預(yù)處理的宏定義也都做了相應(yīng)的處理。
第二步:將 test.i 編譯為目標(biāo)代碼,使用 -c 參數(shù)
gcc -c test.c -o test.o
第三步:生成匯編源文件
gcc -S test.c -o test.s
第四步:將生成的目標(biāo)文件鏈接成可執(zhí)行文件
gcc test.o - o test
對(duì)于稍微復(fù)雜的情況,比如有多個(gè)源代碼文件、需要鏈接庫(kù)或有其他比較特別的要求,就要給定適當(dāng)?shù)恼{(diào)用選項(xiàng)參數(shù)。
例子:整個(gè)源代碼程序由兩個(gè)文件 testmain.c 和 testsub.c 組成,程序中使用了系統(tǒng)提供的數(shù)學(xué)庫(kù)(所有與浮點(diǎn)相關(guān)的數(shù)學(xué)運(yùn)算都必須使用數(shù)學(xué)庫(kù))。
gcc testmain.c testsub.c -lm -o test
其中,-lm 表示鏈接系統(tǒng)的數(shù)學(xué)庫(kù) libm.a 。
說(shuō)明:
在編譯一個(gè)包含許多源文件的工程時(shí),若只用一條gcc命令來(lái)完成編譯是非常浪費(fèi)時(shí)間的。假如項(xiàng)目中有100個(gè)源文件需要編譯,并且每個(gè)源文件中都包含一萬(wàn)行代碼,如果像上面那樣僅用一條gcc命令來(lái)完成編譯工作,那么gcc需要將每個(gè)源文件都重新編譯一遍,然后再全部鏈接起來(lái)。很顯然,這樣浪費(fèi)的時(shí)間相當(dāng)多,尤其是當(dāng)用戶只是修改了其中某個(gè)文件的時(shí)候,完全沒(méi)有必要將每個(gè)文件都重新編譯一遍,因?yàn)楹芏嘁呀?jīng)生成的目標(biāo)文件是不會(huì)發(fā)生改變的。要解決這個(gè)問(wèn)題,需要借助像make這樣的工具。
(二) 警告提示功能選項(xiàng)
gcc包含完整的出錯(cuò)檢查和警告提示功能,它們可以幫助Linux程序員寫(xiě)出更加專(zhuān)業(yè)的代碼。
(1) -pedantic 選項(xiàng)
當(dāng)gcc在編譯不符合ANSI/ISO C 語(yǔ)言標(biāo)準(zhǔn)的源代碼時(shí),將產(chǎn)生相應(yīng)的警告信息。
[cpp] view plain copy
print?
#include
void main(void)
{
long long int var = 1;
printf("It is not standard C code!/n");
}
它有以下問(wèn)題:
> main 函數(shù)的返回值被聲明為 void,但實(shí)際上應(yīng)該是 int。
> 使用了 GNU 語(yǔ)法擴(kuò)展,即使用 long long 來(lái)聲明64位整數(shù),不符合 ANSI/ISO C 語(yǔ)言標(biāo)準(zhǔn)。
> main 函數(shù)在終止前沒(méi)有調(diào)用 return 語(yǔ)句。
(2) -Wall 選項(xiàng)
除了 -pedantic 之外,gcc 還有一些其他編譯選項(xiàng),也能夠產(chǎn)生有用的警告信息。這些選項(xiàng)大多以 -W 開(kāi)頭。其中最有價(jià)值的當(dāng)數(shù) -Wall 了,使用它能夠使 gcc 產(chǎn)生盡可能多的警告信息。
gcc 給出的警告信息雖然從嚴(yán)格意義上說(shuō)不能算作錯(cuò)誤,但卻和可能成為錯(cuò)誤來(lái)源。一個(gè)優(yōu)秀的程序員應(yīng)該盡量避免產(chǎn)生警告信息,使自己的代碼始終保持簡(jiǎn)潔、優(yōu)美和健壯的特性。
建議:gcc 給出的警告信息是很有價(jià)值的,它們不僅可以幫助程序員寫(xiě)出更加健壯的程序,而且還是跟蹤和調(diào)試程序的有力工具。建議在用 gcc 編譯源代碼時(shí)始終帶上 -Wall 選項(xiàng),并把它逐漸培養(yǎng)成一種習(xí)慣,這對(duì)找出常見(jiàn)的隱式編程錯(cuò)誤很有幫助。
(3) -Werror 選項(xiàng)
在處理警告方面,另一個(gè)常用的編譯選項(xiàng)是 -Werror。它要求 gcc 將所有的警告當(dāng)成錯(cuò)誤進(jìn)行處理,這在使用自動(dòng)編譯工具(如 Make 等)時(shí)非常有用。如果編譯時(shí)帶上 -Werror 選項(xiàng),那么 gcc 會(huì)在所有產(chǎn)生警告的地方停止編譯,迫使程序員對(duì)自己的代碼進(jìn)行修改。只有當(dāng)相應(yīng)的警告信息消除時(shí),才可能將編譯過(guò)程繼續(xù)朝前推進(jìn)。
(4) -Wcast-align 選項(xiàng)
當(dāng)源程序中地址不需要對(duì)齊的指針指向一個(gè)地址需要對(duì)齊的變量地址時(shí),則產(chǎn)生一個(gè)警告。例如,char * 指向一個(gè) int * 地址,而通常在機(jī)器中 int 變量類(lèi)型是需要地址能被2或4整除的對(duì)齊地址。
(5) 其他常用選項(xiàng)
-v 輸出 gcc 工作的詳細(xì)過(guò)程
--target-help 顯示目前所用的gcc支持CPU類(lèi)型
-Q 顯示編譯過(guò)程的統(tǒng)計(jì)數(shù)據(jù)和每一個(gè)函數(shù)名
(三) 庫(kù)操作選項(xiàng)
在Linux下開(kāi)發(fā)軟件時(shí),完全不使用第三方函數(shù)庫(kù)的情況是比較少見(jiàn)的,通常來(lái)講都需要借助一個(gè)或多個(gè)函數(shù)庫(kù)的支持才能夠完成相應(yīng)的功能。
從程序員的角度看,函數(shù)庫(kù)實(shí)際上就是一些頭文件(.h)和庫(kù)文件(.so 或 .a)的集合。雖然Linux下的大多數(shù)函數(shù)都默認(rèn)將頭文件放到 /usr/include/ 目錄下,而庫(kù)文件則放到 /usr/lib/ 目錄下,但并不是所有的情況都是這樣。正因如此,gcc 在編譯時(shí)必須有自己的辦法來(lái)查找所需要的頭文件和庫(kù)文件。常用的方法有:
(1) -I
可以向 gcc 的頭文件搜索路徑中添加新的目錄。
(2) -L
如果使用了不在標(biāo)準(zhǔn)位置的庫(kù)文件,那么可以通過(guò) -L 選項(xiàng)向 gcc 的庫(kù)文件搜索路徑中添加新的目錄。
(3) -l
Linux下的庫(kù)文件在命名時(shí)有一個(gè)約定,就是應(yīng)該以 lib 這3個(gè)字母開(kāi)頭,由于所有的庫(kù)文件都遵循了同樣的規(guī)范,因此在用 -l 選項(xiàng)指定鏈接的庫(kù)文件名時(shí)可以省去 lib 這3個(gè)字母。例如,gcc 在對(duì) -lfoo 進(jìn)行處理時(shí),會(huì)自動(dòng)去鏈接名為 libfoo.so 的文件。
(4) -static
Linux下的庫(kù)文件分為兩大類(lèi),分別是:動(dòng)態(tài)鏈接庫(kù)(通常以 .so 結(jié)尾)和靜態(tài)鏈接庫(kù)(通常以 .a 結(jié)尾)。
兩者的差別僅在程序執(zhí)行時(shí)所需的代碼是在運(yùn)行時(shí)動(dòng)態(tài)加載的,還是在編譯時(shí)靜態(tài)加載的。
默認(rèn)情況下,gcc 在鏈接時(shí)優(yōu)先使用動(dòng)態(tài)鏈接庫(kù),只有當(dāng)動(dòng)態(tài)鏈接庫(kù)不存在時(shí)才考慮使用靜態(tài)鏈接庫(kù)。
如果需要的話,可以在編譯時(shí)加上 -static 選項(xiàng),強(qiáng)制使用靜態(tài)鏈接庫(kù)。
(5) -shared
生成一個(gè)共享的目標(biāo)文件,它能夠與其他的目標(biāo)一起鏈接生成一個(gè)可執(zhí)行的文件。
(四) 調(diào)試選項(xiàng)
對(duì)于Linux程序員來(lái)講,gdb(GNU Debugger)通過(guò)與 gcc 的配合使用,為基于Linux的軟件開(kāi)發(fā)提供了一個(gè)完善的調(diào)試環(huán)境。常用的有:
(1) -g 和 -ggdb
默認(rèn)情況下,gcc 在編譯時(shí)不會(huì)將調(diào)試符號(hào)插入到生成的二進(jìn)制代碼中,因?yàn)檫@樣會(huì)增加可執(zhí)行文件的大小。如果需要在編譯時(shí)生成調(diào)試符號(hào)信息,可以使用 gcc 的 -g 或 -ggdb 選項(xiàng)。
gcc 在產(chǎn)生調(diào)試符號(hào)時(shí),同樣采用了分級(jí)的思路,開(kāi)發(fā)人員可以通過(guò)在 -g 選項(xiàng)后附加數(shù)字1、2、3指定在代碼中加入調(diào)試信息的多少。默認(rèn)的級(jí)別是2(-g2),此時(shí)產(chǎn)生的調(diào)試信息包括:擴(kuò)展的符號(hào)表、行號(hào)、局部或外部變量信息。
級(jí)別3(-g3)包含級(jí)別2中的所有調(diào)試信息以及源代碼中定義的宏。
級(jí)別1(-g1)不包含局部變量和與行號(hào)有關(guān)的調(diào)試信息,因此只能夠用于回溯跟蹤和堆棧轉(zhuǎn)儲(chǔ)。
回溯追蹤:指的是監(jiān)視程序在運(yùn)行過(guò)程中函數(shù)調(diào)用歷史。
堆棧轉(zhuǎn)儲(chǔ):則是一種以原始的十六進(jìn)制格式保存程序執(zhí)行環(huán)境的方法。
注意:使用任何一個(gè)調(diào)試選項(xiàng)都會(huì)使最終生成的二進(jìn)制文件的大小急劇增加,同時(shí)增加程序在執(zhí)行時(shí)的開(kāi)銷(xiāo),因此,調(diào)試選項(xiàng)通常僅在軟件的開(kāi)發(fā)和調(diào)試階段使用。
(2) -p 和 -pg
會(huì)將剖析(Profiling)信息加入到最終生成的二進(jìn)制代碼中。剖析信息對(duì)于找出程序的性能瓶頸很有幫助,是協(xié)助Linux程序員開(kāi)發(fā)出高性能程序的有力工具。
(3) -save-temps
保存編譯過(guò)程中生成的一些列中間文件。
# gcc test.c -o test -save-temps
除了生成執(zhí)行文件test之外,還保存了test.i 和 test.s 中間文件,供用戶查詢(xún)調(diào)試。
(五) 交叉編譯選項(xiàng)
通常情況下使用 gcc 編譯的目標(biāo)代碼都與使用的機(jī)器是一致的,但 gcc 也支持交叉編譯的功能,能夠編譯其他不同CPU的目標(biāo)代碼。
使用 gcc 開(kāi)發(fā)嵌入式系統(tǒng),我們幾乎都是以通用的PC機(jī)(X86)平臺(tái)來(lái)做宿主機(jī),通過(guò) gcc 的交叉編譯功能對(duì)其他嵌入式CPU的開(kāi)發(fā)任務(wù)。
(具體的選項(xiàng)設(shè)置,此處省略)
GCC常用參數(shù)詳解
簡(jiǎn)介
gcc and g++現(xiàn)在是gnu中最主要和最流行的c & c++編譯器 .gcc/g++在執(zhí)行編譯工作的時(shí)候,總共需要以下幾步:
1.預(yù)處理,生成.i的文件[預(yù)處理器cpp]
2.將預(yù)處理后的文件不轉(zhuǎn)換成匯編語(yǔ)言,生成文件.s[編譯器egcs]
3.有匯編變?yōu)槟繕?biāo)代碼(機(jī)器代碼)生成.o的文件[匯編器as]
4.連接目標(biāo)代碼,生成可執(zhí)行程序[鏈接器ld]
GCC能夠處理的后綴有:
a. *.c *.C (C語(yǔ)言)
b. *.cxx *.cc (C++語(yǔ)言)
c. *.m (面向?qū)ο蟮腃)
d. *.i (預(yù)處理后的C語(yǔ)言源文件)
e. *.ii (預(yù)處理后的C++語(yǔ)言源文件)
f. *.s *.S (匯編語(yǔ)言)
h. *.h (頭文件)
目標(biāo)文件可以是:
a. *.o 編譯連接后的目標(biāo)文件
b. *.a 庫(kù)文件
gcc與g++有什么區(qū)別?
gcc和g++都是GNU(組織)的一個(gè)編譯器。
誤區(qū)一:gcc只能編譯c代碼,g++只能編譯c++代碼
兩者都可以,但是請(qǐng)注意:
1.后綴為.c的,gcc把它當(dāng)作是C程序,而g++當(dāng)作是c++程序;后綴為.cpp的,兩者都會(huì)認(rèn)為是c++程序,注意,雖然c++是c的超集,但是兩者對(duì)語(yǔ)法的要求是有區(qū)別的。C++的語(yǔ)法規(guī)則更加嚴(yán)謹(jǐn)一些。
2.編譯階段,g++會(huì)調(diào)用gcc,對(duì)于c++代碼,兩者是等價(jià)的,但是因?yàn)間cc命令不能自動(dòng)和C++程序使用的庫(kù)聯(lián)接,所以通常用g++來(lái)完成鏈接,為了統(tǒng)一起見(jiàn),干脆編譯/鏈接統(tǒng)統(tǒng)用g++了,這就給人一種錯(cuò)覺(jué),好像cpp程序只能用g++似的。
誤區(qū)二:gcc不會(huì)定義__cplusplus宏,而g++會(huì)
實(shí)際上,這個(gè)宏只是標(biāo)志著編譯器將會(huì)把代碼按C還是C++語(yǔ)法來(lái)解釋?zhuān)缟纤?,如果后綴為.c,并且采用gcc編譯器,則該宏就是未定義的,否則,就是已定義。
誤區(qū)三:編譯只能用gcc,鏈接只能用g++
嚴(yán)格來(lái)說(shuō),這句話不算錯(cuò)誤,但是它混淆了概念,應(yīng)該這樣說(shuō):編譯可以用gcc/g++,而鏈接可以用g++或者gcc -lstdc++。因?yàn)間cc命令不能自動(dòng)和C++程序使用的庫(kù)聯(lián)接,所以通常使用g++來(lái)完成聯(lián)接。但在編譯階段,g++會(huì)自動(dòng)調(diào)用gcc,二者等價(jià)。
參數(shù)詳解
無(wú)選項(xiàng)編譯鏈接
將test.c預(yù)處理、匯編、編譯并鏈接形成可執(zhí)行文件。這里未指定輸出文件,默認(rèn)輸出為a.out。
例子用法:
gcc test.c
無(wú)選項(xiàng)鏈接
gcc test.o -o test
將編譯輸出文件test.o鏈接成最終可執(zhí)行文件test。
-x language filename
設(shè)定文件所使用的語(yǔ)言,使后綴名無(wú)效,對(duì)以后的多個(gè)有效.也就是根據(jù)約定C語(yǔ)言的后綴名稱(chēng)是.c的,而C++的后綴名是.C或者.cpp,如果你很個(gè)性, 決定你的C代碼文件的后綴名是.pig 哈哈,那你就要用這個(gè)參數(shù),這個(gè)參數(shù)對(duì)他后面的文件名都起作用,除非到了下一個(gè)參數(shù)的使用。
可以使用的參數(shù)嗎有下面的這些
`c', `objective-c', `c-header', `c++', `cpp-output', `assembler', and `assembler-with-cpp'.
看到英文,應(yīng)該可以理解的。
例子用法:
gcc -x c hello.pig
-x none filename
關(guān)掉上一個(gè)選項(xiàng),也就是讓gcc根據(jù)文件名后綴,自動(dòng)識(shí)別文件類(lèi)型
例子用法:
gcc -x c hello.pig -x none hello2.c
-c
只激活預(yù)處理,編譯,和匯編,也就是他只把程序做成obj文件
例子用法:
gcc -c hello.c
他將生成.o的obj文件
gcc -c test.s
將匯編輸出文件test.s編譯輸出test.o文件。
-S
只激活預(yù)處理和編譯,就是指把文件編譯成為匯編代碼。
例子用法:
gcc -S hello.c
他將生成.s的匯編代碼,你可以用文本編輯器察看
gcc -S test.i
將預(yù)處理輸出文件test.i匯編成test.s文件
-E
只激活預(yù)處理,這個(gè)不生成文件,你需要把它重定向到一個(gè)輸出文件里面.
例子用法:
gcc -E hello.c >; pianoapan.txt
gcc -E hello.c | more
慢慢看吧,一個(gè)hello word 也要與處理成800行的代碼
gcc -E test.c -o test.i
-o
制定目標(biāo)名稱(chēng),缺省的時(shí)候,gcc 編譯出來(lái)的文件是a.out,很難聽(tīng),如果你和我有同感,改掉它,哈哈
例子用法:
gcc -o hello.exe hello.c (哦,windows用習(xí)慣了)
gcc -o hello.asm -S hello.c
-pipe
使用管道代替編譯中臨時(shí)文件,在使用非gnu匯編工具的時(shí)候,可能有些問(wèn)題
gcc -pipe -o hello.exe hello.c
-ansi
關(guān)閉gnu c中與ansi c不兼容的特性,激活ansi c的專(zhuān)有特性(包括禁止一些asm inline typeof關(guān)鍵字,以及UNIX,vax等預(yù)處理宏,
-fno-asm
此選項(xiàng)實(shí)現(xiàn)ansi選項(xiàng)的功能的一部分,它禁止將asm,inline和typeof用作關(guān)鍵字。
-fno-strict-prototype
只對(duì)g++起作用,使用這個(gè)選項(xiàng),g++將對(duì)不帶參數(shù)的函數(shù),都認(rèn)為是沒(méi)有顯式的對(duì)參數(shù)的個(gè)數(shù)和類(lèi)型說(shuō)明,而不是沒(méi)有參數(shù).
而gcc無(wú)論是否使用這個(gè)參數(shù),都將對(duì)沒(méi)有帶參數(shù)的函數(shù),認(rèn)為城沒(méi)有顯式說(shuō)明的類(lèi)型
-fthis-is-varialble
就是向傳統(tǒng)c++看齊,可以使用this當(dāng)一般變量使用.
-fcond-mismatch
允許條件表達(dá)式的第二和第三參數(shù)類(lèi)型不匹配,表達(dá)式的值將為void類(lèi)型
-funsigned-char
-fno-signed-char
-fsigned-char
-fno-unsigned-char
這四個(gè)參數(shù)是對(duì)char類(lèi)型進(jìn)行設(shè)置,決定將char類(lèi)型設(shè)置成unsigned char(前兩個(gè)參數(shù))或者 signed char(后兩個(gè)參數(shù))
-include file
包含某個(gè)代碼,簡(jiǎn)單來(lái)說(shuō),就是便以某個(gè)文件,需要另一個(gè)文件的時(shí)候,就可以用它設(shè)定,功能就相當(dāng)于在代碼中使用#include;
例子用法:
gcc hello.c -include /root/pianopan.h
-imacros file
將file文件的宏,擴(kuò)展到gcc/g++的輸入文件,宏定義本身并不出現(xiàn)在輸入文件中
-Dmacro
以字符串“1”定義 MACRO 宏
相當(dāng)于C語(yǔ)言中的#define macro
-Dmacro=defn
以字符串“DEFN”定義 MACRO 宏
相當(dāng)于C語(yǔ)言中的#define macro defn
-Umacro
取消對(duì) MACRO 宏的定義
相當(dāng)于C語(yǔ)言中的#undef macro
-undef
取消對(duì)任何非標(biāo)準(zhǔn)宏的定義
-Idir
在你是用#include"file"的時(shí)候,gcc/g++會(huì)先在當(dāng)前目錄查找你所制定的頭文件,如果沒(méi)有找到,他回到缺省的頭文件目錄找,如果使用-I制定了目錄,他
回先在你所制定的目錄查找,然后再按常規(guī)的順序去找.
對(duì)于#include;,gcc/g++會(huì)到-I制定的目錄查找,查找不到,然后將到系統(tǒng)的缺省的頭文件目錄查找
-I-
就是取消前一個(gè)參數(shù)的功能,所以一般在-Idir之后使用
-idirafter dir
在-I的目錄里面查找失敗,講到這個(gè)目錄里面查找.
-iprefix prefix
-iwithprefix dir
一般一起使用,當(dāng)-I的目錄查找失敗,會(huì)到prefix+dir下查找
-nostdinc
使編譯器不再系統(tǒng)缺省的頭文件目錄里面找頭文件,一般和-I聯(lián)合使用,明確限定頭文件的位置
-nostdin C++
規(guī)定不在g++指定的標(biāo)準(zhǔn)路經(jīng)中搜索,但仍在其他路徑中搜索,.此選項(xiàng)在創(chuàng)libg++庫(kù)使用
-C
在預(yù)處理的時(shí)候,不刪除注釋信息,一般和-E使用,有時(shí)候分析程序,用這個(gè)很方便的
-M
生成文件關(guān)聯(lián)的信息。包含目標(biāo)文件所依賴(lài)的所有源代碼你可以用gcc -M hello.c來(lái)測(cè)試一下,很簡(jiǎn)單。
-MM
和上面的那個(gè)一樣,但是它將忽略由#include;造成的依賴(lài)關(guān)系。
-MD
和-M相同,但是輸出將導(dǎo)入到.d的文件里面
-MMD
和-MM相同,但是輸出將導(dǎo)入到.d的文件里面
-Wa,option
此選項(xiàng)傳遞option給匯編程序;如果option中間有逗號(hào),就將option分成多個(gè)選項(xiàng),然后傳遞給會(huì)匯編程序
-Wl.option
此選項(xiàng)傳遞option給連接程序;如果option中間有逗號(hào),就將option分成多個(gè)選項(xiàng),然后傳遞給會(huì)連接程序.
-llibrary
制定編譯的時(shí)候使用的庫(kù)
例子用法
gcc -lcurses hello.c
使用ncurses庫(kù)編譯程序
-Ldir
制定編譯的時(shí)候,搜索庫(kù)的路徑。比如你自己的庫(kù),可以用它制定目錄,不然
編譯器將只在標(biāo)準(zhǔn)庫(kù)的目錄找。這個(gè)dir就是目錄的名稱(chēng)。
-O0
-O1
-O2
-O3
編譯器的優(yōu)化選項(xiàng)的4個(gè)級(jí)別,-O0表示沒(méi)有優(yōu)化,-O1為缺省值,-O3優(yōu)化級(jí)別最高
例子用法:
gcc -O1 test.c -o test
使用編譯優(yōu)化級(jí)別1編譯程序。級(jí)別為1~3,級(jí)別越大優(yōu)化效果越好,但編譯時(shí)間越長(zhǎng)
-g
只是編譯器,在編譯的時(shí)候,產(chǎn)生調(diào)試信息。
-gstabs
此選項(xiàng)以stabs格式聲稱(chēng)調(diào)試信息,但是不包括gdb調(diào)試信息.
-gstabs+
此選項(xiàng)以stabs格式聲稱(chēng)調(diào)試信息,并且包含僅供gdb使用的額外調(diào)試信息.
-ggdb
此選項(xiàng)將盡可能的生成gdb的可以使用的調(diào)試信息.
-static
此選項(xiàng)將禁止使用動(dòng)態(tài)庫(kù),所以,編譯出來(lái)的東西,一般都很大,也不需要什么
動(dòng)態(tài)連接庫(kù),就可以運(yùn)行.
-share
此選項(xiàng)將盡量使用動(dòng)態(tài)庫(kù),所以生成文件比較小,但是需要系統(tǒng)由動(dòng)態(tài)庫(kù).
-traditional
試圖讓編譯器支持傳統(tǒng)的C語(yǔ)言特性
-IDIRECTORY
指定額外的頭文件搜索路徑DIRECTORY
-LDIRECTORY
指定額外的函數(shù)庫(kù)搜索路徑DIRECTORY
-lLIBRARY
連接時(shí)搜索指定的函數(shù)庫(kù)LIBRARY
-m486
針對(duì) 486 進(jìn)行代碼優(yōu)化
-shared
生成共享目標(biāo)文件。通常用在建立共享庫(kù)時(shí)
-static
禁止使用共享連接
-w
不生成任何警告信息
-Wall
生成所有警告信息
-save-temps
一次獲得全部的中文輸出文件,正常的進(jìn)行編譯連接,.i、.s、.o為后綴,文件名相同
-fsyntax-only
不會(huì)執(zhí)行預(yù)處理、編譯、匯編、連接,只會(huì)測(cè)試輸入文件的語(yǔ)法是否正確
-std
指定C方言,如:-std=c99,gcc默認(rèn)的方言是GNU C
多源文件的編譯方法
如果有多個(gè)源文件,基本上有兩種編譯方法:
[假設(shè)有兩個(gè)源文件為test.c和testfun.c]
1. 多個(gè)文件一起編譯
用法:#gcc testfun.c test.c -o test
作用:將testfun.c和test.c分別編譯后鏈接成test可執(zhí)行文件。
2. 分別編譯各個(gè)源文件,之后對(duì)編譯后輸出的目標(biāo)文件鏈接。
用法:
#gcc -c testfun.c //將testfun.c編譯成testfun.o
#gcc -c test.c //將test.c編譯成test.o
#gcc -o testfun.o test.o -o test //將testfun.o和test.o鏈接成test
以上兩種方法相比較,第一中方法編譯時(shí)需要所有文件重新編譯,而第二種方法可以只重新編譯修改的文件,未修改的文件不用重新編譯。
FAQ
1、為什么會(huì)出現(xiàn)undefined reference to 'xxxxx'錯(cuò)誤?
首 先這是鏈接錯(cuò)誤,不是編譯錯(cuò)誤,也就是說(shuō)如果只有這個(gè)錯(cuò)誤,說(shuō)明你的程序源碼本身沒(méi)有問(wèn)題,是你用編譯器編譯時(shí)參數(shù)用得不對(duì),你沒(méi)有指定鏈接程序要用到得 庫(kù),比如你的程序里用到了一些數(shù)學(xué)函數(shù),那么你就要在編譯參數(shù)里指定程序要鏈接數(shù)學(xué)庫(kù),方法是在編譯命令行里加入-lm。
2、-l參數(shù)和-L參數(shù)
-l參數(shù)就是用來(lái)指定程序要鏈接的庫(kù),-l參數(shù)緊接著就是庫(kù)名,那么庫(kù)名跟真正的庫(kù)文
件名有什么關(guān)系呢?
就拿數(shù)學(xué)庫(kù)來(lái)說(shuō),他的庫(kù)名是m,他的庫(kù)文件名是libm.so,很容易看出,把庫(kù)文件名的頭lib和尾.so去掉就是庫(kù)名了。
好 了現(xiàn)在我們知道怎么得到庫(kù)名了,比如我們自已要用到一個(gè)第三方提供的庫(kù)名字叫l(wèi)ibtest.so,那么我們只要把libtest.so拷貝到/usr /lib里,編譯時(shí)加上-ltest參數(shù),我們就能用上libtest.so庫(kù)了(當(dāng)然要用libtest.so庫(kù)里的函數(shù),我們還需要與 libtest.so配套的頭文件)。
放在/lib和/usr/lib和/usr/local/lib里的庫(kù)直接用-l參數(shù)就能鏈接了,但如果庫(kù)文件
沒(méi) 放在這三個(gè)目錄里,而是放在其他目錄里,這時(shí)我們只用-l參數(shù)的話,鏈接還是會(huì)出錯(cuò),出錯(cuò)信息大概是:“/usr/bin/ld: cannot find -lxxx”,也就是鏈接程序ld在那3個(gè)目錄里找不到libxxx.so,這時(shí)另外一個(gè)參數(shù)-L就派上用場(chǎng)了,比如常用的X11的庫(kù),它放在/usr /X11R6/lib目錄下,我們編譯時(shí)就要用-L/usr/X11R6/lib -lX11參數(shù),-L參數(shù)跟著的是庫(kù)文件所在的目錄名。
再比如我們把libtest.so放在/aaa/bbb/ccc目錄下,那鏈接參數(shù)就是-L/aaa/bbb/ccc -ltest
另 外,大部分libxxxx.so只是一個(gè)鏈接,以RH9為例,比如libm.so它鏈接到/lib/libm.so.x,/lib/libm.so.6又 鏈到/lib/libm-2.3.2.so,如果沒(méi)有這樣的鏈接,還是會(huì)出錯(cuò),因?yàn)閘d只會(huì)找libxxxx.so,所以如果你要用到xxxx庫(kù),而只有 libxxxx.so.x或者libxxxx-x.x.x.so,做一個(gè)鏈接就可以了ln -s libxxxx-x.x.x.so libxxxx.so,手工來(lái)寫(xiě)鏈接參數(shù)總是很麻煩的,還好很多庫(kù)開(kāi)發(fā)包提供了生成鏈接參數(shù)的程序,名字一般叫xxxx-config,一般放在/usr /bin目錄下,比如gtk1.2的鏈接參數(shù)生成程序是gtk-config,執(zhí)行g(shù)tk-config --libs就能得到以下輸出"-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic -lgmodule -lglib -ldl -lXi -lXext -lX11 -lm",這就是編譯一個(gè)gtk1.2程序所需的gtk鏈接參數(shù),xxx-config除了--libs參數(shù)外還有一個(gè)參數(shù)是--cflags用來(lái)生成頭 文件包含目錄的,也就是-I參數(shù),在下面我們將會(huì)講到。你可以試試執(zhí)行g(shù)tk-config --libs --cflags,看看輸出結(jié)果?,F(xiàn)在的問(wèn)題就是怎樣用這些輸出結(jié)果了,最笨的方法就是復(fù)制粘貼或者照抄,聰明的辦法是在編譯命令行里加入這個(gè) `xxxx-config --libs --cflags`,比如編譯一個(gè)gtk程序:gcc gtktest.c `gtk-config --libs --cflags`這樣就差不多了。注意`不是單引號(hào),而是1鍵左邊那個(gè)鍵。除了xxx-config以外,現(xiàn)在新的開(kāi)發(fā)包一般都用pkg-config 來(lái)生成鏈接參數(shù),使用方法跟xxx-config類(lèi)似,但xxx-config是針對(duì)特定的開(kāi)發(fā)包,但pkg-config包含很多開(kāi)發(fā)包的鏈接參數(shù)的生 成,用pkg-config --list-all命令可以列出所支持的所有開(kāi)發(fā)包,pkg-config的用法就是pkg-config pagName --libs --cflags,其中pagName是包名,是pkg-config--list-all里列出名單中的一個(gè),比如gtk1.2的名字就是 gtk+,pkg-config gtk+ --libs --cflags的作用跟gtk-config --libs --cflags是一樣的。比如:gcc gtktest.c `pkg-config gtk+ --libs --cflags`。
3、-include和-I參數(shù)
-include用來(lái)包含頭文件,但一般情況下包含頭文件都在源碼 里用#include xxxxxx實(shí)現(xiàn),-include參數(shù)很少用。-I參數(shù)是用來(lái)指定頭文件目錄,/usr/include目錄一般是不用指定的,gcc知道去那里找,但 是如果頭文件不在/usr/include里我們就要用-I參數(shù)指定了,比如頭文件放在/myinclude目錄里,那編譯命令行就要加上-I /myinclude參數(shù)了,如果不加你會(huì)得到一個(gè)"xxxx.h: No such file or directory"的錯(cuò)誤。-I參數(shù)可以用相對(duì)路徑,比如頭文件在當(dāng)前目錄,可以用-I.來(lái)指定。上面我們提到的--cflags參數(shù)就是用來(lái)生成-I 參數(shù)的。
4、幾個(gè)相關(guān)的環(huán)境變量
PKG_CONFIG_PATH:用來(lái)指定pkg-config用到的pc文件的路徑,默認(rèn)是/usr/lib/pkgconfig,pc文件是文本文件,擴(kuò)展名是.pc,里面定義開(kāi)發(fā)包的安裝路徑,Libs參數(shù)和Cflags參數(shù)等等。
CC:用來(lái)指定c編譯器。
CXX:用來(lái)指定cxx編譯器。
LIBS:跟上面的--libs作用差不多。
CFLAGS:跟上面的--cflags作用差不多。
CC,CXX,LIBS,CFLAGS手動(dòng)編譯時(shí)一般用不上,在做configure時(shí)有時(shí)用到,一般情況下不用管。
環(huán)境變量設(shè)定方法:export ENV_NAME=xxxxxxxxxxxxxxxxx
CPATH、C_INCLUDE_PATH
用逗號(hào)隔開(kāi)的目錄列表,提供頭文件搜索位置
COMPILER_PATH
用逗號(hào)隔開(kāi)的目錄列表,以提供GCC子程序的搜索位置
GCC_EXEC_PREFIX
當(dāng)GCC調(diào)用子程序時(shí),需要“加在前面”的前置名稱(chēng)
LIBRARY_PATH
用逗號(hào)隔開(kāi)的目錄列表,以提供連接庫(kù)的位置
LD_LIBRARY_PATH
用逗號(hào)隔開(kāi)的目錄列表,以提供共享庫(kù)文件的搜索位置
TMPDIR
臨時(shí)文件所使用的目錄
5、關(guān)于交叉編譯
交叉編譯通俗地講就是在一種平臺(tái)上編譯出能運(yùn)行在體系結(jié)構(gòu)不同的另一種平臺(tái)上,比 如在我們地PC平臺(tái)(X86 CPU)上編譯出能運(yùn)行在sparc CPU平臺(tái)上的程序,編譯得到的程序在X86 CPU平臺(tái)上是不能運(yùn)行的,必須放到sparc CPU平臺(tái)上才能運(yùn)行。當(dāng)然兩個(gè)平臺(tái)用的都是linux。
這種方法在異平臺(tái)移植和嵌入式開(kāi)發(fā)時(shí)用得非常普遍。
聯(lián)系客服