前言
首先本文中所讲述的例题皆来自于《高质量 C++/C 编程指南》这本书中。这本书中包含了很多C/C++中的陷阱和编程规则,读完这本书之后,相信你会对C/C++有了一个新的认识。
第一题
大家先来看这一段代码,请问Test函数运行的结果是什么?
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
首先这里的答案是程序会崩溃掉,那么为什么程序会崩溃,你是不是觉得这里面并没有一点问题?
这里涉及到指针传参,这里GetMemory(str)虽然将str传了进去,形参是实参的一份临时拷贝,那么这里的形参p=str,如果你只改变p的值是不会改变str的值,str的值还是NULL,所以这里程序会崩溃,当然这里申请了空间却没有free也会造成内存泄漏。
如果想要改变str的值,你需要从地址访问str,那么你这里就需要传入str的地址,代码如下
void GetMemory(char **p)
{
*p = (char *)malloc(100); //对二级指针p解引用,**p=&str
}
void Test(void)
{
char *str = NULL;
GetMemory(&str); //传入str的地址
strcpy(str, "hello world");
printf(str);
}
第二题
那么这段代码Test函数运行的结果又是什么呢?
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
答案是:不能确定。为什么不能确定,str不应该是‘h’的地址吗?这里涉及到了函数堆栈的知识,这些函数都是在栈上开辟的空间,在栈上开辟空间有一个特点就是进栈开辟空间,出栈自动释放空间。我们来解析GetMemory函数。
char *GetMemory(void) //在栈上开辟空间
{
char p[] = "hello world"; //给p分配空间
return p; //返回p的值后自动释放刚刚开辟的空间
}
这里大家就能看出问题,如果刚刚开辟的空间被释放了,那“hello world"字符串就不存在了,但是这个指针p将‘h’的地址返回给了str,虽然GetMemory的空间被释放掉,可是str依旧指向原来的’h’,只是字符串已经不在了,这个时候str就变成了一个野指针,现在这个位置(原‘h’)上存放的是什么内容我们就不得而知了。
第三题
大家是否感觉很有意思,接下来是第三题
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
这道题很简单,但这种错误造成的后果很严重,答案是会篡改动态内存区的内容。这里虽然使用free释放了刚才开辟的动态内存空间,但是str依然指向原‘h’所在位置,这个时候str便成了一个野指针,这里的if语句对str根本没有任何作用。我们需要养成一个好习惯,不仅malloc和free要成对出现,而且在free释放之后要将刚刚保存开辟空间首地址的指针置NULL。
《高质量 C++/C 编程指南》这本书中有很多习惯可以使你能更稳定的编写高难度的程序。