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

时间:2015-07-19 12:21:59

标签: c

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

我感兴趣的是C中的这种情况。类似的问题是否也适用于C?我想在C中我们不使用构造函数/析构函数,C中的情况可能不同。那么在C中使用exit是否可以?

我见过下面的函数,在某些情况下我觉得很好用,但是如果我们在使用exit的C中遇到类似的问题感兴趣,如上面用C ++所述? (这将使使用下面的功能不是一个好主意。)。

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

    exit(1);
}

7 个答案:

答案 0 :(得分:82)

而不是abort(),C中的exit()函数被认为是一个优雅的"退出。

来自C11(N1570)7.22.4.4/p2 退出功能(强调我的):

  

exit功能会导致正常程序终止。

标准还在7.22.4.4/p4中说:

  

接下来,所有打开的未写入缓冲数据的开放流都会被刷新   开放流已关闭,以及tmpfile功能创建的所有文件   被删除。

还值得一看7.21.3 / p5 Files

  

如果main函数返回其原始调用方,或者exit   函数被调用,所有打开的文件都被关闭(因此所有输出   在程序终止之前刷新流。其他途径   程序终止,例如调用abort函数,不需要   正确关闭所有文件。

但是,正如下面的评论中所述,您无法假设它将涵盖所有其他资源,因此您可能需要诉诸atexit()并为其发布定义回调个别。实际上它正是atexit()的意图,正如7.22.4.2/p2 atexit函数中所述:

  

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

值得注意的是,C标准没有准确说明分配的存储持续时间(即malloc())对象应该发生什么,因此要求您了解如何在特定实现上完成。对于面向主机的现代操作系统,系统很可能会处理它,但您仍可能需要自己处理这个问题,以便使Valgrind等内存调试器静音。

答案 1 :(得分:22)

是的,可以在C中使用exit

为了确保所有缓冲区和正常有序关闭,建议使用此函数atexit,有关此here的更多信息

示例代码如下:

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将被执行,这可以保持正常关闭,清理缓冲区,内存等。

答案 2 :(得分:14)

您没有构造函数和析构函数,但您可以拥有资源(例如文件,流,套接字),并且正确关闭它们非常重要。无法同步写入缓冲区,因此在未正确关闭资源的情况下退出程序可能会导致损坏。

答案 3 :(得分:9)

使用exit()可以

尚未提及的代码设计的两个主要方面是“线程”和“库”。

在单线程程序中,在您编写的代码中实现该程序,使用exit()就可以了。当程序出错并且代码无法恢复时,我的程序会定期使用它。

可是...

但是,调用exit()是一项无法撤消的单方面行动。这就是'线程'和'图书馆'需要仔细考虑的原因。

线程程序

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

图书馆代码

并且“认识到程序设计”条款也适用于库中的代码。通用库函数调用exit()很少是正确的。如果其中一个标准C库函数由于错误而无法返回,那么你有理由感到不安。 (显然,exit()_Exit()quick_exit()abort()等函数不会返回;这是不同的。)因此,C库中的函数“不能”失败“或以某种方式返回错误指示。如果您正在编写代码以进入通用库,则需要仔细考虑代码的错误处理策略。它应该适合与其一起使用的程序的错误处理策略,或者可以使错误处理可配置。

我有一系列库函数(在包含标题为"stderr.h"的程序包中,这个名称位于薄冰上),它们在用于错误报告时会退出。这些功能按设计退出。在同一个包中有一系列相关的函数报告错误而不退出。当然,现有功能是根据非退出功能实现的,但这是一个内部实现细节。

我有许多其他库函数,其中很多都依赖"stderr.h"代码进行错误报告。这是我做出的一个设计决定,也是我很满意的。但是当使用退出的函数报告错误时,它会限制库代码的一般用途。如果代码调用了不退出的错误报告函数,那么函数中的主代码路径必须明智地处理错误返回 - 检测它们并将错误指示转发给调用代码。

我的错误报告包的代码在GitHub上的SOQ(堆栈溢出问题)存储库中可用作src/libsoq子目录中的文件stderr.cstderr.h

答案 4 :(得分:6)

exit以外的函数中避免main()的一个原因是您的代码可能会脱离上下文。请记住,退出是一种非本地控制流。像无法捕获的例外。

例如,您可能会编写一些在关键磁盘错误时退出的存储管理功能。然后有人决定将它们移到图书馆。退出库会导致调用程序以不一致的状态退出,而该状态可能没有准备好。

或者您可以在嵌入式系统上运行它。没有地方可以退出,整个事情都在while(1)的{​​{1}}循环中运行。它甚至可能没有在标准库中定义。

答案 5 :(得分:2)

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

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循环,但这很简单,并且可以做它需要做的事情。如果您正在处理任何打开的缓冲区,如文件和设备,您应该在关闭之前清理它们以保持一致性。

答案 6 :(得分:-1)

在一个大项目中,除了coredump之外,任何代码都可以退出,这很糟糕。 Trace对于维护在线服务器非常重要。