一个C语言内存管理模块的实现

C 内存管理模块的编写

C语言手动管理内存很困难,有时候很难发现内存泄漏,这两天看了一下里面有写了一个简单的内存管理模块,发现挺精巧,可以有效检测内存泄漏

原理很简单,就是把C的malloc函数分配的内存用一个链表记录起来,最后查看这个链表是否还有节点就可以判断是否有内存泄漏了

首先我们先看一下这个链表的数据结构

//标记数组的大小
#define MARK_SIZE 4
typedef struct Mem_Header Mem_Header;
struct Mem_Header
{
    int size;//后面内存分配的大小
    Mem_Header *next;//下一个节点的指针
    Mem_Header *prev;//上一个节点的指针
    char *filename;//文件名指针
    int line;//在第几行
    unsigned char mark[MARK_SIZE];//标记数组,这个数据用来表示这块内存是否被破坏了
}

这个mark数组我们将用 0xcd填充,如果这个数组被动过了就说明这块内存被破坏了

我们每次malloc一块内存的时候,这个结构体就会被添加到这块内存的头部

就像这样:

void *p = malloc(size);
Mem_Header *header = (Mem_Header *)p;
//然后设置header信息
/*省略一些赋值过程*/
return (char*)header+sizeof(Mem_Header);

这样外部拿到的指针就可以随便用了,回收的时候还可以重新推出头部指针的信息,然后从链表中删除这个节点

为了防止后面溢出,可以在后面内存也加上 mark数组 这样我们每次分配的内存大小是 sizeof(Mem_Header) + size + MARK_SIZE其中size是需要分配的大小,MARK_SIZE是在后面加的标记数组

我们用malloc初始化的时候经常碰到0,我们把这块内存填充0xcc(没意义的值别的其实也行),更容易发现错误

memset(p, 0xcc, willAlloc);

我们先来看第一个函数:

//size 需要分配的大小
//filename 文件名
//line 行好
void *Mem_alloc(Mem_Controller *controller, size_t size, char *filename, int line)
{
    int willAlloc = size + MEM_HEADER_SIZE + MARK_SIZE;//实际分配大小
    void *p = malloc(willAlloc);
    if (!p)
        controller->handle(controller, filename, line, "malloc null");
    //填充数组
    memset(p, 0xcc, willAlloc);
    //设置头部一些信息
    Mem_setHeader(p, size, filename, line);
    //设置tail的信息
    Mem_setTail(p, size);
    //把这个节点添加到链表中
    Mem_addChain(controller, (Mem_Header *)p);
    return (char *)p + MEM_HEADER_SIZE;
}

这个函数就是分配一个内存,controller是控制器,实际上就是负责打印和保存头指针的

filename和line就是分配空间时候的文件和行号,可以用宏定义解决可以百度一下这两个宏__LINE__,__FILE__

内存释放:从链表中删除这个节点

void Mem_free(Mem_Controller *controller, void *p, char *filename, int line)
{
    if (!p)
        return;
    //头信息
    Mem_Header *header = (char *)p - MEM_HEADER_SIZE;
    //check header and tail
    //检查是不是mark数组损坏了
    Mem_check(controller, header, filename, line);
    //移除节点
    Mem_rmChain(controller, header);
    free(header);
}

打印所有链表中的节点

void Mem_dumps(Mem_Controller *controller)
{
    Mem_Header *pos = controller->memHeader;
    FILE *fp = stderr;
    int counter = 0;
    for (pos = controller->memHeader; pos; pos = pos->next)
    {
        Mem_check(controller, pos, pos->filename, pos->line);
        fprintf(fp, "[%04d]%p********************\n", counter,
                (char *)pos + MEM_HEADER_SIZE);
        fprintf(fp, "%s line %d size..%d\n",
                pos->filename, pos->line, pos->size);
        counter++;
    }
}

为了方便我们使用,我们可以设置几个宏定义

typedef struct Mem_Controller Mem_Controller;
extern Mem_Controller *pcontrol;

void *Mem_alloc(Mem_Controller *controller, size_t size, char *filename, int line);
void Mem_free(Mem_Controller *controller, void *p, char *filename, int line);
void Mem_dumps(Mem_Controller *controller);

#define current_mem_controller pcontrol
#define Mem_alloc_func(size) Mem_alloc(current_mem_controller, size, __FILE__, __LINE__)
#define Mem_free_func(p) Mem_free(current_mem_controller, p, __FILE__, __LINE__)
#define Mem_dump_func() Mem_dumps(current_mem_controller)

那个extern全局变量是别的文件定义的指针,__FILE__会自动替换文件名,这样我们就可以看到是哪里的内存没有被释放

使用的时候也简单了很多直接用宏定义就可以了

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MEM.h"

void fill_buffer(unsigned char *buf, int size)
{
    int i;

    for (i = 0; i < size; i++)
    {
        buf[i] = i;
    }
}
void mem_print(unsigned char *p, int size)
{
    for (int i = 0; i < size; i++)
    {
        fprintf(stderr, "%02x", p[i]);
    }
    fprintf(stderr, "\n");
}

int main()
{
    void *p = Mem_alloc_func(10);
    mem_print(p, 10);
    void *p2 = Mem_alloc_func(10);
    mem_print(p2, 10);
    void *p3 = Mem_alloc_func(10);
    mem_print(p3, 10);
    Mem_dump_func();
    fprintf(stderr, "try to clear\n");
    Mem_free_func(p2);
    Mem_free_func(p);
    Mem_free_func(p3);
    Mem_dump_func();
    fprintf(stderr, "then to malloc\n");
    p = Mem_alloc_func(10);
    fill_buffer(p, 10);
    p = Mem_realloc_func(p, 20);
    mem_print(p, 24);
    Mem_dump_func();
    return 0;
}

1620884-20190306165948571-1124346902.png

我们可以看到泄漏的大小和行号

原版的代码的联合体是用来内存对齐的加快访问速度

转载于:https://www.cnblogs.com/stdpain/p/10484403.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值