通过复制返回局部变量 - 它是如何工作的

时间:2013-09-19 15:09:58

标签: c++ function stack

鉴于下面的示例程序,retlocal1可以工作,而retlocal2却没有。我知道关于不返回引用或指向局部变量的指针的规则,但我想知道它是如何工作的。

当retlocal1返回时,它将其值复制到EAX?但是EAX是一个有足够空间来容纳整数的寄存器吗?那么EAX如何保存std :: string的整个副本(当然可以是一个很长的字符串)。

引擎盖下一定有什么东西我不明白?

这个例子是C ++,但我认为C的工作方式完全相同?

#include <string>

std::string retlocal1() {
   std::string s;
   s.append(3, 'A');
   return s;
}

std::string& retlocal2() {
   std::string s;
   s.append(3, 'A');
   return s;
}

int main(int argc, char* argv[]){

   std::string d = retlocal1();
   std::string e = retlocal2();
   return 0;
}

3 个答案:

答案 0 :(得分:6)

调用约定将指定如何返回对于单个寄存器来说太大的值。可以在多个寄存器中返回小类型;通过将“隐藏”指针参数传递给函数来指定大型类型,指定返回值的放置位置。

如果您想了解所有血腥细节,Wikipedia是一个很好的起点。

答案 1 :(得分:2)

  

当retlocal1返回时,它将其值复制到EAX?但是EAX是一个有足够空间来容纳整数的寄存器吗?那么EAX如何保存std :: string的整个副本(当然可以是一个很长的字符串)。

这不正确。您应该检查您的平台的ABI,但最常见的方法是返回大(大于寄存器)对象的函数的调用约定将函数转换为一个函数,该函数接受返回对象的隐式指针。调用者为std::string分配空间,并将return语句转换为复制构造到该位置:

// Transformed function (with no NRVO)
void retlocal(std::string *ret) {
   std::string s; s.append(3, 'A');
   new (ret) std::string(s);
   return;
}

该特定案例的编译器将应用命名返回值优化,它将删除对象s并构造代替返回的对象,避免复制:

void retlocal(std::string *ret) {
   new (ret) std::string();
   ret->append(3,'A');
   return;
}

答案 2 :(得分:0)

  

引擎盖下一定有什么东西我不明白?

有。

retlocal2返回对本地对象的引用,这是未定义的行为(即,对象超出范围并被销毁,然后返回对调用代码的无效引用)。

retlocal1返回一个可移动的临时对象(一个r值引用)。

如果您想要更准确的答案(不确定您不理解的内容:),您需要提出更具体的问题。)。