前面的一篇文章动态内存管理 前篇,我们已经了解过了动态内存管理的相关信息,接下来,让我们来一起来踩“坑”。当然,今天的踩坑,是为了面试不踩坑哦
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;
}
我们来找一找上面的代码有什么问题?
好了,不绕弯子了,来看看到底错了哪些地方
printf(str)???这样写也能行???为什么吗??
我们来看看下面的代码:
#include
int main()
{
char ptr[] = "hehehe";
char* p = "hehehe";
printf("%s\n", ptr);
printf("%s\n", p);
printf("hehehe");
return 0;
}
//运行结果:
*****
hehehe
hehehe
hehehe
*****
纳尼??这是为什么呢?
在解释这个问题前,我们先想一下printf的第一个参数是什么?答案是:必须是指针。那就意味printf(“hehehe”)里面的“hehehe”不是我们所看到的那样,而是一个地址。那么它是谁的地址?下面的代码会给我们答案:
#include
int main()
{
char* p = "hehehe";
printf("%p\n", "hehehe");
printf("%p\n", p);
return 0;
}
//运行结果:
*****
00AF7B54
00AF7B54
*****
奥,那我就明白了:ptr指针指向的是字符串的第一个字符,那么运行结果告诉我们,printf(“hehehe”)里面传的就是第一个字符的地址。
修改:
//修改后
// #include
#include
void GetMemory(char** p) //二级指针接收
{
*p = (char*)malloc(100); //解引用就是一级指针
}
void Test(void)
{
char* str = NULL;
GetMemory(&str); //传址
strcpy(str, "hello world");
printf(str);
//释放空间
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
#include
#include
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}
这个除了没有释放空间就没有其他的问题了吧。
但真相是这样吗??
让我们看看运行结果是什么吧
嗯??没释放空间也不会导致这种结果吧…
让我们来全面的分析一下吧:
返回栈空间地址的问题???这是什么啊??
栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返
回地址等
那我们就明白了为什么叫做返回栈空间地址,那问题呢??
当函数调用结束(出了函数),在函数里面的空间就会返还给操作系统,我们用户没有操作权限。操作系统会把这个空间重新分配给其他变量,这样我们就不得而知了
修改:
#include
#include
char* GetMemory(void)
{
char* p = "hello world"; //将第一个字符的地址传给p
//static char p[]="hello world"; //被static修饰的变量存放在数据段(静态区),
//数据段的特点是在上面创建的变量,直到程序结束才销毁
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
//释放
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
#include
#include
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;
}
这个就很简单了,没有释放空间
修改:
#include
#include
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;
}
#include
#include
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
这个问题是啥?感觉没问题啊??不会是str没有置空吧?
哈哈,其实这个问题很隐蔽,不注意看是看不出来的
free的动作是将该内存块返还给操作系统,我们没有操作权限,故str是野指针,造成了非法访问内存
修改:
#include
#include
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
str = NULL; //在这里置空,也是为了符合下面判断的逻辑
//那么也不会进入if里面造成非法访问
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
在在这里,终于把C语言中的动态内存管理的知识讲解完了。想必大家大概的框架是有的,但是对一些专业术语跟底层逻辑不是很清楚。比如:什么是堆区,什么又是栈区?内存中的每一个区域的作用是什么?等等