天天看點

《Linux 進階程式設計(第三版)》——2.2 GCC/GDB編譯調試工具基礎

本節書摘來自異步社群《linux 進階程式設計(第三版)》一書中的第2章,第2.2節,作者:楊宗德 , 呂光宏 , 劉雍著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

linux 進階程式設計(第三版)

gcc/g++是gnu最優秀的自由軟體之一,它主要提供c/c++程式的編譯工作。linux下的c、c++程式開發過程中,一般都采用gcc/g++/gdb工具。将c語言程式編譯成一個可執行檔案一般都需經過以下4個步驟。

(1)預處理(preprocessing):對源代碼檔案中的檔案包含、宏定義、預編譯語句進行分析和替換。

(2)編譯(compilation):根據編譯器的文法規則,将進階語言轉換為以.s為字尾的彙編語言檔案。

(3)彙編(assembly):将.s和.s為字尾的彙編語言檔案經過預編譯和彙編成為以.o為字尾的目标檔案。

(4)連接配接(linking):當所有的目标檔案都生成之後,将它們安排到可執行程式中恰當的位置上,同時,該程式所調用到的庫函數也需要連接配接到合适的地方。

2.2.1 gcc/g++簡單介紹

1.gcc版本資訊

gcc/g++是gnu最優秀的自由軟體之一,除了可以作為c/c++語言的編譯器外,還可以編譯其他語言程式,在shell提示符号下鍵入“gcc -v”,螢幕上就會顯示出目前正在使用的gcc的版本及相關資訊:

“target: i386-redhat-linux”資訊說明目前正在使用的gcc是i386、i486、i586微處理器寫的。這3種微處理器的晶片所編譯而成的程式代碼,彼此間可相容使用。如果是交叉編譯工具,則為其他類型的處理器,例如arm。

“configured with:”用來辨別目前gcc相關配置,具體包括如下。

--prefix=/usr:安裝路徑。

--mandir=/usr/share/man:man手冊路徑。

--infodir=/usr/share/info:info資訊路徑。

--enable-shared:生成共享庫。

--enable-threads=posix:線程類型為posix。

--enable-checking=release:檢查内部發行版本一緻性。

--with-system-zlib:安裝zlib庫。

--enable-cxa_atexit:使用cxa_atexit,而不是atexit。

--disable-libunwind-exceptions:禁止libunwind異常。

--enable-libgcj-multifile:将所有.java編譯到.class檔案。

--enable-languages=c,c++,objc,java,f95,ada:支援的語言類型。

--enable-java-awt=gtk:java類型。

--with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre:java所在主目錄。

--host=i386-redhat-linux:主機類型。

“thread model: posix”資訊說明目前使用的線程為posix線程庫。

“gcc version 4.4.3 20110519 (red hat 4.0.0-8)”資訊說明目前gcc為4.4版本。

安裝後的gcc主要目錄結構如下:

2.gcc編譯過程

gcc/g++是gnu中c和c++的編譯器,其編譯格式如下:

其中options就是編譯器所需要的參數,filename是檔案名稱。linux下的c和c++編譯器将程式編譯成一個可執行檔案需要經過以下4個步驟。

(1)預處理(也稱預編譯,preprocessing):即進行預處理。在預處理過程中,對源代碼檔案中的檔案包含、預編譯語句進行分析,使用-e參數。

(2)編譯(compilation):即調用cc進行編譯。這個階段根據輸入檔案生成以.s為字尾的彙編檔案,使用-s參數。

(3)彙編(assembly):即調用as進行編譯,将.s和.s為字尾的彙編語言檔案彙編成為以.o為字尾的目标檔案,使用-c參數。

(4)連接配接(linking):當所有的目标檔案都生成之後,調用ld來完成最後的關鍵性工作,這個階段就是連接配接。在連接配接階段,所有的目标檔案被安排到可執行程式中恰當的位置上,同時,該程式所調用到的庫函數也從各自所在的檔案庫中連到合适的地方,使用-o參數。

