#include<stdio.h>
int * fun(int a1,int b)
{
int a[2];
a[0]=a1;
a[1]=b;
return a;
}
int main()
{
int *r=fun(3,5);
printf("%d\n",*r);
printf("%d\n",*r);
}
运行代码后的输出:
3
-1073855580
我知道a [2]对于fun()来说是本地的,但是为什么值被改变了相同的指针?
答案 0 :(得分:7)
变量a确实很有趣。从该函数返回时,将弹出堆栈。记忆本身保持不变(暂时)。当您第一次取消引用时,内存就是您期望的内存。由于取消引用在调用printf之前发生了,所以没有任何不好的事情发生。当printf执行时,它会修改堆栈并清除值。第二次通过你看到第一次通过printf发生的任何价值。
“正常”调用约定的事件顺序(我知道,我知道 - 没有这样的事情):
r
(第一次通过,这就应该是它)如果您将int a[2];
更改为static int a[2];
,则可以缓解此问题。
答案 1 :(得分:2)
因为r
指向堆栈上可能被函数调用覆盖的位置。
在这种情况下,它是第一次调用printf
本身正在改变该位置。
详细地说,fun
的返回仅仅因为没有覆盖它而保留了该特定位置。
然后评估*r
(作为3)并传递给printf
进行打印。对printf
的实际调用会更改该位置的内容(因为它将内存用于其自己的堆栈帧),但该值已在该点提取,因此它是安全的。
在随后的通话中,*r
具有不同的值,由第一次通话更改。 那是为什么在这种情况下会有所不同。
当然,这只是可能的解释。实际上,任何都可能发生,因为您编码的内容存在未定义的行为。完成后,所有投注均已关闭。
答案 2 :(得分:1)
正如您所提到的,a[2]
是fun()
的本地人;意味着它是在fun()
内的代码开始执行之前就在堆栈上创建的。当fun
退出堆栈时弹出,意味着它被展开,以便堆栈指针指向fun
开始执行之前的位置。
编译器现在可以自由地将任何想要的东西粘贴到那些被解开的位置。因此,有可能出于各种原因跳过a
的第一个位置。也许现在它代表了一个未初始化的变量。也许是为了另一个变量的内存对齐。简单的回答是,通过从函数返回指向局部变量的指针,然后取消引用该指针,您将调用未定义的行为并且可能发生任何,包括demons flying out of your nose。< / p>
答案 3 :(得分:1)
使用以下命令编译代码时:
$ gcc -Wall yourProgram.c
它将产生一个警告,说。
In function ‘fun’:
warning: function returns address of local variable
在第一个r
语句中取消引用printf
时,可以保留内存。但是,第二个printf
语句会覆盖堆栈,因此会得到不希望的结果。
答案 4 :(得分:0)
因为printf正在使用堆栈位置并在打印第一个值后更改它。