這個內存洩漏檢測工具很簡單,只能檢測同一個模塊,同一個線程中發送的內存洩漏,對於在編寫代碼過程中的代碼調試有一定的幫助。如果要在集成測試或功能測試中檢測內存洩漏,還需借助專門的工具。
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; }