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

呼び出し規約は、1 つのレジスターには大きすぎる値を返す方法を指定します。小さい型は複数のレジスタで返される場合があります。「隠し」ポインター引数を関数に渡し、戻り値を配置する場所を指定することにより、大きな型。

詳細をすべて知りたい場合は、ウィキペディアが出発点として適しています。

于 2013-09-19T15:13:51.550 に答える
2

retlocal1 が戻ると、その値を EAX にコピーしますか? しかし、EAXは整数を保持するのに十分なスペースを持つレジスタですか? では、EAX は std::string のコピー全体をどのように保持しますか (これはもちろん長い長い文字列である可能性があります)。

これは正しくありません。プラットフォームの ABI を確認する必要がありますが、最も一般的なアプローチは、大きな (レジスタよりも大きい) オブジェクトを返す関数の呼び出し規約が、関数を、返されたオブジェクトへの暗黙的なポインターを取る関数に変換することです。呼び出し元が にスペースを割り当て、std::stringreturn ステートメントがその場所へのコピー構築に変換されます。

// 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移動可能な一時オブジェクト (右辺値参照) を返します。

より正確な回答が必要な場合は、より具体的な質問をする必要があります(理解できないことはわかりません:))。

于 2013-09-19T15:15:52.877 に答える