1. 程式人生 > >Linux下C語言的除錯

Linux下C語言的除錯

除錯是每個程式設計師都會面臨的問題. 如何提高程式設計師的除錯效率, 更好更快地定位程式中的問題從而加快程式開發的進度, 是大家共同面對的問題. 可能Windows使用者順口就會說出:用VC唄 :-) , 它提供了設定斷點, 單步跟蹤等的圖形介面, 使除錯起來直觀易用. 但Linux使用者可能要生悶氣了 O:-) : 難道我們Linux程式設計師就只能使用原始的除錯方法, 在程式碼中加入printf資訊嗎?難道Linux下就沒有好的C語言除錯工具嗎?

當然不是了. GNU早就組織開發了一套C語言編譯器(Gcc)和除錯工具(Gdb). Gdb雖然沒有圖形化的友好介面, 但是它強大的功能也足以與微軟的VC工具相媲美, 給Linux程式設計師帶來了福音. 下面通過一個簡單的例子, 演示一下Gdb的使用流程:

示例檔案 demo.c 的原始碼如下:

#include <stdio.h>

int sum(int, int);

    int
main()
{
    int result;
    int a = 1, b = 2;
    result = sum(a, b);
    printf("%d + %d = %d\n", a, b, result);
    return 0;
}

    int
sum(int a, int b)
{
    return a + b;
}

編譯原始檔, 生成可執行檔案

$ gcc -g -Wall -o demo demo.c

雖然這段程式沒有錯誤, 但除錯完全正確的程式可以更加了解Gdb的使用流程. 接下來就啟動Gdb進行除錯.

注意:

  • Gdb進行除錯的是可執行檔案, 而不是”.c”原始檔, 因此, 需要先通過Gcc編譯生成可執行檔案才能用Gdb進行除錯.
  • 一定要加上選項”-g”, 這樣編譯出的可執行程式碼中才包含除錯資訊, 否則Gdb無法載入該可執行檔案.
  • 不能使用 -O2選項對可執行檔案進行優化, 因為優化之後可執行檔案裡的符號表資訊將被刪除, 這樣Gdb就無法找到使可執行檔案與原始檔之間的關聯了, 也就不能除錯了.

(1) 啟動Gdb

$ gdb demo
GNU gdb (GDB) 7.0-debian
Copyright (C) 2009 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".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/wangsheng/tmp/demo/gdb/demo...done.

可以看出, 在Gdb的啟動畫面中指出了Gdb的版本號, 使用的庫檔案等頭資訊, 接下來就進入了由”(gdb)”開頭的命令列介面了.

(2) 檢視原始檔

在Gdb中鍵入”l”(list的縮寫)可以檢視所載入的檔案, 如下所示:

(gdb) l
1       #include
2
3       int sum(int, int);
4
5           int
6       main()
7       {
8           int result;
9           int a = 1, b = 2;
10          result = sum(a, b);
(gdb) l
11          printf("%d + %d = %d\n", a, b, result);
12          return 0;
13      }
14
15          int
16      sum(int a, int b)
17      {
18          return a + b;
19      }
(gdb) l
Line number 20 out of range; demo.c has 19 lines.

可以看出, Gdb列出的原始碼中明確地給出了對應的行號, 這樣就可以大大地方便程式碼的定位.

(3) 設定斷點

設定斷點是除錯程式中一個非常重要的手段, 它可以使程式到一定位置暫停執行. 因此,可以在該位置方便地檢視變數的值, 堆疊情況等, 從而找出程式碼的癥結所在.

在Gdb中設定斷點非常簡單, 只需在”b”後加入對應的行號即可(這是最常用的方式). 如下所示:

(gdb) b 9
Breakpoint 2 at 0x4004f4: file demo.c, line 9.

注意: 該斷點的作用是當程式執行到第 9 行時暫停(第 8 行執行完畢, 第 9 行未執行)

(4) 檢視斷點資訊

(gdb) info b
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x00000000004004f4 in main at demo.c:9

(5) 執行程式碼

接下來就可執行程式碼了, Gdb預設從首行開始執行程式碼, 可鍵入”r”(run的縮寫)即可. 若想從程式中指定的行開始執行, 可在r後面加上行號.

(gdb) r
Starting program: /home/wangsheng/tmp/demo/gdb/demo

Breakpoint 2, main () at demo.c:9
9           int a = 1, b = 2;

可以看到程式執行到斷點處就停止了.

(6) 檢視變數值
鍵入p(print的縮寫)+變數名即可檢視該變數在此時的值

(gdb) p a
$1 = 1
(gdb) p b
$2 = 2
(gdb) p result
$3 = 32767

注意: 這裡之所以result是一個莫名其妙的值, 是因為宣告result是沒有初始化, 其值是不固定的。

(7) 單步執行

單步執行可以使用n(next的縮寫)或者s(step的縮寫), 它們之間的區別在於: 若有函式呼叫的時候, s會進入該函式而n不會. 因此, s就類似於VC等工具中的”step in”, n就類似於VC等工具中的”step over”.

如果使用n命令顯示如下:

(gdb) n
10          result = sum(a, b);

下面使用 s 命令,跟蹤進入 sum 函式:

(gdb) s
sum (a=1, b=2) at demo.c:18
18          return a + b;

可以看出執行 s 命令時進入了sum函式內部, 如果用 n 命令則跳過函式的呼叫部分

(8) 恢復程式執行

在檢視變數值以及堆疊之後, 就可以使用命令c(continue)恢復程式的正常運行了. 這時, 它會把剩餘還未執行的程式執行完, 並顯示剩餘程式的執行結果.

(gdb) c
Continuing.
1 + 2 = 3

Program exited normally.

可以看出, 程式在執行完後退出, 之後程式處於”停止狀態”.
說明: 在Gdb中, 程式的執行狀態有”執行”,”暫停”和”停止”3種. 其中”暫停”狀態是程式遇到了斷點或者觀察點, 程式暫時停止執行, 而此時函式的地址, 函式引數, 函式內的區域性變數都會被壓入”棧(Stack)中. 故在這種狀態下可以檢視函式的變數值等各種屬性. 但在函式處於”停止”狀態之後, “棧”就會自動撤銷, 它也就無法檢視各種資訊了

關於Gdb的更多命令, 你可以在啟用Gdb後, 輸入help命令檢視.