2011-09-06 41 views
3

可能重复:
Returning the address of local or temporary variable内存分配:为什么这个C程序有效?

的附加功能被错误执行。它应该返回一个值而不是一个指针。 为什么打印ans和* ans_ptr时没有任何错误,并且程序甚至给出了正确的结果?我想z的变量已经超出范围,应该有分段错误。

#include <stdio.h> 

int * add(int x, int y) { 
    int z = x + y; 
    int *ans_ptr = &z; 
    return ans_ptr; 
} 

int main() { 
    int ans = *(add(1, 2)); 
    int *ans_ptr = add(1, 2); 

    printf("%d\n", *ans_ptr); 
    printf("%d\n", ans); 

    return 0; 
} 
+3

阅读与1845年upvotes答案:http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed - 范围外 –

回答

9

它'工作'的原因是因为你有幸运。返回一个指向局部变量的指针是Undefined Behavior !!你不应该这样做。

int * add(int x, int y) { 
    int z = x + y; //z is a local variable in this stack frame 
    int *ans_ptr = &z; // ans_ptr points to z 
    return ans_ptr; 
} 


// at return of function, z is destroyed, so what does ans_ptr point to? No one knows. UB results 
+0

真相告诉,这个简单的情况很可能“工作”。如果有什么操纵'add'调用和获取值之间的堆栈指针,它会中断,但是这个值在技术上并没有被破坏 - 只是被遗忘了。尽管如此,这仍然是一个可怕的想法。一旦函数返回,它不再拥有堆栈的那一小块,并且任何函数调用(或信号,或可能的中断等)都可以覆盖该值。根据标准它是UB,但是在任何能够在堆栈中传递值的架构中,行为几乎是相同的。 – cHao

+0

我不认为它是随机发生的。这是我的CS课程中的一个问题。在你的机器上试试它,你可以得到正确的结果。我想知道这是怎么发生的 – Byron

+1

@Bryon,看看'未定义的行为'的定义,这意味着**任何事情**都可能发生,它可以覆盖MBR,如果它想。 –

3

因为C没有垃圾回收,所以当“z”变量超出范围时,实际内存没有任何反应。如果编译器满意,它可以被另一个变量覆盖。

由于在调用“添加”和打印之间没有分配内存,该值仍在内存中,您可以访问它,因为您有它的地址。你好幸运啊。”

但是,正如托尼指出的,你永远不应该这样做。它会在一段时间内工作,但是一旦你的程序变得越来越复杂,你就会开始结束虚假值。

0

不可以。您的问题显示对C内存模型工作原理的基本缺乏理解。

在控制进入add()时创建的框架中,值z被分配在堆栈地址处。然后将ans_ptr设置为该内存地址并返回。

栈上的空间将在下一个调用的函数被覆盖,但要记住,C从不执行内存清理除非明确告知(例如,通过像释放calloc()的函数)。

这意味着存储器位置& z(来自刚刚腾出的堆栈帧)中的值在紧随其后的语句中仍然完整无缺,即。 main()中的printf()语句。

你应该永远永远依赖于这种行为 - 只要你添加额外的代码到它上面很可能会打破。

0

答案是:这个程序的工作,因为你是幸运的,但它会采取任何时间背叛,当你返回的地址是不是保留给你了任何一个可以再次使用它。它就像租用房间,制作重复密钥,释放房间一样,稍后在您释放房间后,尝试用重复键输入房间。在这种情况下,如果房间是空的而不是租给其他人,那么你很幸运,否则它可能会让你受到警方的拘留(不好的事情),如果房间的锁被改变,你会得到一段段错误,只要相信你在没有购买房间的情况下制作的重复密钥。

z是在堆栈中分配的局部变量,其范围是只要给功能块的特定的呼叫。你返回这样一个局部变量的地址。一旦你从函数返回,所有块的本地地址(在函数调用堆栈帧中分配)可能被用于另一个调用并被覆盖,因此你可能会或可能不会得到你所期望的。这是未定义的行为,因此这种操作是不正确的。

如果您获得正确的输出,那么您很幸运,该内存位置保存的旧值不会被覆盖,但是您的程序可以访问该地址所在的页面,因此您不会收到分段错误错误。

0

正如OP指出的那样,快速测试显示GCC 4.3和MSVC 10都不提供任何警告。但锵静态分析器确实

ccc-analyzer -c foo.c 
... 
ANALYZE: foo.c add 
foo.c:6:5: warning: Address of stack memory associated with local 
        variable 'z' returned to caller 
    return ans_ptr; 
    ^ ~~~~~~~