行业资讯
您现在所在的位置:首页>企业动态>行业资讯

C语言动态内存分配实现(附代码)

编辑:学到牛牛IT培训    发布日期: 2022-01-13 09:46:28  

前言

首先要明白为何需要动态内存分配,熟悉C语言的读者应该对这个比较熟悉,需要一段内存时会使用malloc函数来申请所需要大小的内存,函数返回一段内存的首地址。简单来说,动态内存分配的好处在于需要内存的时候可以按需分配,当不需要内存的时候可以将其释放掉,这样可以高效的利用内存。下面本文从零开始实现一个完整的动态内存分配。

简单动态内存分配实现

内存分配是将没有使用的内存块给需要的变量(普通变量、指针变量、结构体变量等等)使用,由于其使用后需要进行释放,这就会导致空闲的内存是分散在内存池中的。因此,必须要对内存进行管理,也就是对内存的使用情况做标记。

C语言动态内存分配实现.jpg

上图是一个内存池使用后的某一时刻,可以看到,使用的块和没有使用的块并不是连续的,这样就需要用一个表对其进行标记,这个表称为BitMap。假设现在将内存按照每个Byte进行划分,然后用一个bit对块进行标记,1表示已使用,0表示没有使用,这样一个块需要一个bit。

C语言动态内存分配实现2.jpg

下面来用C语言来实现这个简单的动态内存分配。

#include <stdio.h>
#define MEM_POOL_SIZE  64
unsigned char MemPool[MEM_POOL_SIZE];
unsigned char BitMap[MEM_POOL_SIZE/8]={0};
//BitMap[0] MSB->LSB  MemPool[0 ~ 8]
//BitMap[1] MSB->LSB  MemPool[9 ~15]
//BitMap[2] MSB->LSB  MemPool[16~23]
// ...
void InitMemAlloc(void)
{
    int i=MEM_POOL_SIZE;
    while(i--)
    {
        MemPool[i]=0;
    }
    i=MEM_POOL_SIZE/8;
    while(i--)
    {
        BitMap[i]=0;
    }
}
void *MemAlloc(unsigned int m_size)
{
    unsigned int i=0,j=0,k=0,index=0,count=0,mapv=0,cache;
    if(m_size>MEM_POOL_SIZE)
    {
        return NULL;
    }
    else
    {
        for(;i<MEM_POOL_SIZE/8;i++)
        {
            mapv=BitMap[i];   //取出高位
            for(j=0;j<8;j++)
            {
                cache=(mapv&0x80);
                if(cache==0)
                {
                    count++;
                    if(count==m_size)
                    {
                        for(;k<m_size;k++)
                        {
                            BitMap[(index+k)/8]|=(1<<(7-((index+k)%8)));
                        }
                        return &MemPool[index];
                    }
                }
                else
                {
                    count=0;
                    index=i*8+j+1;
                }
                mapv<<=1;
            }
        }
        return NULL;
    }
} 
void MemFree(void *p,unsigned int m_size)
{
    unsigned int k=0,index=(((unsigned int)p)-(unsigned int)MemPool);
    for(;k<m_size;k++)
    {
        BitMap[(index+k)/8]&=~(1<<(7-((index+k)%8)));
    }
}
void MemPrintBitMap(void)
{
    unsigned int i,j,value;   
    for(i=0;i<MEM_POOL_SIZE/8;i++)
    {
        value=BitMap[i];
        for(j=0;j<8;j++)
        {
            if(value&0x80)
                printf("1 ");
            else
                printf("0 ");
            value<<=1;
        }
        printf("
");
    }
}
double MemGetUsedPercent(void)
{
    unsigned int i,j,value;
    double ret=0.0;
    for(i=0;i<MEM_POOL_SIZE/8;i++)
    {
        value=BitMap[i];
        for(j=0;j<8;j++)
        {
            if(value&0x80)
                ret++;
            value<<=1;
        }
    }
    return (ret*100)/MEM_POOL_SIZE;
}
int main(int argc, char **argv)
{
    int *p=MemAlloc(10);
    printf("The pool is used=%f
",MemGetUsedPercent());
    MemPrintBitMap();
    int *q=MemAlloc(5);
    printf("The pool is used=%f
",MemGetUsedPercent());
    MemPrintBitMap();
    MemFree(p,5);
    printf("The pool is used=%f
",MemGetUsedPercent());
    MemPrintBitMap();
    return 0;
}

最终终端输出结果如下:

C语言动态内存分配实现.jpg

上面已经实现了一个简单的动态内存分配,可以完成内存的分配和释放以及输出使用率和查看位图。这种方式实现的动态内存分配不会产生内部碎片,这也是其优势所在,但其缺点很明显就是利用率太低。

实用的动态内存分配

细心的读者可能已经发现上面的简单动态内存分配有一个缺点,就是一个bit只能表示一个字节,也就是说表示8个字节就需要一个字节的位图,这种映射导致其内存的

C语言动态内存分配实现6.jpg

这对于很多情况是比较浪费的。为了提高利用率,就必须将映射块的粒度增大,也就是一个Bit的映射范围对应多个字节。

C语言动态内存分配实现7.jpg

上图给出了一个bit映射到64Byte,这样:

C语言动态内存分配实现8.jpg

虽然利用率变高了,但是其会产生内部碎片,所谓内部碎片就是在最小粒度内无法使用的内存空间,为何这个空间无法使用了,原因在于当在申请内存块的时候,其内存只能以64B对齐的,即使小于64B,也得按64B来看作,因为这个粒度已经被bitmap标记使用了,当下次使用时,其无法被分配。因此,可以看到,粒度越大,其可能产生的内部内存碎片越大,内存利用率和碎片是需要权衡了,好的算法只能解决外部碎片问题,无法解决内部碎片问题,因此在实现动态内存分配时必须权衡考虑,以达到最优结果。

免费试学
课程好不好,不如实地听一听

封闭学习

2

1

联系我们

电话:028-61775817

邮箱:1572396657@qq.com

地址:成都高新西区西芯大道4号

  • 学到牛牛在线咨询

    扫一扫,免费咨询

  • 学到牛牛公众号

    微信公众号

学一流技术,找高薪工作

7-24小时服务热线:

028-61775817

版权声明 网站地图

蜀ICP备2021001672号

课程问题轻松问