指向临时变量的指针的返回

来源:互联网 发布:汇通易贷 知乎 编辑:程序博客网 时间:2024/04/30 17:04

一直以为对于函数返回的指针了解得还可以,但是真实不用不知道,一用吓一跳。今天在一篇博客上面看到如下两段代码,博客的作者给出了一个问题,但是并没有解释为什么不同。自己通过实验给出了解释,但是不知道对不对,仅供参考!

下面是个错误的例子:
char* get_str(void)
{
    char str[] = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%s\n", p);
     return 0;
}

下面这个例子没有问题,大家知道为什么吗?
char* get_str(void)
{
    char* str = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%s\n", p);
     return 0;
}
我在linux环境下面验证了作者的说法,的确是那样,上面一个有错,下面一个正确。问题就出在一个用的是数组类型,而另外一个用的是指针类型(从侧面亦可以看出并不是有些大学老师说的那样char str[]等价于char* str),由于栈里面的变量都是临时的。当前函数执行完成时,相关的临时变量和参数都被清除了。不能把指向这些临时变量的指针返回给调用者,这样的指针指向的数据是随机的,会给程式造成不可预料的后果。但是指针却有所不同,指针的地址在栈上,但是它所指向的内容却是在堆上面,所以并没有被清除。这就是为什么一个正确一个错误的原因。

其实这博客提前写了一天,我并没有发表,因为怕自己理解得有错,所以跟博客的作者写信交流下了,下面贴出我们的信件内容,希望对需要的朋友有所帮助:

 

 问:
大家都知道,栈里面的变量都是临时的。当前函数执行完成时,相关的临时变量和参数都被清除了。不能把指向这些临时变量的指针返回给调用者,这样的指针指向的数据是随机的,会给程式造成不可预料的后果。
下面是个错误的例子:
char* get_str(void)
{
    char str[] = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%s\n", p);
     return 0;
}

下面这个例子没有问题,大家知道为什么吗?
char* get_str(void)
{
    char* str = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%s\n", p);
     return 0;
}
我在linux环境下跑了,对代码做了点修改,相应的修改和运行结果如下:
#include <stdio.h>
char* get_str(void)
{
    char str[] = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%d/n", *p);
     printf("%d/n", *(p+1));
     return 0;
}
运行结果:
[root@localhost singnode]# ./ss
97
-12
 
#include <stdio.h>
char* get_str(void)
{
    char* str = {"abcd"};
     return str;
}
int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%d/n", *p);
     printf("%d/n", *(p+1));
     return 0;
}
运行结果:
[root@localhost singnode]# ./cc
97
98

对于这样的运行结果是不是因为指针的地址虽然是在栈上,但是它指向的内容却是在堆上面,所以并没有被清除。而数组的地址和内容都是在栈上面(首地址除外),所以出错。而对于     printf("%d/n", *p);能够输出正确的97,是因为首地址被当成指针来处理了,所以它的内容保存在堆上面,而其它的值保存在栈上面则被清除,从而产生了随机值,如-12。

(在此解释下我最初的理解,红字部分,一开始我并不知道数组返回的p也是正确的值,只是在调用printf语句后p的值才被破坏掉了,所以这是我之前根据运行结果来做的解释,在此纠正下。)

 

答:

int main(int argc, char* argv[])
{
    char* p = get_str();
     printf("%d/n", *p);
     printf("%d/n", *(p+1));
     return 0;
}
运行结果:
[root@localhost singnode]# ./cc
97
98

对于这样的运行结果是不是因为指针的地址虽然是在栈上,但是它指向的内容却是在堆上面,所以并没有被清除。而数组的地址和内容都是在栈上面(首地址除外),所以出错。

答:是的。


而对于     printf("%d/n", *p);能够输出正确的97,是因为首地址被当成指针来处理了,所以它的内容保存在堆上面,而其它的值保存在栈上面则被清除,从而产生了随机值,如-12。

答:

    char* p = get_str();
此时p指向的内容还没有破坏,你可以在调试器中看一下。
     printf("%d/n", *p);
此时因为调用了printf,所以p就被破坏了。

     printf("%d/n", *(p+1));

即使再调用:
     printf("%d/n", *p);
结果也是错误的。

 

这是最后写的回信:

非常感谢!我验证了下,如下:
(gdb) print p
$2 = 0xbffff703 "dbcd"
(gdb) s
98
12      printf("%d/n", *p);
(gdb) print p
$3 = 0xbffff703 "��/017m"
(gdb)
在调用printf之前的p内容还没有被破坏掉,但是调用printf之后就被破坏了

原创粉丝点击