這個內存洩漏檢測工具很簡單,只能檢測同一個模塊,同一個線程中發送的內存洩漏,對於在編寫代碼過程中的代碼調試有一定的幫助。如果要在集成測試或功能測試中檢測內存洩漏,還需借助專門的工具。
1. 先取向malloc,free和calloc這幾個標識符的定義:注意這一步非常重要,否則後面的malloc、free和calloc函數會和我們稍後在頭文件中定義的宏沖突
// 取消malloc, calloc, free的宏定義 #undef malloc #undef calloc #undef free
2. 定義保存內存信息的單向鏈表
/**
* 定義鏈表節點,表示一個內存洩漏信息
*/
typedef struct _mem_node
{
void *ptr; // 洩漏內存地址
size_t block; // 洩漏內存大小
size_t line; // 洩露發生的代碼行
char *filename; // 洩漏發生的文件名
struct _mem_node *next; // 下一個節點指針
} mem_node;
// 定義指向頭節點的指針
mem_node *head = NULL;
3. 用於將節點加入單項鏈表的函數
/**
* 產生一個節點並加入鏈表
* @param ptr 分配的內存地址
* @param block 分配的內存單元大小
* @param line 代碼行號
* @param filename 文件名稱
*/
static void mem_node_add(void *ptr, size_t block, size_t line, char *filename)
{
// 產生節點
mem_node *node = malloc(sizeof(mem_node));
node->ptr = ptr;
node->block = block;
node->line = line;
node->filename = filename;
node->next = NULL;
// 加入鏈表頭節點
if (head)
{
node->next = head;
head = node;
}
else
head = node;
}
4. 從單項鏈表中刪除節點的函數
/**
* 從鏈表中刪除一個節點
* @param ptr 分配的內存地址
*/
static void mem_node_remove(void *ptr)
{
// 判斷頭節點是否存在
if (head)
{
// 處理頭節點
if (head->ptr == ptr)
{
// 獲取頭節點的下一個節點
mem_node *pn = head->next;
// 刪除頭節點
free(head);
// 令頭節點指針指向下一個節點
head = pn;
}
else // 判斷鏈表是否為空
{
// 指向節點的指針
mem_node *pn = head->next;
// 指向前一個節點的指針
mem_node *pc = head;
// 遍歷所有節點
while (pn)
{
// 獲取指向下一個節點的指針
mem_node *pnext = pn->next;
if (pn->ptr == ptr)
{
pc->next = pnext; // 刪除當前節點
free(pn);
}
else
pc = pc->next;
pn = pnext;
}
}
}
}5. 顯示內存洩露信息報告
/**
* 顯示內存洩漏信息
*/
void show_block()
{
if (head)
{
// 保存總內存洩漏數量
size_t total = 0;
// 指向頭節點的指針
mem_node *pn = head;
// 輸出標題
puts("\n\n-------------------------------內存洩漏報告------------------------------------\n");
// 遍歷鏈表
while (pn)
{
mem_node *pnext = pn->next;
// 處理文件名
char *pfile = pn->filename, *plast = pn->filename;
while (*pfile)
{
// 找到\字符
if (*pfile == '\\')
plast = pfile + 1; // 獲取\字符的位置
pfile++;
}
// 輸出內存洩漏信息
printf("位置:%s(%d), 地址:%p(%dbyte)\n", plast, pn->line, pn->ptr, pn->block);
// 累加內存洩漏總量
total += pn->block;
// 刪除鏈表節點
free(pn);
// 指向下一個節點
pn = pnext;
}
printf("總計內存洩漏:%dbyte\n", total);
}
}6. 定義調試用malloc函數
/**
* 用於調試的malloc函數
* @param elem_size 分配內存大小
* @param filename 文件名稱
* @param line 代碼行號
*/
void *dbg_malloc(size_t elem_size, char *filename, size_t line)
{
void *ptr = malloc(elem_size);
// 將分配內存的地址加入鏈表
mem_node_add(ptr, elem_size, line, filename);
return ptr;
}/**
* 用於調試的calloc函數
* @param count 分配內存單元數量
* @param elem_size 每單元內存大小
* @param filename 文件名稱
* @param line 代碼行號
*/
void *dbg_calloc(size_t count, size_t elem_size, char *filename, size_t line)
{
void *ptr = calloc(count, elem_size);
// 將分配內存的地址加入鏈表
mem_node_add(ptr, elem_size * count, line, filename);
return ptr;
}/**
* 用於調試的free函數
* @param ptr 要釋放的內存地址
*/
void dbg_free(void *ptr)
{
free(ptr);
// 從鏈表中刪除節點
mem_node_remove(ptr);
}上述代碼應包含在一個C文件中(例如memcheck.c),完成上述步驟,就可以利用這一組函數來檢測內存洩露了,需要定義如下頭文件,該頭文件應該被書寫上述函數的C文件include:
#ifndef _MEM_CHECK_H #define _MEM_CHECK_H #include// instead of malloc #define malloc(s) dbg_malloc(s, __FILE__, __LINE__) // instead of calloc #define calloc(c, s) dbg_calloc(c, s, __FILE__, __LINE__) // instead of free #define free(p) dbg_free(p) /** * allocation memory */ void *dbg_malloc(size_t elem_size, char *filename, size_t line); /** * allocation and zero memory */ void *dbg_calloc(size_t count, size_t elem_size, char *filename, size_t line); /** * deallocate memory */ void dbg_free(void *ptr); /** * show memory leake report */ void show_block(); #endif // _MEM_CHECK_H
#ifdef DEBUG
#include "memcheck.h"
#endif
int main()
{
int* p;
#ifdef DEBUG
atexit(show_block); // 在程序結束後顯示內存洩漏報告
#endif // DEBUG
// 分配內存並不回收,顯示內存洩漏報告
p = (int*)malloc(1000);
return 0;
}