Lab 2-3: Memory Leakage Detection

 
實習目標 練習運用 crtdbg 函式庫偵測記憶體未釋放的錯誤
 
步驟一 下列程式片段中有記憶體未釋放的錯誤:
    struct File
    {
        char *name;
        char *data;
    } *filePtr;
    ...
    filePtr = (struct File *) malloc(sizeof(struct File));
    filePtr->name = (char *) malloc(20*sizeof(char));
    filePtr->data = (char *) malloc(500*sizeof(char));
    ...
    free(filePtr);
上面這個程式片段中配置給 name 和 data 的 520 個位元組並沒有釋放, 只釋放了配置給 struct File 結構的記憶體。
程式裡有這種錯誤會導致什麼現象? 如果你用過微軟早期的視窗系統的話, 你可能還有印象說系統執行幾天以後就會出現 "系統資源不足", 不穩定當機的現象, 常常就是作業系統裡面的程式或是所執行的應用程式裡有這種記憶體 未釋放的錯誤, 程式只執行幾分鐘或是幾個小時還不會發生什麼問題, 可是一旦長時間執行就穿幫了。
步驟二 下面幾個執行程式都有類似的錯誤, 可是短時間執行時不見得看得到錯誤, 當撰寫的程式很大時, 要找到究竟在什麼地方發生這種錯誤其實有相當的困難度: 撰寫 C/C++ 程式時由於希望給程式撰寫者最大的操作彈性, 所使用的記憶體的配置與釋放完全由程式設計者來控制, 因此必須徹底了解到 "程式配置多少個位元組的記憶體就必須釋放多少個位元組的記憶體", 在現在個人電腦動輒有一兩 Giga byte 的記憶體, 很容易給人一種錯覺, 覺得少釋放幾十個 byte 應該沒有什麼了不起的, 甚至很難看到系統因此而當掉; 不過這是一種習慣, 一旦養成這種習慣, 你的程式裡就常常會忘記釋放記憶體, 一旦你的程式必須在資源有限的硬體上來運作, 例如: 手機, PDA, 嵌入式系統, 硬體配件 (DVD, 相機, ...) (這些都是目前台灣賺錢的產品), 那不但會讓自己的程式死掉, 也會害整個機器無法運作下去, 一個工作團隊裡如果有一個成員有這種習慣, 整個開發團隊常常必須付出幾倍的偵錯時間。
步驟三 請下載 memory_leak.hmemory_leak.cpp
請撰寫一個使用 malloc 配置記憶體的程式如下:
    #include <malloc.h>
    
    void main()
    {
        int *ptr;
        int i;
        ptr = (int *) malloc(100*sizeof(int));
        for (i=0; i<100; i++)
            ptr[i] = i;
        free(ptr);
    }
請編譯並執行, 如果刪掉 free() 那一列的話, 程式還是可以編譯, 還是可以執行, 但是就有記憶體未釋放的錯誤, 請以下述方法來自動偵測這種錯誤
  1. 將上面程式中 free(ptr); 那一列註解掉
  2. 在此程式最前面加入
        #include "memory_leak.h"
  3. 在 Visual Studio 界面中點選選單 Project/Add to Project/Files... , 將 memory_leak.h 及 memory_leak.cpp 加入此 project
  4. 編輯 memory_leak.h, 確定下列定義的常數值為 0
        #define TEST_MEM_LEAKS_BREAK_NUM  0
  5. 在 main() 函式一開始的地方加入下列函式呼叫
        set_initial_leak_test();
編譯 (debug 組態, cl -MLd ...), 執行就會發現錯誤訊息如下圖
    c:/testMemLeakage3

    [Leak test being performed]
    Detected memory leaks!
    Dumping objects ->
    {40} normal block at 0x003707A8, 400 bytes long.
    Data: <                > 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00
    Object dump complete.
    
步驟四 如果希望透過 memory_leak.h 來找到究竟是那一次配置的記憶體沒有釋放, 需要編輯 memory_leak.h 檔案, 將下列中的 0 修改為上面錯誤訊息中的 40
    #define TEST_MEM_LEAKS_BREAK_NUM  40
再編譯, 並且執行, 可以看到下列訊息視窗

點選除錯, 開啟除錯視窗

如果你的機器上只安裝了 VC6.0, 應該會開啟 VC6.0 來偵錯, 如果你的機器有安裝 VC .net, 有可能此時會開啟 VC .net 的 debugger 讓你使用, 如果你希望用 VC6.0 的 debugger 的話, 可以參考相關設定

此時以滑鼠右鍵點選上方工具列, 選擇 Call Stack 如下圖:
此時出現 Call Stack 視窗, 顯示沒有釋放的記憶體是在程式哪一列配置的
步驟五 請撰寫一個使用 C++ 的 new 配置記憶體的程式如下:
    void main()
    {
        int *ptr;
        int i;

        ptr = new int[100];
        for (i=0; i<100; i++)
            ptr[i] = i;
//        delete ptr;  // 這是一個非常容易犯的錯誤
        delete[] ptr;
    }
請將 delete[] ptr 這一列註解掉, 重複步驟三和步驟四的測試。
步驟六 請向助教說明你如何找到是哪一列的記憶體配置敘述錯誤, 將所完成的 project (去掉 debug/ 資料匣下的所有內容) 壓縮起來, 選擇 Lab2-3 上傳

在你自己的系統上,如果你有灌其他的開發環境 (例如 visual studio 2005), 發生錯誤時系統可能不會執行 VC6.0 的 debugger, 此時你可以用下列設定可以改變系統預設的偵錯環境 (你需要有適當的權限)

 
 

C++ 物件導向程式設計課程 首頁

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