目录
1、为什么存在动态内存分配
2、动态内存函数的介绍
2.1 malloc和free
功能:
参数和返回值:
注意事项:
tip:
2.2 calloc
2.3 realloc函数
功能:
参数和返回值:
realloc开辟空间的两种情况
realloc会顺序采取以下措施
疑问:那realloc的返回值用原有的指针接受吗?
3、常见的动态内存错误
3.1、对NULL指针的解引用操作
3.2、对动态开辟空间的越界访问
3.3、对非动态开辟的内存进行free释放
3.4、使用free释放一块动态开辟内存的一部分
3.5、对同一块动态内存多次释放
3.6、动态开辟内存忘记释放(内存泄露)开辟空间一定要释放
1、为什么存在动态内存分配
我们已经掌握的内存开辟方式有:
int a = 0;
char b[10] = {0};
但是上述的开辟空间的方式有两个特点:
- 空间开辟的大小是固定的。
- 数组在申明时,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的需求,不仅仅是上述的情况。有时我们需要的空间大小在程序运行的时候才知道,
那数组的编译时开辟空间的方式就不能满足了。
这时候就只能采用动态内存开辟。
2、动态内存函数的介绍
2.1 malloc和free
C语言提供了一个动态内存开辟的函数——malloc
功能:
向内存申请一块连续可用的空间,并返回指向这块空间的指针。
参数和返回值:
参数是以字节为单位的一个无符号整型,返回值是void*,指向我们开辟这块空间的指针。
注意事项:
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个NULL指针,因此malloc函数的返回值必须要做检查!
- 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候由使用者自己决定。
- 如果参数size是0,malloc的行为是标准未定义的。
tip:
- malloc申请到空间后,直接返回这块空间的起始地址,不会初始化空间的内容。
- malloc申请的内存空间,当程序退出时,还给操作系统,当程序不退出,动态生气的内存是不会主动释放的,需要用free函数来释放。
- 每当free函数释放空间后,指针就没有被指向的内容,为了防止野指针,需要将其设为控制在(即每次使用free函数后,都需要置成空指针)
- free函数不能释放非动态开辟的内存空间,这种行为是标准未定义的
- 如果free函数的参数是空指针,则free函数什么都不做
int main()
{
//int arr[10];
int* p = (int*)malloc(40);
if (p == NULL)
{
perror("malloc");
return 1;
}
//开辟成功
for (int i=0;i<10;i++)
{
printf("%d\n", *(p + 1));
}
return 0;
}
2.2 calloc
C语言还提供了一个函数是calloc,calloc函数也用来动态内存分配,原型如下:
- 函数的功能是num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0.
- 与函数malloc的区别只在于calloc会在返回地址之前把申请的每个字节初始化为0
举个例子:
2.3 realloc函数
功能:
realloc函数的出现让动态内存管理更加灵活,调整对动态内存的大小。
参数和返回值:
ptr是要调整的内存地址,size是调整之后的新大小(以字节为单位),返回值为调整之后的内存起始位置。如果ptr为空指针,那么函数的功能与malloc函数一样。
realloc开辟空间的两种情况
1、原始空间的后面空间足够
直接返回原始空间的起始地址
2、原始空间的后面空间不够
realloc会顺序采取以下措施
(1)、开辟新的空间
(2)、会将旧的空间中的数据拷贝到新的空间
(3)、释放旧的空间
(4)、返回新空间的起始地址
疑问:那realloc的返回值用原有的指针接受吗?
回答:不能,因为假如realloc增容失败,会返回空指针,而原有的指针指向的空间还没有释放,就被置成空指针,这就造成了内存泄露!因此我们应该用一个新的指针去接受,然后判断增容是否成功,如果成功,就将新指针的内容赋给原有的指针,否则程序结束。
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
perror(malloc);
return 1;
}
for (int i=0;i<2;i++)
{
*p = i + 1;
p++;
//p[i]=i+1;
}
int* ret = (int*)realloc(p, 80);
if (ret == NULL)
{
perror(realloc);
return 1;
}
p = ret;
for (int i=0;i<20;i++)
{
printf("%d\n", p[i]);
}
free(p);
p = NULL;
return 0;
}
上面是正确的使用realloc增容的方式。如果减容,直接size输入一个小于原来的值即可。
3、常见的动态内存错误
3.1、对NULL指针的解引用操作
解决方法:判断内存开辟函数malloc/calloc/realloc返回值是否为空
3.2、对动态开辟空间的越界访问
3.3、对非动态开辟的内存进行free释放
3.4、使用free释放一块动态开辟内存的一部分
注意此时的p指向的不是我们动态开辟空间的起始地址,而是后面相当于动态内存的一部分,此时再用free释放是不对的,free释放应该从动态内存的起始地址开始释放!
不能让起始指针跑偏!
3.5、对同一块动态内存多次释放
3.6、动态开辟内存忘记释放(内存泄露)开辟空间一定要释放
解决方法:
动态内存申请的空间不会出了作用域自动销毁,只有两种方式销毁(将内存还给操作系统):退出程序和free函数。
malloc和free函数成对使用!