版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、.gcc詳解在為Linux開發(fā)應(yīng)用程序時(shí),絕大多數(shù)情況下使用的都是C語言,因此幾乎每一位Linux程序員面臨的首要問題都是如何靈活運(yùn)用C編譯器.目前Linux下最常用的C語言編譯器是GCC(GNU Compiler Collection),它是GNU項(xiàng)目中符合ANSI C標(biāo)準(zhǔn)的編譯系統(tǒng),能夠編譯用C、C+和Object C等語言編寫的程序.GCC不僅功能非常強(qiáng)大,結(jié)構(gòu)也異常靈活.最值得稱道的一點(diǎn)就是它可以通過不同的前端模塊來支持各種語言,如Java、Fortran、Pascal、Modula-3和Ada等.開放、自由和靈活是Linux的魅力所在,而這一點(diǎn)在GCC上的體現(xiàn)就是程序員通過它能夠更好
2、地控制整個(gè)編譯過程.在使用GCC編譯程序時(shí),編譯過程可以被細(xì)分為四個(gè)階段: 預(yù)處理(Pre-Processing) 編譯(Compiling) 匯編(Assembling) 鏈接(Linking)Linux程序員可以根據(jù)自己的需要讓GCC在編譯的任何階段結(jié)束,以便檢查或使用編譯器在該階段的輸出信息,或者對最后生成的二進(jìn)制文件進(jìn)行控制,以便通過加入不同數(shù)量和種類的調(diào)試代碼來為今后的調(diào)試做好準(zhǔn)備.和其它常用的編譯器一樣,GCC也提供了靈活而強(qiáng)大的代碼優(yōu)化功能,利用它可以生成執(zhí)行效率更高的代碼.GCC提供了30多條警告信息和三個(gè)警告級(jí)別,使用它們有助于增強(qiáng)程序的穩(wěn)定性和可移植性.此外,GCC還對標(biāo)準(zhǔn)
3、的C和C+語言進(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# ./helloHello world, Linux programming!
4、從程序員的角度看,只需簡單地執(zhí)行一條GCC命令就可以了,但從編譯器的角度來看,卻需要完成一系列非常繁雜的工作.首先,GCC需要調(diào)用預(yù)處理程序cpp,由它負(fù)責(zé)展開在源文件中定義的宏,并向其中插入“#include”語句所包含的內(nèi)容;接著,GCC會(huì)調(diào)用ccl和as將處理后的源代碼編譯成目標(biāo)代碼;最后,GCC會(huì)調(diào)用鏈接程序ld,把生成的目標(biāo)代碼鏈接成一個(gè)可執(zhí)行程序.為了更好地理解GCC的工作過程,可以把上述編譯過程分成幾個(gè)步驟單獨(dú)進(jìn)行,并觀察每步的運(yùn)行結(jié)果.第一步是進(jìn)行預(yù)編譯,使用-E參數(shù)可以讓GCC在預(yù)處理結(jié)束后停止編譯過程:# gcc -E hello.c -o hello.i 此時(shí)若查看hel
5、lo.cpp文件中的內(nèi)容,會(huì)發(fā)現(xiàn)stdio.h的內(nèi)容確實(shí)都插到文件里去了,而其它應(yīng)當(dāng)被預(yù)處理的宏定義也都做了相應(yīng)的處理.下一步是將hello.i編譯為目標(biāo)代碼,這可以通過使用-c參數(shù)來完成:# gcc -c hello.i -o hello.o GCC默認(rèn)將.i文件看成是預(yù)處理后的C語言源代碼,因此上述命令將自動(dòng)跳過預(yù)處理步驟而開始執(zhí)行編譯過程,也可以使用-x參數(shù)讓GCC從指定的步驟開始編譯.最后一步是將生成的目標(biāo)文件鏈接成可執(zhí)行文件:# gcc hello.o -o hello 在采用模塊化的設(shè)計(jì)思想進(jìn)行軟件開發(fā)時(shí),通常整個(gè)程序是由多個(gè)源文件組成的,相應(yīng)地也就形成了多個(gè)編譯單元,使用GCC能
6、夠很好地管理這些編譯單元.假設(shè)有一個(gè)由foo1.c和foo2.c兩個(gè)源文件組成的程序,為了對它們進(jìn)行編譯,并最終生成可執(zhí)行程序foo,可以使用下面這條命令:# gcc foo1.c foo2.c -o foo 如果同時(shí)處理的文件不止一個(gè),GCC仍然會(huì)按照預(yù)處理、編譯和鏈接的過程依次進(jìn)行.如果深究起來,上面這條命令大致相當(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命令來完成編譯是非常浪費(fèi)時(shí)間的.假設(shè)項(xiàng)目中有100
7、個(gè)源文件需要編譯,并且每個(gè)源文件中都包含10000行代碼,如果像上面那樣僅用一條GCC命令來完成編譯工作,那么GCC需要將每個(gè)源文件都重新編譯一遍,然后再全部連接起來.很顯然,這樣浪費(fèi)的時(shí)間相當(dāng)多,尤其是當(dāng)用戶只是修改了其中某一個(gè)文件的時(shí)候,完全沒有必要將每個(gè)文件都重新編譯一遍,因?yàn)楹芏嘁呀?jīng)生成的目標(biāo)文件是不會(huì)改變的.要解決這個(gè)問題,關(guān)鍵是要靈活運(yùn)用GCC,同時(shí)還要借助像Make這樣的工具.警告提示功能GCC包含完整的出錯(cuò)檢查和警告提示功能,它們可以幫助Linux程序員寫出更加專業(yè)和優(yōu)美的代碼.先來讀讀清單2所示的程序,這段代碼寫得很糟糕,仔細(xì)檢查一下不難挑出很多毛病:main函數(shù)的返回值被聲
8、明為void,但實(shí)際上應(yīng)該是int;使用了GNU語法擴(kuò)展,即使用long long來聲明64位整數(shù),不符合ANSI/ISO C語言標(biāo)準(zhǔn);main函數(shù)在終止前沒有調(diào)用return語句.清單2:illcode.c#include void main(void) long long int var = 1; printf(It is not standard C code!n); 下面來看看GCC是如何幫助程序員來發(fā)現(xiàn)這些錯(cuò)誤的.當(dāng)GCC在編譯不符合ANSI/ISO C語言標(biāo)準(zhǔn)的源代碼時(shí),如果加上了-pedantic選項(xiàng),那么使用了擴(kuò)展語法的地方將產(chǎn)生相應(yīng)的警告信息:# gcc -pedantic
9、illcode.c -o illcodeillcode.c: In function main:illcode.c:9: ISO C89 does not support long longillcode.c:8: return type of main is not int 需要注意的是,-pedantic編譯選項(xiàng)并不能保證被編譯程序與ANSI/ISO C標(biāo)準(zhǔn)的完全兼容,它僅僅只能用來幫助Linux程序員離這個(gè)目標(biāo)越來越近.或者換句話說,-pedantic選項(xiàng)能夠幫助程序員發(fā)現(xiàn)一些不符合ANSI/ISO C標(biāo)準(zhǔn)的代碼,但不是全部,事實(shí)上只有ANSI/ISO C語言標(biāo)準(zhǔn)中要求進(jìn)行編譯器診斷的那
10、些情況,才有可能被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 illcodeillcode.c:8: warning: return type of main is not intillcode.c: In function main:illcode.c:9: warning: unused variable var GCC給出的警告信息雖然從嚴(yán)格意義上說不能算作是錯(cuò)誤,但卻很可能成為錯(cuò)誤的
11、棲身之所.一個(gè)優(yōu)秀的Linux程序員應(yīng)該盡量避免產(chǎ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)生警告的地方停止編譯,迫使程序員對自己的代碼進(jìn)行修改.只有當(dāng)相應(yīng)的警告信息消除時(shí),才可能將編譯過程繼續(xù)朝前推進(jìn).執(zhí)行情況如下:# gcc -Wall -Werror illcode.c -o illcodecc1: warnings being treated as errorsillcode.
12、c:8: warning: return type of main is not intillcode.c: In function main:illcode.c:9: warning: unused variable var 對Linux程序員來講,GCC給出的警告信息是很有價(jià)值的,它們不僅可以幫助程序員寫出更加健壯的程序,而且還是跟蹤和調(diào)試程序的有力工具.建議在用GCC編譯源代碼時(shí)始終帶上-Wall選項(xiàng),并把它逐漸培養(yǎng)成為一種習(xí)慣,這對找出常見的隱式編程錯(cuò)誤很有幫助.庫依賴在Linux下開發(fā)軟件時(shí),完全不使用第三方函數(shù)庫的情況是比較少見的,通常來講都需要借助一個(gè)或多個(gè)函數(shù)庫的支持才能夠完成
13、相應(yīng)的功能.從程序員的角度看,函數(shù)庫實(shí)際上就是一些頭文件(.h)和庫文件(.so或者.a)的集合.雖然Linux下的大多數(shù)函數(shù)都默認(rèn)將頭文件放到/usr/include/目錄下,而庫文件則放到/usr/lib/目錄下,但并不是所有的情況都是這樣.正因如此,GCC在編譯時(shí)必須有自己的辦法來查找所需要的頭文件和庫文件.GCC采用搜索目錄的辦法來查找所需要的文件,-I選項(xiàng)可以向GCC的頭文件搜索路徑中添加新的目錄.例如,如果在/home/xiaowp/include/目錄下有編譯時(shí)所需要的頭文件,為了讓GCC能夠順利地找到它們,就可以使用-I選項(xiàng):# gcc foo.c -I /home/xiaow
14、p/include -o foo 同樣,如果使用了不在標(biāo)準(zhǔn)位置的庫文件,那么可以通過-L選項(xiàng)向GCC的庫文件搜索路徑中添加新的目錄.例如,如果在/home/xiaowp/lib/目錄下有鏈接時(shí)所需要的庫文件libfoo.so,為了讓GCC能夠順利地找到它,可以使用下面的命令:# gcc foo.c -L /home/xiaowp/lib -lfoo -o foo 值得好好解釋一下的是-l選項(xiàng),它指示GCC去連接庫文件libfoo.so.Linux下的庫文件在命名時(shí)有一個(gè)約定,那就是應(yīng)該以lib三個(gè)字母開頭,由于所有的庫文件都遵循了同樣的規(guī)范,因此在用-l選項(xiàng)指定鏈接的庫文件名時(shí)可以省去lib三
15、個(gè)字母,也就是說GCC在對-lfoo進(jìn)行處理時(shí),會(huì)自動(dòng)去鏈接名為libfoo.so的文件.Linux下的庫文件分為兩大類分別是動(dòng)態(tài)鏈接庫(通常以.so結(jié)尾)和靜態(tài)鏈接庫(通常以.a結(jié)尾),兩者的差別僅在程序執(zhí)行時(shí)所需的代碼是在運(yùn)行時(shí)動(dòng)態(tài)加載的,還是在編譯時(shí)靜態(tài)加載的.默認(rèn)情況下,GCC在鏈接時(shí)優(yōu)先使用動(dòng)態(tài)鏈接庫,只有當(dāng)動(dòng)態(tài)鏈接庫不存在時(shí)才考慮使用靜態(tài)鏈接庫,如果需要的話可以在編譯時(shí)加上-static選項(xiàng),強(qiáng)制使用靜態(tài)鏈接庫.例如,如果在/home/xiaowp/lib/目錄下有鏈接時(shí)所需要的庫文件libfoo.so和libfoo.a,為了讓GCC在鏈接時(shí)只用到靜態(tài)鏈接庫,可以使用下面的命令:#
16、 gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo 代碼優(yōu)化代碼優(yōu)化指的是編譯器通過分析源代碼,找出其中尚未達(dá)到最優(yōu)的部分,然后對其重新進(jìn)行組合,目的是改善程序的執(zhí)行性能.GCC提供的代碼優(yōu)化功能非常強(qiáng)大,它通過編譯選項(xiàng)-On來控制優(yōu)化代碼的生成,其中n是一個(gè)代表優(yōu)化級(jí)別的整數(shù).對于不同版本的GCC來講,n的取值范圍及其對應(yīng)的優(yōu)化效果可能并不完全相同,比較典型的范圍是從0變化到2或3.編譯時(shí)使用選項(xiàng)-O可以告訴GCC同時(shí)減小代碼的長度和執(zhí)行時(shí)間,其效果等價(jià)于-O1.O1在這一級(jí)別上能夠進(jìn)行的優(yōu)化類型雖然取決于目標(biāo)處理器,但一般都會(huì)包括線程跳轉(zhuǎn)
17、(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)化工作.通常來說,數(shù)字越大優(yōu)化的等級(jí)越高,同時(shí)也就意味著程序的運(yùn)行速度越快.許多Linux程序員都喜歡使用-O2選項(xiàng),因?yàn)樗趦?yōu)化長度、編譯時(shí)間和代碼大小之間,取得了一個(gè)比較理想的平衡點(diǎn).下面通過具體實(shí)例來感受一下GCC的代碼優(yōu)化功能,所用程序如清單3所示.清單3:optimize.c#include int ma
18、in(void) double counter; double result; double temp; for (counter = 0; counter 2000.0 * 2000.0 * 2000.0 / 20.0 + 2020; counter += (5 - 1) / 4) temp = counter / 1979; result = counter; printf(Result is %lfn, result); return 0; 首先不加任何優(yōu)化選項(xiàng)進(jìn)行編譯:# gcc -Wall optimize.c -o optimize 借助Linux提供的time命令,可以大致統(tǒng)計(jì)出
19、該程序在運(yùn)行時(shí)所需要的時(shí)間:# time ./optimizeResult is 400002019.000000real 0m14.942suser 0m14.940ssys 0m0.000s 接下去使用優(yōu)化選項(xiàng)來對代碼進(jìn)行優(yōu)化處理:# gcc -Wall -O optimize.c -o optimize 在同樣的條件下再次測試一下運(yùn)行時(shí)間:# time ./optimizeResult is 400002019.000000real 0m3.256suser 0m3.240ssys 0m0.000s 對比兩次執(zhí)行的輸出結(jié)果不難看出,程序的性能的確得到了很大幅度的改善,由原來的14秒縮短到
20、了3秒.這個(gè)例子是專門針對GCC的優(yōu)化功能而設(shè)計(jì)的,因此優(yōu)化前后程序的執(zhí)行速度發(fā)生了很大的改變.盡管GCC的代碼優(yōu)化功能非常強(qiáng)大,但作為一名優(yōu)秀的Linux程序員,首先還是要力求能夠手工編寫出高質(zhì)量的代碼.如果編寫的代碼簡短,并且邏輯性強(qiáng),編譯器就不會(huì)做更多的工作,甚至根本用不著優(yōu)化.優(yōu)化雖然能夠給程序帶來更好的執(zhí)行性能,但在如下一些場合中應(yīng)該避免優(yōu)化代碼: 程序開發(fā)的時(shí)候 優(yōu)化等級(jí)越高,消耗在編譯上的時(shí)間就越長,因此在開發(fā)的時(shí)候最好不要使用優(yōu)化選項(xiàng),只有到軟件發(fā)行或開發(fā)結(jié)束的時(shí)候,才考慮對最終生成的代碼進(jìn)行優(yōu)化. 資源受限的時(shí)候 一些優(yōu)化選項(xiàng)會(huì)增加可執(zhí)行代碼的體積,如果程序在運(yùn)行時(shí)能夠申請到
21、的內(nèi)存資源非常緊張(如一些實(shí)時(shí)嵌入式設(shè)備),那就不要對代碼進(jìn)行優(yōu)化,因?yàn)橛蛇@帶來的負(fù)面影響可能會(huì)產(chǎn)生非常嚴(yán)重的后果. 跟蹤調(diào)試的時(shí)候 在對代碼進(jìn)行優(yōu)化的時(shí)候,某些代碼可能會(huì)被刪除或改寫,或者為了取得更佳的性能而進(jìn)行重組,從而使跟蹤和調(diào)試變得異常困難.調(diào)試一個(gè)功能強(qiáng)大的調(diào)試器不僅為程序員提供了跟蹤程序執(zhí)行的手段,而且還可以幫助程序員找到解決問題的方法.對于Linux程序員來講,GDB(GNU Debugger)通過與GCC的配合使用,為基于Linux的軟件開發(fā)提供了一個(gè)完善的調(diào)試環(huán)境.默認(rèn)情況下,GCC在編譯時(shí)不會(huì)將調(diào)試符號(hào)插入到生成的二進(jìn)制代碼中,因?yàn)檫@樣會(huì)增加可執(zhí)行文件的大小.如果需要在編譯
22、時(shí)生成調(diào)試符號(hào)信息,可以使用GCC的-g或者-ggdb選項(xiàng).GCC在產(chǎn)生調(diào)試符號(hào)時(shí),同樣采用了分級(jí)的思路,開發(fā)人員可以通過在-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)行過程中的函數(shù)調(diào)用歷史,堆棧轉(zhuǎn)儲(chǔ)則是一種以原始的十六進(jìn)制格式保存程序執(zhí)行環(huán)境的方法,兩者都是經(jīng)常用到的調(diào)試手段.GCC產(chǎn)生的調(diào)試符號(hào)具有普遍的
23、適應(yīng)性,可以被許多調(diào)試器加以利用,但如果使用的是GDB,那么還可以通過-ggdb選項(xiàng)在生成的二進(jìn)制代碼中包含GDB專用的調(diào)試信息.這種做法的優(yōu)點(diǎn)是可以方便GDB的調(diào)試工作,但缺點(diǎn)是可能導(dǎo)致其它調(diào)試器(如DBX)無法進(jìn)行正常的調(diào)試.選項(xiàng)-ggdb能夠接受的調(diào)試級(jí)別和-g是完全一樣的,它們對輸出的調(diào)試符號(hào)有著相同的影響.需要注意的是,使用任何一個(gè)調(diào)試選項(xiàng)都會(huì)使最終生成的二進(jìn)制文件的大小急劇增加,同時(shí)增加程序在執(zhí)行時(shí)的開銷,因此調(diào)試選項(xiàng)通常僅在軟件的開發(fā)和調(diào)試階段使用.調(diào)試選項(xiàng)對生成代碼大小的影響從下面的對比過程中可以看出來:# gcc optimize.c -o optimize# ls opti
24、mize -l-rwxrwxr-x 1 xiaowp xiaowp 11649 Nov 20 08:53 optimize (未加調(diào)試選項(xiàng))# gcc -g optimize.c -o optimize# ls optimize -l-rwxrwxr-x 1 xiaowp xiaowp 15889 Nov 20 08:54 optimize (加入調(diào)試選項(xiàng)) 雖然調(diào)試選項(xiàng)會(huì)增加文件的大小,但事實(shí)上Linux中的許多軟件在測試版本甚至最終發(fā)行版本中仍然使用了調(diào)試選項(xiàng)來進(jìn)行編譯,這樣做的目的是鼓勵(lì)用戶在發(fā)現(xiàn)問題時(shí)自己動(dòng)手解決,是Linux的一個(gè)顯著特色.為調(diào)試編譯代碼(Compiling Code
25、 for Debugging)為了使 gdb 正常工作, 你必須使你的程序在編譯時(shí)包含調(diào)試信息. 調(diào)試信息包含你程序里的每個(gè)變量的類型和在可執(zhí)行文件里的地址映射以及源代碼的行號(hào). gdb 利用這些信息使源代碼和機(jī)器碼相關(guān)聯(lián). 在編譯時(shí)用 -g 選項(xiàng)打開調(diào)試選項(xiàng). gdb 基本命令 gdb 支持很多的命令使你能實(shí)現(xiàn)不同的功能. 這些命令從簡單的文件裝入到允許你檢查所調(diào)用的堆棧內(nèi)容的復(fù)雜命令, 表27.1列出了你在用 gdb 調(diào)試時(shí)會(huì)用到的一些命令. 想了解 gdb 的詳細(xì)使用請參考 gdb 的指南頁. 表 27.1. 基本 gdb 命令.命 令 描 述 file 裝入想要調(diào)試的可執(zhí)行文件. ki
26、ll 終止正在調(diào)試的程序. list 列出產(chǎn)生執(zhí)行文件的源代碼的一部分. next 執(zhí)行一行源代碼但不進(jìn)入函數(shù)內(nèi)部. step 執(zhí)行一行源代碼而且進(jìn)入函數(shù)內(nèi)部. run 執(zhí)行當(dāng)前被調(diào)試的程序 quit 終止 gdb watch 使你能監(jiān)視一個(gè)變量的值而不管它何時(shí)被改變. break 在代碼里設(shè)置斷點(diǎn), 這將使程序執(zhí)行到這里時(shí)被掛起. make 使你能不退出 gdb 就可以重新產(chǎn)生可執(zhí)行文件. shell 使你能不離開 gdb 就執(zhí)行 UNIX shell 命令. gdb 支持很多與 UNIX shell 程序一樣的命令編輯特征. 你能象在 bash 或 tcsh里那樣按 Tab 鍵讓gdb 幫
27、你補(bǔ)齊一個(gè)唯一的命令, 如果不唯一的話 gdb 會(huì)列出所有匹配的命令. 你也能用光標(biāo)鍵上下翻動(dòng)歷史命令.下面還是通過一個(gè)具體的實(shí)例說明如何利用調(diào)試符號(hào)來分析錯(cuò)誤,所用程序見清單4所示.清單4:crash.c#include int main(void) int input =0; printf(Input an integer:); scanf(%d, input); printf(The integer you input is %dn, input); return 0; 編譯并運(yùn)行上述代碼,會(huì)產(chǎn)生一個(gè)嚴(yán)重的段錯(cuò)誤(Segmentation fault)如下:# gcc -g crash.
28、c -o crash# ./crashInput an integer:10Segmentation fault 為了更快速地發(fā)現(xiàn)錯(cuò)誤所在,可以使用GDB進(jìn)行跟蹤調(diào)試,方法如下:1step) # gdb crashGNU 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)在可以通過run命令讓程序開始在GDB的監(jiān)控下運(yùn)行:2step) (gdb) runStarting program: /home/xiaowp/thesis/gcc/code/crashInput an intege
29、r:10Program received signal SIGSEGV, Segmentation fault.0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6 仔細(xì)分析一下GDB給出的輸出結(jié)果不難看出,程序是由于段錯(cuò)誤而導(dǎo)致異常中止的,說明內(nèi)存操作出了問題,具體發(fā)生問題的地方是在調(diào)用_IO_vfscanf_internal ( )的時(shí)候.為了得到更加有價(jià)值的信息,可以使用GDB提供的回溯跟蹤命令backtrace,執(zhí)行結(jié)果如下:3step) (gdb) backtrace#0 0x4008576b in _IO_vfscanf
30、_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 跳過輸出結(jié)果中的前面三行,從輸出結(jié)果的第四行中不難看出,GDB已經(jīng)將錯(cuò)誤定位到crash.c中的第11行了.現(xiàn)在仔細(xì)檢查一下:4step) (gdb) frame 3#3 0x08048393 in main ()
31、 at crash.c:1111 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了,命令如下:5step) (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)代碼都作為文件保存起來.如果想檢
32、查生成的代碼是否能夠通過手工調(diào)整的辦法來提高執(zhí)行性能,在編譯過程中生成的中間文件將會(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)制代碼中.剖析信息對于找出程序的性能瓶頸很有幫助,是協(xié)助Linux程序員開發(fā)出高性能程序的有力工具.在編譯時(shí)加入-p選項(xiàng)會(huì)在生成的代碼中加入通用剖析工具(Prof)能夠識(shí)別的統(tǒng)計(jì)信息,而-pg選項(xiàng)則生成只有GNU剖析工具(Gprof)才能識(shí)別的統(tǒng)計(jì)信息.最后提醒一點(diǎn),
33、雖然GCC允許在優(yōu)化的同時(shí)加入調(diào)試符號(hào)信息,但優(yōu)化后的代碼對于調(diào)試本身而言將是一個(gè)很大的挑戰(zhàn).代碼在經(jīng)過優(yōu)化之后,在源程序中聲明和使用的變量很可能不再使用,控制流也可能會(huì)突然跳轉(zhuǎn)到意外的地方,循環(huán)語句有可能因?yàn)檠h(huán)展開而變得到處都有,所有這些對調(diào)試來講都將是一場噩夢.建議在調(diào)試的時(shí)候最好不使用任何優(yōu)化選項(xiàng),只有當(dāng)程序在最終發(fā)行的時(shí)候才考慮對其進(jìn)行優(yōu)化.上次的培訓(xùn)園地中介紹了GCC的編譯過程、警告提示功能、庫依賴、代碼優(yōu)化和程序調(diào)試六個(gè)方面的內(nèi)容.這期是最后的一部分內(nèi)容.加速在將源代碼變成可執(zhí)行文件的過程中,需要經(jīng)過許多中間步驟,包含預(yù)處理、編譯、匯編和連接.這些過程實(shí)際上是由不同的程序負(fù)責(zé)完成
34、的.大多數(shù)情況下GCC可以為Linux程序員完成所有的后臺(tái)工作,自動(dòng)調(diào)用相應(yīng)程序進(jìn)行處理.這樣做有一個(gè)很明顯的缺點(diǎn),就是GCC在處理每一個(gè)源文件時(shí),最終都需要生成好幾個(gè)臨時(shí)文件才能完成相應(yīng)的工作,從而無形中導(dǎo)致處理速度變慢.例如,GCC在處理一個(gè)源文件時(shí),可能需要一個(gè)臨時(shí)文件來保存預(yù)處理的輸出、一個(gè)臨時(shí)文件來保存編譯器的輸出、一個(gè)臨時(shí)文件來保存匯編器的輸出,而讀寫這些臨時(shí)文件顯然需要耗費(fèi)一定的時(shí)間.當(dāng)軟件項(xiàng)目變得非常龐大的時(shí)候,花費(fèi)在這上面的代價(jià)可能會(huì)變得很沉重.解決的辦法是,使用Linux提供的一種更加高效的通信方式管道.它可以用來同時(shí)連接兩個(gè)程序,其中一個(gè)程序的輸出將被直接作為另一個(gè)程序的
35、輸入,這樣就可以避免使用臨時(shí)文件,但編譯時(shí)卻需要消耗更多的內(nèi)存.在編譯過程中使用管道是由GCC的-pipe選項(xiàng)決定的.下面的這條命令就是借助GCC的管道功能來提高編譯速度的:# gcc -pipe foo.c -o foo 在編譯小型工程時(shí)使用管道,編譯時(shí)間上的差異可能還不是很明顯,但在源代碼非常多的大型工程中,差異將變得非常明顯.文件擴(kuò)展名在使用GCC的過程中,用戶對一些常用的擴(kuò)展名一定要熟悉,并知道其含義.為了方便大家學(xué)習(xí)使用GCC,在此將這些擴(kuò)展名羅列如下:.c C原始程序;.C C+原始程序;.cc C+原始程序;.cxx C+原始程序;.m Objective-C原始程序;.i 已經(jīng)
36、過預(yù)處理的C原始程序;.ii 已經(jīng)過預(yù)處理之C+原始程序;.s 組合語言原始程序;.S 組合語言原始程序;.h 預(yù)處理文件(標(biāo)頭文件);.o 目標(biāo)文件;.a 存檔文件.GCC常用選項(xiàng)GCC作為Linux下C/C+重要的編譯環(huán)境,功能強(qiáng)大,編譯選項(xiàng)繁多.為了方便大家日后編譯方便,在此將常用的選項(xiàng)及說明羅列出來如下:-rpath告訴動(dòng)態(tài)鏈接器,也就是加載器,到-rpath指定的目錄中尋找程序需要的共享庫。因?yàn)?rpath制定的搜索路徑, 1, 是優(yōu)先于LD_LIBRARY_PATH和/etc/ld.so.conf的;2, 它被寫入到可執(zhí)行文件中。就因?yàn)榈?條,所以無論可執(zhí)行文件到那里,加載時(shí)都是到
37、編譯時(shí)用-rpath制定的路徑去找-c 通知GCC取消鏈接步驟,即編譯源碼并在最后生成目標(biāo)文件;-Dmacro 定義指定的宏,使它能夠通過源碼中的#ifdef進(jìn)行檢驗(yàn);-E 不經(jīng)過編譯預(yù)處理程序的輸出而輸送至標(biāo)準(zhǔn)輸出;-g3 獲得有關(guān)調(diào)試程序的詳細(xì)信息,它不能與-o選項(xiàng)聯(lián)合使用;-Idir在包含頭文件搜索路徑的起點(diǎn)處添加指定目錄;編譯時(shí)庫查找-Ldir指定編譯時(shí),庫的搜索路徑。第三方或自己提供的庫,可以用它制定目錄,否則編譯器將只在標(biāo)準(zhǔn)庫的目錄找。這個(gè)dir就是目錄的名稱。-O、-O2、-O3 將優(yōu)化狀態(tài)打開,該選項(xiàng)不能與-g選項(xiàng)聯(lián)合使用;-llibrary制定編譯的時(shí)候使用的庫(指定了路徑當(dāng)
38、然還得指定庫名),常用的lib庫有l(wèi)pthread(線程庫),lm(數(shù)學(xué)庫),lz(zlib庫)及 lcrypto(linux下的MD5加密庫)等,當(dāng)然可以添加自己或第三方的庫文件。例子:gcc -lncurses hello.c 使用ncurses庫編譯程序。-S 要求編譯程序生成來自源代碼的匯編程序輸出;-v 啟動(dòng)所有警報(bào);-Wall 在發(fā)生警報(bào)時(shí)取消編譯操作,即將警報(bào)看作是錯(cuò)誤;-Werror 在發(fā)生警報(bào)時(shí)取消編譯操作,即把報(bào)警當(dāng)作是錯(cuò)誤;-w 禁止所有的報(bào)警.小結(jié)GCC是在Linux下開發(fā)程序時(shí)必須掌握的工具之一.本文對GCC做了一個(gè)簡要的介紹,主要講述了如何使用GCC編譯程序、產(chǎn)生警
39、告信息、調(diào)試程序和加快GCC的編譯速度.對所有希望早日跨入Linux開發(fā)者行列的人來說,GCC就是成為一名優(yōu)秀的Linux程序員的起跑線.實(shí)例1. 無選項(xiàng)編譯鏈接用法:#gcc test.c作用:將test.c預(yù)處理、匯編、編譯并鏈接形成可執(zhí)行文件。這里未指定輸出文件,默認(rèn)輸出為a.out。編譯成功后可以看到生成了一個(gè)a.out的文件。在命令行輸入./a.out執(zhí)行程序。./表示在當(dāng)前目錄,a.out為可執(zhí)行程序文件名。2. 選項(xiàng) -o用法:#gcc test.c -o test作用:將test.c預(yù)處理、匯編、編譯并鏈接形成可執(zhí)行文件test。-o選項(xiàng)用來指定輸出文件的文件名。輸入./tes
40、t執(zhí)行程序。3. 選項(xiàng) -E用法:#gcc -E test.c -o test.i作用:將test.c預(yù)處理輸出test.i文件。4. 選項(xiàng) -S用法:#gcc -S test.i作用:將預(yù)處理輸出文件test.i匯編成test.s文件。5. 選項(xiàng) -c用法:#gcc -c test.s作用:將匯編輸出文件test.s編譯輸出test.o文件。6. 無選項(xiàng)鏈接用法:#gcc test.o -o test作用:將編譯輸出文件test.o鏈接成最終可執(zhí)行文件test。輸入./test執(zhí)行程序。7. 選項(xiàng)-O用法:#gcc -O1 test.c -o test作用:使用編譯優(yōu)化級(jí)別1編譯程序。級(jí)別為
41、13,級(jí)別越大優(yōu)化效果越好,但編譯時(shí)間越長。輸入./test執(zhí)行程序。8.編譯使用C+ std庫的程序用法:#gcc test.cpp -o test -lstdc+作用:將test.cpp編譯鏈接成test可執(zhí)行文件。-lstdc+指定鏈接std c+庫。變量我們可以把隱含規(guī)則中使用的變量分成兩種:一種是命令相關(guān)的,如“CC”;一種是參數(shù)相的關(guān),如“CFLAGS”。下面是所有隱含規(guī)則中會(huì)用到的變量:1、關(guān)于命令的變量。AR 函數(shù)庫打包程序。默認(rèn)命令是“ar”。 AS 匯編語言編譯程序。默認(rèn)命令是“as”。CC C語言編譯程序。默認(rèn)命令是“cc”。CXX C+語言編譯程序。默認(rèn)命令是“g+”。
42、CO 從 RCS文件中擴(kuò)展文件程序。默認(rèn)命令是“co”。CPP C程序的預(yù)處理器(輸出是標(biāo)準(zhǔn)輸出設(shè)備)。默認(rèn)命令是“$(CC) E”。FC Fortran 和 Ratfor 的編譯器和預(yù)處理程序。默認(rèn)命令是“f77”。GET 從SCCS文件中擴(kuò)展文件的程序。默認(rèn)命令是“get”。 LEX Lex方法分析器程序(針對于C或Ratfor)。默認(rèn)命令是“l(fā)ex”。PC Pascal語言編譯程序。默認(rèn)命令是“pc”。YACC Yacc文法分析器(針對于C程序)。默認(rèn)命令是“yacc”。YACCR Yacc文法分析器(針對于Ratfor程序)。默認(rèn)命令是“yacc r”。MAKEINFO 轉(zhuǎn)換Texin
43、fo源文件(.texi)到Info文件程序。默認(rèn)命令是“makeinfo”。TEX 從TeX源文件創(chuàng)建TeX DVI文件的程序。默認(rèn)命令是“tex”。TEXI2DVI 從Texinfo源文件創(chuàng)建軍TeX DVI 文件的程序。默認(rèn)命令是“texi2dvi”。WEAVE 轉(zhuǎn)換Web到TeX的程序。默認(rèn)命令是“weave”。CWEAVE 轉(zhuǎn)換C Web 到 TeX的程序。默認(rèn)命令是“cweave”。TANGLE 轉(zhuǎn)換Web到Pascal語言的程序。默認(rèn)命令是“tangle”。CTANGLE 轉(zhuǎn)換C Web 到 C。默認(rèn)命令是“ctangle”。RM 刪除文件命令。默認(rèn)命令是“rm f”。2、關(guān)于命令
44、參數(shù)的變量下面的這些變量都是相關(guān)上面的命令的參數(shù)。如果沒有指明其默認(rèn)值,那么其默認(rèn)值都是空。ARFLAGS 函數(shù)庫打包程序AR命令的參數(shù)。默認(rèn)值是“rv”。ASFLAGS 匯編語言編譯器參數(shù)。(當(dāng)明顯地調(diào)用“.s”或“.S”文件時(shí))。 CFLAGS C語言編譯器參數(shù)。CXXFLAGS C+語言編譯器參數(shù)。COFLAGS RCS命令參數(shù)。 CPPFLAGS C預(yù)處理器參數(shù)。( C 和 Fortran 編譯器也會(huì)用到)。FFLAGS Fortran語言編譯器參數(shù)。GFLAGS SCCS “get”程序參數(shù)。LDFLAGS 鏈接器參數(shù)。(如:“l(fā)d”)LFLAGS Lex文法分析器參數(shù)。PFLAGS
45、 Pascal語言編譯器參數(shù)。RFLAGS Ratfor 程序的Fortran 編譯器參數(shù)。YFLAGS Yacc文法分析器參數(shù)。 附錄linux下gdb調(diào)試實(shí)例2008-09-08 00:36GDB 概述GDB 是 GNU 開源組織發(fā)布的一個(gè)強(qiáng)大的 UNIX 下的程序調(diào)試工具。或許,各位比較喜歡那種圖形界面方式的,像 VC 、 BCB 等 IDE 的調(diào)試,但如果你是在 UNIX 平臺(tái)下做軟件,你會(huì)發(fā)現(xiàn) GDB 這個(gè)調(diào)試工具有比 VC 、 BCB 的圖形化調(diào)試器更強(qiáng)大的功能。所謂 “ 寸有所長,尺有所短 ” 就是這個(gè)道理。一般來說, GDB 主要幫忙你完成下面四個(gè)方面的功能: 1 、啟動(dòng)你的程
46、序,可以按照你的自定義的要求隨心所欲的運(yùn)行程序。 2 、可讓被調(diào)試的程序在你所指定的調(diào)置的斷點(diǎn)處停住。(斷點(diǎn)可以是條件表達(dá)式) 3 、當(dāng)程序被停住時(shí),可以檢查此時(shí)你的程序中所發(fā)生的事。 4 、動(dòng)態(tài)的改變你程序的執(zhí)行環(huán)境。從上面看來, GDB 和一般的調(diào)試工具沒有什么兩樣,基本上也是完成這些功能,不過在細(xì)節(jié)上,你會(huì)發(fā)現(xiàn) GDB 這個(gè)調(diào)試工具的強(qiáng)大,大家可能比較習(xí)慣了圖形化的調(diào)試工具,但有時(shí)候,命令行的調(diào)試工具卻有著圖形化工具所不能完成的功能。讓我們一一看來。單步執(zhí)行和跟蹤函數(shù)調(diào)用 /media/ch10s01.htmlbacktrace(或bt)查看各級(jí)函數(shù)
47、調(diào)用及參數(shù)finish連續(xù)運(yùn)行到當(dāng)前函數(shù)返回為止,然后停下來等待命令frame(或f) 幀編號(hào)選擇棧幀info(或i) locals查看當(dāng)前棧幀局部變量的值list(或l)列出源代碼,接著上次的位置往下列,每次列10行l(wèi)ist 行號(hào)列出從第幾行開始的源代碼list 函數(shù)名列出某個(gè)函數(shù)的源代碼next(或n)執(zhí)行下一行語句print(或p)打印表達(dá)式的值,通過表達(dá)式可以修改變量的值或者調(diào)用函數(shù)quit(或q)退出gdb調(diào)試環(huán)境set var修改變量的值start開始執(zhí)行程序,停在main函數(shù)第一行語句前面等待命令step(或s)執(zhí)行下一行語句,如果有函數(shù)調(diào)用則進(jìn)入到函數(shù)中斷點(diǎn) http:/lea
48、/media/ch10s02.html命令描述break(或b) 行號(hào)在某一行設(shè)置斷點(diǎn)break 函數(shù)名在某個(gè)函數(shù)開頭設(shè)置斷點(diǎn)break . if .設(shè)置條件斷點(diǎn)continue(或c)從當(dāng)前位置開始連續(xù)運(yùn)行程序delete breakpoints 斷點(diǎn)號(hào)刪除斷點(diǎn)display 變量名跟蹤查看某個(gè)變量,每次停下來都顯示它的值disable breakpoints 斷點(diǎn)號(hào)禁用斷點(diǎn)enable 斷點(diǎn)號(hào)啟用斷點(diǎn)info(或i) breakpoints查看當(dāng)前設(shè)置了哪些斷點(diǎn)run(或r)從頭開始連續(xù)運(yùn)行程序display ,undisplay 跟蹤顯示號(hào)取消跟蹤顯示設(shè)置觀察點(diǎn) 表
49、10.3. gdb基本命令3 /media/ch10s03.html命令描述watch設(shè)置觀察點(diǎn)whatis:識(shí)別數(shù)組或變量的類型ptype:比whatis的功能更強(qiáng),他可以提供一個(gè)結(jié)構(gòu)的定義info(或i) watchpoints查看當(dāng)前設(shè)置了哪些觀察點(diǎn)x從某個(gè)位置開始打印存儲(chǔ)單元的內(nèi)容,全部當(dāng)成字節(jié)來看,而不區(qū)分哪個(gè)字節(jié)屬于哪個(gè)變量一個(gè)調(diào)試示例段錯(cuò)誤 /media/ch10s04.html源程序: tst.c 1 #include 2 3 int func(int n) 4 5 int sum=0,i; 6 for
50、(i=0; in; i+) 7 8 sum+=i; 9 10 return sum; 11 12 13 14 main() 15 16 int i; 17 long result = 0; 18 for(i=1; i cc -g tst.c -o tst使用 GDB 調(diào)試:hchen/test gdb tst - 啟動(dòng) GDBGNU gdb 5.1.1Copyright 2002 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type show copying to see the conditions.There
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 鍋爐本體檢修工安全教育模擬考核試卷含答案
- 調(diào)理肉制品加工工安全實(shí)操競賽考核試卷含答案
- 植保機(jī)械操作工安全生產(chǎn)基礎(chǔ)知識(shí)模擬考核試卷含答案
- 廢礦物油再生處置工沖突管理知識(shí)考核試卷含答案
- 金箔制作工安全培訓(xùn)效果強(qiáng)化考核試卷含答案
- 氯甲烷生產(chǎn)工安全素養(yǎng)水平考核試卷含答案
- 硫回收裝置操作工達(dá)標(biāo)水平考核試卷含答案
- 皮具制作工崗前核心管理考核試卷含答案
- 纖維碳化裝置操作工安全意識(shí)強(qiáng)化模擬考核試卷含答案
- 2024年畢節(jié)醫(yī)學(xué)高等??茖W(xué)校輔導(dǎo)員考試筆試真題匯編附答案
- 2026年冀教版初一地理上冊期末真題試卷+解析及答案
- 2026年孝昌縣供水有限公司公開招聘正式員工備考題庫及答案詳解參考
- 2025年文化產(chǎn)業(yè)版權(quán)保護(hù)與運(yùn)營手冊
- 四川省樂山市高中高三上學(xué)期第一次調(diào)查研究考試數(shù)學(xué)試題【含答案詳解】
- 《創(chuàng)新創(chuàng)業(yè)基礎(chǔ)》課件-項(xiàng)目1:創(chuàng)新創(chuàng)業(yè)基礎(chǔ)認(rèn)知
- 2026年初一寒假體育作業(yè)安排
- 物流行業(yè)運(yùn)輸司機(jī)安全駕駛與效率績效評定表
- 2026北京市通州區(qū)事業(yè)單位公開招聘工作人員189人筆試重點(diǎn)基礎(chǔ)提升(共500題)附帶答案詳解
- 2025~2026學(xué)年山東省菏澤市牡丹區(qū)第二十一初級(jí)中學(xué)八年級(jí)上學(xué)期期中歷史試卷
- 2026國家統(tǒng)計(jì)局儀征調(diào)查隊(duì)招聘輔助調(diào)查員1人(江蘇)考試參考試題及答案解析
- 水利工程施工質(zhì)量檢測方案
評論
0/150
提交評論