天天看點

用gdb 調試GCC 程式

用gdb 調試GCC 程式

Linux 包含了一個叫gdb 的GNU 調試程式。gdb 是一個用來調試C 和C++ 程式的強

力調試器。它使你能在程式運作時觀察程式的内部結構和記憶體的使用情況。以下是gdb 所

提供的一些功能:

l 它使你能監視你程式中變量的值。

l 它使你能設定斷點以使程式在指定的代碼行上停止執行。

l 它使你能一行行的執行你的代碼。

在指令行上鍵入gdb 并按Enter鍵就可以運作gdb 了,如果一切正常的話,gdb 将被啟

動并且你将在螢幕上看到類似的内容:

GDB is free software and you are welcome to distribute copies of it under certain conditions; type

“show copying” to see the conditions. There is absolutely no warranty for GDB; type “show

warranty” for details.

GDB 4.14 (i486-slakware-linux), Copyright 1995 Free Software Foundation, Inc.

(gdb)

當你啟動gdb 後,你能在指令行上指定很多的選項。你也可以以下面的方式來運作gdb :

gdb <fname>

當你用這種方式運作gdb,你能直接指定想要調試的程式。這将告訴gdb 裝入名為fname

的可執行檔案。你也可以用gdb 去檢查一個因程式異常終止而産生的core 檔案,或者與一

個正在運作的程式相連。你可以參考gdb 指南頁或在指令行上鍵入gdb –h 得到一個有關這

些選項的說明的簡單清單。

1.為調試編譯代碼(Compiling Code for Debugging)

為了使gdb 正常工作,你必須使你的程式在編譯時包含調試資訊。調試資訊包含你程

序裡的每個變量的類型和在可執行檔案裡的位址映射以及源代碼的行号。gdb 利用這些信

息使源代碼和機器碼相關聯。

在編譯時用 –g 選項打開調試選項。

2.gdb 基本指令

gdb 支援很多的指令使你能實作不同的功能。這些指令從簡單的檔案裝入到允許你檢

查所調用的堆棧内容的複雜指令,表C-1 列出了你在用gdb 調試時會用到的一些指令。想

了解gdb 的詳細使用請參考gdb 的指南頁。

 gdb 的常用指令

指令 描述

File 裝入想要調試的可執行檔案。

Kill 終止正在調試的程式。

List 列出産生執行檔案的源代碼的一部分。

Next 執行一行源代碼但不進入函數内部。

Step 執行一行源代碼而且進入函數内部。

Run 執行目前被調試的程式

Quit 終止 gdb

Watch 使你能監視一個變量的值而不管它何時被改變。

Break 在代碼裡設定斷點,這将使程式執行到這裡時被挂起。

Make 使你能不退出 gdb 就可以重新産生可執行檔案。

Shell 使你能不離開 gdb 就執行UNIX shell 指令。

gdb 支援很多與UNIX shell 程式一樣的指令編輯特征。你能象在bash 或tcsh 裡那樣按

Tab 鍵讓gdb 幫你補齊一個唯一的指令,如果不唯一的話gdb 會列出所有比對的指令。你

也能用光标鍵上下翻動曆史指令。

3.gdb 應用舉例

本節用一個執行個體教你一步步的用gdb 調試程式。被調試的程式相當的簡單,但它展示

了gdb 的典型應用。

下面列出了将被調試的程式。這個程式被稱為greeting,它顯示一個簡單的問候,再用

反序将它列出。

#include <stdio.h>

main ()

{

char my_string[] = “hello there”;

my_print (my_string);

my_print2 (my_string);

}

void my_print (char *string)

{

printf (“The string is %s\n”, string);

}

void my_print2 (char *string)

{

char *string2;

int size, i;

size = strlen (string);

string2 = (char *) malloc (size + 1);

for (i = 0; i < size; i++)

{

string2[size – i] = string[i];

}

string2[size+1] = ‘\0’;

printf (“The string printed backward is %s\n”, string2);

}

用下面的指令編譯它:

gcc –ggdb –o test test.c

這個程式執行時顯示如下結果:

The string is hello there

The string printed backward is

輸出的第一行是正确的,但第二行列印出的東西并不是我們所期望的。我們所設想的

輸出應該是:

The string printed backward is ereht olleh

由于某些原因,my_print2 函數沒有正常工作。讓我們用gdb 看看問題究竟出在哪兒,

先鍵入如下指令:

gdb greeting

注意: 記得在編譯 greeting 程式時把調試選項打開。

