☰
一篇文章,弄懂动态内存管理
2026/10/8 2:12:43 网站建设 项目流程

目录

  • 为什么要有动态内存管理
  • malloc
  • calloc
  • realloc
  • 柔性数组
    • 柔性数组的特点
    • 柔性数组的使用
    • 柔性数组的好处

动态内存管理

为什么要有动态内存管理

我们目前已知道的开辟内存空间的方式如下

inta=4;//向栈区申请四个字节的空间chari[10]={0};//向栈区申请连续的10个字节空间

上述申请空间的方式有如下特点

  • 申请后,空间的大小是固定的
  • 数组在声明空间大小后,空间的大小就不能更改了

可是在我们运行程序时,很多时候内存的大小是要在运行程序的过程中才知道的,这就意味着对提前申请的静态空间,可能造成空间的浪费或者溢出。
于是c语言就引用了动态内存管理,允许程序员实时申请或者释放内存,以达到高效利用。

malloc

void*malloc(size_tsize)

功能:向堆区申请一块连续的空间,并返回这块空间的起始地址。
参数:size:表示要申请空间的字节数大小

  • 若申请成功,则返回这块空间的起始地址。
  • 若申请失败(比如内存不足),则返回一个空指针NULL,因此在使用时,要注意检查返回值是否为NULL
  • 由于返回值的类型是void*,因此接受指针还要根据数据类型来定义

注意事项:

  • 若参数size为0,则是标准的未定义行为,可能会导致错误。
  • 要包含头文件<stdlib.h>

功能:用来释放之前动态内存函数(realloc,calloc,malloc)申请的动态空间使用前要包含头文件<stdlib.h>

参数:ptr指向要释放空间的起始地址
注意事项

  • 若ptr指向的内存空间是静态的,则该行为未定义
  • 若ptr是空指针NULL,那么free函数什么也不会做
  • 在释放完ptr指向的动态空间后,一般把该指针置为NULL

calloc

void*calloc(size_tnum,size_tsize);

功能:在堆区上申请num个大小为size个字节的空间,并把每个空间的每个字节初始化为0
其他与malloc类似,唯一的区别是calloc会把申请空间的每一个字节初始化为0

realloc

  • realloc的出现让动态内存分配更灵活
  • 当我们发现过去申请的内存过大或者过小,可以通过realloc重新进行动态内存分配
void*realloc(void*ptr,size_tsize);

功能:重新分配原有内存块的大小,在不丢失原有内存块中数据的前提下,合理扩大或缩小内存。
参数:

  • ptr指向要调整内存块的起始地址,如果ptr是空指针NULL,则realloc的功能类似malloc。
  • size表示要分配内存的大小(单位字节)
    返回值
  • 成功:返回该内存块的起始地址,该指针可能和ptr相同或者不同
  • 失败:返回NULL指针,并且保持原来内存块不变。
    注意事项
    realloc在追加空间时,有以下两种情况
  • 当原有空间充足时,会直接在原有空间后面追加空间,数据内存不变,返回传入的地址。
  • 当原有空间不够时候,会在堆区中寻找内存足够的空间,并返回新地址,有如下过程
    • 开辟空间
    • 将原有数据拷贝到新的空间,保证数据不丢失
    • 自动释放旧空间
    • 返回新空间的起始地址

柔性数组

结构中最后一个元素允许是未知大小的数组,这就叫作柔性数组成员

structst_type{inta;intarr[];//柔性数组成员}

柔性数组的特点

  • 结构中柔性数组前至少有一个成员
  • 使用sizeof计算结构内存大小时,柔性数组不参与内存计算
  • 包含柔性数组的结构使用malloc()函数进行内存的动态内存分配,并且分配的大小应该大于结构的大小,以适应柔性数组的预期大小。

柔性数组的使用

#include<stdlib.h>structS{inta;intarr[];};intmain(){structS*p=(structS*)maalloc(sizeof(structS)+5*sizeof(int));for(inti=0;i<5;i++){p->arr[i]=i+1;}structS*ptr=realloc(p,sizeof(structS)+10*sizeof(int));p=ptr;ptr=NULL;for(inti=5;i<10;i++){p->arr[i]=i+1;free(p);p=NULL;}return0;}

上述代码也可以通过非柔性数组的方式实现

#include<stdlib.h>structS{inta;int*ps;};intmain(){structS*p=(structS*)malloc(sizeof(structS));p->a=100;int*pp=(int*)malloc(p->a*sizeof(int));p->ps=pp;pp=NULL;for(p->a=0;p->a<100;p->a++){p->ps[p->a]=p->a+1;}free(p->ps);p->ps=NULL;free(p);p=NULL;return0;}

柔性数组的好处

从上面的写法对比,可以总从上面的写法对比,可以总结出柔性数组的好处。

第一个好处:方便内存释放

如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这件事。因此,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。

第二个好处:有利于访问速度

连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,我个人觉得也没多高了,反正你跑不了要用做偏移量的加法来寻址)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询