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