如果你在輸入指令時忘了把要調試的程式作為參數傳給gdb,你可以在gdb 提示符下

用file 指令來載入它:

(gdb) file greeting

這個指令将載入greeting 可執行檔案就象你在gdb 指令行裡裝入它一樣。這時你能用gdb

的run 指令來運作greeting 了。當它在gdb 裡被運作後結果大約會象這樣:

(gdb) run

Starting program: /root/greeting

The string is hello there

The string printed backward is

Program exited with code 041

這個輸出和在gdb 外面運作的結果一樣。問題是,為什麼反序列印沒有工作? 為了找

出症結所在,我們可以在my_print2 函數的for 語句後設一個斷點,具體的做法是在gdb 提

示符下鍵入list 指令三次,列出源代碼:

(gdb) list

(gdb) list

(gdb) list

技巧:在gdb 提示符下按回車健将重複上一個指令。

第一次鍵入list 指令的輸出如下:

1 #include <stdio.h>

2

3 main ()

4 {

5 char my_string[] = “hello there”;

6

7 my_print (my_string);

8 my_print2 (my_string);

9 }

10

如果按下回車,gdb 将再執行一次list 指令,給出下列輸出:

11 my_print (char *string)

12 {

13 printf (“The string is %s\n”, string);

14 }

15

16 my_print2 (char *string)

17 {

18 char *string2;

19 int size, i;

再按一次回車将列出greeting 程式的剩餘部分:

21 size = strlen (string);

22 string2 = (char *) malloc (size + 1);

23 for (i = 0; i < size; i++)

24 string2[size - i] = string[i];

25 string2[size+1] = ‘\0’;

26 printf (“The string printed backward is %s\n”, string2);

27 }

根據列出的源程式,你能看到要設斷點的地方在第24 行,在gdb 指令行提示符下鍵入

如下指令設定斷點:

(gdb) break 24

gdb 将作出如下的響應:

Breakpoint 1 at 0x139: file greeting.c, line 24

(gdb)

現在再鍵入run 指令,将産生如下的輸出:

Starting program: /root/greeting

The string is hello there

Breakpoint 1, my_print2 (string = 0xbfffdc4 “hello there”) at greeting.c :24

24 string2[size-i]=string[i]

你能通過設定一個觀察string2[size - i] 變量的值的觀察點來看出錯誤是怎樣産生的,

做法是鍵入:

(gdb) watch string2[size - i]

gdb 将作出如下回應:

Watchpoint 2: string2[size - i]

現在可以用next 指令來一步步的執行for 循環了:

(gdb) next

經過第一次循環後,gdb 告訴我們string2[size - i] 的值是 ‘h‘。gdb 用如下的顯示來告

訴你這個資訊:

Watchpoint 2, string2[size - i]

Old value = 0 ‘\000’

New value = 104 ‘h’

my_print2(string = 0xbfffdc4 “hello there”) at greeting.c:23

23 for (i=0; i<size; i++)

這個值正是期望的。後來的數次循環的結果都是正确的。當i=10 時,表達式string2[size

- i] 的值等于 ‘e‘,size - i 的值等于1,最後一個字元已經拷到新串裡了。

如果你再把循環執行下去,你會看到已經沒有值配置設定給string2[0] 了,而它是新串的

第一個字元,因為malloc 函數在配置設定記憶體時把它們初始化為空(null)字元。是以string2 的

第一個字元是空字元。這解釋了為什麼在列印string2 時沒有任何輸出了。

現在找出了問題出在哪裡,修正這個錯誤是很容易的。你得把代碼裡寫入string2 的第

一個字元的的偏移量改為size-1 而不是size。這是因為string2 的大小為12,但起始偏移量

是0,串内的字元從偏移量0 到偏移量 10,偏移量11 為空字元保留。

為了使代碼正常工作有很多種修改辦法。一種是另設一個比串的實際大小小1 的變量。

這是這種解決辦法的代碼:

#include <stdio.h>

main ()

{

char my_string[] = “hello there”;

my_print (my_string);

my_print2 (my_string);

}

my_print (char *string)

{

printf (“The string is %s\n”, string);

}

my_print2 (char *string)

{

char *string2;

int size, size2, i;

size = strlen (string);

size2 = size -1;

string2 = (char *) malloc (size + 1);

for (i = 0; i < size; i++)

string2[size2 - i] = string[i];

string2[size] = ‘\0’;

printf (“The string printed backward is %s\n”, string2);

}