除非使用了-c、-s或-e選項(這些參數可以在附錄中查找到)或者編譯錯誤阻止了過程的進行,否則連接配接總是最後的步驟。在連接配接階段,所有對應于源程式的.o檔案,-l庫檔案 将按指令行中的順序傳遞給連接配接器。

對源代碼檔案來說,字尾名控制着預設設定。

gcc:認為預處理後的檔案(.i)是c檔案,并且設定c形式的連接配接。

g++:認為預處理後的檔案(.ii)是c++檔案,并且設定c++形式的連接配接。

在編輯源檔案時,源檔案字尾名辨別源檔案的語言類型以及後期的操作,各語言類型說明如表2-2所示。

《Linux 進階程式設計(第三版)》——2.2 GCC/GDB編譯調試工具基礎
《Linux 進階程式設計(第三版)》——2.2 GCC/GDB編譯調試工具基礎

2.2.2 gdb調試工具簡介

gnu的調試器稱為gdb,該調試工具是一個互動式工具,在字元模式下工作。很多程式員習慣于圖形界面的程式開發,如vc、vb等內建開發環境,但是在unix/linux環境下開發軟體,gdb比傳統c語言的開發環境具有更強大的功能。gdb作為功能強大的調試工具,可完成如下的調試任務。

(1)設定斷點。

(2)監視程式變量的值。

(3)程式的單步執行。

(4)修改變量的值。

預設情況下,linux系統安裝了gdb調試工具。檢視本機gdb版本資訊的指令如下:

為了使用gdb調試工具,在編譯源檔案時必須使用-g選項(即gcc -c -g *.c)加上調試資訊。另外,如果使用makefile檔案,還可以在makefile(關于makefile本書在後面章節将詳細介紹)中定義cflags變量。

<code>cflags = -g</code>

2.2.3 使用gcc編譯c程式示例

以下給出使用gcc編譯c程式示例。目前有兩個源檔案main.c和factorial.c,現在要編譯生成一個計算階乘的程式(因int類型大小的限制,讀者不能輸入太大的值),源程式如下:

以下是按編譯連接配接程式為可執行程式的步驟:

(1)編輯源代碼。使用vim等工具編輯源代碼檔案,完成後,檔案資訊如下:

(2)使用gcc-c指令編譯源代碼(此步驟包含了-e對應的預處理操作,-s的彙編操作)。

(3)使用gcc-o指令連接配接程式。

(4)執行程式。

另外,以上步驟也可以直接使用-o參數一次性完成。具體指令如下:

2.2.4 使用g++編譯c++程式示例

gcc工具可用來同時編譯c程式和c++程式。一般來說,編譯器通過源檔案的字尾名來判斷是c程式還是c++程式(雖然linux系統并不是通過字尾來判斷檔案)。在linux中,c源檔案的字尾名為.c(小寫),而c++源檔案的字尾名為.c(大寫)、.cc或.cpp。

但是,gcc指令隻能編譯c++源檔案,而不能自動和c++程式使用的庫連接配接。是以,通常使用g++指令來完成c++程式的編譯和連接配接,程式會自動調用gcc實作連接配接。

假設有一個如下的c++源檔案hello.c:

c++程式可以調用g++指令編譯連接配接并生成可執行檔案:

同理,也可以使用-o參數一步完成編譯連接配接操作。指令如下:

其中,-wno-deprecated參數用于忽略頭檔案信賴的警告資訊。

2.2.5 gdb示範示例

下面以一個簡單的執行個體來示範gdb的調試方法。此示例程式源代碼如下:

這個程式很簡單,目的是接受使用者的輸入,并将使用者的輸入列印出來。但是,程式的第8行使用了未初始化的字元指針string,是以,編譯并運作之後,将出現段錯誤。

下面利用gdb工具查找該程式中出現的問題,具體步驟如下。

(1)運作 gdb bug指令,裝入bug可執行檔案。

(2)使用list(可以使用l縮寫)指令檢視代碼:

(4)使用where指令檢視程式出錯位置:

以上資訊說明gets函數出錯。從代碼中可以看出,唯一能夠導緻gets函數出錯的原因就是變量string。是以,使用print(可以簡寫成p)指令檢視string變量:

使用quit指令退出gdb調試器: