内存分配:为什么这个C程序有效?

时间:2011-09-06 14:37:31

标签: c memory

  

可能重复:
  Returning the address of local or temporary variable

错误地实现了添加功能。它应该返回一个值而不是一个指针。 当打印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;
}

5 个答案:

答案 0 :(得分: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

答案 1 :(得分:3)

因为C没有垃圾收集,当“z”变量超出范围时,实际内存没有任何反应。如果编译器喜欢,它可以简单地释放另一个变量来覆盖它。

由于在调用“add”和打印之间没有分配内存,因此该值仍然位于内存中,您可以访问它,因为您有自己的地址。你“很幸运。”

然而,正如Tony指出的那样,你永远不应该这样做。它会在某些时候起作用,但是一旦你的程序变得更复杂,你就会开始以虚假的值结束。

答案 2 :(得分:0)

没有。您的问题显示出对C内存模型如何工作的基本缺乏理解。

值z分配在堆栈上的地址中,在控件进入add()时创建的帧中。然后将ans_ptr设置为此内存地址并返回。

堆栈中的空间将被下一个被调用的函数覆盖,但请记住,除非明确告知(例如通过calloc()之类的函数),否则C永远不会执行内存清理。

这意味着存储位置&amp; z(来自刚刚腾出的堆栈帧)中的值在紧接着的语句中仍然完好无损,即。 main()中的printf()语句。

你应该从不 永远依赖这种行为 - 只要你在上面添加额外的代码,它就可能会破坏。

答案 3 :(得分:0)

答案是:这个程序有效,因为你很幸运,但它会花费时间背叛,因为你返回的地址不再为你保留,任何人都可以再次使用它。它就像租房间,制作复制钥匙,释放房间,以及稍后释放房间后,您尝试使用重复键输入房间。在这种情况下,如果房间是空的,而不是租给其他人,那么你很幸运,否则它可以让你被警察拘留(有些不好),如果房间的锁被改变,你会得到一个段错误,所以你可以'只需信任您在没有获得房间的情况下制作的重复密钥。

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

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

答案 4 :(得分:0)

正如OP指出的那样,快速测试显示,GCC 4.3和MSVC 10都没有提供任何警告。但是Clang Static Analyzer

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;
    ^      ~~~~~~~