一个简单的内存泄漏检测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 <stdlib.h>

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值