dev C++ 5.11

原始程式碼除錯器

前言

整合程式開發環境 (Integrated Development Environment, IDE, 例如 Visual C/C++, Borland C++, Dev C++, Turbo C, Visual Studio Code, CodeBlocks, XCode 等等) 環境中原始程式碼除錯器 (Source Code Debugger) 是一個很好用的工具, 正確使用的情況下你可以很快地能夠看到程式執行時的錯誤,很快地找到你的程式中可能有錯誤的地方,尤其是對於電腦系統工程師來說, 必須和很多別人寫的程式一起工作, 遇見記憶體存取相關的錯誤時這個工具更是不可或缺。

在初學時很多同學也會覺得它一列一列顯示程式的動作, 可以讓你了解究竟CPU如何執行你所寫的敘述,對於所學到的新語法能夠有深刻的印象, 能夠更有體會。

不過這個工具對初學者不是沒有缺點的, 有時太依賴這樣的工具來偵錯, 使得製作程式的過程變成

  1. 先寫出一個大概的程式或是把可能可以用的程式碼集合複製在一個檔案裡
  2. 先能夠執行後, 不管他做出什麼東西, 慢慢用除錯器去更正調整程式

用這種方法寫的程式變成一步一步 "拼湊" 起來的了, 最常看到的現象是好不容易更正好, 程式跑出希望要的答案以後, "原本所使用的語法與邏輯到底是哪裡出錯了, 哪些語法和你腦中認知的不太一樣" 概念變得很模糊...如此辛辛苦苦除錯完畢, 雖然可以應付作業的要求, 但是卻常常沒有得到練習程式設計的效果, 還是沒有辦法完全預期自己所寫的程式到底做了些什麼事情, 每次寫程式都確定變成辛苦的 debug, 寫程式完全沒有快樂地操控電腦的感覺了!!

另外透過除錯器偵錯的過程裡, 常常只看到局部的邏輯, 反正可以直接看到資料的數值, 就可以省掉預測你的程式會如何處理資料的這一個步驟, 你對於多段程式敘述合在一起的效果的掌握能力也就會建立不起來, 會一直沒有辦法規劃完整的程式。

所以這也是我們一直沒有急著跟大家介紹「原始程式碼除錯器」這個工具該如何使用的原因。(當然還有一個原因是 dev C++ 的原始碼除錯器不是很穩定...免費的總是不要期待太高)

以下我們一步一步地介紹 dev C++ 5.11 (MingGW gcc 4.9.2, gdb 7.6.1) 原始程式碼除錯器的基本功能。

dev C++ 5.11 的除錯器是基於 GNU gdb 7.6.1 作出來的免費軟體 (CodeBlocks 也是), 有圖形化界面, 但是在功能上其實比起商用的軟體, 例如 M$ 的 Visual C/C++, 有一些差距, 不是那麼穩定, 不要太苛求。(對了, 澄清一下, GNU gdb 是功能很強、很穩定的工具, 只是它沒有圖形化的界面, 不是那麼適合初學者使用, 不穩定的部份是 dev C++ 有界面的除錯器。)

另外當 dev C++ 執行程式遇見程式的記憶體存取錯誤 (segmentation fault) 時, 就出現下面的視窗

作業系統希望執行 Just-in-Time 原始碼除錯, 但是目前這個畫面顯示系統上只有 Visual Studio 2008 的選項, 你可以由 Visual Studio / 工具 / 選項 / 偵錯 / Just-in-Time 把所有的都取消, 但是 dev c++ 好像沒有提供 Just-in-Time 偵錯, 所以你還是得關掉視窗, 只知道程式執行時發生嚴重的記憶體存取錯誤, 卻不知道是執行哪一段程式時發生的。如果你用 Visual Studio 的話, 就可以很快地在 JIT 除錯器中看到程式執行到哪裡發生這個錯誤了。

建立新原始碼

這裡我們盡量簡化, 如果你新增一個專案, 然後在裡面加入新的原始碼也是可以的。

拷貝下列程式進入編輯區域, 存檔為 testDebug.cpp

(由於 devcpp 除錯器的一點點 bug, 所以存檔的時候請不要存在磁碟機最上層根目錄, 需要在某一資料匣內)

 

程式內容如下

#include <stdio.h>

double square(double x);

int main(void)
{
    double data, result;
    int i;

    printf("請輸入正方形的邊長:");
    scanf("%lf", &data);

    result = square(data);

    printf("邊長 %f 的正方形, 面積是 %f\n", data, result);

    for (i=0; i<5; i++)
    {
        result = result / 2;
        printf("%d %f\n", i, result);
    }

    return 0;
}

double square(double x)
{
    double sq;
    sq = x * x;
    return sq;
}
上面這個程式有用到自己定義的函式 square() 還有迴圈 for ..., 如果在課堂裡沒有講到, 因為在這裡你有編譯器還有除錯器, 等一下藉由除錯器一步一步的動作, 你應該可以很快看出來它們在程式裡的作用。以下我們先修改一些設定, 然後編譯執行一下程式 :

產生除錯資訊

編譯程式 (快速鍵 F9)

編譯結果如下

編譯成功後可以直接執行程式 (快速鍵 F10)

顯示下列執行視窗, 輸入資料 123 並顯示結果

