内存分配:这个C程序为什么工作?

内存分配:这个C程序为什么工作?,c,memory,C,Memory,可能重复: add函数实现错误。它应该返回一个值而不是指针。 为什么在打印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

可能重复:

add函数实现错误。它应该返回一个值而不是指针。 为什么在打印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;
}
#包括
整数*相加(整数x,整数y){
intz=x+y;
int*ans_ptr=&z;
返回ANSUPTR;
}
int main(){
int ans=*(添加(1,2));
int*ans_ptr=add(1,2);
printf(“%d\n”,*ans\u ptr);
printf(“%d\n”,ans);
返回0;
}

它之所以“有效”,是因为你运气好。返回指向局部变量的指针是未定义的行为!!你不应该这样做

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

因为C没有垃圾收集,所以当“z”变量超出范围时,实际内存不会发生任何变化。如果编译器愿意,只需释放另一个变量即可覆盖它

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


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

不。你的问题表明你根本不了解C内存模型是如何工作的

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

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

这意味着内存位置&z中的值(来自刚刚腾出的堆栈帧)在紧接着的语句中仍然保持不变,即main()中的printf()语句


您应该永远不要依赖这种行为-一旦您向上面添加了额外的代码,它可能会崩溃

答案是:这个程序之所以有效,是因为你很幸运,但它不会花时间背叛,因为你返回的地址不再是你的专属地址,任何人都可以再次使用它。这就像租了房间,做了一把复制钥匙,释放了房间,在你释放了房间之后,你试着用一把复制钥匙进入房间。在这种情况下,如果房间是空的,并且没有租给其他人,那么你是幸运的,否则它会让你被警察拘留(很糟糕),如果房间的锁被换了,你会得到一个SEGFULT,因此你不能只信任你在没有获得房间的情况下制作的复制钥匙

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


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

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

说实话,这个简单的案子很有可能“奏效”。如果在调用
add
和获取值之间对堆栈指针进行任何操作,它就会中断,但从技术上讲,值并没有被破坏——只是被遗忘了。不过,这仍然是一个可怕的想法;一旦函数返回,它就不再拥有堆栈的一小部分,任何函数调用(或信号,或可能的中断等)都可以覆盖该值。根据标准,这是UB,但在任何在堆栈上传递值的架构中,行为几乎相同。我认为这不是随机发生的。这是我的CS课程中的一个问题。在你的机器上试一下,你会得到正确的结果。我想知道这是怎么回事happens@Bryon,看看“未定义的行为”的定义,它意味着任何事情都可能发生,如果它愿意,它可以覆盖MBR。@Tony:可以,但不会。C规范没有定义会发生什么,所以它说“任何事情都可能发生”。但是您在上面测试的每台机器(假设它使用堆栈作为局部变量)都将执行以下三项操作之一:返回正确的值、返回错误的值或segfault。它不会点燃任何东西——或者覆盖你的MBR,或者订购一打比萨饼,或者其他任何东西——除非你依赖于这个值来做一些重要的事情。@Tony:还要注意,如果这个代码在普通用户运行时覆盖了MBR,那么不管C标准对此有什么规定,那就是一个操作系统错误。仅仅因为C标准说您的操作系统供应商可以在总线前运行,并不意味着它会。我认为赵的观点是,虽然它是UB,因此C标准对它是否有效或为什么有效没有一点线索,但我们是聪明的人,对典型的C实现略知一二,所以我们可以在描述用户机器上发生的情况时冒险猜测一下。也就是说,他是如何幸运的。用1845张选票阅读答案: