2

为什么这段代码可以在 Code::block 中成功运行。美洲开发银行只是报告

警告:“返回对局部变量 '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;
}
4

6 回答 6

2

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

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

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

于 2011-09-21T12:41:06.450 回答
1

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

于 2011-09-21T12:40:05.703 回答
1

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

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

string getString(const string &s)
{
...
于 2011-09-21T12:41:26.200 回答
0

正如您所看到的,下面的示例代码只是通过调用 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;
}
于 2011-09-21T17:53:45.187 回答
0

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

const string& garbage = getString("Hello World!");
callSomeFunctionThatUsesALotOfStackMemory();
cout<< garbage << endl;
于 2011-09-21T13:05:07.813 回答
0

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

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

于 2011-09-21T12:42:36.673 回答