1.常见的动态内存错误
1.1对NULL指针的解引用
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
1.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的时候p+i就会越界就会发生越界访问。
}
free(p);
}
1.3对动态开辟内存使用free释放
void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}
1.4使用free释放一块动态开辟内存的一部分
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不在指向动态内存的起始位置
}
1.5对同一块动态内存多次释放
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
1.6动态开辟内存忘记释放了(内存泄露)
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
关于这一点我们要记住,忘记释放不在使用的空间会造成内存泄漏。
切记,动态开辟的空间一定要释放,并且正常释放。
下面还有一些题,我们来练习一下:
2.动态内存经典笔试题分析
2.1题目
请问test函数会有怎样的结果?
#define _CRT_SECURE_NO_WARNINGS
#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;
}
会有两种问题:1.有malloc但是没有free函数释放空间,这样就会导致内存泄漏。
2. 这串代码是打印不出数据的,因为程序崩溃了,为什么会崩溃?我会讲解一下。
GetMomory函数传递的是值所以对形参的修改影响不到实参,所以这串代码
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
对p的改动返回不到实参的结果,再出这个代码之后p申请的空间就会销毁。最终p还是一个空指针,如果对NULL再进行解引用的话,就会导致程序崩溃。
如果要进行修改的话,就是下面的样子:进行传址操作
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void GetMemory(char** p)
{
*p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;//1.我创建的是str这个变量,给他放入一个空指针
GetMemory(&str);//2.我调用GetMemory函数,这里我传的是str指针变量,这个str虽然说是一个地址,但是它指向只是首元素的地址,我们的传址操作是传递整个str的地址所以要对str进行取地址操作。
strcpy(str, "hello world");
printf(str);
free(str);
str = NULL;
}
int main()
{
test();
return 0;
}
就是这样。 其实还有一种比较简单的方式可以使用:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
char* GetMemory()//如果换成这样的方式,传参就没有意义了,就可以不用传参
{
char* p = (char*)malloc(100);直接写成这样就行了。
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMomory();
strcpy(str, "hello world");
printf(str);
free(str);
str = NULL;
}
int main()
{
test();
return 0;
}
2.2题目:
我们来看一下,这个题目test函数的结果是什么:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
test();
return 0;
}
这串代码会不会打印出hello world呢? 有可能,为什么会有可能呢?接下来我会讲解一下:
首先我们创建了一个空指针str,然后调用GetMomory函数,在GetMemory函数中我们创建了一个字符数组p,这个数组里面的元素是hello world,最后返回数组名,我们知道数组名是首元素的地址,也就是直接返回了首元素的地址,就是h的地址,之后我们就会把这个地址赋给str,但是呢,p是getmemory函数里面的局部变量,你如果一出getmemory函数的话,这个p就会销毁。p所指向的空间就会还给操作系统,但是str指向的地址还是这个地址,只不过变成野指针了,如果p指向的那块空间,没有被占用就能打印出来,如果被占用的话就打印不出来。
下面我在给一个简单的代码,和上面的代码错误是一样的。
int * test()
{
int n = 10;
return &n;
}
int main()
{
int *p = test();
printf("%d\n",*p);
return 0;
}
这两串代码在性质上是一样的。但是呢,这串代码打印出来就是10,其实这就是巧合,就是释放的空间刚好没有被覆盖掉。
如果在printf("%d\n",*p);前面加上printf("hehe\n");打印出来就不是10了。
2.3题目:
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
int main()
{
test();
return 0;
}
这传代码有没有问题?有,就是忘记释放空间了,导致内存泄漏。
下面这样改造完就可以了。
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
free(str);
str = NULL;
}
int main()
{
test();
return 0;
}
2.4题目:
我已经在题目中进行讲解
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void Test(void)
{
char* str = (char*)malloc(100);//melloc申请了100个字节的空间。str指向这块空间。
strcpy(str, "hello");//我们再把hello拷贝到str这块空间上去
free(str);//我们对str进行释放,就是把str指向的空间还给操作系统了,这块空间就没有使用权限了。但是这块空间在内存中还在,
if (str != NULL)//str不等于空
{
strcpy(str, "world");//这里就是把world这个字符串覆盖到hello上去,但是我们没有str这块空间的访问权限了,这里就是属于非法访问。
printf(str);
}
}
int main()
{
test();
return 0;
}