1. 程式人生 > >【轉】gcc/g++常用編譯選項和gdb常用除錯命令

【轉】gcc/g++常用編譯選項和gdb常用除錯命令

  gcc/g++編譯器是我們寫編譯C/C++程式時離不開的編譯工具,而gdb又是除錯C/C++程式的利器,這一篇文章我們記錄一下它們的慣常用法。

gcc/g++常用編譯選項

選項 作用
-c 生成可目標檔案,但不進行連結
-o 指定生成檔案的檔名
-g 在目標檔案中新增除錯資訊,便於gdb除錯或objdump反彙編
-Wall 顯示所有的警告資訊(建議使用)
-Werror 視警告為錯誤,出現警告即放棄編譯
-w 不顯示任何警告資訊(不建議使用)
-v 顯示編譯步驟
-On (n=0,1,2,3) 設定編譯器優化等級,O0為不優化,O3為最高等級優化,O1為預設優化等級
-L 指定庫檔案的搜尋目錄
-l (小寫的L)連結某一庫
-I (大寫的i)指定標頭檔案路徑
-D 定義巨集,例如-DAAA=1,-DBBBB
-U 取消巨集定義,例如-UAAA

gdb常用除錯命命令

我們的可執行檔案要能夠被gdb除錯,必須在編譯時加上除錯資訊,也即是加上-g選項.
$ gcc -g example.c -o example
如上我們生成了一個帶有除錯資訊的可執行檔案example,要除錯example可以執行下列命令:
$ gdb example
這樣我們就進入了gdb的除錯命令列,如下所示:

GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (
C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from example...done. (gdb)

 如上可以看到命令列提示符為(gdb),接著我們就可以在這個gdb的命令列提示符上面輸入各種gdb的除錯命令了(補充:這裡也可以在shell中輸入gdb,然後回車,這樣直接進入到gdb的除錯命令列,之後可以通過file example命令來載入待除錯的可執行程式)。

下面我們就以如下的程式為例,詳細的看下,在gdb的除錯命令列中我們都有那些命令可用:

#include <stdio.h>

long func(int a){
    long sum = 0;
    for(int j=1;j<=a;j++){
    	sum += j;
    }
    return sum;
}

int main(void){
    int a =100;
    long sum = func(a);
    printf("%ld",sum);
    return 0;
}

檢視原始碼

載入待除錯的可執行檔案之後,在gdb的命令列中輸入list或者其簡寫l可以檢視到程式的原始碼以及行號,如下:

(gdb) l
1	#include <stdio.h>
2	
3	long func(int a){
4	    long sum = 0;
5	    for(int j=1;j<=a;j++){
6	    	sum += j;
7	    }
8	    return sum;
9	}
10	
(gdb) 
11	int main(void){
12	    int a =100;
13	    long sum = func(a);
14	    printf("%ld",sum);
15	    return 0;
16	}
(gdb) 
Line number 17 out of range; example.c has 16 lines.
(gdb)

如上輸入l之後,預設會顯示10行原始碼,按回車之後會顯示接下來的10行,直到檔案的末尾。

新增斷點

在gdb下新增斷點使用命令break或簡寫 b,有下面幾個常見用法(這裡統一用 b:

b 函式名
b 行號
b 檔名:行號
b 行號 if條件

比如我們在 main函式和func函式上各新增一個斷點:

(gdb) b main
Breakpoint 1 at 0x685: file example.c, line 12.
(gdb) break func
Breakpoint 2 at 0x651: file example.c, line 4.

檢視斷點

在加上斷點之後,我們可以通過info break命令檢視斷點的資訊:

(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000000685 in main at example.c:12
2       breakpoint     keep y   0x0000000000000651 in func at example.c:4

禁用和解禁斷點

通過disable <break number>來禁用指定Num的斷點,如下我們禁用1號斷點:

(gdb) disable 1
(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep n   0x0000000000000685 in main at example.c:12
2       breakpoint     keep y   0x0000000000000651 in func at example.c:4

如上,disable 1之後,斷點1的Enb列由之前的y變成了n,說明斷點1已被禁用。

通過enable <break number>可以來解禁斷點,如下我們對剛才禁用的斷點1解禁:

(gdb) enable 1
(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000000685 in main at example.c:12
2       breakpoint     keep y   0x0000000000000651 in func at example.c:4

如上,斷點1的Enb列又變成y了,它被成功解禁。

刪除斷點

我們可以用delete <break number>命令來刪除掉一個斷點,如下我們刪除斷點1:

(gdb) delete 1
(gdb) info break
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x0000000000000651 in func at example.c:4

如上,斷點1被成功刪除。

啟動程式

我們可以使用run命令或者簡寫r來啟動程式的執行:

(gdb) r
Starting program: /home/andrew/example 

Breakpoint 2, func (a=100) at example.c:4
4	    long sum = 0;

檢視變數的值

p <variable name>/print <variable name>可以檢視某一個變數的當前值:

(gdb) p sum
$1 = 0

如上,當前sum的值為0

單步執行

next命令或者n可以單步執行,如下:

(gdb) n
5	    for(int j=1;j<=a;j++){
(gdb) p sum
$2 = 0
(gdb) n
6	    	sum += j;
(gdb) p sum
$3 = 0
(gdb) n
5	    for(int j=1;j<=a;j++){
(gdb) p sum
$4 = 1
(gdb) n
6	    	sum += j;
(gdb) p sum
$5 = 1
(gdb) n
5	    for(int j=1;j<=a;j++){
(gdb) p sum
$6 = 3

跳入跳出函式

(gdb) break 13
Breakpoint 1 at 0x693: file example.c, line 13.
(gdb) r
Starting program: /home/andrew/example 

Breakpoint 1, main () at example.c:13
13	    long sum = func(a);
(gdb) s
func (a=100) at example.c:4
4	    long sum = 0;
(gdb) n
5	    for(int j=1;j<=a;j++){
(gdb) n
6	    	sum += j;
(gdb) n
5	    for(int j=1;j<=a;j++){
(gdb) n
6	    	sum += j;
(gdb) finish
Run till exit from #0  func (a=100) at example.c:6
0x000055555555469d in main () at example.c:13
13	    long sum = func(a);
Value returned is $1 = 5050
(gdb) n
14	    printf("%ld",sum);
(gdb)

 如上,我在func()函式呼叫行加上了斷點,然後r開始執行程式,之後程式在斷點處停住,此時我執行step命令或其簡寫s來跳入func()函式內部除錯,在內部依然像執行外部除錯一樣,如果要從函式跳出則執行finished,這時會導致函式執行完畢,並且打印出一些函式的返回資訊,並且程式停在函式後的第一條語句處。

監控變數

使用watch <varible name>命令可以實現監控變數,使用info watch命令可以檢視監控的變數。同時break所擁有的enable,disable,delete等動詞對於watch依然使用,且用法大同小異。這裡就不再贅述。

顯示變數的值

使用display <varible name>命令可以在每一步執行之後列印變數的當前值,如下:

(gdb) start 
Temporary breakpoint 1 at 0x685: file example.c, line 12.
Starting program: /home/andrew/example 

Temporary breakpoint 1, main () at example.c:12
12	    int a =100, b =50;
(gdb) display sum
1: sum = 140737488346112
(gdb) n
13	    long sum = func(a);
1: sum = 140737488346112
(gdb) 
14	    printf("%ld",sum);
1: sum = 5050
(gdb) 
16	    long sum2 = func(b);
1: sum = 5050
(gdb) 
17	    printf("%ld",sum2);
1: sum = 5050
(gdb) 
18	    return 0;
1: sum = 5050
(gdb)

進入shell

shell命令可以讓我們從gdb命令列環境進入到shell的命令列環境,當我們在shell命令列環境中輸入exit退出後,我們就又回到了之前的gdb命令列環境了。

視覺化除錯

在gdb命令列環境中輸入wi命令,可以讓我們進入視覺化除錯環境,這個環境可以看到原始碼,所使用的除錯命令與上面講到的一致。