目录
为什么存在动态内存分配
大家好,我是家宝boom。在这=这么久的代码学习过程中,我们已经掌握的内存开辟方式有:
int val=20;//在栈空间上开辟四个字节
char arr[10]={0};//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的方式有有两个特点:1、空间开辟的大小是固定的。 2、数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的需求,不仅仅是上述的情况,有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。这时候只能试试动态内存开辟。
动态内存函数的介绍
malloc函数和free
c语言提供了一个动态内存开辟的函数:
void* malloc(size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
如果开辟成功,则返回一个指向开辟好空间的指针。
如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
如果参数size为0,malloc的行为是标准未定义的,取决于编译器。
例如:int*p=(int*)malloc(40),这个代码的意思就是开辟一个40字节的空间,因为在定义里malloc函数的返回值是void*,所以我们需要进行一个强转,这里我们是开辟整型空间,所以强转成int*类型。注意,使用malloc函数需要引用头文件include <stdlib.h>。
从上图我们可以发现,以成功使用malloc开辟了40个字节的整型数据的空间,还可以看到malloc函数申请到空间后,直接返回这个空间的起始地址,并不会初始化空间的内容,所以这里面存放的都是随机值。
malloc申请的内存空间,不会主动的还给操作系统,除非程序结束。不退出程序的话,内存空间不会释放。所以这里就需要使用free。
c语言提供了另一个函数free,专门用来做动态内存的释放和回收的。
void free(void* ptr);
例如在上述的代码中,我们在最后加上free(p),并把p指针置空,以避免p指针成为野指针,P=NULL;注意,free函数只能用来释放动态开辟的空间。
calloc函数
c语言还提供了一个函数calloc,calloc函数也用来动态内存分配,原型如下:
void*calloc(size_t num,size_t size);
函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为。
与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0。
举个例子:代码如下
上述代码中我们就可以很清晰的看到申请到的十个整型存放的数据都是0.
realloc函数
在动态内存管理的这几个函数中,最需要注意的就是realloc函数。
realloc函数的出现让动态内存管理更加灵活。
有时我们会发现申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的使用内存,我们一定会对内存大小做灵活的调整。那realloc函数就可以做到对动态开辟内存大小的调整。函数原型如下:
void* realloc (void* ptr,size_t size);
ptr是要调整的内存地址
size调整之后新大小
返回值为调整之后的内存起始位置
这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
realloc在调整内存空间的时候存在两种情况:1、原有空间之后有足够大的空间,就直接在空间后面增加。2、原有空间之后没有足够的空间大小,就会开辟一个新的足够大的空间,然后将旧的空间里的数据拷贝到新的空间,并释放旧的空间,最后返回新的空间的地址。
在使用realloc函数调整动态内存大小的时候,为了防止p指针变成空指针,将原来存放的数据都给损失了,造成内存泄漏,所以我们会定义一个指针让他来接收realloc函数的返回值,判断是否为空指针,如果不是我们再讲原来的指针p来接收新指针的 内容。
常见的动态内存错误
对NULL指针的解引用操作
void test()
{
int *p=(int*)malloc(INT_MAX/4);
*p=20;//如果p的值是NULL,就会有问题
free(p);
}
所以我们再解引用之前一定要对指针进行判断,也可以使用perror或者assert函数,可以参考上述博主举例子时使用的代码。
对动态内存开辟空间的越界访问
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);
}
对非动态开辟内存使用free
void test()
{
int a=10;
int *p=&a;
free(p);//错误
}
使用free释放一块动态开辟内存的一部分
void tese()
{
int *p=(int*)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
p=NULL;
}
一块连续的空间必须从头开始释放,并且这块空间必须全部释放完,不能让起始指针跑偏了。
动态开辟内存忘记释放(内存泄漏)
void test()
{
int *p=(int *)malloc(100);
if(NULL!=p)
{
*p=20;
}
}
int main()
{
test();
while(1);
}
忘记释放不再使用的动态开辟的空间会造成内存泄漏。
切记:动态开辟的空间一定要释放,并且正确释放。