用C语言手写动态内存管理


文章对应视频教程:

暂无,可以关注我的B站账号等待更新。


点击图片或链接访问我的B站主页~~~


1、动态内存管理过程

在这里插入图片描述
绿色矩形代表一片连续内存。
在这里插入图片描述
把内存分配出去,就是动态内存申请的过程。
在这里插入图片描述
程序不再使用内存,退还给系统内存,就是动态内存的回收。

程序的申请与回收就是动态内存的管理。
是不是很简单~~~

2、什么是动态内存,什么是静态内存?

动态内存和静态内存的区别主要在于内存分配和管理的时机和方式。
静态内存
• 分配时机:在编译时分配,内存分配大小在编译时确定。
• 作用范围:通常用于全局变量、静态变量和常量。
• 生存期:从程序开始运行到程序结束。
• 优点:分配和释放内存的开销较低,不存在碎片化问题。
• 缺点:内存使用不够灵活,难以处理动态变化的需求。
动态内存
• 分配时机:在运行时分配,内存分配大小在运行时确定。
• 作用范围:通常用于堆内存,程序运行时通过函数(如 malloc、calloc、realloc、free)进行管理。
• 生存期:由程序员控制,可以在运行时任意分配和释放。
• 优点:内存使用灵活,可以根据实际需要分配合适大小的内存,适合处理动态变化的需求。
• 缺点:需要显式管理内存,容易引发内存泄漏和碎片化问题。


3、为什么要使用动态内存?动态内存能解决什么问题?

3.1 为什么要使用动态内存?动态内存能解决什么问题?

  1. 灵活性:可以在程序运行时根据实际需求分配合适大小的内存,适应动态变化的情况。
  2. 内存效率:避免了静态分配中可能的内存浪费,减少了内存的占用,提高了内存的利用率。
  3. 数据结构:支持复杂数据结构(如链表、树、图等)的实现,这些数据结构需要在运行时动态分配内存。
  4. 程序设计:在一些设计模式和算法中,动态内存分配是必要的,以便处理不确定的数据量和变化的需求。

3.2 动态内存能解决的问题

• 变长数据:处理大小在编译时无法确定的数据,如用户输入、文件内容等。
• 复杂数据结构:实现需要动态调整大小的数据结构,如链表、树、哈希表等。
• 模块化和复用:在模块化设计和库函数中,动态内存分配使得函数更加通用,能够处理不同大小的数据。


4、实现动态内存管理需要实现哪些功能

4.1 实现动态内存管理通常需要以下基本功能

  1. 内存分配:从内存池或堆中分配一块指定大小的内存,并返回指向该内存块的指针(如 malloc)。
  2. 内存释放:将不再需要使用的内存块返回给内存池或堆,以便再次使用(如 free)。
  3. 内存重分配:调整已分配内存块的大小,可能会移动内存块以适应新的大小(如 realloc)。
  4. 边界检查和安全性:防止内存越界访问,检测和处理非法的内存操作。

4.2 常见问题及解决方法

内存泄漏(Memory Leak):
程序分配了内存但未能正确释放,导致内存不可用,随着时间的推移,内存泄漏会导致系统内存耗尽。
内存碎片(Memory Fragmentation):
频繁的内存分配和释放会导致内存不连续,碎片化问题严重时会导致无法找到足够大的连续内存块。
内存越界(Buffer Overflow/Underflow):
访问数组或内存块边界之外的内存,导致数据破坏或程序崩溃。


5、写一个简单的动态内存管理

首先将内存分为固定大小的内存块。
在这里插入图片描述
创建一个Used和一个Free的内存块的双向有序链表。将内存池中的两种内存块分别连接起来,方便管理。
在这里插入图片描述
设计一个动态内存管理的数据结构,用于管理整个内存池,包括Used、Free内存块的链表头储存,已经使用内存大小的储存等信息。
在这里插入图片描述
该动态内存管理支持多段内存同时管理、内存合并、最佳匹配申请、内存申请记录、内存溢出检测等功能。
实现这部分动态内存管理也很简单,代码如下:
fmpool.c

/**
 * @file dmpool.c
 * @author zhuxuanlin
 * @brief 
 * @version 0.1
 * @date 2024-06-20
 * 
 * @copyright Copyright (c) 2024
 * 
 */
#include "fmpool.h"
#include "string.h"
#include "stdio.h"

static fm_byte_t fmpool[FMPOOL_SIZE] FM_ALIGNED;
static fm_allocator_t *fm_alt;

#include <windows.h>
HANDLE hMutex;

static int __fm_mutex_init(void)
{
    #ifdef FMPOOL_MUTEX
        hMutex = CreateMutex(NULL,FALSE,NULL);
        if(hMutex == NULL)
        {
            FM_PRINT("create mutex error\r\n");
            return -1;
        }
        return 0;
    #else
        return 0;
    #endif 
}

static int __fm_mutex_take(void)
{
    #ifdef FMPOOL_MUTEX
        WaitForSingleObject(hMutex,INFINITE);
        return 0;
    #else
        return 0;
    #endif    
}

static int __fm_mutex_release(void)
{
    #ifdef FMPOOL_MUTEX
        ReleaseMutex(hMutex);
        return 0;
    #else
        return 0;
    #endif     
}

static fm_size_t __fm_align(fm_size_t size)
{
    size += sizeof(fm_used_t);
    return (size % FMPOOL_PAGE_SIZE)?(size + (FMPOOL_PAGE_SIZE - (size % FMPOOL_PAGE_SIZE) )):size;
}

static void __fm_check_free_magic(fm_free_t *ff)
{
    FM_ASSERT(ff);
    FM_ASSERT(fm_alt->mem_start <= (void*)ff);
    FM_ASSERT((fm_alt->mem_start + FMPOOL_SIZE) > (void*)ff);
    FM_ASSERT(ff->start_magic == FM_FREE_MAGIC);
    FM_ASSERT(ff->end_magic == FM_FREE_MAGIC);
}

static void __fm_check_used_magic(fm_used_t *fu)
{
    FM_ASSERT(fu);
    FM_ASSERT(fm_alt->mem_start <= (void*)fu);
    FM_ASSERT((fm_alt->mem_start + FMPOOL_SIZE) > (void*)fu);
    FM_ASSERT(fu->start_magic == FM_USED_MAGIC);
    FM_ASSERT(fu->end_magic == FM_USED_MAGIC);
}

static int __add_free_block(void *p ,fm_size_t size)
{
    FM_ASSERT(p);
    fm_free_t *ff = p;

    if(!fm_alt->free_head)
    {
        fm_alt->free_head = ff;
        ff->last = NULL;
        ff->next = NULL;
    }
    else if(ff > fm_alt->free_head)
    {
        fm_free_t *fh = fm_alt->free_head;
        while (fh->next)
        {
            if(fh < ff && (void*)ff < fh->next)
            {
                fm_free_t *fh_n = fh->next;
                fh_n->last = ff;
                ff->next = fh_n;
                break;
            }
            if(ff == fh->next)
            {
                return -2;
            }
            fh = fh->next;
        }
        if(!fh->next)
        {
            ff->next = NULL;
        }
        ff->last = fh;
        fh->next = ff;
    }
    else if(ff < fm_alt->free_head && (void*)ff >= fm_alt->mem_start)
    {
        fm_free_t *fh = fm_alt->free_head;
        fh->last = ff;
        ff->next = fh;
        ff->last = NULL;
        fm_alt->free_head = ff;
    }
    else
    {
        return -1;
    }

    ff->start_magic = FM_FREE_MAGIC;
    ff->bsize = size - (size % FMPOOL_PAGE_SIZE);
    ff->end_magic = FM_FREE_MAGIC;

    return ff->bsize;
}

static int __add_used_block(void *p ,fm_size_t rsize,fm_size_t asize,char *name,fm_size_t line)
{
    FM_ASSERT(p);
    fm_used_t *fu = p;

    if(!fm_alt->used_head)
    {
        fm_alt->used_head = fu;
        fu->last = NULL;
        fu->next = NULL;
    }
    else if(fu > fm_alt->used_head)
    {
        fm_used_t *fh = fm_alt->used_head;
        while (fh->next)
        {
            if(fh < fu && (void*)fu < fh->next)
            {
                fm_used_t *fh_n = fh->next;
                fh_n->last = fu;
                fu->next = fh_n;
                break;
            }
            if(fu == fh->next)
            {
                return -2;
            }
            fh = fh->next;
        }
        if(!fh->next)
        {
            fu->next = NULL;
        }
        fu->last = fh;
        fh->next = fu;
    }
    else if(fu < fm_alt->used_head && (void*)fu >= fm_alt->mem_start)
    {
        fm_used_t *fh = fm_alt->used_head;
        fh->last = fu;
        fu->next = fh;
        fu->last = NULL;
        fm_alt->used_head = fu;
    }
    else
    {
        return -1;
    }

    fu->start_magic = FM_USED_MAGIC;
    fu->real_size = rsize;
    fu->align_size = asize;
    fu->name = name;
    fu->line = line;
    fu->end_magic = FM_USED_MAGIC;

    return fu->real_size;
}


static int __delete_free_block(void *p)
{
    __fm_check_free_magic(p);

    fm_free_t *ff = p;
    if(ff == fm_alt->free_head)
    {
        fm_free_t *fh = ff->next;
        if(fh)
        {
            fh->last = NULL;
        }
        fm_alt->free_head = fh;
    }
    else if(ff > fm_alt->free_head)
    {
        fm_free_t *fp = ff->last;
        if(fp)
        {
            fp->next = ff->next;
        }
        fp = ff->next;
        if(fp)
        {
            fp->last = ff->last;
        }
    }
    else
    {
        return -1;
    }

    int ret = ff->bsize;
    memset(ff,0,sizeof(fm_free_t));
    return ret;
}

static int __delete_used_block(void *p)
{
    __fm_check_used_magic(p);
    
    fm_used_t *fu = p;
    if(fu == fm_alt->used_head)
    {
        fm_used_t *fh = fu->next;
        if(fh)
        {
            fh->last = NULL;
        }
        fm_alt->used_head = fh;
    }
    else if(fu > fm_alt->used_head)
    {
        fm_used_t *fp = fu->last;
        if(fp)
        {
            fp->next = fu->next;
        }
        fp = fu->next;
        if(fp)
        {
            fp->last = fu->last;
        }
    }
    else
    {
        return -1;
    }

    int ret = fu->align_size;
    memset(fu,0,sizeof(fm_used_t));
    return ret;
}

int fm_init(void)
{
    // page must align the aligned num
    FM_ASSERT(FMPOOL_PAGE_SIZE % FMPOOL_ALIGNED == 0);
    int ret = __fm_mutex_init();
    if(ret < 0)
    {
        FM_PRINT("fm_mutex init error\r\n");
        return -1;
    }
    
    __fm_mutex_take();

    fm_size_t fm_alt_size = sizeof(fm_allocator_t);
    fm_alt_size = (fm_alt_size % FMPOOL_PAGE_SIZE)?(fm_alt_size + (FMPOOL_PAGE_SIZE - (fm_alt_size % FMPOOL_PAGE_SIZE) )):fm_alt_size;

    fm_alt = (void*)fmpool;
    fm_alt->mem_start = (void*)(fmpool+fm_alt_size);
    fm_alt->all_size = FMPOOL_SIZE;
    fm_alt->used_size = fm_alt_size;
    fm_alt->max_used_size = fm_alt_size;

    fm_alt->free_head = NULL;
    fm_alt->used_head = NULL;

    __add_free_block( fm_alt->mem_start,FMPOOL_SIZE - fm_alt_size);

    __fm_mutex_release();
    return 0;
}

void *__fm_malloc_with_info(fm_size_t size,char *name,fm_size_t line)
{
    if(size == 0)
    {
        return NULL;
    }
    fm_size_t align_size = __fm_align(size);
    fm_free_t *suit_ff = NULL;

    int ret = __fm_mutex_take();
    if(ret < 0 )
    {
        return NULL;
    }

    fm_free_t *ff = fm_alt->free_head;
    while (ff)
    {
        if(ff->bsize == align_size)
        {
            suit_ff = ff;
            break;
        }
        else if(ff->bsize > align_size)
        {
            if(suit_ff)
            {
                suit_ff = (suit_ff->bsize > ff->bsize)?ff:suit_ff;
            }
            else
            {
                suit_ff = ff;
            }

        }
        ff = ff->next;
    }

    if(!suit_ff)
    {
        __fm_mutex_release();
        return NULL;
    }
    if(suit_ff->bsize == align_size)
    {
        __delete_free_block(suit_ff);
        __add_used_block(suit_ff,size,align_size,name,line);
    }
    else
    {
        fm_free_t *add_ff = FM_OFFSET(suit_ff,align_size);
        fm_size_t add_size = suit_ff->bsize - align_size;
        __delete_free_block(suit_ff);
        __add_free_block(add_ff,add_size);
        __add_used_block(suit_ff,size,align_size,name,line);
        
    }
    
    fm_alt->used_size += align_size;

    if(fm_alt->max_used_size < fm_alt->used_size)
    {
        fm_alt->max_used_size = fm_alt->used_size;
    }

    __fm_mutex_release();

    return FM_OFFSET_USER_AREA(suit_ff);
}

void *fm_malloc(fm_size_t size)
{
    return __fm_malloc_with_info(size,NULL,0);
}

