我有下面的代码。

#include <iostream>

int * foo()
{
    int a = 5;
    return &a;
}

int main()
{
    int* p = foo();
    std::cout << *p;
    *p = 8;
    std::cout << *p;
}

并且代码正在运行,没有运行时异常!

输出是58

怎么可能呢?局部变量的内存在其函数之外不是不可访问的吗?


当前回答

你在编译程序时启用了优化器吗?foo()函数非常简单,可能已经在结果代码中被内联或替换。

但是我同意Mark B的观点,结果行为是不确定的。

其他回答

What you're doing here is simply reading and writing to memory that used to be the address of a. Now that you're outside of foo, it's just a pointer to some random memory area. It just so happens that in your example, that memory area does exist and nothing else is using it at the moment. You don't break anything by continuing to use it, and nothing else has overwritten it yet. Therefore, the 5 is still there. In a real program, that memory would be re-used almost immediately and you'd break something by doing this (though the symptoms may not appear until much later!)

当您从foo返回时,您告诉操作系统您不再使用该内存,并且可以将其重新分配给其他内存。如果你很幸运,它从来没有被重新分配,而且操作系统也没有发现你再次使用它,那么你就可以摆脱这个谎言。很有可能你最终会以那个地址结尾。

现在,如果你想知道为什么编译器没有报错,这可能是因为foo被优化淘汰了。它通常会警告你这类事情。C假设您知道自己在做什么,而且从技术上讲,这里没有违反scope(在foo之外没有引用a本身),只有内存访问规则,这只会触发警告而不是错误。

简而言之:这通常不会起作用,但有时会偶然起作用。

正如Alex指出的那样,这种行为是未定义的——事实上,大多数编译器都会警告不要这样做,因为这很容易导致崩溃。

对于你可能遇到的令人毛骨悚然的行为,可以试试这个例子:

int *a()
{
   int x = 5;
   return &x;
}

void b( int *c )
{
   int y = 29;
   *c = 123;
   cout << "y=" << y << endl;
}

int main()
{
   b( a() );
   return 0;
}

这将输出“y=123”,但您的结果可能会有所不同(真的!)你的指针正在敲打其他不相关的局部变量。

可以,因为a是在其作用域的生命周期内临时分配的变量(foo函数)。从foo返回后,内存是空闲的,可以被覆盖。

你所做的被描述为未定义的行为。结果无法预测。

在典型的编译器实现中,您可以将代码视为“打印出内存块的值,该值带有曾经被a占用的地址”。此外,如果您将一个新的函数调用添加到一个约束局部整型的函数中,则a的值(或a所指向的内存地址)很有可能发生变化。这是因为堆栈将被包含不同数据的新帧覆盖。

然而,这是未定义的行为,你不应该依赖它来工作!

注意所有警告。不要只解决错误。 GCC显示此警告

警告:返回局部变量'a'的地址

这就是c++的强大之处。你应该关心记忆。有了-Werror标志,这个警告就变成了一个错误,现在您必须调试它。