返回对局部变量或临时变量的引用

42

看一下下面的代码。我知道它没有返回局部变量的地址,但是为什么它仍然能够运行并将主函数中的变量i赋值为6?如果该变量已经从堆栈内存中移除,它是如何仅仅返回其值的呢?

#include <iostream>

int& foo()
{
    int i = 6;
    std::cout << &i << std::endl; //Prints the address of i before return
    return i;
}

int main()
{
    int i = foo();
    std::cout << i << std::endl; //Prints the value
    std::cout << &i << std::endl; //Prints the address of i after return
}

19
你只是幸运而已。不要做那件事。 - Paul Beckingham
3
这个链接可能会对你有所帮助:https://dev59.com/ZWw15IYBdhLWcg3wuuCn。译文:您可能会发现这个链接有用:https://dev59.com/ZWw15IYBdhLWcg3wuuCn。 - letsc
我相信一些运气在于 foo() 中的 i 没有被改变(这使得编译器可以将其放置在文本或某个长期存在的地方,而不是堆栈中)。 - Om Deshmane
从局部变量中获取地址可能会阻止编译器/运行时使用寄存器。结果导致性能差。 - user502187
5个回答

24

你很幸运。从函数返回并不会立即清除你刚刚退出的堆栈帧。

顺便问一下,你是怎么确认你得到了一个6?表达式 std::cout << &i ... 打印的是i的地址,而不是它的值。


3
当他在主函数中打印时,数字6会显示出来。 - San Jacinto
2
@San:现在问题已经被@Dave18编辑过了,所以可以了。 - Marcelo Cantos
1
抱歉,我之前可能没有看清问题 :) - San Jacinto
@smartmuki:说得好!此外,我认为调试编译有时会在退出时对堆栈进行分割,以突出访问超出作用域的变量。 - Marcelo Cantos
这里有可能出现段错误吗? - Debashish
显示剩余3条评论

3

3

返回对局部变量的引用或指针是未定义行为。未定义行为意味着标准将决策留给编译器。这意味着,未定义行为有时可以正常工作,而有时则不能


4
它表现不佳的可能性取决于观看演示的人的重要性。 - KeithB

2
main()中,i的地址永远不会改变,但其中包含的值会改变。您正在获取局部变量的引用,并在该引用已超出范围后使用它。 (不精确的语言警告)值6位于堆栈上。由于在将6放入堆栈后未对堆栈执行任何操作,因此对它的引用仍将包含相同的值。因此,正如其他人所说,您很幸运。
要查看您调用foo()后使用堆栈的情况,请尝试运行此代码:
#include <iostream>
#include <ctime>
#include <numeric>

int& foo()
{
    int i = 6;
    std::cout << &i << " = " << i << std::endl; //Prints the address of i before return
    return i;
}

long post_foo(int f)
{
    srand((unsigned)time(0));

    long vals[10] = {0};
    size_t num_vals = sizeof(vals)/sizeof(vals[0]);
    for( size_t i = 0; i < num_vals; ++i )
    {
        int r = (rand()%2)+1;
        vals[i] = (i+f)*r;
    }

    long accum = std::accumulate(vals, &vals[num_vals], 0);
    return accum * 2;
}

int main()
{
    int &i = foo();
//  std::cout << "post_foo() = " << post_foo(i) << std::endl;
    std::cout << &i << " = " << i << std::endl; 
}

当我注释掉post_foo()调用后运行此代码,6仍然留在堆栈中,输出结果为:
002CF6C8 = 6
002CF6C8 = 6

...但是当我取消对post_foo()的注释并再次运行时,6已经不见了:

001FFD38 = 6
post_foo() = 310
001FFD38 = 258923464

1

虽然您的函数通过引用返回一个整数,但它立即被分配给main()中的局部变量'i'。这意味着为foo()分配的堆栈内存必须持续足够长的时间以进行返回赋值。虽然这是不好的形式,但通常可以工作。如果您尝试保留引用,则会导致未定义的行为。

int &i = foo();

它更有可能失败。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接