3

在注意到 C++ 结构的奇怪行为后,一位同事向我提出了这个问题。

拿这个简单的代码:

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

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

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

我在没有显式析构函数的情况下生成了一次汇编代码:

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. 相反,在第二种情况下,结构体是在堆栈上分配的,但foo()函数是使用 in 中的指针调用的%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

但是一旦我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 回答