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

gio*_*gim 78 c

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

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

我已经看到了下面的函数,我觉得在某些情况下可以使用它,但是如果我们在C中使用类似的问题感兴趣exit,如上面用C++所述?(这将使使用下面的功能不是一个好主意.).

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

    exit(1);
}
Run Code Online (Sandbox Code Playgroud)

Grz*_*ski 81

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

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

exit功能导致正常程序终止.

该标准还在7.22.4.4/p4中说:

接下来,刷新所有带有未写入缓冲数据的打开流,关闭所有打开的流,并tmpfile删除该功能创建的所有文件.

它还值得一看7.21.3/p5 文件:

如果main函数返回其原始调用者,或者调用该exit 函数,则在程序终止之前关闭所有打开的文件(因此刷新所有输出流).程序终止的其他路径(例如调用abort函数)无需正确关闭所有文件.

但是,如下面的评论中所述,您不能假设它将涵盖所有其他资源,因此您可能需要atexit()单独使用并定义其发布的回调.实际上它正是atexit()打算做的,正如7.22.4.2/p2中所说的atexit函数:

atexit函数注册指向的函数func,在正常程序终止时无参数调用.

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

  • 使用"良好实践"编写"正确"的软件就是为什么我有这么多应用程序在被要求时不会立即关闭的原因.:(如果操作系统可以做某事,你应该让它做,而不是用用户代码搞砸尝试做已经存在的事情,已经过测试并且已经过调试.如果你的软件系统无法承受突然的,意外的关机,(例如,从一些线程要求立即进行中的终止,任务管理器'结束进程','杀死-9'或电源故障),无论如何质量都很差. (5认同)
  • 这在我看来是正确的答案.套接字连接在退出时关闭,因为进程结束关闭所有文件描述符,无论是否用`stdio`打开,即相当于FD上的`close()`.我不知道@BlueMoon在什么意义上认为这是不正确的,但它并不比调用`close()`更不正确,如果需要进一步清理,那正是`atexit()`的用途. (3认同)
  • @black如果需要进一步清除,可以使用`atexit()`.使用`exit()`本身并不比从`main()`执行`return`更残酷. (3认同)

t0m*_*13b 22

是的,可以exit在C中使用.

为确保所有缓冲区和正常有序关闭,建议使用此功能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;
}
Run Code Online (Sandbox Code Playgroud)

现在,每当exit调用时,该函数cleanup将被执行,这可以容纳正常关闭,清理缓冲区,内存等.


enr*_*cis 14

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

  • 我认为这是不正确的.如果调用`exit()`(而不是`_exit()`),则调用`atexit`例程并将`stdio`缓冲刷新到磁盘.`exit()`正好允许程序有序退出. (10认同)
  • 这个答案是"不学习编程语言;学习如何编程"的一个很好的例子.您通常不能通过选择其他语言来避免理解问题. (8认同)
  • @abligh实时系统不会在exit()上释放malloc内存,例如,导致泄漏.POSIX共享内存是另一个例子.所以它取决于环境而不是严格遵循C标准. (2认同)
  • @abligh:并非所有资源都是标准库.虽然标准库提供某些保证肯定是正确的,但您仍需要考虑程序的全局控制流程及其各部分的*职责*,并确保适当处理每个待处理的职责.术语"资源"是这个概念的简明术语,超越了任何特定的库,处理资源最终是程序员的责任.像'atexit`这样的设施可以提供帮助,尽管它们并不总是合适的. (2认同)
  • @abligh:如果你愿意,你*当然可以使用`exit`,但它是一个全局性的控制跳跃,伴随着我们讨论过的非结构化控制的所有缺点.作为一种从故障状态终止的方法,它可能是合适的(并且它似乎是OP想要的),但是对于正常的控制流程,我可能更喜欢设计具有适当退出条件的主循环,以便您确实最终从主要回来. (2认同)

Jon*_*ler 10

使用exit()还可以

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

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

但…

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

线程程序

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

图书馆代码

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

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

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


我的错误报告包中的代码是在我使用SOQ(堆栈溢出问题)库在GitHub上的文件stderr.c,并stderr.hSRC/libsoq子目录.


pjc*_*c50 6

避免exit使用其他函数的一个原因main()是您的代码可能会脱离上下文.请记住,exit是一种非本地控制流.像无法捕捉的例外.

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

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

  • 一个相当粗鲁的比喻.C充满了*可以*可以完成的事情,但应该避免.因此,IOCCC的存在.请注意,我的帖子没有说"不应该". (3认同)
  • 人们不应该被允许拥有厨房刀具,因为有些人可能会抓住他们的一把并尝试玩弄它们,或者与他的孩子玩"抓住".显然,我不同意.如果有人决定将我的代码复制到他的程序中,并且我的代码结果不符合他的目的,那就是他的问题,因为他没有阅读他同化的代码. (2认同)