亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫(kù)
最近訪問(wèn)板塊 發(fā)新帖
查看: 784 | 回復(fù): 0
打印 上一主題 下一主題

轉(zhuǎn)-gcc 精彩之旅 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2009-09-13 14:25 |只看該作者 |倒序?yàn)g覽

gcc 精彩之旅
在為L(zhǎng)inux開發(fā)應(yīng)用程序時(shí),絕大多數(shù)情況下使用的都是C語(yǔ)言,因此幾乎每一位Linux程序員面臨的首要問(wèn)題都是如何靈活運(yùn)用C編譯器。目前
Linux
下最常用的C語(yǔ)言編譯器是GCC(GNU Compiler Collection),它是GNU項(xiàng)目中符合ANSI
C標(biāo)準(zhǔn)的編譯系統(tǒng),
能夠編譯用C、C++和Object
C等語(yǔ)言編寫的程序。GCC不僅功能非常強(qiáng)大,結(jié)構(gòu)也異常靈活。最值得稱道的一點(diǎn)就是它可以通過(guò)不同的前端模塊來(lái)
支持各種語(yǔ)言,如Java、
Fortran、Pascal、Modula-3和Ada等。
開放、自由和靈活是Linux的魅力所在,而這一點(diǎn)在GCC上的體現(xiàn)就是程序員通過(guò)它能夠更好地控制整個(gè)編譯過(guò)程。在使用GCC編譯程序時(shí),編譯過(guò)程可
以被細(xì)分為四個(gè)階段:

預(yù)處理(Pre-Processing)
◆ 編譯(Compiling)
◆ 匯編(Assembling)

