程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Linux環境下的C/C++基礎調試技術1——初步了解

Linux環境下的C/C++基礎調試技術1——初步了解

編輯:C++入門知識

1.調試技術的幾個准則

驚喜准則:找到錯誤是一種驚喜,心理上不要畏懼而是要懷著感恩的心去面對。
從小處開始准則:剛開始測試的使用從小處著手,暫時不涉及邊界數據,雖然這樣可能會掩蓋一些Bug,但是這樣或許能查到最主要的Bug,例如你的程序包含了一個巨大的循環體,最容易發現的Bug在第一個循環或第二次循環執行的時候。
自頂向下准則:優先選擇step over而不是step into,以節省時間。
Segmentation Fault准則:出現段錯誤時,第一個想到的不應該是printf而是Debugger,因為在調試器中你能看到你的哪一行代碼導致了錯誤,更重要的是你可以通過backtrace等工具得到更多有用的信息。
折半查找准則:在尋找bug時可以充分利用編輯器等工具來進行折半查找,具體在後邊有例子說明。
2.Linux下代碼調試工具

主要使用的GDB,以及基於GDB的圖形化工具,如DDD或eclipse,選擇上看個人習慣了。

命令行式的GDB啟動較快,可以在ssh終端下使用,操作簡潔,並且在調試GUI程序時不會崩潰,但較之圖形化則在單步調試或設置斷點時非常不方便。

當然你可以使用Vim等編輯器的插件或者補丁(clewn or vimGDB)來彌補這一缺憾,並且在GDB6.1以上的版本你可以使用GDB -tui這個模式(或者在GDB的命令行模式下按CTRL-x-a)打開一個類似於圖形界面的文本界面模式,在這個界面中你可以使用上下鍵查看源代碼(CTRL-P 和 CTRL-N完成輸入過的命令的查看).

或者你還可以使用cGDB這個工具(很慶幸這個項目在停止了三年後又有人開始維護了),這個工具是將GDB用curses包裝了一下,提供了一些很好用的feature(Esc和i鍵在代碼和命令框間切換;在代碼框中支持vim型的操作;在命令框中支持tab鍵補全命令;在移動到想加入斷點的行(行號為高亮白色)直接用空格鍵,設定好後行號會變紅;)。另外,在調試C-S程序時推薦使用eclipse。

在本文中,重點介紹ddd的操作,因為這個工具即結合了GDB命令行和圖形界面的操作。其余請參閱各個工具的手冊。

3.GDB命令行最基本操作

程序啟動:
A.冷啟動
gdb program              e.g., gdb ./cs
gdb –p pid                 e.g., gdb –p `pidof cs`
gdb program core      e.g., gdb ./cs core.xxx
B.熱啟動
(gdb) attach pid        e.g., (gdb) attach 2313
C.傳入命令行參數
gdb program --args arglist
(gdb) set args arglist
(gdb) run arglist
使用shell命令:shell command
Make:make make-args(=shell make make-args) 
設置斷點:b LineNumber
運行程序:r args1 args2 ...
徹底終止程序:kill
單步執行:n(TIPs1:可以按回車重復上一次操作,在單步調試時這個feature很有用)。
單步進入:s
繼續執行:c
設置臨時斷點:tb LineNumber 可以理解為一次性斷點,與斷點不同,臨時斷點只在第一次執行時起作用。
查看變量:p
設置觀察點:
w Expression,當Expression是一個變量名時,這個變量變化時會停止執行;你也可以使用條件來限定,比如w (z>28),當z大於28時,程序停止。注意觀察點一般使用在更大范圍上的變量,而不是本地變量,因為在局部變量上設置的觀察點在局部結束時(比如該變量所在的函數執行結束時)就被取消了。
當然這並不包含main的情況,因為main函數執行結束後程序就結束了。
查看棧幀:
棧幀指的是在一個函數調用時,該函數調用的運行信息(包含本地變量、參數以及函數被調用的位置)存儲的地方。每當一個函數被調用時,一個新的幀就被系統壓入一個由系統維護的幀,在這個棧的頂端是現在正在運行的函數信息,當該函數調用結束時被彈出並析構。
在GDB中,frame 0為當前幀,frame 1為當前幀的父幀,frame 2為父幀的父幀,等等,用down命令則是反向的。這是一個很有用的信息,因為在早期的一些幀中的信息可能會給你一些提示。
backtrace(bt/ where)查看整個幀棧
注意:在幀中來回並不影響程序的執行。
實例:插入排序算法調試

用偽代碼描述這個過程如下:

 image

擬調試代碼如下:

//

// insertion sort,

//

// usage:  insert_sort num1 num2 num3 ..., where the numi are the numbers to

// be sorted

int x[10],  // input array

    y[10],  // workspace array 

    num_inputs,  // length of input array

    num_y = 0;  // current number of elements in y

void get_args(int ac, char **av)

{  int i;

   num_inputs = ac - 1;

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

      x[i] = atoi(av[i+1]);

}

void scoot_over(int jj)

{  int k;

   for (k = num_y-1; k > jj; k++)

      y[k] = y[k-1];

}

void insert(int new_y)

{  int j;

   if (num_y = 0)  { // y empty so far, easy case

      y[0] = new_y;

      return;

   }

   // need to insert just before the first y

   // element that new_y is less than

   for (j = 0; j < num_y; j++)  {

      if (new_y < y[j])  {

         // shift y[j], y[j+1],... rightward

         // before inserting new_y

         scoot_over(j);

         y[j] = new_y;

         return;

      }

   }

}

void process_data()

{

   for (num_y = 0; num_y < num_inputs; num_y++)

      // insert new y in the proper place

      // among y[0],...,y[num_y-1]

      insert(x[num_y]);

}

void print_results()

{  int i;

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

      printf("%d ",y[i]);

}

int main(int argc, char ** argv)

{  get_args(argc,argv);

   process_data();

   print_results();

}
我們編譯一下:

gcc -g -Wall -o insert_sort ins.c

注意我們要使用-g選項告訴編譯器在可執行文件中保存符號表——我們程序中變量和代碼對應的內存地址。

現在我們開始運行一下,我們使用“從小處開始准則”,首先使用兩個數進行測試:

./insert_sort 12 5

我們發現該程序沒有退出,貌似進入了一個死循環。我們開始使用ddd調試這個程序:

ddd insert_sort

運行程序,傳入兩個參數:

r 12 5

此時程序一直運行不退出,按Ctrl+C暫停程序的執行

(GDB) r 12 5
^C
Program received signal SIGINT, Interrupt.
0x080484ff in insert (new_y=3) at insert_sort.c:45
/home/gnuhpc/MyCode/Debug/Chapter_01/insert_sort/pg_019/insert_sort.c:45:939:beg:0x80484ff
(GDB)

fbceb452c681af0c296a38f182fc0481

我們可以看到程序停止在第49行。我們看一下num_y現在的值:

(GDB) p num_y
$1 = 1

這裡的$1指的是你要GDB告訴你的第一個變量。找到了這個地方後,我們看看在num_y=1時都發生了什麼,我們在insert函數(第27行)設置斷點(你也可以直接使用break insert在這個函數的入口設置斷點),並且設置GDB在斷點1處(你可以通過info break命令查看斷點)只當num_y==1時才停止:

(GDB) b 27
Breakpoint 1 at 0x80484a1: file insert_sort.c, line 27.
(GDB)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved