【落羽的落羽 C语言篇】动态内存管理·上

在这里插入图片描述

文章目录

  • 一、动态内存管理是什么
  • 二、动态内存管理相关函数
    • 1. malloc
    • 2. free
    • 3. calloc
    • 4. realloc
  • 三、柔性数组
    • 1. 概念
    • 2. 使用

一、动态内存管理是什么

我们之前已经知道,定义变量就是申请一块空间,int a;就是申请四个字节的空间,char arr[20]就是申请20个字节的空间。这样的空间申请方式有两个特点:空间开辟的大小是固定的、开辟好的空间大小不能再修改了。但是对于内存空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行时才能知道,那么这种申请内存空间的方式就不能满足我们的需求的

所以,C语言中引入了动态内存管理(开辟),让我们能自由地申请和释放内存空间。
而要实现这一点,必须要依靠下面的相关函数。

二、动态内存管理相关函数

以下四个函数malloc、free、calloc、realloc,都包含在头文件stdlib.h中。

1. malloc

C语言提供了一个动态内存开辟的函数malloc:
在这里插入图片描述
这个函数能申请一块大小为size个字节的内存空间,返回指向这块空间的指针。
但要注意的是:

  • 内存空间可能会开辟失败,如果失败了,会返回NULL空指针,因此使用malloc后一定要对返回值做检查。
  • malloc并不知道开辟空间的存放数据类型是什么,返回指针的类型是void*。所以一般需要使用者对返回值进行强制类型转换
  • 参数size为0,malloc的行为是未定义的,结果取决于编译器。

2. free

C语言还提供了一个函数free,是用来实现动态内存的释放和回收的:
在这里插入图片描述

free能将参数指针指向的空间进行释放和回收,free后该指针变成野指针。以前也讲过,这时最好把野指针置为NULL。
但要注意的是:

  • 如果参数ptr指向的空间不是动态开辟的,那么函数free的行为是未定义的。
  • 如果参数ptr是NULL空指针,那么free函数什么都不做

在一个程序中,如果开辟的动态空间不用free释放回收,操作系统也会在程序退出的时候自动回收这块内存;但一般情况下,我们动态开辟好的空间在使用完后,习惯性free掉,为了节省空间。

举个栗子:用malloc申请一块空间,存放num个int数据,打印出来,再free掉这块空间。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
    int num = 0;
    scanf("%d",&num);
    int* p = NULL;
    p = (int*)malloc(num*sizeof(int));
    assert(p != NULL);//检查是否空间开辟成功
    for(int i=0 ; i<num ; i++)
        scanf("%d", p+i);
    for(int i=0 ; i<num ; i++)
        printf("%d ", *(p+i));
    free(p);
    p = NULL;//有必要的,牢记出现野指针随即置为NULL
    return 0;
}

在这里插入图片描述在这里插入图片描述

3. calloc

还有一个函数是calloc,也是用来进行动态内存分配的:
在这里插入图片描述
这个函数的功能是为num个size大小的数据开辟一块空间,并且把每个字节初始化为0。它与函数malloc的区别只在于calloc能将每个字节初始化为0,其余的使用方法与malloc完全相同。

int* p = (int*)calloc(10, sizeof(int));
assert(p != NULL);
for(int i=0 ; i<10 ; i++)
    printf("%d ",*(p+i));
free(p);
p = NULL;

在这里插入图片描述

如果要求我们对申请的内存空间的内容初始化,那么应该使用calloc。

4. realloc

在申请空间后,有时我们发现申请好的空间太小了,有时又觉得太大了,为了合理地使用内存,我们会对已经开辟的内存大小做一些调整,relloc函数是用来实现这个功能的。
在这里插入图片描述

参数指针ptr代表着要调整的空间,size是调整后想要的大小。函数返回指向调整后的空间的指针。当然,也可能开辟空间失败,返回空指针NULL。

realloc调整空间大小有两种情况:

  • 情况1:原空间之后有足够大的空间
  • 情况2:原空间之后没有足够大的空间

对于这两种情况:

  • 情况1:扩展内存直接从原空间之后追加。
  • 情况2:在堆空间上另外找一个大小合适的连续空间来使用,将原空间中的已有数据拷贝到新空间内,释放回收原空间。

有了这个函数,我们就可以在写项目的过程中,实时调整要使用的内存空间的大小。
很好理解吧~

在这里插入图片描述

三、柔性数组

1. 概念

C99中,结构体中的最后一个元素允许是未知大小的数组,这就是柔性数组。
例如:

struct s
{
    int i;
    char c;
    int a[];
}

要注意的是:

  • 结构体中的柔性数组前面必须有至少一个其他成员,柔性数组必须是结构体的最后一个成员
  • 结构体的大小计算不会算上柔性数组的大小
  • 包含柔性数组的结构体需要用malloc函数进行内存分配,并且分配的内存应该大于结构体的大小,适应柔性数组的预期使用大小。

2. 使用

举个栗子:

struct s
{
   char c;
   int a[];
};

struct s* p = (struct s*)malloc(sizeof(struct s)+100*sizeof(int));
assert(p!=NULL);
p->c = 'x';
for(int i=0 ; i<100 ; i++)
    p->a[i] = i;
free(p);
p = NULL;

这样,结构体成员c获得了值’x’,结构体成员柔性数组a获得了100个整型元素的连续空间并存入了一些数据。

有人可能会有疑惑,即使不使用柔性数组,在结构体中定义一个指针,在这个指针指向的空间,在开辟足够的大小来存储数据,也可以达到我们的目的。那么使用柔性数组的优势是什么?
好处是:

  • 方便内存回收释放。使用柔性数组,在结构体使用完毕后,我们只需进行一次free就可以把所有开辟的空间释放掉。但是不使用柔性数组的话,我们需要free两次,一次释放结构体空间,一次释放结构体成员指针开辟的空间。
  • 有利于提高访问速度。连续的内存有利于提高访问速度,也有利于减少内存碎片。

在这里插入图片描述

本篇完,感谢阅读~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值