动态内存管理
- 🎃为什么存在动态内存管理
- 🎃动态内存函数的介绍
- 🎊malloc
- 🎋perror函数
- 🎊free
- 🎊calloc
- 🎊realloc
🎃为什么存在动态内存管理
在此之前,我们开辟内存空间有两种方式。一种是创建一个已知类型的变量。
比如说:int a=10; //在栈空间上开辟4个字节
向系统申请了4个字节的内存空间。(对于 int型,4个字节它是固定的。)
还有一种是,创建一个数组。
比如说:int arr[10]; //在栈空间上开辟40个字节的连续空间。
向系统申请了40个字节的内存空间。当这个数组开辟好了空间,没有办法改变它的大小。
对于数组的创建,它的内存开辟方式是比较死板的。int arr1[10]; int arr2[100];
我们创建数组时,在一开始时就会指定数组的大小。arr1的内存空间为40个字节,可以存放10个整型元素。arr2的内存空间为400个字节,可以存放100个整型元素。
但有可能我们在使用数组arr1的时候,需要存放11个数组元素,而没有办法把它边长。
我们可能为了尽可能满足很多情况,而创建一个很大的数组arr2,但在实际使用过程中,我们可能只会存放20个元素,而导致了内存空间的浪费。
所以这样的内存开辟方式它是固定的
,是不够灵活的
。
不仅仅是上述的情况,有时候我们需要的空间大小在程序运行的时候才能知道,那么数组在编译时开辟空间的方式就不能满足了。
所以,我们需要学会开辟动态内存
。
🎃动态内存函数的介绍
malloc
free
calloc
realloc
🎊malloc
malloc函数的原型:
void* malloc(size_t size);
malloc声明在stdlib.h
头文件中。
功能:
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回指向这块空间的指针。
- 如果开辟失败,则返回一个NULL指针。
因此,malloc函数的返回值一定要做检查。
举个例子:
原来,我们用数组
在栈区
开辟内存空间:
现在,我们在堆区
动态开辟同样大小的内存空间:
根据malloc函数的原型,我们需要传递一个参数,以字节
为单位的内存。
malloc(40)//即开辟了40个字节的内存空间
然后,我们需要一个指针p
来指向这块儿开辟好的连续的
内存空间
。
但由于 malloc函数的返回值为 void*
,即无类型指针
,所以我们需要先进行强制转换
,将无类型指针转换为整型指针
。
因此,
int* p=(int*)malloc(40);
但是,正如我们上面所提到的,我们只是用malloc函数向内存申请
开辟40个自己的连续空间,不一定开辟成功。所以我们需要利用指针p
进行进一步检验
。
若开辟成功,进行访问:
malloc函数 申请的内存空间,但程序退出时,不会主动释放
的,需要使用free函数
来释放。
🎋perror函数
perror函数(忘的打印输出函数)
来自这篇博客:C语言perror函数详解
🎊free
C语言提供free函数,专门用来做动态内存
的释放和回收
的。
free函数原型:
void free(void *ptr);
free函数也是声明在头文件<stdlib.h>
中的。
free函数用来释放动态开辟
的内存。
- 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
- 如果参数ptr是NULL指针,则函数什么事都不做。
🎊calloc
C语言还提供了一个函数叫calloc,calloc函数也用来动态内存分配。
calloc函数的原型:
void* calloc(size_t num,size_t size);
calloc函数的功能为,为num
个大小为size
的元素开辟一块空间,并且把每个字节初始化为0。
与函数malloc函数的区别只在于calloc会在返回地址之前把申请的每个字节初始化为0。
即:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));//开辟10个大小为sizeof(int),即4个字节 的空间
//判断是否开辟成功
if (p == NULL)
{
perror("calloc");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
printf("%d\n", *(p + i));
return 0;
}
🎊realloc
realloc函数
的出现让动态内存管理更加灵活
有时候我们发现之前申请的内存过小了,有时候我们发现我们申请的内存过大了,所以,在一些时候,我们需要对内存的大小做灵活的调整。那么realloc函数
就可以做到对动态开辟内存大小的调整
。
realloc函数的原型如下:
void* realloc(void* ptr,size_t size);
其中,ptr是要调整的内存的地址
。
size是调整之后的新的
内存的大小。
返回值是调整之后内存的起始位置。
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新
的空间。
举个例子:
我们首先使用malloc函数开辟40个字节的空间:
//malloc函数申请空间
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return 1;
}
//初始化
int i = 0;
for (i = 0; i < 10; i++)
*(p + i) = i;
我们想要将这个空间扩大,扩大为80个字节的空间。
于是我们使用realloc函数进行调整。但是内存空间的变化可能有不同的情况。
情况一,后面有足够的空间。
即这样的情形:
情况二,后面没有足够的空间:
//增加一些空间
int *ptr=realloc(p, 80);
if (ptr != NULL)
{
p = ptr;
}
在用realloc函数调整动态内存空间时,要注意不能将原来的p指针,来接收realloc(p,80)。
这是因为relloc函数不一定开辟成功新的空间而进行调整,即realloc函数的返回值可能为NULL。
那么p=NULL,本来p维护40个字节的空间。那么这样那个40个字节空间的字节就没有指针维护了。但还没有释放,可能用不到了,但可能找不到了,从而造成内存泄露。