【问题标题】:Returning reference to a local variable返回对局部变量的引用
【发布时间】:2024-01-09 22:07:02
【问题描述】:

为什么这段代码可以在 Code::block 中成功运行。 IDB 只是报告

警告:“返回对局部变量‘tmp’的引用”,

但输出结果“hello world”成功。

#include <iostream>
#include<string>
using namespace std;

const string &getString(const string &s)
{
    string tmp = s;
    return tmp;
}

int main()
{
    string a;
    cout<<getString("hello world")<<endl;
    return 0;
}

【问题讨论】:

标签: c++ reference compiler-warnings local-variables


【解决方案1】:

离开函数后,所有局部变量都会被销毁。通过返回对tmp 的引用,您将返回对很快不复存在的对象的引用(也就是说,从技术上讲,它的内容不再有意义的内存区域的地址)。

访问这样一个悬空引用会调用所谓的“未定义行为”——可悲的是,“照常工作”是一种“未定义行为”。这里可能发生的是std::string为小字符串保留一个小的静态缓冲区(与大字符串相反,它从堆中获取内存),并且在离开getString时,该字符串占用的堆栈空间不会归零所以它似乎仍然有效。

如果您尝试调试构建,或在两者之间调用另一个函数(这将有效地覆盖堆栈空间),它将不再工作。

【讨论】:

  • 易于添加 不要这样做 并且不要依赖编译器在某些情况下会返回有效字符串,您可能会发现它有效,但没有保证下次可以正常使用。
【解决方案2】:

您正在导致未定义的行为。该标准没有说明在这种情况下会发生什么,但是您的编译器检测到了它。

【讨论】:

    【解决方案3】:

    tmp 在您从getString 返回的那一刻消失。使用返回的引用是未定义的行为,所以任何事情都可能发生。

    要修复您的代码,请按值返回字符串:

    string getString(const string &s)
    {
    ...
    

    【讨论】:

      【解决方案4】:

      你确定吗?它应该是段错误(它会在大多数平台上使用 gcc)。该代码确实包含一个错误,如果你“幸运”并且它可以工作,那么你就是在把一个讨厌的错误刷到地毯下。

      您的字符串是通过引用返回的,也就是说,不是创建一个在您返回的上下文中有效的新字符串,而是返回一个指向过时的、已破坏的对象的指针,这很糟糕。 const string getString... 将作为函数返回类型的声明。

      【讨论】:

        【解决方案5】:

        临时对象被释放,但是它的内容仍然在堆栈上,直到有东西重写它。在调用你的函数和打印出返回的对象之间尝试调用一些函数:

        const string& garbage = getString("Hello World!");
        callSomeFunctionThatUsesALotOfStackMemory();
        cout<< garbage << endl;
        

        【讨论】:

          【解决方案6】:

          如您所见,下面的示例代码只是通过调用 goodByeString() 稍作修改。就像其他答案已经指出 getString 中名为 tmp 的变量是本地的一样。一旦函数返回,变量就会超出范围。因为它是堆栈分配的,所以当函数返回时内存仍然有效,但是一旦堆栈再次增长,tmp 所在的这部分内存就会被其他东西重写。然后对 a 的引用包含垃圾。

          但是,如果您决定输出 b,因为它不是通过引用返回的,那么内容仍然有效。

          #include <iostream>
          #include<string>
          using namespace std;
          
          const string &getString(const string &s)
          {
            string tmp = s;
            return tmp;
          }
          
          string goodByeString(const string &s)
          {
            string tmp = "lala";
            tmp += s;
            return tmp;
          }
          
          int main()
          {
             const string &a = getString("Hello World!\n");
             string b = goodByeString("ciao\n");
             cout << a << endl;
             return 0;
          }
          

          【讨论】: