【发布时间】:2010-09-28 15:58:10
【问题描述】:
在 C 和 C++ 中,exit() 和 abort() 有什么区别?我正在尝试在出现错误(不是异常)后结束我的程序。
【问题讨论】:
标签: c++ c error-handling exit abort
在 C 和 C++ 中,exit() 和 abort() 有什么区别?我正在尝试在出现错误(不是异常)后结束我的程序。
【问题讨论】:
标签: c++ c error-handling exit abort
abort 发送一个 SIGABRT 信号,exit 只是关闭执行正常清理的应用程序。
您可以随心所欲地处理 abort 信号,但默认行为是关闭应用程序以及错误代码。
abort 不会执行静态和全局成员的对象销毁,但 exit 会。
当然,当应用程序完全关闭时,操作系统会释放所有未释放的内存和其他资源。
在 abort 和 exit 程序终止中(假设您没有覆盖默认行为),返回码将返回到启动您的应用程序的父进程.
看下面的例子:
SomeClassType someobject;
void myProgramIsTerminating1(void)
{
cout<<"exit function 1"<<endl;
}
void myProgramIsTerminating2(void)
{
cout<<"exit function 2"<<endl;
}
int main(int argc, char**argv)
{
atexit (myProgramIsTerminating1);
atexit (myProgramIsTerminating2);
//abort();
return 0;
}
评论:
如果未注释 abort:不打印任何内容,并且不会调用 someobject 的析构函数。
如果 abort 像上面这样注释:someobject destructor will be called 您将得到以下输出:
退出函数2
退出函数1
【讨论】:
abort() 退出程序而不首先调用使用atexit() 注册的函数,也没有首先调用对象的析构函数。 exit() 在退出你的程序之前两者都做。但它不会为自动对象调用析构函数。所以
A a;
void test() {
static A b;
A c;
exit(0);
}
将正确地破坏a 和b,但不会调用c 的析构函数。 abort() 不会调用这两个对象的析构函数。不幸的是,C++ 标准描述了另一种确保正确终止的机制:
具有自动存储期限的对象都在一个程序中销毁,该程序的函数
main()不包含自动对象并执行对exit()的调用。通过抛出在main()中捕获的异常,可以将控制直接转移到这样的main()。
struct exit_exception {
int c;
exit_exception(int c):c(c) { }
};
int main() {
try {
// put all code in here
} catch(exit_exception& e) {
exit(e.c);
}
}
不要调用exit(),而是安排代码throw exit_exception(exit_code);。
【讨论】:
abort 发送SIGABRT 信号。 abort 不会返回给调用者。 SIGABRT 信号的默认处理程序关闭应用程序。 stdio 文件流被刷新,然后关闭。但是,C++ 类实例的析构函数不是(不确定这一点——也许结果未定义?)。
exit 有自己的回调,用atexit 设置。如果指定了回调(或仅指定了一个),则按照与注册顺序相反的顺序调用它们(如堆栈),然后程序退出。与abort 一样,exit 不会返回给调用者。 stdio 文件流被刷新,然后关闭。此外,还会调用 C++ 类实例的析构函数。
【讨论】:
来自 exit() 手册页:
exit() 函数导致正常的进程终止,并且 status & 0377 返回给父级。
来自 abort() 手册页:
abort() 首先解除对 SIGABRT 信号的阻塞,然后引发该信号 调用进程的信号。这会导致进程异常终止,除非捕捉到 SIGABRT 信号并且该信号 处理程序不返回。
【讨论】:
当程序调用exit()时会发生以下情况:
atexit 函数注册的函数被执行tmpfile 创建的文件被删除abort() 函数将SIGABRT 信号发送到当前进程,如果它没有被捕获,程序将终止,但不能保证打开的流被刷新/关闭,或者通过tmpfile 创建的临时文件是移除后,atexit 注册的函数不会被调用,并且返回一个非零的退出状态给主机。
【讨论】: