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

Ang*_*ber 4 c++ stack function

鉴于下面的示例程序,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;
}
Run Code Online (Sandbox Code Playgroud)

Mik*_*our 6

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

如果您想了解所有血腥细节,维基百科是一个很好的起点.


Dav*_*eas 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;
}
Run Code Online (Sandbox Code Playgroud)

针对该特定情况的编译器将应用命名返回值优化,这将删除对象s并构造以代替返回的对象,从而避免复制:

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