鉴于下面的示例程序,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;
}
答案 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值引用)。
如果您想要更准确的答案(不确定您不理解的内容:),您需要提出更具体的问题。)。