問題代碼片段
void MergeSort(int a[], int n) {
int *b = new int[n]; // 動態配置暫存陣列
int s = 1;
while (s < n) {
MergePass(a, b, s, n);
s += s;
MergePass(b, a, s, n);
s += s;
}
// 缺少 delete[] b; → 記憶體洩漏!
}
洩漏原因分析
| 階段 | 狀態 |
| 函式進入 | b 指向堆上新配置的 n * sizeof(int) 位元組 |
| 函式執行 | 使用 b 作為歸併暫存區,邏輯正確 |
| 函式返回 | b 區域變數銷毀,但堆上記憶體未釋放,產生洩漏 |
| 程式結束 | OS 回收進程記憶體,但長期運行程式會累積耗盡記憶體 |
修正方案
方法一:函式結束前釋放(最簡單)
void MergeSort(int a[], int n) {
int *b = new int[n];
int s = 1;
while (s < n) {
MergePass(a, b, s, n);
s += s;
MergePass(b, a, s, n);
s += s;
}
delete[] b; // 關鍵:配對釋放
}
方法二:RAII 智慧指標(現代 C++ 風格)
#include
void MergeSort(int a[], int n) {
std::unique_ptr b(new int[n]); // 自動釋放
int s = 1;
while (s < n) {
MergePass(a, b.get(), s, n);
s += s;
MergePass(b.get(), a, s, n);
s += s;
}
// 離開作用域自動 delete[]
}
方法三:std::vector(推薦,最安全簡潔)
#include
void MergeSort(int a[], int n) {
std::vector b(n); // 自動管理記憶體
int s = 1;
while (s < n) {
MergePass(a, b.data(), s, n);
s += s;
MergePass(b.data(), a, s, n);
s += s;
}
}
記憶體洩漏檢測工具
| 工具 | 平台 | 用法 | 輸出範例 |
| Valgrind | Linux/macOS | valgrind --leak-check=full ./prog | definitely lost: 40 bytes in 1 blocks |
| AddressSanitizer (ASan) | GCC/Clang | gcc -fsanitize=address -g prog.c | Direct leak of 40 byte(s) in 1 object(s) |
| Visual Studio CRT | Windows | _CrtDumpMemoryLeaks() | Detected memory leaks! {123} normal block at 0x... |
| Dr. Memory | Windows/Linux | drmemory -- prog.exe | 類似 Valgrind |
NVWA 除錯工具使用說明
使用者提到的 NVWA (New Version of Wu's Allocator) 包含 debug_new.cpp:
// 編譯連結
g++ your_code.cpp debug_new.cpp -o your_prog
// 執行
./your_prog
// 若有洩漏,程式結束時輸出類似:
// Leaked memory at address 0x... size 40 bytes allocated from your_code.cpp:123
使用者遇到的「加上 delete[] 後無法運行」問題分析
| 可能原因 | 檢查方法 |
| delete[] 位置錯誤(提前釋放) | 確認 delete[] b; 在 所有使用 b 之後、函式返回前 |
| 重複釋放 | 檢查是否有其他地方也 delete[] b |
| b 被修改(指標偏移) | 確保 b 指向原始配置位址,未做 b++ 等運算 |
| 編譯器/執行環境問題 | 嘗試 std::vector 版本,排除手動管理錯誤 |
歸併排序完整可運行版(C++ 風格)
#include
#include
void Merge(int src[], int dest[], int left, int mid, int right) {
int i = left, j = mid + 1, k = left;
while (i <= mid && j <= right) {
dest[k++] = (src[i] <= src[j]) ? src[i++] : src[j++];
}
while (i <= mid) dest[k++] = src[i++];
while (j <= right) dest[k++] = src[j++];
}
void MergePass(int src[], int dest[], int seg, int n) {
int i = 0;
while (i <= n - 2 * seg) {
Merge(src, dest, i, i + seg - 1, i + 2 * seg - 1);
i += 2 * seg;
}
if (i + seg < n) Merge(src, dest, i, i + seg - 1, n - 1);
else for (int j = i; j < n; j++) dest[j] = src[j];
}
void MergeSort(int a[], int n) {
std::vector b(n);
int s = 1;
while (s < n) {
MergePass(a, b.data(), s, n);
s <<= 1;
MergePass(b.data(), a, s, n);
s <<= 1;
}
}
int main() {
int a[] = {54, 6, 64, 84, 98, 498, 446, 49, 98, 981};
int n = sizeof(a) / sizeof(a[0]);
MergeSort(a, n);
for (int i = 0; i < n; i++) std::cout << a[i] << ' ';
std::cout << '\n';
return 0;
}
核心原則總結
- 誰配置,誰負責釋放:
new[] 必配 delete[],malloc 必配 free
- RAII 原則:資源獲取即初始化,利用物件生命週期自動管理資源
- 優先使用標準容器:
std::vector/std::string/std::unique_ptr 避免手動記憶體管理
- CI 整合檢測:編譯加
-fsanitize=address,跑測試自動捕獲洩漏