接下來我們要說明 原始程式碼除錯器 的基本用法

原始程式碼除錯器最主要兩個功能是

  1. 顯示程式執行過程中每一個變數裡資料的變化
  2. 顯示程式敘述的執行順序

我們以剛才這個 testDebug.cpp 程式為例

首先在懷疑有錯誤的地方以滑鼠左鍵點選該列前方設定中斷點

開始偵錯 (快速鍵 F5)

或是在工具列上按除錯按鈕

如果編譯前沒有打開「產生除錯資訊」, 就會出現下面訊息

原始程式碼除錯器 停在第一個中斷點之前, 藍色列代表目前正準備執行的程式敘述

左邊視窗自動跳到 "除錯" 面板, 以便觀察目前區域變數裡存放的數值或是任何 "監看式" 的數值, 目前我們都還沒有設定所以是空白的

下方也跳到 "除錯" 控制面板, 顯示在除錯時會用到的一些快速控制按鈕

另外也出現執行程式的視窗, 目前因為 printf 敘述還沒開始執行, 所以畫面是全黑的

請按逐行執行 (F7或Alt-N), 藍色列以及前面的藍色箭頭移到 scanf, 執行視窗中也顯示 printf 的列印結果

目前準備執行的是呼叫 scanf() 函數, 我們打算在 "除錯" 視窗 中觀察程式的執行過程和每一個敘述的作用, 所以我們來設定一些要監看的變數和監看式, 點選新增監看式, 輸入區域變數名稱 data, 按 OK

可以看到在除錯視窗裡出現 data = ... 的顯示, 由於 scanf 函數還沒有執行, 所以 data 變數裡的資料是這台機器執行你的程式之前, 執行其它程式時記憶體裡留下的數值, 對你現在執行的這個程式而言完全沒有意義

另外把滑鼠移到變數上, 也會出現同樣的資料在浮動的工具提示視窗裡

也可以輸入運算式 data*data

以滑鼠右鍵點選監看式 data*data=0, 可以選擇「移除監看式」或是直接按 delete 來刪除

還可以選擇「修改數值」來直接修改變數內存放的數值

 

可惜這裡沒有做得很好, 所以修改以後只能在 浮動視窗 裡看到修改的數值, 在除錯監看視窗裡顯示的還是舊的資料

你也嘗試把變數 result 和變數 i 都加進來, 然後你可以按下方的新增監看式, 出現下面視窗, 輸入 i<5

如此就可以在 「除錯」 窗格裡看到如下畫面

i < 5 是迴圈執行時測試是否要繼續的條件, 當你輸入監看式的時候, 除錯器根據程式執行時變數 i 的數值來計算 i < 5 的數值, 目前 i 是 0 (其它程式執行後留在記憶體裡的數值), 計算的結果 i 的確小於 5, 所以數值為 true (邏輯敘述為真, C 裡是 1), 如果大於 5 的話, 數值為 false (邏輯敘述為假)

接下來就請你按 "逐行執行 (F7)" 並且在執行視窗內輸入數字, 例如 123.4

請注意每一步到底執行了哪一列以及觀察變數裡的資料變化

此時藍色箭頭往下移動到下一列 result = square(data);, 準備呼叫 square() 函數, 函數執行完以後的結果會儲存在 result 變數中

此時請點選 「進入函數 (F8)」, 於是程式暫停在第 31 列, square() 函式的第一列, 請將變數 x 加入監看視窗, 請注意此時參數 x 的資料已經是 123.4 了

我們可以點選下方的「檢視 CPU 視窗」 按鈕, 可以看到「反向追蹤」窗格顯示函式呼叫的順序 (這個叫做 call stack),
現在的內容表示 testDebug.cpp main() 函數的第 14 列呼叫 testDebug.cpp square() 函數, 目前執行到第 31 列 , 其參數 x 的數值是 123.400000, 可以點選其中任何一列, 視窗裡就會跳出那個函式的內容 (這是一個常駐的視窗, 可以一直開著)

如果你的程式有 300 列, 這樣可以很快地看到那一列的程式

接下來請你按 「逐行執行」, 並觀察變數 sq 裡面資料的變動

接下來請你按 「逐行執行」 一直到 return() 執行完畢, 回到呼叫端後停在下一列程式 (第 16 列), 這時候也請你注意 square() 執行完以後, 還有一個 result = 的動作, 完成以後當然 result 變數的內容就變了

如果你覺得 「除錯」 窗格 裡有一些資料是你不再需要的, 你可以用滑鼠右鍵點選它, 然後選擇 Delete

接下來請你按 「逐行執行」 執行到 printf 函數, 在執行視窗裡顯示下列資料

接下來請你按 「逐行執行」 進入 for 迴圈, 此時請特別注意左方變數 i 的內容, 還有監看式 i<5 的數值, 以及變數 result 的數值

任何時候如果你不想除錯了, 可以按 「中斷執行 (F6)」 來結束偵錯

思考有盲點, 越心急盲點越多, 尤其是先入為主的假設常常讓你作白工

他的盲點是?!

 

盲點是?!

程式設計課程 首頁

製作日期: 104/12/20 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw TEL: 02 24622192x6615
海洋大學 電機資訊學院 資訊工程學系 Lagoon