我有下面的代码。
#include <iostream>
int * foo()
{
int a = 5;
return &a;
}
int main()
{
int* p = foo();
std::cout << *p;
*p = 8;
std::cout << *p;
}
并且代码正在运行,没有运行时异常!
输出是58
怎么可能呢?局部变量的内存在其函数之外不是不可访问的吗?
我有下面的代码。
#include <iostream>
int * foo()
{
int a = 5;
return &a;
}
int main()
{
int* p = foo();
std::cout << *p;
*p = 8;
std::cout << *p;
}
并且代码正在运行,没有运行时异常!
输出是58
怎么可能呢?局部变量的内存在其函数之外不是不可访问的吗?
当前回答
这是典型的未定义行为,两天前在这里讨论过——搜索一下网站。简而言之,您是幸运的,但是任何事情都可能发生,并且您的代码正在对内存进行无效的访问。
其他回答
在c++中,你可以访问任何地址,但这并不意味着你应该这样做。您正在访问的地址不再有效。它之所以能工作,是因为在foo返回后没有其他东西扰乱内存,但在许多情况下它可能崩溃。试着用Valgrind分析你的程序,甚至只是优化编译它,然后看看…
你只是返回一个内存地址,这是允许的,但可能是一个错误。
是的,如果你试图解引用该内存地址,你将有未定义的行为。
int * ref () {
int tmp = 100;
return &tmp;
}
int main () {
int * a = ref();
//Up until this point there is defined results
//You can even print the address returned
// but yes probably a bug
cout << *a << endl;//Undefined results
}
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”,但您的结果可能会有所不同(真的!)你的指针正在敲打其他不相关的局部变量。
注意所有警告。不要只解决错误。 GCC显示此警告
警告:返回局部变量'a'的地址
这就是c++的强大之处。你应该关心记忆。有了-Werror标志,这个警告就变成了一个错误,现在您必须调试它。