调用堆栈 - 当函数将局部变量的引用返回给调用函数,然后调用另一个函数

时间:2012-07-27 19:45:57

标签: c callstack

#include<stdio.h>

int *fun();
int main()    
{
   int *ptr;
   ptr=fun();
   printf("%d",*ptr);
   printf("%d",*ptr);
}
int * fun()
{
   int k=4;//If auto then cannot print it two times.....stack will be changed
   return(&k);
}

O/P: 4
    -2

首次调用printf()会打印正确的值。

在调用fun()后立即调用任何函数(甚至是printf())。这次printf()打印一个垃圾值。为什么会发生这种情况?为什么我们在第一个print语句本身没有得到垃圾值????

3 个答案:

答案 0 :(得分:1)

这不是你可以依赖的行为;它可能并且可能在不同系统上有所不同,甚至是不同版本的编译器或不同的编译器开关。

鉴于此,可能发生的是:fun返回指向存储k的位置的指针。堆栈的那一部分不再可靠,因为分配它的功能已经退出。尽管如此,还没有人写过它,所以4仍然是它写的地方。然后main准备调用printf。为此,它获得第一个参数* ptr。为此,它从ptr点的位置加载,这是k的(前)地址,因此负载得到了那里的4。该4存储在寄存器或堆栈位置以传递给printf。然后存储格式字符串“%d”的地址以传递给printf。然后调用printf。此时,printf使用了大量的堆栈并在k曾经的位置写入新数据。但是,作为参数传递的4是在一个安全的地方,其中printf的参数应该是,所以printf打印它。然后printf返回。然后主程序准备再次调用printf。这一次,当它从ptr指向的地方加载时,4不再存在;它是在第一次调用printf期间写的一些值。因此,该值是传递给printf的值,并且是打印的内容。

永远不要编写使用此行为的代码。它不可靠,而且代码不正确。

答案 1 :(得分:1)

为什么让你感到惊讶?行为是未定义的,但在观察你观察到的内容时没有什么不寻常的。

所有变量都存在于内存中。当变量被正式销毁时(如函数退出时的局部变量),它曾经占用的内存仍然存在,并且很可能仍然保留写入它的最后一个值。现在这个内存正式免费,但它会继续保持最后一个值,直到其他一些代码将该内存重用于其他目的并覆盖它。

这是您在实验中观察到的内容。即使变量k不再存在,指针ptr仍然指向其以前的位置。而之前的位置仍然会保留k的最后一个值,即4

第一个printf“成功”收到该值的副本以进行打印。而第一个printf实际上是重用旧内存位置并覆盖k的前一个值的那个ptr。取消引用4的所有进一步尝试都会显示printf不再存在,这就是您的第二个{{1}}打印其他内容的原因。

答案 2 :(得分:0)

变量kfun()的本地变量,意味着它将在函数返回时被销毁。这是一种非常糟糕的编码技术,并且总会导致问题。

第一个printf返回正确值的原因:

首先,它可能会也可能不会返回值。假设k写在堆栈内存的某处。第一次函数返回printf时可能会得到正确的值,因为该部分内存可能存在一段时间。但这不能保证。