鏈接(Linking)
Linux程序員可以根據(jù)自己的需要讓
GCC在編譯的任何階段結(jié)束,以便檢查或使用編譯器在該階段的輸出信息,或者對(duì)最后生成的二進(jìn)制文件進(jìn)行控制,
以便通過(guò)加入不同數(shù)量和種類的調(diào)試代碼來(lái)為今后的調(diào)試做好準(zhǔn)備。和其它常用的編譯器一樣,GCC也提供了靈活而強(qiáng)大的代碼優(yōu)化功能,利用它可以生成執(zhí)行
效率更高的代碼。
GCC提供了30多條警告信息和三個(gè)警告級(jí)別,使用它們有助于增強(qiáng)程序的穩(wěn)定性和可移植性。此外,GCC還對(duì)標(biāo)準(zhǔn)的C和C++語(yǔ)言進(jìn)行了大量的擴(kuò)展,提
高程序的執(zhí)行效率,有助于編譯器進(jìn)行代碼優(yōu)化,能夠減輕編程的工作量。
GCC起步
在學(xué)習(xí)使用GCC之前,下面的這個(gè)例子能夠幫助用戶迅速理解GCC的工作原理,并將其立即運(yùn)用到實(shí)際的項(xiàng)目開發(fā)中去。首先用熟悉的編輯器輸入清單1所示
的代碼:
清單1:hello.c
#include
int main(void)
{
printf ("Hello world, Linux
programming!\\n");
return 0;
}
然后執(zhí)行下面的命令編譯和運(yùn)行這段程序:
# gcc hello.c -o
hello
# ./hello
Hello world, Linux
programming!
從程序員的角度看,只需簡(jiǎn)單地執(zhí)行一條GCC命令就可以了,但從編譯器的角度來(lái)看,卻需要完成一系列非常繁雜的工作。首先,GCC需要調(diào)用預(yù)處理程序
cpp,由它負(fù)責(zé)展開在源文件中定義的宏,并向其中插入"#include"語(yǔ)句所包含的內(nèi)容;接著,GCC會(huì)調(diào)用ccl和as將處理后的源代碼編譯成
目標(biāo)代碼;最后,GCC會(huì)調(diào)用鏈接程序ld,把生成的目標(biāo)代碼鏈接成一個(gè)可執(zhí)行程序。
為了更好地理解GCC的工作過(guò)程,可以把上述編譯過(guò)程分成幾個(gè)步驟單獨(dú)進(jìn)行,并觀察每步的運(yùn)行結(jié)果。第一步是進(jìn)行預(yù)編譯,使用-E參數(shù)可以讓GCC在預(yù)
處理結(jié)束后停止編譯過(guò)程:
#
gcc -E hello.c -o
hello.i
此時(shí)若查看hello.cpp文件中的內(nèi)容,會(huì)發(fā)現(xiàn)stdio.h的內(nèi)容確實(shí)都插到文件里去了,而其它應(yīng)當(dāng)被預(yù)處理的宏定義也都做了相應(yīng)的處理。下一步
是將hello.i編譯為目標(biāo)代碼,這可以通過(guò)使用-c參數(shù)來(lái)完成:
#
gcc -c hello.i -o
hello.o
GCC默認(rèn)將.i文件看成是預(yù)處理后的C語(yǔ)言源代碼,因此上述命令將自動(dòng)跳過(guò)預(yù)處理步驟而開始執(zhí)行編譯過(guò)程,也可以使用-x參數(shù)讓GCC從指定的步驟開
始編譯。最后一步是將生成的目標(biāo)文件鏈接成可執(zhí)行文件:
#
gcc hello.o -o
hello
在采用模塊化的設(shè)計(jì)思想進(jìn)行軟件開發(fā)時(shí),通常整個(gè)程序是由多個(gè)源文件組成的,相應(yīng)地也就形成了多個(gè)編譯單元,使用GCC能夠很好地管理這些編譯單元。假
設(shè)有一個(gè)由foo1.c和foo2.c兩個(gè)源文件組成的程序,為了對(duì)它們進(jìn)行編譯,并最終生成可執(zhí)行程序foo,可以使用下面這條命令:
#
gcc foo1.c foo2.c -o
foo
如果同時(shí)處理的文件不止一個(gè),GCC仍然會(huì)按照預(yù)處理、編譯和鏈接的過(guò)程依次進(jìn)行。如果深究起來(lái),上面這條命令大致相當(dāng)于依次執(zhí)行如下三條命令:
#
gcc -c foo1.c -o foo1.o
# gcc -c foo2.c -o foo2.o
# gcc foo1.o foo2.o -o
foo
在編譯一個(gè)包含許多源文件的工程時(shí),若只用一條GCC命令來(lái)完成編譯是非常浪費(fèi)時(shí)間的。假設(shè)項(xiàng)目中有100個(gè)源文件需要編譯,并且每個(gè)源文件中都包含
10000行代碼,如果像上面那樣僅用一條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ì)改變的。要解
決這個(gè)問(wèn)題,關(guān)鍵是要靈活運(yùn)用GCC,同時(shí)還要借助像Make這樣的工具。
警告提示功能
GCC包含完整的出錯(cuò)檢查和警告提示功能,它們可以幫助Linux程序員寫出更加專業(yè)和優(yōu)美的代碼。先來(lái)讀讀清單2所示的程序,這段代碼寫得很糟糕,仔
細(xì)檢查一下不難挑出很多毛。
◆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:illcode.c
#include
void main(void)
{
long long int var = 1;
printf("It
is not standard C
code!\\n");
}
下面來(lái)看看GCC是如何幫助程序員來(lái)發(fā)現(xiàn)這些錯(cuò)誤的。當(dāng)GCC在編譯不符合ANSI/ISO
C語(yǔ)言標(biāo)準(zhǔn)的源代碼時(shí),如果加上了-pedantic選
項(xiàng),那么使用了擴(kuò)展語(yǔ)法的地方將產(chǎn)生相應(yīng)的警告信息:
# gcc -pedantic
illcode.c -o illcode
illcode.c: In function `main':
illcode.c:9: ISO C89
does not support `long long'
illcode.c:8: return type of `main' is not
`int'
需要注意的是,-pedantic編譯選項(xiàng)并不能保證被編譯程序與ANSI/ISO
C標(biāo)準(zhǔn)的完全兼容,它僅僅只能用來(lái)幫助Linux程序員離這個(gè)目標(biāo)
越來(lái)越近。或者換句話說(shuō),-pedantic選項(xiàng)能夠幫助程序員發(fā)現(xiàn)一些不符合
ANSI/ISO C標(biāo)準(zhǔn)的代碼,但不是全部,事實(shí)上只有ANSI/
ISO
C語(yǔ)言標(biāo)準(zhǔn)中要求進(jìn)行編譯器診斷的那些情況,才有可能被GCC發(fā)現(xiàn)并提出警告。
除了-pedantic之外,GCC還有一些其它編譯選項(xiàng)也能夠產(chǎn)生有用的警告信息。這些選項(xiàng)大多以-W開頭,其中最有價(jià)值的當(dāng)數(shù)-Wall了,使用它
能夠使GCC產(chǎn)生盡可能多的警告信息:
#
gcc -Wall illcode.c -o illcode
illcode.c:8: warning: return type of `main' is
not `int'
illcode.c: In function `main':
illcode.c:9: warning: unused
variable
`var'
GCC給出的警告信息雖然從嚴(yán)格意義上說(shuō)不能算作是錯(cuò)誤,但卻很可能成為錯(cuò)誤的棲身之所。一個(gè)優(yōu)秀的Linux程序員應(yīng)該盡量避免產(chǎn)生警告信息,使自己
的代碼始終保持簡(jiǎn)潔、優(yōu)美和健壯的特性。
在處理警告方面,另一個(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)。執(zhí)行情況如下:
#
gcc -Wall -Werror illcode.c -o illcode
cc1: warnings being treated as
errors
illcode.c:8: warning: return type of `main' is not `int'
illcode.c:
In function `main':
illcode.c:9: warning: unused variable
`var'
對(duì)Linux程序員來(lái)講,GCC給出的警告信息是很有價(jià)值的,它們不僅可以幫助程序員寫出更加健壯的程序,而且還是跟蹤和調(diào)試程序的有力工具。建議在用
GCC編譯源代碼時(shí)始終帶上-Wall選項(xiàng),并把它逐漸培養(yǎng)成為一種習(xí)慣,這對(duì)找出常見(jiàn)的隱式編程錯(cuò)誤很有幫助。
庫(kù)依賴
在Linux
下開發(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ù)文件。
GCC采用搜索目錄的辦法來(lái)查找所需要的文件,-I選項(xiàng)可以向GCC的頭文件搜索路徑中添加新的目錄。例如,如果在/home/xiaowp/
include/目錄下有編譯時(shí)所需要的頭文件,為了讓GCC能夠順利地找到它們,就可以使用-I選項(xiàng):
#
gcc foo.c -I /home/xiaowp/include -o
foo
同樣,如果使用了不在標(biāo)準(zhǔn)位置的庫(kù)文件,那么可以通過(guò)-L選項(xiàng)向GCC的庫(kù)文件搜索路徑中添加新的目錄。例如,如果在/home/xiaowp/
lib/目錄下有鏈接時(shí)所需要的庫(kù)文件libfoo.so,為了讓GCC能夠順利地找到它,可以使用下面的命令:
#
gcc foo.c -L /home/xiaowp/lib -lfoo -o
foo
值得好好解釋一下的是-l選項(xiàng),它指示GCC去連接庫(kù)文件libfoo.so。Linux下的庫(kù)文件在命名時(shí)有一個(gè)約定,那就是應(yīng)該以lib三個(gè)字母開
頭,由于所有的庫(kù)文件都遵循了同樣的規(guī)范,因此在用-l選項(xiàng)指定鏈接的庫(kù)文件名時(shí)可以省去lib三個(gè)字母,也就是說(shuō)GCC在對(duì)-lfoo進(jìn)行處理時(shí),會(huì)
自動(dòng)去鏈接名為libfoo.so的文件。
Linux下的庫(kù)文件分為兩大類分別是動(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ù)。例如,如果在/home/xiaowp/lib/目錄下有鏈接時(shí)所需要的庫(kù)文件
libfoo.so和
libfoo.a,為了讓 GCC在鏈接時(shí)只用到靜態(tài)鏈接庫(kù),可以使用下面的命令:
# gcc foo.c -L /home/xiaowp/lib
-static -lfoo -o
foo
代碼優(yōu)化
代碼優(yōu)化指的是編譯器通過(guò)分析源代碼,找出其中尚未達(dá)到最優(yōu)的部分,然后對(duì)其重新進(jìn)行組合,目的是改善程序的執(zhí)行性能。GCC提供的代碼優(yōu)化功能非常強(qiáng)
大,它通過(guò)編譯選項(xiàng)-On來(lái)控制優(yōu)化代碼的生成,其中n是一個(gè)代表優(yōu)化級(jí)別的整數(shù)。對(duì)于不同版本的GCC來(lái)講,n的取值范圍及其對(duì)應(yīng)的優(yōu)化效果可能并不
完全相同,比較典型的范圍是從0變化到2或3。
編譯時(shí)使用選項(xiàng)-O可以告訴
GCC同時(shí)減小代碼的長(zhǎng)度和執(zhí)行時(shí)間,其效果等價(jià)于-O1。在這一級(jí)別上能夠進(jìn)行的優(yōu)化類型雖然取決于目標(biāo)處理器,但一般
都會(huì)包括線程跳轉(zhuǎn)(Thread
Jump)和延遲退棧(Deferred Stack
Pops)兩種優(yōu)化。選項(xiàng)-O2告訴GCC除了完成所有-O1
級(jí)別的優(yōu)化之外,同時(shí)還要進(jìn)行一些額外的調(diào)整工作,如處理器指令調(diào)度等。選項(xiàng)-O3則除了完成所有-O2級(jí)別的優(yōu)化之外,還包括循環(huán)展開和其它一些與處
理器特性相關(guān)的優(yōu)化工作。通常來(lái)說(shuō),數(shù)字越大優(yōu)化的等級(jí)越高,同時(shí)也就意味著程序的運(yùn)行速度越快。許多Linux程序員都喜歡使用-O2選項(xiàng),因?yàn)樗?br /> 優(yōu)化長(zhǎng)度、編譯時(shí)間和代碼大小之間,取得了一個(gè)比較理想的平衡點(diǎn)。
下面通過(guò)具體實(shí)例來(lái)感受一下GCC的代碼優(yōu)化功能,所用程序如清單3所示。
清單3:optimize.c
#include
int main(void)
{
double counter;
double
result;
double temp;
for (counter = 0;
counter
int main(void)
{
int input =0;
printf("Input an
integer:");
scanf("%d", input);
printf("The integer you input is %d\\n",
input);
return 0;
}
編譯并運(yùn)行上述代碼,會(huì)產(chǎn)生一個(gè)嚴(yán)重的段錯(cuò)誤(Segmentation
fault)如下:
# gcc -g crash.c -o crash
# ./crash
Input an
integer:10
Segmentation
fault
為了更快速地發(fā)現(xiàn)錯(cuò)誤所在,可以使用GDB進(jìn)行跟蹤調(diào)試,方法如下:
# gdb crash
GNU gdb Red
Hat Linux
(5.3post-0.20021129.18rh)
......
(gdb)
當(dāng)GDB提示符出現(xiàn)的時(shí)候,表明GDB已經(jīng)做好準(zhǔn)備進(jìn)行調(diào)試了,現(xiàn)在可以通過(guò)run命令讓程序開始在GDB的監(jiān)控下運(yùn)行:
(gdb)
run
Starting program: /home/xiaowp/thesis/gcc/code/crash
Input an
integer:10
Program received signal SIGSEGV, Segmentation
fault.
0x4008576b in _IO_vfscanf_internal () from
/lib/libc.so.6
仔細(xì)分析一下GDB給出的輸出結(jié)果不難看出,程序是由于段錯(cuò)誤而導(dǎo)致異常中止的,說(shuō)明內(nèi)存操作出了問(wèn)題,具體發(fā)生問(wèn)題的地方是在調(diào)用
_IO_vfscanf_internal
( )的時(shí)候。為了得到更加有價(jià)值的信息,可以使用GDB提供的回溯跟蹤命令backtrace,執(zhí)行結(jié)果
如下:
(gdb)
backtrace
#0 0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6
#1
0xbffff0c0 in ?? ()
#2 0x4008e0ba in scanf () from /lib/libc.so.6
#3
0x08048393 in main () at crash.c:11
#4 0x40042917 in __libc_start_main ()
from
/lib/libc.so.6
跳過(guò)輸出結(jié)果中的前面三行,從輸出結(jié)果的第四行中不難看出,GDB已經(jīng)將錯(cuò)誤定位到crash.c中的第11行了,F(xiàn)在仔細(xì)檢查一下:
(gdb)
frame 3
#3 0x08048393 in main () at crash.c:11
11 scanf("%d",
input);
使用GDB提供的frame命令可以定位到發(fā)生錯(cuò)誤的代碼段,該命令后面跟著的數(shù)值可以在backtrace命令輸出結(jié)果中的行首找到。現(xiàn)在已經(jīng)發(fā)現(xiàn)錯(cuò)
誤所在了,應(yīng)該將
scanf("%d",
input);
改為
scanf("%d", &input);
完成后就可以退出GDB了,命令如下:
(gdb)
quit
GDB的功能遠(yuǎn)遠(yuǎn)不止如此,它還可以單步跟蹤程序、檢查內(nèi)存變量和設(shè)置斷點(diǎn)等。
調(diào)試時(shí)可能會(huì)需要用到編譯器產(chǎn)生的中間結(jié)果,這時(shí)可以使用-save-temps選項(xiàng),讓GCC將預(yù)處理代碼、匯編代碼和目標(biāo)代碼都作為文件保存起來(lái)。
如果想檢查生成的代碼是否能夠通過(guò)手工調(diào)整的辦法來(lái)提高執(zhí)行性能,在編譯過(guò)程中生成的中間文件將會(huì)很有幫助,具體情況如下:
#
gcc -save-temps foo.c -o foo
# ls foo*
foo foo.c foo.i foo.s
GCC
支持的其它調(diào)試選項(xiàng)還包括-p和-pg,它們會(huì)將剖析(Profiling)信息加入到最終生成的二進(jìn)制代碼中。剖析信息對(duì)于找出程序的性能瓶
頸很有幫助,是協(xié)助Linux程序員開發(fā)出高性能程序的有力工具。在編譯時(shí)加入-p選項(xiàng)會(huì)在生成的代碼中加入通用剖析工具(Prof)能夠識(shí)別的統(tǒng)計(jì)信
息,而-
pg選項(xiàng)則生成只有GNU剖析工具(Gprof)才能識(shí)別的統(tǒng)計(jì)信息。
最后提醒一點(diǎn),雖然GCC允許在優(yōu)化的同時(shí)加入調(diào)試符號(hào)信息,但優(yōu)化后的代碼對(duì)于調(diào)試本身而言將是一個(gè)很大的挑戰(zhàn)。代碼在經(jīng)過(guò)優(yōu)化之后,在源程序中聲明
和使用的變量很可能不再使用,控制流也可能會(huì)突然跳轉(zhuǎn)到意外的地方,循環(huán)語(yǔ)句有可能因?yàn)檠h(huán)展開而變得到處都有,所有這些對(duì)調(diào)試來(lái)講都將是一場(chǎng)噩夢(mèng)。建
議在調(diào)試的時(shí)候最好不使用任何優(yōu)化選項(xiàng),只有當(dāng)程序在最終發(fā)行的時(shí)候才考慮對(duì)其進(jìn)行優(yōu)化。
               
               
               

本文來(lái)自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u2/72955/showart_2051287.html
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國(guó)互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過(guò)ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP