返回本地变量的引用及其行为

3

我只是在学习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的值。在我的情况下,这是输出结果:

value : 10

value : 264952704

即使我使用指针并在引用的位置返回地址,情况仍然相同。

我的问题是,在这些打印之间我没有运行任何进程,但是值正在改变。那么这意味着我的系统中的其他进程正在使用localnum的内存区域吗?


6
这是未定义行为,所以行为可能是任何情况。试图推理它没有太多意义 - 基本上你正在返回一个可以被重新使用的内存位置的引用(因为对象的生命周期已经结束)。它可能如何被重用是随意的。局部变量通常分配在堆栈上,编译器会使用堆栈进行各种操作 - 如调用输出函数。 - Michael Burr
2
你是否知道<<是一个操作符,它意味着一些库函数的执行?在你的MCVE中,你确实使用了一些#include,对吧?即使你没有提供MCVE...所以不需要“不同的进程”。 - Yunnosch
5个回答

4
如果你只考虑语言本身,那么这就是未定义行为,理论上结果可能是任何值。
根据你的实现推理,通常不需要另一个进程覆盖在堆栈中分配的局部变量。除其他事项外,任何对另一个函数的调用都会这样做。在这种情况下,你对cout对象的operator <<的第一次调用涉及到一个函数调用,该调用重复使用了func()使用的堆栈区域,导致覆盖了临时创建localnum变量的内存位置。
但是,观察到的结果并不一致;它很大程度上取决于实现、编译器、编译器选项等。

3

<< 的使用涉及到相当多的活动,而不需要另外的进程。检查您引用的示例程序中所需的#includes。顺便说一下,给出完整的代码将被称为“MCVE”。https://stackoverflow.com/help/mcve

实际上,能够改变“你的”堆栈的唯一活动是同一进程,甚至是该进程内的同一线程。因为每个进程都有自己的堆栈。

您谈论的内存是堆栈。管理数据只不过是指向可用内存的第一个指针;可能还有隐含的了解堆栈的总大小。也就是说,对于堆栈上的每个字节,没有单独的标志,否则会增加堆栈所需的严重内存量。

对于您仅在执行相同的代码一次时获得“10”的事实,我提供以下(依赖于编译器但可能是普遍的)解释:

  • << 开始第一行
  • 读取 i 的值
  • 将该值放在某个不会被覆盖的地方
    (可能是 CPU 寄存器,或者安全地放置在堆栈的某个地方,
    即为此活动保留的一个位置)
  • 使用堆栈将流式传输到输出
  • 这样做会覆盖变量
  • 但该值已经在别处并且是安全的
  • 成功输出它
  • 对于第二行再次执行所有操作,
    但这次值是被覆盖后的值

只是重复一下您自己和一些评论所说的内容:
在离开函数后使用堆栈是有问题的


2

这是一种未定义的行为,意味着任何事情都有可能发生。真正发生的情况取决于编译器的实现。


1
我认为即使func()执行完成,localnum的值仍将被保留且不会被销毁,直到任何进程使用内存空间(但设置一些位/标志,以便剩余进程将此内存块视为可用并可以使用)。- 如果我说错了,请纠正我。
由于这是未定义行为,你并不完全正确。某些编译器可能设置一个位/标志,而其他编译器可能将值设置为零...由于UB赋予每个编译器自由进行喜欢的操作,因此你的解释只是众多解释之一(尽管我认为这是最常见的解释)。
我的问题是,在这些打印之间我没有运行任何进程,但值仍在改变。那么这意味着我的系统中的任何其他进程都在使用localnum的内存区域吗?
如果你的进程没有这样做,并且值已更改,则是的,其他人正在更改该值。请注意,即使你没有直接更改该地址上的值,你的代码也可能以一种在你背后更改它的方式实现(例如,如Yunnosch所说的<<运算符)。

在这里,我返回了一个本地变量的引用(实际上我不应该这样做,但我这么做只是为了学习行为)。

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


0

由于 int localnum 的作用域仅限于 func 函数,因此您实际上正在返回对函数返回后不再可用的某些内容的引用。


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