在注意到 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
现在,我的组装知识有点生疏,但在我看来:
在第一种情况下,结构是“按值”传递的。即,它的内存内容被复制到
%edi
寄存器中,如果我没记错的话,它是第一个用于在x86-64
ABI 中传递参数的寄存器。相反,在第二种情况下,结构体是在堆栈上分配的,但
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] 对于这个问题,我删除了一些我认为不必要的行。