程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> 一個簡單的內存洩漏檢測C工具

一個簡單的內存洩漏檢測C工具

編輯:關於C

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

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;
}

7. 定義調試用的calloc函數

/**
 * 用於調試的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;
}

8. 定義調試用的free函數

/**
 * 用於調試的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

  使用的時候只需要包含上述頭文件(例如命名為memcheck.h),並將上述C文件引入到項目中即可。測試代碼如下:

#ifdef DEBUG
#include "memcheck.h"
#endif

int main()
{
	int* p;

#ifdef DEBUG
	atexit(show_block); // 在程序結束後顯示內存洩漏報告
#endif // DEBUG
	// 分配內存並不回收,顯示內存洩漏報告
	p = (int*)malloc(1000);

	return 0;
}



  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved