在開發調試階段,快速定位同模組、同線程的記憶體洩漏至關重要。本文實現一個基於鏈表記錄分配/釋放的簡易檢測工具,通過宏替換標準庫函數,零侵入式集成到項目中。
#undef malloc/free/calloc 避免衝突。dbg_malloc(size, file, line):分配後插入鏈表頭。dbg_calloc(count, size, file, line):同理,計算總大小。dbg_free(ptr):釋放記憶體並從鏈表移除節點。show_block() 遍歷鏈表輸出洩漏位置、位址、大小,並統計總量。memcheck.h#ifndef _MEM_CHECK_H
#define _MEM_CHECK_H
#include <stdlib.h>
#define malloc(s) dbg_malloc(s, __FILE__, __LINE__)
#define calloc(c, s) dbg_calloc(c, s, __FILE__, __LINE__)
#define free(p) dbg_free(p)
void *dbg_malloc(size_t size, const char *file, size_t line);
void *dbg_calloc(size_t count, size_t size, const char *file, size_t line);
void dbg_free(void *ptr);
void show_block(void);
#endif
memcheck.c 核心邏輯static mem_node *head = NULL;
static void mem_node_add(void *ptr, size_t block, size_t line, const char *file) {
mem_node *node = malloc(sizeof(mem_node));
node->ptr = ptr; node->block = block; node->line = line; node->filename = file;
node->next = head; head = node;
}
static void mem_node_remove(void *ptr) {
if (!head) return;
if (head->ptr == ptr) { head = head->next; free(ptr); return; }
mem_node *prev = head, *curr = head->next;
while (curr) {
if (curr->ptr == ptr) { prev->next = curr->next; free(curr); return; }
prev = curr; curr = curr->next;
}
}
void show_block() {
if (!head) return;
size_t total = 0;
puts("\n--- 記憶體洩漏報告 ---");
for (mem_node *p = head; p; p = p->next) {
const char *file = strrchr(p->filename, '\\'); // 簡化路徑顯示
file = file ? file + 1 : p->filename;
printf("位置: %s(%zu), 位址: %p (%zu bytes)\n", file, p->line, p->ptr, p->block);
total += p->block;
}
printf("總計洩漏: %zu bytes\n", total);
}
memcheck.c 加入專案編譯。#include "memcheck.h"(建議僅在 DEBUG 模式下)。atexit(show_block);。#ifdef DEBUG
#include "memcheck.h"
#endif
int main() {
#ifdef DEBUG
atexit(show_block);
#endif
int *p = malloc(1000); // 故意不釋放
return 0;
}
--- 記憶體洩漏報告 --- 位置: main.c(10), 位址: 0x55a3c8 (1000 bytes) 總計洩漏: 1000 bytes
realloc、new/delete(C++)、系統級分配。