本章重点一、为什么存在动态内存分配二、动态内存函数的介绍mallocfreecallocrealloc三、常见的动态内存错误四、柔性数组
一、为什么存在动态内存分配
因为在实际的开发过程中,有时候需要的内存空间并不需要太大,我们需要的是一种动态改变的空间。例如,实现一个通讯录代码的时候往往需要创建一个存放联系人的数组,如果没有动态内存分配,可能就会产生两种情况:
1.数组满了放不进联系人,需要手动修改数组大小
2.完全不需要数组开辟这么大的内存空间,而我们为了防止空间不够多开辟了内存,造成了内存的浪费
二、动态内存分配函数的介绍
0.内存的分配情况
在讲函数之前,我想先介绍一下系统的内存布局:
内存有栈区,堆区,静态区三个区域。其中栈区就是我们存放函数形参和临时变量的地方,也就是临时的数据都是在这开辟的。而malloc relloc calloc这样动态开辟的内存都是在堆区中。静态区也就是存放全局变量和静态变量的地方。
1.malloc函数
函数全称叫做——memory allocate,也就是内存分配。
它的返回值是void*的指针,指向了我们所开辟空间的起始位置,参数size_t size是所要开辟空间的字节数。
注意:如果内存空间不够的时候,也就是malloc无法向内存申请我们需要的空间,这时候它会返回NULL——一个空指针。像这样:
我们向内存申请了最大整形字节数的空间,显然这是不够的, 这时malloc返回了一个空指针,并且perror打印了错误的原因。
如果向内存申请0字节的空间 这种行为是c语言标准未定义的。
头文件:
具体的使用方法:
#include<stdio.h>
#include<malloc.h>
int main()
{
int* p = (int*)malloc(10);
free(p);
p = NULL;
return 0;
}
由于malloc返回的是一个void*的指针,真正要使用它的时候需要把它强制类型转换为我们所需的数据类型。
2.free函数
free函数没有返回值,它的参数是指向我们申请的空间起始位置的一个指针。
它的作用是将我们动态申请的内存还给系统,如果我们没有手动free掉我们申请的空间,在程序结束时会由OS自动回收。但是,如果程序无法结束呢?
int main()
{
while (1)
{
malloc(50);
}
return 0;
}
如上面的代码,程序进入了一个死循环,不停的在申请内存,造成了内存的浪费,如果系统一直运行,会耗干所有的内存,也就是内存泄漏。
所以使用完一定要记得free掉申请的空间,并且要注意,原本指向我们申请空间的指针会变成野指针,记得要把它置为NULL。
3.calloc函数
其实calloc函数和malloc函数的差别并不是很大,calloc的第一个参数是要申请的元素个数,比如要申请10个int类型的数据就是10,第二个就是单个元素的字节数。
#include<stdio.h>
#include<malloc.h>
int main()
{
int* p = (int*)calloc(10,4)//申请10个int类型大小的空间
free(p);
p = NULL;
return 0;
}
calloc和malloc最大的区别是calloc在申请空间的时候会帮我们把空间所有字节的数据初始化为0。相反的,malloc在申请空间的时候空间内都是随机值。
malloc申请的空间:
calloc:
4.realloc函数
realloc函数用于调整我们所开辟空间的大小。第一个参数是已经开辟的空间的起始位置,第二个参数是调整后的空间大小。
realloc如果想要调小空间,一般不会有太大的问题,如果要调大空间,有两种情况:
1.内存空间足够的时候,它返回的指针就是原来空间的指针。
2.后面的内存空间不足够的时候,它会找到另外一个空间,这个空间足够我们开辟数据,而且它会把在原有空间内已经修改过的数据原原本本搬到新空间中,返回新空间的起始地址。
三、常见的动态内存错误
1.对NULL指针的解引用操作
#include<stdio.h>
#include<malloc.h>
int main()
{
int* p = (int*)malloc(40);//申请空间
*p = 1;//考虑malloc是否会申请空间失败,此时p是一个空指针,对空指针解引用的非法操作
return 0;
}
正确的做法:
#include<stdio.h>
#include<malloc.h>
int main()
{
int* p = (int*)malloc(40);
if(p != NULL)
{
*p = 1;
}
free(p);
p = NULL;
return 0;
}
2.对开辟的空间越界访问
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
p = NULL;
}
3.对非开辟的空间进行free操作
free函数只能free掉我们动态开辟的内存。
void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}
4.对开辟的空间多次free操作
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
5.对开辟的空间free一部分
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
6.忘记释放所开辟的空间
这个上面已经提到了,每次开辟空间后都一定要记得free掉空间,并且指针置空。
这次的介绍就到此结束,柔性数组我放在下一篇博客介绍。