首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C/C++ 內存洩漏檢測工具 Valgrind Memcheck 使用經歷

     編輯:關於C++

背景

Linux 下的 Valgrind 是檢測內存錯誤的利器。曾因 VS2013 下運行正常的程序在 Linux g++ 編譯後崩潰,且輸出大量匯編碼無法定位。後意識到可能是內存管理錯誤:VS 對此類檢查較弱,只要不影響運行往往不報錯,導致代碼存在隱患。

更新: 讀者推薦 VS 插件 VLD (Visual Leak Detector),安裝後在入口文件包含 <vld.h> 即可,但我測試時無論是否有洩漏都顯示 "No memory leaks detected.",原因未知。

Valgrind 實戰記錄

首次檢測結果慘不忍睹(程序規模較大)。

首次檢測結果

逐步修改後,雖仍有少量問題,但程序已能成功運行。

修改後結果

常見錯誤類型總結

1. new/delete 与 malloc/free 不匹配

最低級錯誤:混用 new/new[] 與 free/delete/delete[]。統一改用 new/new[] 和 delete/delete[] 後問題消失。

正確釋放模式

基本數據類型

// 一維
int *d = new int[5];
delete[] d;

// 二維
int **d = new int*[5];
for (int i = 0; i < 5; i++) d[i] = new int[10];
for (int i = 0; i < 5; i++) delete[] d[i];
delete[] d;

自定義類型 (含析構函數)

class DFA { ... ~DFA() { ... } };

// 一維
DFA *d = new DFA();
delete d;

// 二維
DFA **d = new DFA*[5];
for (int i = 0; i < 5; i++) d[i] = new DFA();
for (int i = 0; i < 5; i++) delete d[i];
delete[] d;

原理深度解析: 編譯器分配的內存塊包含頭部信息(大小、標記等)。對於有析構函數的類型,new[] 會在用戶指針前額外存儲數組元素個數,以便 delete[] 正確調用析構函數。若基本類型用 delete[] 或 delete 釋放效果相同,但為了統一規範,建議始終配對使用。

2. 零長數組導致的 Invalid read/write

結構體使用柔性數組成員 char app_name[0] 實現按需分配:

struct accept_pair {
    bool is_accept_state;
    bool is_strict_end;
    char app_name[0];
};

int main() {
    char *s = "Alexia";
    accept_pair *ap = (accept_pair*)malloc(sizeof(accept_pair) + sizeof(s)); // 錯誤!sizeof(s) 是指針大小
    strcpy(ap->app_name, s);
    printf("app name: %s\n", ap->app_name);
    free(ap);
    return 0;
}

Valgrind 報大量 Invalid read/write。原因: sizeof(s) 返回指針大小 (4/8 字節),而非字符串長度。應改為 strlen(s) + 1。修正後問題解決。

3. 擴容邏輯導致的 definitely lost

DFA::add_size 擴容時,為新數組每個元素預先 new 了 accept_pair,隨後 add_state 又對同一索引 new 覆蓋,導致原對象洩漏。

// 錯誤版 add_size
void DFA::add_size(int size) {
    accept_pair **tmp_states = new accept_pair*[size + _size];
    for (int s = 0; s < size + _size; s++) 
        tmp_states[s] = new accept_pair(false, false); // 這裡預分配了所有元素
    // ... 拷貝舊數據 ...
    // 釋放舊數組
    _size += size;
    delete[] accept_states;
    accept_states = tmp_states;
}

// 正確版:新數組元素初始化為 NULL,按需分配
void DFA::add_size(int size) {
    accept_pair **tmp_states = new accept_pair*[size + _size];
    for (int s = 0; s < size + _size; s++) tmp_states[s] = NULL; // 關鍵修改
    for (int s = 0; s < _size; s++) {
        tmp_states[s] = new accept_pair(false, false);
        // ... 拷貝數據 ...
    }
    // 釋放舊數組
    _size += size;
    delete[] accept_states;
    accept_states = tmp_states;
}

Valgrind 精確定位洩漏 16 字節 (32位系統下 accept_pair 8 字節,兩個對象),修正後問題解決。

感悟

Valgrind 幫我發現大量低級錯誤,深感慚愧也受益匪淺。記錄下來以儆後學。C++ 內存管理需極謹慎,工具輔助不可或缺。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved