我们应该使用C中的exit()吗?

有关于在C ++中使用exit 问题 。 答案讨论了主要是因为RAII不是好主意,例如,如果在代码中某处调用了exit ,那么将不会调用对象的析构函数,因此,如果例如析构函数意图将数据写入文件,则不会发生,因为析构函数没有被调用。

我感兴趣的是,这种情况在C中是怎样的?在C中也有类似的问题吗? 我认为,因为在C中,我们不使用构造函数/析构函数,情况可能会在C不同,所以在C中使用exit吗?

我已经看到下面的函数,我觉得在某些情况下可以使用它,但是如果我们在C中使用exit有类似的问题,就像上面用C ++描述的那样感兴趣? (这会使使用下面的function不是一个好主意)。

 void die(const char *message) { if(errno) { perror(message); } else { printf("ERROR: %s\n", message); } exit(1); } 

C中的exit()函数被认为是“优雅的”退出,而不是abort()

从C11(N1570)7.22.4.4/p2 退出函数 (重点矿):

exitfunction导致正常的程序终止发生。

该标准还在7.22.4.4 / p4中说:

接下来,所有具有未写入缓冲数据的开放stream将被刷新 ,所有打开的stream将被closures ,并且由tmpfile函数创build的所有文件都将被删除。

这也是值得一看的7.21.3 / p5 文件

如果main函数返回到原来的调用者,或者调用了exit函数, 那么在程序终止之前, 所有打开的文件都是closures的 (因此所有的输出stream都被刷新)。 程序终止的其他path,如调用abort函数,不需要正确closures所有文件。

但是,正如下面的评论中所提到的,你不能假设它会覆盖所有其他资源 ,所以你可能需要求助于atexit()并分别为它们的发布定义callback。 实际上正是atexit()所要做的,正如它在7.22.4.2/p2中所说的那样:atexit函数

atexit函数注册func指向的func ,在正常的程序终止时被调用而不带参数。

值得注意的是,C标准并没有准确地说明分配的存储时间对象(即malloc() )会发生什么情况,因此您需要了解在特定实现中如何完成这些操作。 对于现代的面向主机的操作系统,系统很可能会照顾它,但是你仍然可能想自己来处理这个问题,以便消除像Valgrind这样的内存debugging器。

是的,可以在C中使用exit

为了确保所有的缓冲区和顺利关机,build议使用这个函数atexit , 这里有更多的信息

示例代码将如下所示:

 void cleanup(void){ /* example of closing file pointer and free up memory */ if (fp) fclose(fp); if (ptr) free(ptr); } int main(int argc, char **argv){ /* ... */ atexit(cleanup); /* ... */ return 0; } 

现在,只要调用exit ,函数cleanup就会被执行,从而可以正常关机,清理缓冲区,内存等。

你没有构造函数和析构函数,但是你可以拥有资源 (例如文件,stream,套接字),正确closures它们是非常重要的。 一个缓冲区不能被同步写入,所以如果没有正确地closures资源,退出程序可能会导致损坏。

使用exit()是可以的

还没有提到代码devise的两个主要方面是“线程”和“库”。

在一个单线程程序中,在你正在编写的用于实现该程序的代码中,使用exit()很好。 当程序出现问题,代码不能恢复时,我的程序会经常使用它。

但…

但是,调用exit()是一个不能撤销的单方面行为。 这就是为什么“穿线”和“图书馆”都需要仔细考虑的原因。

螺纹程序

如果一个程序是multithreading的,那么使用exit()是一个戏剧性的行为,它会终止所有的线程。 退出整个程序可能是不合适的。 退出线程可能是合适的,报告错误。 如果你认识到这个计划的devise,那么也许这个单方面退出是允许的,但总的来说,这是不能接受的。

库代码

而且“程序devise的认识”也适用于图书馆的代码。 通用库函数调用exit()是非常less见的。 如果其中一个标准C库函数由于错误而无法返回,那么您会有理由感到不安。 (显然,像exit()quick_exit() _Exit()quick_exit()abort()这样的函数是不会返回的,这是不同的)。因此,C库中的函数或者“不能失败”,或者以某种方式返回错误指示。 如果您正在编写代码以进入通用库,则需要仔细考虑代码的error handling策略。 它应该适合于打算使用的程序的error handling策略,或者error handling可以被configuration。

我有一系列库函数(在头文件为"stderr.h"的包中,名字在薄冰上),当它们用于错误报告时,它们将被退出。 这些function通过devise退出。 在同一个软件包中有一系列相关的函数可以报告错误并且不会退出。 当然,退出function是以非退出function来实现的,但这是一个内部的实现细节。

我有很多其他库函数,其中很多依赖于"stderr.h"代码来进行错误报告。 这是我做的一个devise决定,是我可以做的。 但是,如果报告的错误与退出的函数一起使用,则会限制库代码的一般用处。 如果代码调用不返回的错误报告函数,则函数中的主代码path必须处理错误返回 – 检测它们并向调用代码中继错误指示。

避免在main()以外的函数中exit一个原因是你的代码可能会被取消掉。 请记住,退出是一种非本地控制stream 。 像不可捕捉的例外。

例如,您可能会编写一些存储pipe理function,在关键磁盘错误时退出。 然后有人决定把他们搬到图书馆。 从库中退出会导致调用程序以不可预知的状态退出。

或者你可以在embedded式系统上运行它。 无处可 ,整个事情在main()中的while(1)循环中运行。 它甚至可能不在标准库中定义。

根据你在做什么,退出可能是在C程序中最合乎逻辑的方式。我知道这是非常有用的检查,以确保callback链正常工作。 以最近使用的示例callback:

 unsigned char cbShowDataThenExit( unsigned char *data, unsigned short dataSz,unsigned char status) { printf("cbShowDataThenExit with status %X (dataSz %d)\n", status, dataSz); printf("status:%d\n",status); printArray(data,dataSz); cleanUp(); exit(0); } 

在主循环中,我为这个系统设置了一切,然后等待(1)循环。 可以使用全局标志来退出while循环,但是这很简单,并且可以做它需要做的事情。 如果您正在处理文件和设备等任何打开的缓冲区,则应该在closures之前清理它们以保持一致性。

任何代码都可以退出,除了coredump之外,在一个大项目中是非常糟糕的。 跟踪是非常重要的维护一个在线服务器。