返回对局部变量的引用及其行为

时间:2017-04-19 05:21:16

标签: c++

我正在使用C ++的基础知识并尝试了下面的代码行,

int& func()
{
    int localnum = 10;
    return localnum;
}

int main()
{
    int &i = func();
    cout<<"value : "<<i<<endl;
    cout<<"value : "<<i<<endl;
    return 0;
}

在这里,我返回一个局部变量作为参考(实际上我不应该这样做,但我只是为了学习行为而这样做。)

我认为即使func()执行完成,localnum的值仍将被保留,并且在任何进程使用内存空间之前不会被销毁(但设置一些位/标志以便剩余的进程处理这个内存块是免费的,可以使用它) - 纠正我,如果我错了

为了检查这一点,我打印了i的值。就我而言,这是输出

  

值:10

     

价值:264952704

即使我使用指针并返回地址代替引用,这也是同样的情况。

我的问题是,我没有在这些版画之间运行任何过程,但价值仍在变化。这意味着我的系统中的任何其他进程都在这些打印之间使用localnum的内存区域?

5 个答案:

答案 0 :(得分:4)

如果你纯粹推理语言,这只是未定义的行为,所以理论上结果可能是任何东西。

根据实现的推理,不需要另一个进程来覆盖通常在堆栈中分配的局部变量。除此之外,任何对另一个函数的调用都可以。在这种情况下,第一次调用Reshape对象的operator <<涉及函数调用,该函数调用重用了cout使用的堆栈区域,导致覆盖内存位置临时创建变量func()的地方。

但同样,观察到的结果并不一致;它在很大程度上取决于实现,编译器,编译器选项等。

答案 1 :(得分:3)

使用<<意味着相当多的活动,而不需要不同的过程。检查您引用的示例程序中所需的#includes。顺便给出整个代码将被称为&#34; MCVE&#34;。 https://stackoverflow.com/help/mcve

实际上唯一能够改变&#34;你的&#34; stack 相同的进程,甚至是该进程中的同一个线程。因为每个进程都有自己的堆栈。

你正在谈论的内存是堆栈。管理数据不过是指向内存的第一个可用部分的指针;也许隐含的知识总共有多大。即堆栈上的每个字节都没有单独的标志,这会增加堆栈所需的大量内存。

因为你得到了&#34; 10&#34;只有一次,对于正在执行的相同代码,我提供以下(编译器依赖但可能是常见的)解释:

  • 使用<<
  • 开始第一行
  • 读取i的值
  • 将值放在某个不会被覆盖的地方 (可能是CPU寄存器,或堆栈上安全的地方,
    即在本活动中为此目的保留的地方)
  • 使用堆栈进行流式输出
  • 这样做会覆盖变量
  • 但价值已经在其他地方且安全了
  • 成功输出
  • 再次为第二行做这一切,
    但这次的价值是被覆盖的

重复一下你自己和一些评论说:
离开函数后使用堆栈是 evil

答案 2 :(得分:2)

未定义的行为,这意味着一切皆有可能。真正发生的事情取决于编译器的实现。

答案 3 :(得分:1)

  

我认为即使func()执行完成,localnum的值仍将被保持并且不会被销毁,直到任何进程使用内存空间(但设置一些位/标志,以便剩余的进程将此内存块视为空闲和可以使用它) - 如果我错了,请纠正我

因为这是一个UB,所以你并不完全正确。有些编译器可能会设置一个位/标志,而其他一些编译器可能会将该值设置为零......因为UB为每个编译器提供了自由行动,你的解释只是其中之一(尽管我认为它是最常见的一个)

  

我的问题是,我没有在这些版画之间运行任何过程,但价值仍在变化。这意味着我的系统中的任何其他进程都在这些打印之间使用了localnum的内存区域?

如果您的流程没有执行,并且值已更改,那么,是的,其他人正在更改该值。请注意,即使您没有直接更改该地址中的值,您的代码也可能以一种在背后更改它的方式实现(例如,&lt;&lt;运算符,如Yunnosch所说)

  

在这里,我返回一个局部变量作为参考(实际上我不应该这样做,但我只是为了学习行为而这样做。)

我不建议学习UB案例中的行为。它因机器而异。

答案 4 :(得分:0)

因为int localnum 的范围是func函数,所以实际上是在函数返回后返回对不再可用的东西的引用