5
(gdb) disas func
Dump of assembler code for function func:
0x00000000004004b8 <func+0>:    push   %rbp
0x00000000004004b9 <func+1>:    mov    %rsp,%rbp
0x00000000004004bc <func+4>:    movl   $0x64,0xfffffffffffffff0(%rbp)
0x00000000004004c3 <func+11>:   movb   $0x61,0xfffffffffffffff4(%rbp)
0x00000000004004c7 <func+15>:   mov    0xfffffffffffffff0(%rbp),%rax
0x00000000004004cb <func+19>:   leaveq
0x00000000004004cc <func+20>:   retq
End of assembler dump.


t_test func()
{
    t_test t;
    t.i = 100;
    t.c = 'a';
    return t;
}

ローカル変数を返しているようですtが、この種の仕事は動作することが保証されており、戻り時にローカル変数を参照しないようになっているのではないでしょうか??

4

5 に答える 5

6

私の経験では、C が構造体を返す標準的な方法はありません。構造体を渡すことができるようにするために、コンパイラは通常 (ユーザーには見えないように) 構造体へのポインターを渡します。これにより、関数はそこに内容をコピーできます。このポインターがどのように渡されるか (スタックの最初または最後) は、実装に依存します。32 ビット MSVC++ などの一部のコンパイラは、EAX や EDX などのレジスタに小さな構造体を返します。どうやら、GCC は 64 ビット モードの RAX でそのような構造体を返します。

しかし、繰り返しになりますが、これを行う標準的な方法はありません。関数を使用する残りのコードも同じコンパイラによってコンパイルされる場合は問題ありませんが、関数が DLL または lib のエクスポートされた関数である場合は問題になります。このような関数を別の言語 (Delphi) や別のコンパイラで C から使用しているときに、私はこれに何度か悩まされました。こちらのリンクもご覧ください。

于 2011-07-18T10:46:17.910 に答える
5

RAXは、構造全体を保持するのに十分な大きさです。0x00000000004004c7では、アドレスではなく、構造全体(movを使用)をロードしています(代わりにleaを使用します)。

x86-64 System V ABIの呼び出し規約は、RDX:RAXまたはRAXで最大16バイトのC構造体を返します。 x86-64上のC++:構造体/クラスはいつレジスタに渡されて返されますか?

より大きな構造体の場合、呼び出し元から渡される「非表示」の出力ポインタ引数があります。

于 2011-07-18T09:53:10.317 に答える
3

返される方法はまったく標準的ではありませんが、通常は RAX です。あなたの例では、t_test::i と t_test::c が t_test の唯一のメンバーであり、それぞれ最大で 32 ビットであると仮定すると、構造全体が 64 ビット レジスタに収まるため、RAX を介して直接値を返すだけです。 、通常、2 つのレジスタに収まるものは RAX:RDX (または RDX:RAX、一般的な順序は忘れました) で返されます。

3 つ以上のレジスタの場合、通常、最初のパラメーターとして渡される隠しポインター パラメーターが含まれます。これは、呼び出し元の関数 (通常は、戻り値が直接割り当てられるオブジェクト) 内のオブジェクトを指します。このオブジェクトは、呼び出された関数から戻る前に書き込まれ (通常は、呼び出された関数で使用されるローカル構造からコピーされます)、通常、渡されたものと同じポインターが RAX に返されます。

32 ビット x86 システムでは、EAX/EDX を RAX/RDX に置き換えることができます。

スタックで "this" ポインターを渡す規則 (標準の x86 GCC 規則など) では、戻り値ポインターは通常、最初のパラメーターではなく、2 番目の隠しパラメーターとして渡されます。

于 2011-07-18T11:30:09.010 に答える
0

スタック ポインターは関数の開始時に変更されないため、関数内での割り当てt_testは行われず、関数によって解放されません。これがどのように処理されるかは、使用される呼び出し規約によって異なります。関数がどのように呼び出されるかを見ると、それがどのように行われるかを簡単に理解できます。

于 2011-07-18T09:55:20.700 に答える