在main函数中,return和exit经常混用,两者的一个区别:return会执行statck unwinding,而exit不会。如果触发了信号,exit也同样不会做stack unwinding,除此之外异常如果没有相应的catch,也同样不会有栈展开(stack unwinding)。
原因是C++编译器只会在遇到“}”或“return”时,才会安插栈展开代码,对于exit等则没这回事。
#include
#include
#include
class X
{
public:
X(int m): _m(m) { printf("X::ctor:%d\n", m); }
~X() { printf("X::dtor:%d\n", _m); }
private:
int _m;
};
int main()
{
X x(1);
#if USE_EXIT
exit(0);
#if USE_RAISE
raise(SIGSEGV);
#else
return 0;
#endif
}
以上述代码为例,通过汇编,可很容易看出这两者的区别:
1) return代码
int main()
{
X x(1);
return(0);
}
反汇编main函数,可以看到有调用~X:
0x08048474 <main+0>: lea 0x4(%esp),%ecx
0x08048478 : and $0xfffffff0,%esp
0x0804847b : pushl 0xfffffffc(%ecx)
0x0804847e : push %ebp
0x0804847f : mov %esp,%ebp
0x08048481 : push %ebx
0x08048482 : push %ecx
0x08048483 : sub $0x20,%esp
0x08048486 : movl $0x1,0x4(%esp)
0x0804848e : lea 0xfffffff4(%ebp),%eax
0x08048491 : mov %eax,(%esp)
0x08048494 : call 0x80484b6
0x08048499 : mov $0x0,%ebx
0x0804849e : lea 0xfffffff4(%ebp),%eax
0x080484a1 : mov %eax,(%esp)
0x080484a4 : call 0x80484da <~X>
0x080484a9 : mov %ebx,%eax
0x080484ab : add $0x20,%esp
0x080484ae : pop %ecx
0x080484af : pop %ebx
0x080484b0 : pop %ebp
0x080484b1 : lea 0xfffffffc(%ecx),%esp
0x080484b4 : ret
2) exit代码
int main()
{
X x(1);
exit(0);
}
反汇编main函数,可以看到没有调用~X:
0x080484a4 <main+0>: lea 0x4(%esp),%ecx
0x080484a8 : and $0xfffffff0,%esp
0x080484ab : pushl 0xfffffffc(%ecx)
0x080484ae : push %ebp
0x080484af : mov %esp,%ebp
0x080484b1 : push %ecx
0x080484b2 : sub $0x24,%esp
0x080484b5 : movl $0x1,0x4(%esp)
0x080484bd : lea 0xfffffff8(%ebp),%eax
0x080484c0 : mov %eax,(%esp)
0x080484c3 : call 0x80484d4
0x080484c8 : movl $0x0,(%esp)
0x080484cf : call 0x80483c8 <exit@plt>
附:汇编指令
call指令 | 分两步: 1) 将当前的IP或CS和IP压入栈中 2) 转移(能实现短转移,它的书写格式同jmp指令) |
---|---|
ret指令 | 相当于pop IP |
retf指令 | 相当于: 1) pop IP 2) pop CS |
lea指令 | 把操作数OPRD的偏移地址传送到寄存器REG,语法:LEA REG, OPRD |