内存泄漏是C语言编程中一个很常见的问题,而且由于内存泄漏所导致的问题出现较缓慢,所以不容易觉察,所以写一个简单的程序来检测内存泄漏很有必要。
内存泄漏通常是指堆内存的泄漏,也就是通过malloc、calloc函数申请的内存,因此内存泄漏的检测方法核心思想就是通过宏定义自定义内存分配及释放函数来替换用户的malloc、calloc、free等函数。设计数据结构记录内存申请信息,申请内存时,记录并插入到全局的链表中;释放内存时从全局链表中查找对应的记录,并删除。程序结束时,将链表中信息写入文件,并清除链表。
如下所示,通过宏定义替换malloc、calloc、free等函数,并插入__FILE__、__LINE__定位语句位置。
- #define malloc(size) malloc_detector(size,__FILE__,__LINE__)
- #define calloc(element_num,element_size) calloc_detector (element_num,element_size,__FILE__,__LINE__)
- #define free(addr) free_detector(addr)
- 设计内存申请信息如下所示:
- typedef struct __info{
- void* addr; //the memory address
- unsigned char file[128]; //the file of the memory alloc statement
- unsigned int line; //the line of the memory alloc statment
- unsigned int size; //the size of the memory alloced
- }Alloc_Info;
- typedef struct __node{
- Alloc_Info info;
- Alloc_Info*next;
- }Alloc_Info_Node,*Alloc_Info_List;
#define malloc(size) malloc_detector(size,__FILE__,__LINE__)
#define calloc(element_num,element_size) calloc_detector (element_num,element_size,__FILE__,__LINE__)
#define free(addr) free_detector(addr)
设计内存申请信息如下所示:
typedef struct __info{
void* addr; //the memory address
unsigned char file[128]; //the file of the memory alloc statement
unsigned int line; //the line of the memory alloc statment
unsigned int size; //the size of the memory alloced
}Alloc_Info;
typedef struct __node{
Alloc_Info info;
Alloc_Info*next;
}Alloc_Info_Node,*Alloc_Info_List;
最终信息被写入到文件output_file中,以“w+”模式打开文件
- #define output_file "Leak_Detector_Report.txt"
#define output_file "Leak_Detector_Report.txt"
在定义被替换的malloc等函数之前,必须通过undef语句将malloc等的宏定义取消,以防止出现死循环的问题,然后才可以定义替换函数
- #undef malloc
- #undef calloc
- #undef free
- malloc_detector、free_detector定义如以下代码所示。
- void *malloc_detector(size_t size,unsigned char *file,unsigned int line)
- {
- void *ptr=malloc(size);
- Alloc_Info info;
- info.addr=ptr;
- info.line=line;
- info.size=size;
- strncpy(info.file,file,127);
- add_info(info); //Add info to the global Alloc_Info_List head
- return ptr;
- }
- void free_detector(void *addr)
- {
- delete_info(addr); //Find and Delete the info which has the address==addr from Alloc_Info_List head
- free(addr);
- /*we only detecte the memory-leak, not the wrong free-addr which not in the Alloc_Info_LIst*/
- }
#undef malloc
#undef calloc
#undef free
malloc_detector、free_detector定义如以下代码所示。
void *malloc_detector(size_t size,unsigned char *file,unsigned int line)
{
void *ptr=malloc(size);
Alloc_Info info;
info.addr=ptr;
info.line=line;
info.size=size;
strncpy(info.file,file,127);
add_info(info); //Add info to the global Alloc_Info_List head
return ptr;
}
void free_detector(void *addr)
{
delete_info(addr); //Find and Delete the info which has the address==addr from Alloc_Info_List head
free(addr);
/*we only detecte the memory-leak, not the wrong free-addr which not in the Alloc_Info_LIst*/
}
程序结束时刻调用report_info函数保存检测信息。
- void report_info()
- {
- FILE *fp_write=fopen(output_file,"w+");
- if(!fp_write){printf("can't open file\n");exit(1);}
- char info[sizeof(Alloc_Info)+128];
- Alloc_Info_List i=head,pre;
- if(i==NULL){
- sprintf(info,"%s","no memory leak");
- fwrite(info,strlen(info)+1,1,fp_write);
- }
- for(i=head;i!=NULL;)
- {
- sprintf(info,"memory leak:file %s line %d,addr %x,size %d\n",i->info.file,i->info.line,i->info.addr,i->info.size);
- fwrite(info,strlen(info)+1,1,fp_write);
- pre=i;
- i=i->next;
- free(pre);
- }
- flcose(fp_write);
- }
void report_info()
{
FILE *fp_write=fopen(output_file,"w+");
if(!fp_write){printf("can't open file\n");exit(1);}
char info[sizeof(Alloc_Info)+128];
Alloc_Info_List i=head,pre;
if(i==NULL){
sprintf(info,"%s","no memory leak");
fwrite(info,strlen(info)+1,1,fp_write);
}
for(i=head;i!=NULL;)
{
sprintf(info,"memory leak:file %s line %d,addr %x,size %d\n",i->info.file,i->info.line,i->info.addr,i->info.size);
fwrite(info,strlen(info)+1,1,fp_write);
pre=i;
i=i->next;
free(pre);
}
flcose(fp_write);
}
以下两个函数为链表操作
- void add_info(Alloc_Info info); //add memory alloc info to Alloc_Info_List
- void delete_info(void *addr); //find and delete the alloc info
void add_info(Alloc_Info info); //add memory alloc info to Alloc_Info_List
void delete_info(void *addr); //find and delete the alloc info
测试程序如下:
- #define DEBUG
- #include<stdio.h>
- #include<malloc.h>
- #include"leak_detector_c.h"
- extern Alloc_Info_List head;
- int main()
- {
- char *ptr1=malloc(100);
- char *ptr2=malloc(101);
- char *ptr3=malloc(102);
- char *ptr4=calloc(103,1);
- char *ptr5=calloc(104,1);
- char *ptr6=calloc(105,1);
- free(ptr1);
- free(ptr6);
- report_info();
- }
#define DEBUG
#include<stdio.h>
#include<malloc.h>
#include"leak_detector_c.h"
extern Alloc_Info_List head;
int main()
{
char *ptr1=malloc(100);
char *ptr2=malloc(101);
char *ptr3=malloc(102);
char *ptr4=calloc(103,1);
char *ptr5=calloc(104,1);
char *ptr6=calloc(105,1);
free(ptr1);
free(ptr6);
report_info();
}
测试结果如下:
- memory leak:file F:\个人源码库\内存泄漏处理方法\leak_detctor_c_v2\text.c line 9,addr 32620,size 101
- memory leak:file F:\个人源码库\内存泄漏处理方法\leak_detctor_c_v2\text.c line 10,addr 32728,size 102
- memory leak:file F:\个人源码库\内存泄漏处理方法\leak_detctor_c_v2\text.c line 12,addr 32830,size 103
- memory leak:file F:\个人源码库\内存泄漏处理方法\leak_detctor_c_v2\text.c line 13,addr 33ad0,size 104
memory leak:file F:\个人源码库\内存泄漏处理方法\leak_detctor_c_v2\text.c line 9,addr 32620,size 101
memory leak:file F:\个人源码库\内存泄漏处理方法\leak_detctor_c_v2\text.c line 10,addr 32728,size 102
memory leak:file F:\个人源码库\内存泄漏处理方法\leak_detctor_c_v2\text.c line 12,addr 32830,size 103
memory leak:file F:\个人源码库\内存泄漏处理方法\leak_detctor_c_v2\text.c line 13,addr 33ad0,size 104
检测内存泄漏时,默认每一个free所对应的地址已经被申请,即已经保存在全局列表中,可以对删除函数进行扩充,如果搜索完整个列表都没有发现对应的alloc_info则可以判定为free出错,对于定位free error有帮助。