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


80

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

我对C中的这种情况很感兴趣。类似的问题也适用于C吗?我以为既然在C语言中我们不使用构造函数/析构函数,那么exitC语言中的情况可能会有所不同。那么在C语言中使用可以吗?

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


2
“将不会调用对象的析构函数”-并不完全正确(请参阅:cplusplus.com/reference/cstdlib/exit)。您正在考虑quick_exit(请参阅:cplusplus.com/reference/cstdlib/quick_exit/?kw=quick_exit)。
编码员

您可能还会遇到一些特定于操作系统的问题,例如POSIX和Linux上的常规SIGTERM信号作用。.表现良好的服务器应能很好地处理它。并且您应该避免使用SIGKILL(即尝试kill -TERM然后kill -QUIT仅稍后再kill -KILL作为系统管理员使用)
Basile Starynkevitch 2015年

20
在Stack Overflow启动近7年后,这又不是重复吗?
彼得·莫滕森

7
@PeterMortensen:这令人惊讶的,但我不知道一个很好的选择,此副本的。投票功能的使用exit()不是紧迫的(投票率很高是令人惊讶的-这不是一个好问题)。为什么不应该在C语言中使用exit()函数?如果它能回答这个问题的能力,将是一个很好的候选人。没有 将其反向关闭是合适的,因为我已经做到了。
Jonathan Leffler

我不确定您是否要在错误的情况下使用'printf'。该函数使用缓冲区,但是如果内存损坏,则这些缓冲区可能不好用。您可能只想使用'write(2,“ ERROR:”,7); write(2,message,strlen(message));
Alex

Answers:


82

而不是C中abort()exit()函数被认为是“优美的”出口。

从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.2.4/p2中所说的,这正是要执行的操作atexit函数

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

值得注意的是,C标准并没有确切说明分配存储持续时间(即malloc())的对象应该发生什么,因此要求您知道如何在特定实现上完成。对于现代的,面向主机的OS,系统可能会照顾它,但是仍然可能需要您自己处理,以使诸如Valgrind之类的内存调试器静音。


3
在我看来,这是正确的答案。套接字连接在退出时会关闭,因为无论进程是否结束,进程结束都会关闭所有文件描述符stdio,即close()与FD中的a等效。我不知道@BlueMoon在什么意义上认为这是不正确的,但是它至少和对的调用一样不正确close(),并且如果需要进一步清除,那就恰恰是什么atexit()目的。
2015年

1
@abligh现代操作系统将关闭与该进程相关的所有fds;但这是残酷的,不是标准的(蓝月亮在说什么)。
edmz 2015年

3
如果需要进一步清除,atexit()可以使用@black 。使用exit()自身并不比return从中残酷main()
2015年

