什么是动态内存分配?
- 在c/c++语言中,编写程序有时不能确定数组应该定义为多大,因此这时在程序运行时要根据需要从系统中动态多地获得内存空间。
- 所谓动态内存分配,就是指在程序执行的过程中从动态地分配或者回收存储空间的分配内存的方法。
- 动态内存分配是从堆区分配空间的。
动态内存分配所使用的函数
基本要求:
1. 所需头文件为:#include<stdlib.h>
2. 开辟后需判断是否开辟成功,使用完后需free,并且将指针置为空。
3. 使用可当作数组来使用,本质上是通过指针来控制。
一. malloc
- 形参size为要开辟空间的大小,单位为字节。
- 开辟时不初始化。
- 返回值为void*类型的指针,在接收时转化为所需类型的指针即可,如果开辟失败则返回NULL。
使用:
#include<stdlib.h>
int main()
{
int* pa = (int*)malloc(20);//开辟内存空间
//判断返回值
if (pa == NULL)
{
perror("malloc");
return 1;
}
//使用
int i = 0;
for (i = 0; i < 5; i++)
{
scanf("%d", pa + i);
}
for (i = 0; i < 5; i++)
{
printf("%d", pa[i]);
}
//释放空间
free(pa);
pa = NULL;//将指针置为空
return 0;
}
二. calloc
- 形参size为需开辟的类型的大小,形参num为需开辟该类型的个数。
- 在开辟时会将该块内存空间的值初始化为0;
- 返回值为void*类型的指针,在接收时转化为所需类型的指针即可,如果开辟失败则返回NULL。
使用:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char* str = (char*)calloc(10,sizeof(char));//开辟
//判断
if (str == NULL)
{
perror("calloc");
return 1;
}
//使用
scanf("%s", str);
printf("%s", str);
//释放
free(str);
str = NULL;//置为空
return 0;
}
三. realloc
- 其作用是对已经动态内存开辟的空间大小进行调节。
- 形参ptr为需调整空间的地址,形参size为空间将要调整为的大小,单位字节。
- 如果第一个参数传入NULL,则与malloc函数作用一致。
- 返回值为void*类型的指针,在接收时转化为所需类型的指针即可,如果开辟失败则返回NULL。
使用:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* pa = (int*)malloc(20);
if (pa == NULL)
{
perror("malloc");
return 1;
}
int* tmp = (int*)realloc(pa, 40);//开辟
//判断是否开辟成功,如开辟成功则将地址交给pa指针
//避免未开辟成功,又改变了pa的情况
if (tmp == NULL)
{
perror("realloc");
return 1;
}
else
{
pa = tmp;
}
//使用
int i = 0;
for (i = 0; i < 10; i++)
{
scanf("%d", pa + i);
}
for (i = 0; i < 10; i++)
{
printf("%d", pa[i]);
}
//释放
free(pa);
pa = NULL;//置为空
}
注意:realloc在改变所开辟内存大小时可能在原来基础上调节,也可能开辟一块新空间存放(因为原来指针掌控的空间后的空间可能已经被使用,需寻找一块新的大小足够的空间),该情况下realloc的步骤为:开辟新空间——>将原来空间的数据拷贝到新空间——>free掉原空间——>返回新空间的地址。
四. free
- 形参ptr为所需释放的动态内存开辟的空间的地址。
- 无返回值,如传入NULL,则不进行任何操作。
操作:参考上文
柔性数组
- 在c99中有明确的规定:允许结构体中最后一个数组大小是未知的,柔性数组其实是结构体中的最后一个成员(一个未说明大小的数组,数组的形式可以是int arr[0]或int arr[])。
- 结构体中至少包含一个以上其他类型。
- 在计算结构体大小时,不会计算柔性数组的大小,因为柔性数组的大小是不确定的。
使用:
#include<stdlib.h>
#include<stdio.h>
struct S
{
char a;
int arr[];//柔性数组需为最后一个成员
};
int main()
{
//动态开辟内存空间,此时柔性数组大小为5
struct S* pf = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 5);
//判断
if (pf == NULL)
{
perror("malloc:");
return 1;
}
//使用
int i;
for (i = 0; i < 5; i++)
{
*(pf->arr + i) = i;
}
for (i = 0; i < 5; i++)
{
printf("%d ", pf->arr[i]);
}
//释放
free(pf);
pf = NULL;//置为空
return 0;
}
对比直接在结构体中定义指针去使用动态内存的优势
- 方便内存释放:对于柔性数组而言,只需free结构体指针即可,而用指针使用则需要单独对结构体内该指针free。
- 有益于减少内存碎片:对于指针使用而言,结构体和指针在堆区开辟的空间是不连续的,中间可能会产生更多的内存碎片(开辟的两块空间中间的空间较小,难以使用,被称为内存碎片),而柔性数组开辟的空间是连续的。
- 有益于提升效率。