static void* __merge_next_free_bolck(void *p)
{
    FM_ASSERT(p);

    fm_free_t *ff = p;
    fm_free_t *ff_n = ff->next;
    if(ff_n && ff_n == FM_OFFSET(ff,ff->bsize))
    {
        ff->next = ff_n->next;
        ff->bsize += ff_n->bsize;
        fm_free_t *ff_nn = ff_n->next;
        if(ff_nn)
        {
            ff_nn->last = ff;
        }
        memset(ff_n,0,sizeof(fm_free_t));
        return ff;
    }

    return NULL;
}

static void __merge_free_bolck(void *p)
{
    FM_ASSERT(p);

    fm_free_t *ff = p;

    fm_free_t *ffm = __merge_next_free_bolck(ff);
    if(ff->last)
    {
        __merge_next_free_bolck(ff->last);
    } 
}

void fm_free(void *ptr)
{
    FM_ASSERT(ptr);

    __fm_mutex_take();   

    fm_used_t *fu = FM_OFFSET_USED_HEAD(ptr);
    __fm_check_used_magic(fu);

    fm_size_t free_size = fu->align_size;
    __delete_used_block(fu);
    __add_free_block(fu,free_size);

    __merge_free_bolck(fu);  

    fm_alt->used_size -= free_size;

    __fm_mutex_release();   
}

void *fm_calloc(fm_size_t nmemb, fm_size_t size)
{
    fm_size_t msize = nmemb*size;
    void *new = fm_malloc(msize);
    if(new)
    {
        memset(new,0,msize);
    }
    return new;
}

void *fm_realloc(void *ptr, fm_size_t size)
{
    if (!ptr) 
    {
        return fm_malloc(size);
    }
    if(ptr && size == 0)
    {
        fm_free(ptr);
        return NULL;
    }
    fm_used_t *fu = FM_OFFSET_USED_HEAD(ptr);
    __fm_check_used_magic(fu);
    if(size <= fu->align_size)
    {
        fu->real_size = size;
        return ptr;
    }

    void *new = fm_malloc(size);
    if(new)
    {
        memcpy(new,ptr,fu->real_size);
        fm_free(ptr);
    }
    else
    {
        return NULL;
    }
    return new;
}

static int __fm_free_mem_check(void)
{
    fm_free_t *ff = fm_alt->free_head;
    fm_size_t free_size = 0;
    while (ff)
    {
        if(ff->start_magic != FM_FREE_MAGIC || ff->end_magic != FM_FREE_MAGIC)
        {
            FM_PRINT("bad free boclk    0x%p  %4d\r\n",ff,ff->bsize);
            return -1; 
        }
        free_size += ff->bsize;
        ff = ff->next;
    }
    if(free_size != (fm_alt->all_size - fm_alt->used_size))
    {
        FM_PRINT("free boclk size %6d != %6d\r\n",  \
        free_size,fm_alt->all_size - fm_alt->used_size);
        return -2;
    }
    return 0;
}

static int __fm_used_mem_check(void)
{
    fm_used_t *fu = fm_alt->used_head;
    fm_size_t used_size = 0;
    while (fu)
    {
        if(fu->start_magic != FM_USED_MAGIC || fu->end_magic != FM_USED_MAGIC)
        {
            FM_PRINT("bad used boclk    0x%p  %4d        %4d        %s:%d\r\n",       \
            fu,fu->real_size,fu->align_size,fu->name,fu->line); 
            return -1; 
        }
        used_size += fu->align_size;
        fu = fu->next;
    }

    fm_size_t fm_alt_size = sizeof(fm_allocator_t);
    fm_alt_size = (fm_alt_size % FMPOOL_PAGE_SIZE)? \
    (fm_alt_size + (FMPOOL_PAGE_SIZE - (fm_alt_size % FMPOOL_PAGE_SIZE) )):fm_alt_size;
    used_size += fm_alt_size;

    if(used_size != fm_alt->used_size)
    {
        FM_PRINT("used boclk size %6d != %6d\r\n",  \
        used_size,fm_alt->used_size);
        return -2;
    }
    return 0;
}


int fm_memory_check(void)
{
    int ret = __fm_used_mem_check();
    ret += __fm_free_mem_check();
    return ret;
}

static void __fm_print_allocator_info(void)
{
    FM_PRINT("-----------------------------------------------------\r\n");
    FM_PRINT("info        addr\r\n");
    FM_PRINT("fmpool      0x%p\r\n",fmpool);
    FM_PRINT("mem_start   0x%p\r\n",fm_alt->mem_start);
    FM_PRINT("free_head   0x%p\r\n",fm_alt->free_head);
    FM_PRINT("used_head   0x%p\r\n",fm_alt->used_head);
}

static void __fm_print_memory_info(void)
{
    FM_PRINT("-----------------------------------------------------\r\n");
    FM_PRINT("memory          per            used/all\r\n");
    FM_PRINT("free            %5.2f%%         %d/%d\r\n",     \
            100.-(float)fm_alt->used_size/fm_alt->all_size*100.,       \
            fm_alt->all_size - fm_alt->used_size,fm_alt->all_size);
    FM_PRINT("used            %5.2f%%         %d/%d\r\n",         \
            (float)fm_alt->used_size/fm_alt->all_size*100.,     \
            fm_alt->used_size,fm_alt->all_size);
    FM_PRINT("max_used        %5.2f%%         %d/%d\r\n",         \
            (float)fm_alt->max_used_size/fm_alt->all_size*100.,     \
            fm_alt->max_used_size,fm_alt->all_size);
}

static void __fm_print_free_info(void)
{
    FM_PRINT("-----------------------------------------------------\r\n");
    FM_PRINT("info    addr                len\r\n");
    fm_free_t *ff = fm_alt->free_head;
    while (ff)
    {
        FM_PRINT("free    0x%p  %4d\r\n",ff,ff->bsize); 
        ff = ff->next;
    }
}

static void __fm_print_used_info(void)
{
    FM_PRINT("-----------------------------------------------------\r\n");
    FM_PRINT("info    addr                rlen        alen        file:line\r\n");
    fm_used_t *fu = fm_alt->used_head;
    while (fu)
    {
        FM_PRINT("used    0x%p  %4d        %4d        %s:%d\r\n",       \
        fu,fu->real_size,fu->align_size,fu->name,fu->line); 
        fu = fu->next;
    } 
}

void fm_print_all_info(void)
{
    __fm_print_allocator_info();
    __fm_print_memory_info();
    __fm_print_used_info();
    __fm_print_free_info();   
}

fmpool.h

/**
 * @file dmpool.h
 * @author zhuxuanlin
 * @brief 
 * @version 0.1
 * @date 2024-06-20
 * 
 * @copyright Copyright (c) 2024
 * 
 */
#ifndef __FMPOOL_H__
#define __FMPOOL_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "ck_config.h" 
#include "assert.h"

typedef unsigned long fm_size_t;

typedef unsigned char fm_byte_t;

#define FM_ALIGNED  __attribute__((aligned(FMPOOL_ALIGNED)))

#define FM_ASSERT(x)    assert(x)

#define FMPOOL_SIZE (FMPOOL_ALL_SIZE * 1024)

#define FM_FREE_MAGIC    0x5A5A5A5Au

#define FM_USED_MAGIC    0xA5A5A5A5u

#define FM_OFFSET(x,off)    (void*)((void*)(x)+(off))

#define FM_PRINT    printf

/**
 * @brief 空闲内存块的info
 * 
 */
typedef struct 
{
    fm_size_t start_magic;
    void *last;
    void *next;
    fm_size_t bsize;
    fm_size_t end_magic;
}fm_free_t FM_ALIGNED;

typedef struct 
{
    fm_size_t start_magic;
    void *last;
    void *next;
    fm_size_t real_size;
    fm_size_t align_size;
    char *name;
    fm_size_t line;
    fm_size_t end_magic;
}fm_used_t FM_ALIGNED;

/**
 * @brief 内存管理的info
 * 
 */
typedef struct 
{
    void *mem_start;
    fm_size_t all_size;
    fm_size_t used_size;
    fm_size_t max_used_size;
    fm_free_t *free_head;
    fm_used_t *used_head;
}fm_allocator_t;

#define FM_OFFSET_USER_AREA(x)    (void*)((void*)(x)+(sizeof(fm_used_t)))
#define FM_OFFSET_USED_HEAD(x)    (void*)((void*)(x)-(sizeof(fm_used_t)))

void *__fm_malloc_with_info(fm_size_t size,char *name,fm_size_t line);
#define FM_MALLOC(x)   __fm_malloc_with_info((x),__FILE__,__LINE__) 

extern int  fm_init(void);
extern void *fm_malloc(fm_size_t size);
extern void fm_free(void *ptr);
extern void *fm_calloc(fm_size_t nmemb, fm_size_t size);
extern void *fm_realloc(void *ptr, fm_size_t size);
extern int fm_memory_check(void);
extern void fm_print_all_info(void);

#ifdef __cplusplus
}
#endif

#endif /* __FMPOOL_H__ */

验证结果:

在这里插入图片描述


时间流逝、年龄增长,是自己的磨炼、对知识技术的应用,还有那不变的一颗对嵌入式热爱的心!

到这里就结束了!希望大家给我的文章和B站视频
点赞o( ̄▽ ̄)d、关注(o)/~、评论(▽)!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhuzhu、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值