在以下代码中,演示了两个函数。 f1()返回函数作用域中初始化局部变量的引用,f2()返回函数作用域中初始化局部变量的值。
f2()预计会运行良好,因为本地初始化变量。值从堆栈传递到main。
f1()不会起作用,因为局部变量的引用在函数范围之外是无用的。但是,两个函数的输出似乎都没问题。
这是测试代码;
#include <iostream>
using namespace std;
// function declarations
int& f1();
int f2();
int main()
{
cout << "f1: " << f1() << endl; // should not work!
cout << "f2: " << f2() << endl; // should work
return 0;
}
int& f1() // returns reference
{
int i = 10; // local variable
return i; // returns reference
}
int f2() // returns value
{
int i = 5; // local variable
return i; // returns value
}
输出如下;
f1: 10
f2: 5
为什么f1()工作正常,即使f1()返回局部变量的引用?
答案 0 :(得分:7)
从范围外访问局部变量是未定义的行为。未定义的行为意味着程序可能正常工作,它可能是段错误的,它可能会打印垃圾值,所有内容。
1 的低级原因是局部变量位于堆栈上。堆栈属于进程的可写地址空间(至少在大多数(如果不是全部)操作系统上都是如此)。该程序可能会像它想要的那样写入它。 但是,写入堆栈是C ++不支持的。 C ++只定义局部变量,而不是调用帧或返回地址。它驻留在更高级别的抽象上。支持直接写入我所知道的堆栈的唯一语言是Assembly。
1 无论如何,C ++标准都没有规定这个原因。
答案 1 :(得分:5)
欢迎使用 未定义的行为 !
这就是你在做什么。您访问已超出范围的变量。但是,可能是系统没有在已经存在的值上写出某些东西,这解释了行为。
这就是为什么在实际代码中找到这样的逻辑错误很难的原因。因为你可能(非)幸运并且变量具有正确的值(在该特定执行中)。
因此,f1()
的返回值是对超出范围的内容的引用,而f2()
的返回值是该函数的局部变量的副本,这是正常的
然而,下降编译器应警告您这一点,并发出此类警告:
请在编译器中警告:引用本地变量'i'返回[-Wreturn-local-addr]
启用警告标志。 :)