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

我們可以看到泄漏的大小和行號

原版的代碼的聯合體是用來內存對齊的加快訪問速度


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM