4

给定下面的示例程序,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;
}
4

3 回答 3

6

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

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

于 2013-09-19T15:13:51.550 回答
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;
}
于 2013-09-19T15:17:35.547 回答
0

一定有一些我不明白的事情发生在幕后?

有。

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

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

如果您想要更准确的答案(不确定您不明白什么:)),您需要提出更具体的问题。

于 2013-09-19T15:15:52.877 回答