exit()和abort()有什么区别?

150
在C和C++中,exit()abort()有什么区别?我想在程序发生错误时(不是异常情况)结束程序。
5个回答

134

abort()函数会在退出程序时不调用使用atexit()注册的函数和对象析构函数,而exit()函数在退出程序前会先调用它们。但是,exit()函数不会调用自动变量的析构函数。

A a;
void test() { 
    static A b;
    A c;
    exit(0);
}

会正常销毁 ab,但不会调用 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);


2
+1,因为虽然Brian R. Bondy很好,但你提出了中止/退出问题(未调用堆栈对象的析构函数),并提供了一个RAII密集型C++进程的替代方案。 - paercebal
我正在寻找一种退出程序而不调用 dtor(析构函数)的方法,你的答案正是我要找的!谢谢 - acemtp
当然,如果你的自动对象析构函数不被调用真的很重要的话,那就完全正确了 :-) - Chris Huang-Leaver
3
据我所知,exit和abort之间的另一个区别是,根据操作系统的配置,abort可能会导致生成核心转储。 - Dirk Herrmann

36

abort 发送 SIGABRT 信号,exit 则只是正常关闭应用程序。

你可以按照自己的需求处理一个 abort 信号,但默认行为是使用错误代码关闭应用程序。

abort 不会执行静态和全局成员的对象析构,但 exit 会执行。

当应用程序完全关闭时,操作系统将释放任何未释放的内存和其他资源。

abortexit 中,程序终止时(假设您没有覆盖默认行为),返回代码将返回给启动应用程序的父进程。

请参见以下示例:

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的析构函数将被调用,您将得到以下输出:

退出函数2
退出函数1


这里称之为退出函数2,然后退出函数1。gcc 4,Linux 2.6。 - strager
1
atexit 的 man 页面说:“使用 atexit 注册的函数按相反的顺序调用;不传递任何参数。” - strager
@strager 是正确的,由 atexit 注册的函数应当在 exit 调用或 main 返回时按相反顺序被调用。 - Robert Gamble
运行了一个测试,结果显示全局实例的析构函数是在所有atexit回调之后被调用的。 - strager
+1 提醒人们,即使调用 abort() 函数,操作系统最终也会释放所有分配的资源。 - Fingolfin
为什么都要加粗?在我看来,最好组织成一个表格。 - Matthew Woo

12
程序调用exit()函数时会发生以下事情:
  • atexit函数注册的函数将被执行
  • 所有打开的流都会被刷新并关闭,由tmpfile创建的文件将被删除
  • 程序将以指定的退出码终止
abort()函数向当前进程发送SIGABRT信号,如果未被捕获,则程序将终止,不保证打开的流被刷新/关闭或通过tmpfile创建的临时文件被删除,不会调用atexit注册的函数,同时向主机返回非零的退出状态。

标准规定,如果信号处理程序“不返回”,则程序仅在未终止状态下运行。你对C很熟悉,能想象出任何让它在不返回的情况下继续正常执行的场景吗?我想到了longjmp,但我不确定它在信号处理程序中的行为如何。 - Johannes Schaub - litb
1
这似乎可行。 #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <setjmp.h>volatile sig_atomic_t do_abort = 1; jmp_buf env; void abort_handler(int i) { do_abort = 0; longjmp(env, 1);} - Robert Gamble
在Ubuntu 7.04上,这将打印:在setjmp处 调用abort 在setjmp处 没有中止! - Robert Gamble
我觉得流刷新和关闭是最重要的区别。我不在乎我的对象是由C ++ dtors还是操作系统释放,但在IO方面这可能是一个很大的区别。 - Quantum7
你可以在SIGABRT的信号处理程序中调用_exit - ceving
显示剩余3条评论

7

abort 发送 SIGABRT 信号。 abort 不会返回给调用者。 SIGABRT 信号的默认处理程序关闭应用程序。 stdio 文件流被刷新,然后关闭。但是,C++ 类实例的析构函数不会被调用(对此不确定结果是什么?)。

exit 有自己的回调函数,使用 atexit 设置。 如果指定了回调函数(或只有一个),它们以相反的注册顺序(像堆栈一样)调用,然后程序退出。 与 abort 一样,exit 不会返回给调用者。 stdio 文件流被刷新,然后关闭。此外,将调用 C++ 类实例的析构函数。


通过 atexit,退出可以注册多个回调函数,当退出被调用时,所有回调函数将按它们注册的相反顺序被调用。 - Robert Gamble
@Gamble,当然,几分钟前我在对@Bondy的答案的评论中提到过。我会编辑我的回答以反映这一点。 - strager

6

从exit()手册页面:

exit()函数导致正常进程终止,并将status & 0377的值返回给父进程。

从abort()手册页面:

abort()首先取消阻塞 SIGABRT 信号,然后为调用进程引发该信号。这会导致进程异常终止,除非捕获SIGABRT信号并且信号处理程序不返回。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接