提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 一、为什么存在动态内存分配
- 二、动态内存函数的介绍
- 1.malloc
- 2.free
- 3.calloc
- 4.realloc
- 三、常见动态内存错误
- 四、柔性数组
前言
动态内存分配是以后学习中非常重要的一部分,今天我们学习一下。
一、为什么存在动态内存分布
我们已知的内存开辟方式有创建一个类型的数据,直接在栈空间开辟一块空间,或者创建数组,在栈空间上开辟连续空间。
如下代码:
int a = 0;//在栈空间上开辟四个字节
char arr[10] = { 0 };//在栈空间开辟十个字节的连续空间
但是上面的开辟空间方式有两个特点:
1、空间开辟的大小是固定的。
2、数组在申明的时候,必须指定数组长度,它所需要的内存在编译时分配。
但是由于我们所需要的空间经常是不确定的,如果此时我们仍然使用上面的方法就会出现内存不够或者浪费内存的情况。
此时我们就需要一种可以随时改变内存的创建方式,即动态内存管理。
二、动态内存函数
1.malloc函数
参数的单位是字节。
同时,如果size写成0,此时的后果是不可知的,因为这种行为是标准未定义的,最终取决于编译器。
malloc的作用是申请一块内存,然后返回这块内存的首地址,返回类型是void*的指针,所以在使用是需要自己把返回的地址强制类型转换为自己需要的类型。
malloc申请的空间在未初始化时里面存放的是随机值。
但是有可能开辟失败,此时返回空指针NULL。
所以我们在使用时一定要判断是否返回空指针,如果返回空指针我们就不能使用了。
并且此时可以用strerror来接收错误信息,看一下是什么问题。
//申请
int *p=(int*)malloc(20);
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
2.free函数
//申请
int *p=(int*)malloc(20);
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//使用
//释放
free(p);
p = NULL;
free函数是专门做动态内存的释放和回收的。
参数是动态开辟空间的起始地址。
free之后最好再将起始地址置空,这样更安全。
如果ptr指向的空间不是动态开辟的,那此时行为是未定义的。
如果ptr是NULL指针,则函数什么都不做。
同时,malloc和free函数都声明在stdlib.h头文件中。
3.calloc函数
函数的功能是给num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
与malloc函数功能几乎一样,区别是两者参数有差别,malloc函数开辟的是多少字节,calloc函数开辟的是多少个大小为多少字节的空间,其次是calloc函数返回地址前会把每个字节初始化为0。
int* p = (int*)calloc(5,sizeof(int));
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//使用
//释放
free(p);
p = NULL;
4.realloc函数
realloc函数可以调整动态开辟内存的大小。
ptr是要调整的内存地址。
size是调整之后的大小,单位是字节。
返回值为调整之后内存的起始位置。
realloc在调整内存时有两种情况:
情况1:原有空间之后有足够大的空间
情况2:原有空间之后没有足够大的空间
情况1
此时扩展内存在原有内存后面直接追加空间,原来空间的数据不发生变化。
情况2
此时会在堆空间上另找一个合适大小的连续空间来使用,这样函数返回的是一个新的内存地址。
由于以上情况的出现,我们在使用时就要注意一些。
realloc的实现原理:
当我在堆区申请空间之后,这时别人可能也在堆区申请了空间,现在我想扩大一些,会先检测后面的空间是否足够,如果足够就直接申请,不够的话在内存里寻找一个新的空间来满足条件,然后将原来的数据拷贝到新空间里,之后把旧的空间释放,返回新空间的地址。如果申请失败,此时会返回空指针NULL。
由于可能有失败情况出现,我们在使用realloc函数时就不用自身接收返回值,防止函数使用失败地同时之前地地址也找不到了。
总结
上面就是动态内存管理的第一部分,主要讲了动态内存函数的使用,下一篇会讲常见的动态内存的错误以及柔性数组的概念和使用,大家如果感兴趣可以继续阅读哦。