思路:通过宏,将 malloc 进行替换,并封装成自己的malloc
在自己的 malloc 中在用户申请的内存基础上多出一个自己管理的结构体,位于最前面。并管理一个链表。
释放时通过偏移量获取结构体,然后从链表中删除。
memdebug.c
#include <stdio.h>
#include <stdlib.h>
typedef struct memdebug
{
void *p;
size_t size;
int line;
const char *file;
struct memdebug *next;
struct memdebug *prev;
} memdebug_t;
static memdebug_t *head;
void *memdebug_alloc(size_t size, const char *file, int line)
{
memdebug_t *mem = (memdebug_t *)malloc(size + sizeof(memdebug_t));
if (mem == NULL)
return NULL;
mem->p = mem + 1;
mem->size = size;
mem->file = file;
mem->line = line;
mem->next = mem->prev = NULL;
// printf("malloc: %d %s:%d\n", size, file, line);
if (head == NULL)
head = mem;
else
{
if (head->prev)
{
head->prev->next = mem;
mem->prev = head->prev;
}
head->prev = mem;
mem->next = head;
head = mem;
}
return mem->p;
}
void memdebug_free(void *p, const char *file, int line)
{
if (p == NULL)
return;
memdebug_t *mem = (memdebug_t *)(p - sizeof(memdebug_t));
if (mem->p != p)
return;
// printf("free: %d %s:%d, malloc in %s:%d\n", mem->size, file, line, mem->file, mem->line);
if (head == mem && head->next == NULL)
head = NULL;
else if (head == mem)
{
head->next->prev = NULL;
head = head->next;
}
else
{
if (mem->prev)
mem->prev->next = mem->next;
if (mem->next)
mem->next->prev = mem->prev;
}
mem->p = NULL;
free(mem);
}
void memdebug_print(void)
{
for (memdebug_t *mem = head; mem; mem = mem->next)
printf("mem: %d %s:%d\n", mem->size, mem->file, mem->line);
}
memdebug.h
#ifndef __MEMDEBUG_H__
#define __MEMDEBUG_H__
#include <stdint.h>
void *memdebug_alloc(size_t size, const char *file, int line);
void memdebug_free(void *p, const char *file, int line);
void memdebug_print(void);
#define malloc(size) memdebug_alloc(size, __FILE__, __LINE__)
#define free(p) memdebug_free(p, __FILE__, __LINE__)
#endif
main.c
#include <stdio.h>
#include <stdlib.h>
#include "memdebug.h"
void test(void)
{
char *s;
for (int i = 0; i < 10; i++)
{
s = (char*)malloc(i*10);
if(i % 2 == 0)
free(s);
}
char buf[1011];
free(buf);
free(s);
free(s);
}
int main(int argc, char const *argv[])
{
test();
memdebug_print();
return 0;
}
当需要调试时只需在 #include <stdlib.h> 前加入 #include “memdebug.h”,不影响原始代码。
memdebug_print 打印出来的就是未释放的内存信息,这些内存当然包括运行时常驻内存,当然也包括泄漏的内存。
当程序出错时,可以打印出来,然后手动检测每一个没有释放的那些内存。
到这里就只能手动了,是的,这个还没有强大到自动识别泄漏的内存,主要是你无法知道有哪些内存申请了,但是引用丢失了。C++智能指针似乎可以做到。虽然可以实现类似引用计数,但在写代码时会增加很多约束,实现起来似乎也比较麻烦。
这个就适合代码量不大的内存泄漏,但是代码量不大,还担心屁的内存泄漏。所以没什么用的东西。
代码量大的话,有上千个内存申请,一个一个排查,还是挺费时的。但总比原始的方式从源代码中排除好很多。
它能够统计同一行内存申请代码行被调用了多少次,而这部分很可能就是泄漏源。