我有下面的代码。
#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
怎么可能呢?局部变量的内存在其函数之外不是不可访问的吗?
当前回答
在典型的编译器实现中,您可以将代码视为“打印出内存块的值,该值带有曾经被a占用的地址”。此外,如果您将一个新的函数调用添加到一个约束局部整型的函数中,则a的值(或a所指向的内存地址)很有可能发生变化。这是因为堆栈将被包含不同数据的新帧覆盖。
然而,这是未定义的行为,你不应该依赖它来工作!
其他回答
你的代码风险很大。你正在创建一个局部变量(在函数结束后被认为是被销毁的),并且在该变量被销毁后返回该变量的内存地址。
这意味着内存地址可能是有效的,也可能是无效的,您的代码将容易受到可能的内存地址问题的影响(例如分割错误)。
这意味着你正在做一件非常糟糕的事情,因为你正在把一个内存地址传递给一个根本不可信的指针。
考虑这个例子,并测试它:
int * foo()
{
int *x = new int;
*x = 5;
return x;
}
int main()
{
int* p = foo();
std::cout << *p << "\n"; //better to put a new-line in the output, IMO
*p = 8;
std::cout << *p;
delete p;
return 0;
}
不像你的例子,在这个例子中你是:
将int的内存分配到本地函数中 当函数过期时,该内存地址仍然有效(它不会被任何人删除)。 内存地址是可信任的(该内存块不被认为是空闲的,因此在删除它之前不会被覆盖) 内存地址不使用时应删除。(见程序末尾的删除)
给所有的答案补充一点:
如果你这样做:
#include<stdio.h>
#include <stdlib.h>
int * foo(){
int a = 5;
return &a;
}
void boo(){
int a = 7;
}
int main(){
int * p = foo();
boo();
printf("%d\n",*p);
}
输出可能是:7
这是因为从foo()返回后,堆栈被释放,然后被boo()重用。 如果你分解可执行文件,你会清楚地看到它。
你的问题与范围无关。在你所展示的代码中,函数main看不到函数foo中的名称,所以你不能在foo之外直接使用这个名称访问foo中的a。
您遇到的问题是为什么程序在引用非法内存时不发出错误信号。这是因为c++标准在非法内存和合法内存之间没有明确的界限。引用弹出堆栈中的内容有时会导致错误,有时不会。视情况而定。不要指望这种行为。假设在编程时它总是会导致错误,但在调试时它永远不会发出错误信号。
It's 'Dirty' way of using memory addresses. When you return an address (pointer) you don't know whether it belongs to local scope of a function. It's just an address. Now that you invoked the 'foo' function, that address (memory location) of 'a' was already allocated there in the (safely, for now at least) addressable memory of your application (process). After the 'foo' function returned, the address of 'a' can be considered 'dirty' but it's there, not cleaned up, nor disturbed/modified by expressions in other part of program (in this specific case at least). A C/C++ compiler doesn't stop you from such 'dirty' access (might warn you though, if you care). You can safely use (update) any memory location that is in the data segment of your program instance (process) unless you protect the address by some means.
注意所有警告。不要只解决错误。 GCC显示此警告
警告:返回局部变量'a'的地址
这就是c++的强大之处。你应该关心记忆。有了-Werror标志,这个警告就变成了一个错误,现在您必须调试它。