Linux 下的 Valgrind 是檢測內存錯誤的利器。曾因 VS2013 下運行正常的程序在 Linux g++ 編譯後崩潰,且輸出大量匯編碼無法定位。後意識到可能是內存管理錯誤:VS 對此類檢查較弱,只要不影響運行往往不報錯,導致代碼存在隱患。
更新: 讀者推薦 VS 插件 VLD (Visual Leak Detector),安裝後在入口文件包含 <vld.h> 即可,但我測試時無論是否有洩漏都顯示 "No memory leaks detected.",原因未知。
首次檢測結果慘不忍睹(程序規模較大)。

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

最低級錯誤:混用 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 釋放效果相同,但為了統一規範,建議始終配對使用。
結構體使用柔性數組成員 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。修正後問題解決。
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++ 內存管理需極謹慎,工具輔助不可或缺。