3

C ++構造体の奇妙な振る舞いに気づいた後、同僚がこの質問で私を突いた。

この些細なコードを見てください:

struct S {
  int i;
#ifdef TEST
  ~S() {}
#endif
};

void foo (S s) {
  (void)s;
}

int main () {
  foo(S());
  return 0;
}

明示的なデストラクタなしでアセンブリコードを1回生成しました。

g++-4.7.2 destructor.cc -S -O0 -o destructor_no.s

そして後でそれを含める:

g++-4.7.2 destructor.cc -DTEST -S -O0 -o destructor_yes.s

mainこれはinのコード[1]ですdestructor_no.s

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    movl    %eax, %edi
    call    _Z3foo1S   // call to foo()
    movl    $0, %eax
    popq    %rbp
    ret

一方、代わりに、デストラクタが明示的に定義されている場合:

main:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movl    $0, -16(%rbp)
    leaq    -16(%rbp), %rax
    movq    %rax, %rdi
    call    _Z3foo1S   // call to foo()
    leaq    -16(%rbp), %rax
    movq    %rax, %rdi
    call    _ZN1SD1Ev  // call to S::~S()
    movl    $0, %eax
    leave
    ret

さて、私のアセンブリの知識は少し錆びていますが、私には次のように思われます:

  1. 最初のケースでは、構造体は「値で」渡されます。つまり、そのメモリの内容が%ediレジスタにコピーされます。これは、私が間違っていない限り、x86-64ABIで引数を渡すために使用される最初のレジスタです。

  2. 2番目のケースでは、代わりに、構造体がスタックに割り当てられますが、foo()関数は。のポインターを使用して呼び出されます%rdi

なぜそのような違いがあるのですか?


ノート:

  • gcc-4.6.3、、またはを使用しても同じ動作が確認されclang 3.1ます。

  • もちろん、最適化が有効になっている場合、関数の呼び出しfoo()はどのような場合でも完全に最適化されます。

  • structデストラクタが明示的に提供されていない場合、に変数を追加すると、興味深いパターンが現れます。

最大4int秒(= 16バイト)が引数レジスタを通過します。

pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp
movl    $0, -16(%rbp)
movl    $0, -12(%rbp)
movl    $0, -8(%rbp)
movl    $0, -4(%rbp)
movq    -16(%rbp), %rdx
movq    -8(%rbp), %rax
movq    %rdx, %rdi
movq    %rax, %rsi
call    _Z3foo1S

しかし、構造体に5番目を追加するとすぐに、int「値で」渡されたままの関数の引数がスタックに追加されます。

pushq   %rbp
movq    %rsp, %rbp
subq    $56, %rsp
movl    $0, -32(%rbp)
movl    $0, -28(%rbp)
movl    $0, -24(%rbp)
movl    $0, -20(%rbp)
movl    $0, -16(%rbp)
movq    -32(%rbp), %rax
movq    %rax, (%rsp)
movq    -24(%rbp), %rax
movq    %rax, 8(%rsp)
movl    -16(%rbp), %eax
movl    %eax, 16(%rsp)
call    _Z3foo1S

[1]この質問の目的のために不要だと思う行をいくつか削除しました。

4

1 に答える 1

3

C ++ 03では、デストラクタを定義すると、構造体はPODタイプではなくなります。デストラクタのないバリアントのオブジェクトはC構造体変数のように動作します(したがって、値によって渡されるだけです)が、ユーザー定義のオブジェクトはC++オブジェクトのように動作します。

于 2012-10-06T20:18:25.660 に答える