5
使用“良好实践”编写“适当”软件的原因是为什么我有这么多应用程序在被问到时并不会立即关闭。:(如果操作系统可以执行某些操作,则应让它执行该操作,而不是费力地使用用户代码如果您的软件系统无法承受突然的意外关闭(例如,由于某个线程要求立即终止进程,任务管理器“终止进程”,“杀死”),请尝试执行已经存在,已经过测试和调试的操作。 -9'或电源故障),它是质量差无妨。
马丁詹姆斯

如果您可以在回答中添加-如果可能的话,那将是很好的,可能需要释放哪种资源atexit,否则就可以了。@BlueMoon:我不认为使用诸如函数之类的功能die无法编程,在某些情况下可能不希望使用它。否则的确,你也可以处理这种只用返回值,等等
gmoniava

23

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

为了确保所有缓冲区和正常顺序关闭,建议使用此功能atexit,有关更多信息,请参见此处。

示例代码如下所示:

现在,无论何时exit调用,该函数cleanup都会执行,可以容纳正常关机,清理缓冲区,内存等。


@IlmariKaronen不会以任何方式自动处理分配的内存。操作系统可能会确保将内存回收回操作系统。Grzegorz Szpetkowski的论点与您的说法背道而驰:值得注意的是,C标准并未确切说明已分配存储持续时间的对象(iemalloc())会发生什么,因此要求您了解如何在特定实现上完成。对于现代的,面向主机的OS,系统可能会对其进行处理,但是仍然可能需要您自己处理,以使诸如Valgrind之类的内存调试器静音。
这个

15

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


8
这个答案是“不学习编程语言;学习如何编程”的一个很好的例子。通常,您无法避免通过选择其他语言来理解问题。
Kerrek SB 2015年

10
我相信这是不正确的。如果您调用exit()(而不是_exit()),atexit则会调用例程并将stdio缓冲刷新到磁盘。exit()正是为了使程序有序退出。
2015年

2
@abligh实时系统不会在exit()上释放已分配的内存,从而导致泄漏。POSIX共享内存是另一个示例。因此,它取决于环境,而不是严格遵循C标准。
PP

2
@abligh:并非所有资源都提供标准库。尽管您肯定标准库可以提供某些保证,但是您仍然需要考虑程序的全局控制流程及其各个部分的职责,并确保适当地处理每个未决的职责。术语“资源”是此通用概念的简洁术语,它超越任何特定的库,并且处理资源最终是程序员的责任。诸如此类的设施可能atexit会有所帮助,尽管它们并不总是合适的。
Kerrek SB 2015年

2
@abligh:当然可以使用exit,但是这是控件的全局跳跃,伴随我们讨论了非结构化控件的所有缺点。作为一种从失败状态终止的方法,它可能是合适的(这似乎是OP想要的),尽管对于正常的控制流程,我可能更喜欢设计具有适当退出条件的主循环,这样您实际上就可以结束了从主要返回。
Kerrek SB 2015年

10

使用exit()就可以了

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

在单线程程序中,在您编写的用于实现该程序的代码中,可以使用exit()。当出现问题并且代码无法恢复时,我的程序将按常规使用它。

但…

但是,呼叫exit()是无法撤消的单方面行动。这就是为什么“线程”和“库”都需要仔细考虑的原因。

线程程序

如果程序是多线程的,则使用exit()会终止所有线程。退出整个程序可能是不合适的。退出线程,报告错误可能是适当的。如果您对程序的设计有所了解,那么也许可以单方面退出,但总的来说,这是不可接受的。

图书馆代码

“了解程序设计”子句也适用于库中的代码。调用通用库函数很少正确exit()。如果标准C库函数之一仅由于错误而未能返回,您将感到不安。(很明显,功能等exit()_Exit()quick_exit()abort()意图不返回;这是不同的。)在C库中的功能因此不是“不能失败”或以某种方式返回一个错误指示。如果要编写代码以进入通用库,则需要仔细考虑代码的错误处理策略。它应符合打算与其一起使用的程序的错误处理策略,或者可以使错误处理可配置。

我有一系列的库函数(在带有header的包中"stderr.h",一个名字如履薄冰)将在用于错误报告时退出。这些功能是设计使然退出的。同一软件包中有一系列相关的功能,它们会报告错误并且不会退出。当然,出口函数是根据非出口函数实现的,但这是内部实现细节。

我还有许多其他库函数,其中许多函数依赖于"stderr.h"代码来报告错误。这是我做出的设计决定,也是我可以接受的决定。但是,当用退出的函数报告错误时,它限制了库代码的通用性。如果代码调用了不退出的错误报告功能,则该功能中的主要代码路径必须明智地处理错误返回-检测到它们并将错误指示中继给调用代码。


我的错误报告包的代码可在GitHub上的SOQ(堆栈溢出问题)存储库中以文件形式stderr.c以及stderr.hsrc / libsoq子目录中获得。


这很明确。您可能会在此处看到一个库示例,该库示例在调用abort()fa时很难通过malloc()或分配内存realloc(),想象一下,您有一个应用程序,它与100个库链接,并且您想知道哪个库以及如何使应用程序崩溃。而且,我abort()在他们的文档中没有发现任何提及(但请不要误会我的意思。这是一个很棒的库)。
2015年

@GrzegorzSzpetkowski fprinting到stderr之后,它甚至没有手动调用flush。哎哟!
这个

1
@this:它不需要这样做。stderr通常的输出是行缓冲的。如果输出以换行符终止,则无论如何它将被系统刷新。
Jonathan Leffler

@JonathanLeffler依靠用户,不是明智之举。
这个

@this:否,依赖于遵循C标准要求的实现:§7.21.3¶7在程序启动时,预定义了三个文本流,无需显式打开-标准输入(用于读取常规输入),标准输出(用于写入常规输出)和标准错误(用于写入诊断输出)。最初打开时,标准错误流未完全缓冲;…… 它需要程序员进行主动编码,以使标准错误得到充分缓冲(并且如果程序员那太钝,对任何人都没有帮助-除了“不要使用代码”)。
Jonathan Leffler

6

exit除了函数之外,避免使用函数的一个原因main()是您的代码可能会脱离上下文。记住,出口是一种非本地控制流。像不可捕获的异常。

例如,您可能编写了一些在严重磁盘错误时退出的存储管理功能。然后有人决定将它们移入图书馆。从库中退出会导致调用程序以不一致的状态退出,而这种状态可能无法准备。

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


2
而且不应该允许人们拥有厨刀,因为有人可能会抓住其中的一把并试图弄杂它们,或者与他的孩子玩“捉”。显然,我不同意。如果有人决定将我的代码复制到他的程序中,而我的代码却证明不符合他的目的,那就是他的问题,因为他无法读取他所吸收的代码。
G-Man说'Resstate Monica''July

3
一个相当疯狂的比喻。C充满了可以做的事情,但应该避免。因此,国际奥委会的存在。请注意,我的帖子没有说“不应该”。
pjc50

2

根据您正在执行的操作,退出可能是C语言中退出程序最合乎逻辑的方法。我知道检查退出以确保回调链正确工作非常有用。以我最近使用的示例回调为例:

在主循环中,我为此系统设置了所有内容,然后在while(1)循环中等待。可以使全局标志代替退出while循环,但这很简单,并且可以完成它需要做的事情。如果要处理任何打开的缓冲区,例如文件和设备,则应在关闭之前清理它们以确保一致性。


-1

当除了coredump以外的任何代码都可以退出时,这在一个大项目中非常糟糕。跟踪对于维护联机服务器非常重要。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.