Linux 下 C 開發—— gcc , gdb 的使用
作者: zccst
我們學習的過程應該是一個積累的過程,有無到有,又少到多,而不能像下山的猴子。 9 月份的時候是第一次系統學習 gcc, gdb, makefile (詳見前面部落格)。如今作為嵌入式系統的一個組成部分,決定重學一次,感覺理解加深了一些。
gcc 編譯器
前提編輯工具的使用: vi vim Emacs 等。
理論篇
gcc 可以使程式員靈活地控制編譯過程。編譯過程一般可以分為下面四個階段,每個階段分別調用不同的工具進行處理,如圖 9-18 所示。
Linux 系統中可執行檔有兩種格式。第一種格式是 a.out 格式,這種格式用於早期的 Linux 系統以及 Unix 系統的原始格式。 a.out 來自於 Unix C 編譯器預設的可執行檔名。當使用共用庫時, a.out 格式就會發生問題。把 a.out 格式調整為共用庫是一種非常複雜的操作,由於這個原因,一種新的檔案格式被引入 Unix 系統 5 的第四版本和 Solaris 系統中。它被稱為可執行和串連的格式( ELF )。這種格式很容易實現共用庫。
ELF 格式已經被 Linux 系統作為標準的格式採用。 gcc 編譯器產生的所有的二進位檔案都是 ELF 格式的檔案(即使可執行檔的預設名仍然是 a.out )。較舊的 a.out 格式的程式仍然可以運行在支援 ELF 格式的系統上。
註: GCC 支援數種調試和剖析選項。在這些選項裡,最常用的是 -g 和 -pg 選項。
實踐篇
gcc 的使用格式: gcc [options][filenames]
其中 filenames 為所要編譯的程式源檔案。 options 見下文 gcc 的主要參數。
當使用 gcc 時, gcc 會完成預先處理、編譯、彙編和串連。前三步分別產生目標檔案,串連時,把產生的目標檔案連結成可執行檔。 gcc 可以針對支援不同的來源程式檔案進行不同處理,檔案格式以檔案的尾碼來識別。
vi hello.c
一、常見步驟:
對於只有一個源檔案的簡單程式,常常只有編譯,運行兩步。
1 , gcc hello.c -o hello
2 , ./hello
二、 gcc 編譯流程
gcc and g++ 分別是 gnu 的 c & c++ 編譯器 gcc/g++ 在執行編譯工作的時候,總共需要 4 步
hello.c ( 源碼 )
1 , hello.i 產生預先處理檔案,
參數是“ -E ”,把 hello.c -> hello.i 。完整命令為 gcc hello.c -o hello.i -E
2 , hello.s 編譯產生彙編檔案,
參數是“ -S ”,把 hello.i -> hello.s 。完整命令為 gcc hello.i -o hello.s -S
3 , hello.o 將彙編檔案變為目標代碼,
參數是“ -c ”,把 hello.s -> hello.o 。完整命令為 gcc hello.s -o hello.o -c
4 , hello 連結目標代碼,產生可執行程式,
參數無, 把 hello.o -> hello 。 完整命令為 gcc hello.o -o hello
./hello ( 運行 )
三、 gcc 的主要參數
1 ,母體參數
-E 只進行先行編譯,不做其他處理
-S 只是編譯不彙編,產生彙編代碼
-c 只是編譯不連結,產生目標檔案“ .o ”
-o file 把輸出檔案輸出到 file 裡
-g 在可執行程式中包含標準調試資訊
-v 列印出編譯器內部編譯各過程的命令列資訊和編譯器的版本
-I dir 在標頭檔的搜尋路徑列表中添加 dir 目錄
-L dir 在庫檔案的搜尋路徑列表中添加 dir 目錄
-static 連結靜態庫
-llibrary 串連名為 library 的庫檔案
2 ,警告和出錯參數。
-w 關閉警告
-ansi 顯示不符合 ANSI C 標準文法的警告資訊
-pedantic
-Wall 跟蹤調試的有力工具,最後養成使用此參數的習慣。
3 ,尋找選項
gcc 一般使用預設路徑尋找標頭檔和庫檔案。如果檔案所用的標頭檔或庫檔案不在預設目錄下,則編譯時間要指定它們的尋找路徑。
-I 選項:指定標頭檔的搜尋目錄
例: gcc –I/export/home/st –o test1 test1.c
-L 選項:指定庫檔案的搜尋目錄
例: gcc –L/usr/X11/R6/lib –o test1 test1.c
4 ,最佳化參數。
通過參數“ -On ”來產生最佳化代碼。其中 n 是一個代表最佳化層級的整數,較典型範圍是從 0 到 2 或 3. 數字越大最佳化的等級越高,程式運行速度越快。常用 -O2 ,因為它在最佳化長度,編譯時間和代碼大學之間取得一個比較理想的平衡點。比較: 1-8.c( 代碼略 )
gcc 1-8.c -o 1-8
time ./1-8
gcc 1-8.c -o 1-8 -O2
time ./1-8
註:如下場合應避免最佳化代碼。
(1) 程式開發時。只有到軟體發行或開發結束時,才考慮對最終產生的程式碼進行最佳化。
(2) 資源受限時。如記憶體資源非常緊張時(一些即時嵌入式裝置)。
(3) 跟蹤調試時。最佳化可能會刪除、改寫或重組代碼,從而使跟蹤調試變得異常困難。
gdb 調試器
理論篇
gdb 調試的不是 .c 源檔案而是可執行檔,然而,並不是所有的可執行檔都可以用 gdb 調試。如果要讓產生的可執行檔可以用來調試,需在執行 gcc 指令編譯器時,加上 -g 參數,指定程式在編譯時間包含調試資訊。調試資訊包含程式裡的每個變數的類型和在可執行檔裡的地址映射以及原始碼的行號。 gdb 利用這些資訊使原始碼和機器碼相關聯。
實踐篇
gdb 使用格式: gdb filename
其中, filename 是要調試的可執行檔。用這種方式運行 gdb 可以直接指定想要調試的程式。這和啟動 gdb 後執行 file filename ( file 命令:裝入想要調試的可執行檔)命令效果完全一樣。也可以用 gdb 去檢查一個因程式異常終止而產生的 core 檔案,或者與一個正在啟動並執行程式相連。
gdb 支援很多的命令且能實現不同的功能,這些命令從簡單的檔案裝入到允許你檢查所調用的堆棧內容的複雜命令。
1 ,編輯源檔案。
例如, vi 1-9
添加如下內容
#include <stdio.h>
int min(int x, int y);
int main()
{
int a1, a2, min_int;
printf("please input the frist int number:/n");
scanf("%d", &a1);
printf("please input the second int number:/n");
scanf("%d", &a2);
min_int = min(a1, a2);
printf("the min number is:%d/n", min_int);
return 0;
}
int min(int x, int y)
{
if (x < y)
return x;
else
return y;
}
2 ,編譯時間要加上選項“ -g ”,這樣編譯出的可執行代碼才包含調試資訊。
gcc 1-9.c -o 1-9 -g
3 ,進入 gdb 調試環境。
gdb 1-9
斷行符號就進入了 gdb 偵錯模式。在 gdb 的調試環境中,提示符是“ (gdb) ”。
4 ,用 gdb 偵錯工具。
(1) 查看源檔案
文法: 'l' 是 list 縮寫。 list< 行號 >|< 函數名 > 。查看原始碼,一次顯示 10 行
命令 (gdb)l
(2) 設定斷點
文法: break 行號 | 函數名 < 條件運算式 >
本例可以輸入命令
(gdb)b min 在自訂的 min 函數出設定斷點。
(gdb)b 17 功能同上
(3) 查看斷點資訊
文法: info break # info 命令是顯示 XX 資訊,常用有 files,func,local,proc.
命令 (gdb)info b
(4) 運行程式
文法: run # 執行當前被調試的程式
命令 (gdb)r
註: gdb 預設從第一行開始運行,如果要從出現中指定行開始運行,只需輸入“ r ” + 行號。
(5) 查看變數值
文法: p 變數名。程式運行到斷點處會自動暫停,此時可查看指定變數的值。
本例命令
(gdb)p a1
(gdb)p a2
(gdb)p min_int
偵錯工具時,如需修改變數值,可在程式運行至斷點處是,輸入“ set 變數 = 設定值”。
例,給變數“ a2 ”賦值 11 ,輸入“ set a2=11 ”。
(6) 單步調試
文法:
“ n ” (next) ,若有函數,不進入函數調用。
“ s ” (step) ,若有函數,則進入函數調用。
(7) 繼續運行程式
文法: continue
命令 (gdb)c
(8) 退出 gdb 調試環境。
文法: quit
命令 (gdb)q
其他常用命令還有: