本文为数据结构打下基础
备注:数据结构需要掌握指针,结构体和动态内存管理
目录
6.常见的动态内存的错误
1.对空指针解引用
2.对动态空间的越界访问
3.对非动态内存空间进行free释放
4.使用free只释放开辟的内存空间的一部分
5.对同一块动态内存多次释放
6.动态开辟的内存忘记释放
7.动态开辟的内存无法释放
代码改进
7.动态内存练习题
1.VS下,求下列代码的执行结果
答案速查
分析
改进后
方案1
方案2
承接70.【C语言】动态内存管理(重点)(3)文章
6.常见的动态内存的错误
1.对空指针解引用
之前在68.【C语言】动态内存管理(重点)(1)说过
如果真的解引用了, 可能会引发程序崩溃,内存损坏或数据丢失
因此在使用malloc,calloc,recalloc开辟内存空间时,要先判断返回的指针是否为空指针,再做其他操作
2.对动态空间的越界访问
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(4);
*(p + 1) = 2;
*(p + 2) = 3;
return 0;
}
打开VS的内存窗口,输入p
显然脱离了动态分配的空间,入侵了其他数据处,可能会引发程序崩溃,内存损坏或数据丢失
3.对非动态内存空间进行free释放
#include <stdlib.h>
int main()
{
int a[5] = { 0 };
int* p = a;
free(p);
p = NULL;
return 0;
}
导致错误:
4.使用free只释放开辟的内存空间的一部分
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(20);
if (p == NULL)
{
perror("malloc");
return 1;//错误返回
}
for (int i = 0; i < 5; i++)
*(p + i) = i;
p++;
free(p);
p = NULL;
return 0;
}
导致错误:
起始的指针不能移动!(上方代码的p++;是禁止使用的,不能改变p的值)
5.对同一块动态内存多次释放
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(20);
if (p == NULL)
{
perror("malloc");
return 1;//错误返回
}
free(p);
free(p);
p = NULL;
return 0;
}
导致错误:
如果非要多次释放,在第一次释放后使p置NULL,再free(p);
free(p);
p = NULL;
free(p);
free(NULL);时,free函数什么也不做(free函数具体参见69.【C语言】动态内存管理(重点)(2))
6.动态开辟的内存忘记释放
忘记释放导致该内存不能再使用,可能会造成内存泄漏,程序性能下降,系统资源耗尽,程序崩溃问题
7.动态开辟的内存无法释放
#include <stdlib.h>
void function()
{
int* p = (int*)malloc(20);
if (p == NULL)
{
perror("malloc");
return 1;//错误返回
}
//使用
//......
}
int main()
{
function();
free(p);
return 0;
}
function函数内的p是局部变量,函数执行结束时,局部变量被销毁(找不到动态内存的地址),交换给操作系统,如果此时在main函数里free(p);编译无法通过,无法释放空间
(具体介绍局部变量的特性见4.【C语言】初识常量与变量)
同样的,无法释放导致该内存不能再使用,可能会造成内存泄漏,程序性能下降,系统资源耗尽,程序崩溃问题
代码改进
在function函数中返回p
#include <stdlib.h>
int* function()
{
int* p = (int*)malloc(20);
if (p == NULL)
{
perror("malloc");
return 1;//错误返回
}
//使用
//......
return p;
}
int main()
{
//r_p是return_pointer的缩写
int* r_p=function();
free(r_p);
r_p = NULL;
return 0;
}
因此
1.在函数中开辟的动态内存空间一定要返回动态内存空间的起始地址,用于main的free函数释放;
2.malloc和free成对使用;calloc和free成对使用(如果是在自定义函数中,则一定要在其返回前使用free)
7.动态内存练习题
1.VS下,求下列代码的执行结果
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
int main()
{
Test();
return 0;
}
答案速查
程序崩溃
分析
运行到strcpy处发生错误
注意到0x00000000,这实际上是空指针,说明str还是空指针,进一步推导得出:GetMemory并没有改变str的内容,该函数调用结束,p被销毁,即传值调用
(有关传值调用和传址调用的介绍见29.【C语言】函数系列中 自定义函数)
因此:strcpy(str, "hello world");等价为strcpy(NULL, "hello world");
在51.【C语言】字符函数和字符串函数(strcpy函数)文中提到过,strcpy函数的参数不接受空指针,因此这里会报错
而且printf(str);实际上是对空指针解引用,这是本文介绍的6.常见的动态内存的错误的第1点错误
除此之外,该代码有两处明显不规范的地方:
1.malloc函数的返回值没有判断是否为空指针
2.有malloc但没有free,容易发生内存泄漏
改进后
方案1
使用传址(GetMemory(&str);)调用,p为二级指针(接收str指针的指针)
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void GetMemory(char** p)
{
*p = (char*)malloc(100);
}
int Test(void)
{
char* str = NULL;
GetMemory(&str);
if (str == NULL)
{
perror("malloc");
return 1;//错误返回
}
strcpy(str, "hello world");
printf(str);
free(str);
str = NULL;
return 0;
}
int main()
{
Test();
return 0;
}
方案2
p为一级指针,此时GetMemory无需参数
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char* GetMemory()
{
char* p = (char*)malloc(100);
return p;
}
int Test(void)
{
char* str = NULL;
str = GetMemory();
if (str == NULL)
{
perror("malloc");
return 1;
}
strcpy(str, "hello world");
printf(str);
free(str);
str = NULL;
return 0;
}
int main()
{
Test();
return 0;
}