我应该在main()中使用exit()还是仅使用return语句?就我个人而言,我喜欢return语句,因为我觉得它就像在读取代码时读取任何其他函数和流控制一样(在我看来)。即使我想重构main()函数,拥有return似乎比exit()更好。
exit()有没有做过return没有做过的特别的事情?
事实上,这是有区别的,但很微妙。它对C++有更多的含义,但差异是很重要的。
当我在main()中调用return时,将为本地作用域对象调用析构函数。如果我调用exit(),就不会为本地作用域对象调用析构函数!重读一遍。exit()不返回。这意味着一旦我调用它,就不会有"倒退",任何在函数中创建的对象都不会被破坏。这通常没有任何影响,但有时确实如此,比如关闭文件(确实需要将所有数据刷新到磁盘?).
注意,即使调用exit(),static对象也将被清除。最后要注意,如果使用abort(),则不会销毁任何对象。也就是说,不调用全局对象、静态对象和本地对象的析构函数。
当倾向于退出而不是返回时,请小心操作。
http://groups.google.com/group/gnu.gcc.help/msg/8348c50030cfd15a
- abort()退出时带有错误条件(非零退出代码),甚至可能是核心。如果需要退出而不调用静态析构函数,请使用exit。
- @绝对是阿卡迪。中止表示某些错误情况。在风格上,我通常也使用exit()来表示一个错误条件,并从main返回0来表示正常终止。
- 从exit()手册页:2。刷新所有打开的输出流。听起来,不管是否调用了适当的析构函数,任何打开的输出流都将被刷新。
- 迈克:C库文件缓冲区和C++文件流对象之间有区别。作为C库的一部分,Ext()被设计为与前者进行协调和刷新,但是可以绕过后者:即使标准的C++ FSt流内容也没有被刷新到磁盘(尝试它——我做到了,它失败了W/LINUX/GCC),显然,用户定义类型的缓冲I/O也不可能被刷新。
- 注意:语句:不会为本地作用域对象调用析构函数!对于C++ 11不再是真的:-与当前线程相关的对象,线程存储持续时间被破坏(仅C++ 11)。cplusplus.com/reference/cstdlib/exit/退出
- 这意味着,将调用thread_local对象的析构函数。仍然不调用其他本地对象的析构函数。ideone.com/y6dh3f
- 顺便说一句,只是为了学究一点,因为这个答案对于使用C的读者来说仍然是令人困惑的:对于C,有关exit()干净地关闭文件的问题实际上是错误的。唯一可能不会刷新的时间数据是在相反的情况下:即,如果使用来自main()的return,并且调用setbuf()或setvbuf(),其中一个缓冲区声明为main()中的自动存储(如下面R的回答所述)。真是太糟糕了,这个问题用C和C++加标签(和编码风格——这不是一个风格问题!).
- 应更新此答案以考虑评论和其他答案。在它的当前状态中,它是错误的并且具有(例如,它混合了冲洗和关闭,并且C IO被刷新,而C++ FFISH不是)。把它当作公认的答案是令人讨厌的。我会投反对票,直到决定为止。
另一个区别是:exit是一个标准库。功能,因此需要包括标题并与标准链接图书馆。为了说明(在C++中)这是一个有效的程序:
1
| int main() { return 0; } |
但要使用exit,您需要一个包括:
1 2
| #include <stdlib.h>
int main() { exit(EXIT_SUCCESS); } |
另外,这又增加了一个假设:从main调用exit与返回零具有相同的副作用。正如其他人指出的,这取决于您正在构建的可执行文件的类型(即,谁在调用main)。您是否在编写使用C运行时的应用程序?一个玛雅插件?Windows服务?司机?每一个案例都需要研究,看看exit是否等同于return。imho使用exit,当您真正指return时,只会使代码更加混乱。哦,如果你真的是指exit,那就用它吧。
至少有一个理由更喜欢exit:如果您的任何atexit处理程序引用main中的自动存储持续时间数据,或者使用setvbuf或setbuf将main中的自动存储持续时间缓冲区分配给其中一个标准流,那么从main返回将产生未定义的但调用exit是有效的。
另一个潜在的用法(通常为玩具程序保留)是退出具有递归调用main的程序。
- 这很有趣,因为[在c中,至少]5.1.2.2.3p1指出,从最初调用main开始,return 0;等同于exit(0);……这是否可能是一个编译器bug(或者是一个编译器特性,取决于你问谁)?
- @seb关于main()没有什么特别之处——它只是一个功能,和其他功能一样。另一方面,由于在标准中有特别提及,标准必须相当谨慎地定义main()及其附近和昂贵的事物。然而,最终,尽管该标准不(也不一定)要求编译器对main()中的自动存储做任何特殊的事情。请注意阅读您在评论中引用的段落下面的脚注11。
- @格雷加。伍兹很有趣。似乎有一些规范性的文本与一些信息性的文本相矛盾。根据ISO/IEC指令,规范性引用被认为是"必不可少的",其中作为信息性引用仅被认为是补充性引用。此外,根据上述文件(附件H),使用"意志"一词传达要求是无效的。总之,信息性文本肯定是无效的。
- @SEB:目的显然不是为了覆盖对自动存储行为的要求,脚注显然是为了澄清这一点。是的,C标准中有不精确、不好的措辞。任何读过它的人都知道这一点。我们还知道,委员会一般不会解决这样的问题,因为意图已经很明显了。
- @ R.。无论如何,规范性文本是必不可少的,而信息性文本可以删去,删去的标准仍然有效。
- @塞布:这不是一场辩论或法庭案件来证明你是对的。目标应该是对实际的C语言(如预期的和实现的)有一个清晰的理解,并在对读者有用的答案中表达这一点。规范性文本在某种程度上是微妙错误的(与它应该表达的意图相反),其本质上是由脚注固定的。如果您对此不满意,请提交缺陷报告,但不要期望得到答复。这就是wg14滚动的方式…
- @ R.。我同意;没有什么可争论的,尽管你说了一件事,然后你继续辩论……通过将所有(必不可少的)规范性文本汇编在一起,抛弃信息性文本,可以清楚地理解实际的C语言。但是,试着将信息性的文本编译在一起,您将无法对C语言形成清晰的理解。如果你想继续这场辩论,你应该参考参考资料性文本所指的规范性文本部分。在这种情况下,有许多部分与此矛盾,
- @ R.。…事实上,我毫不怀疑标准的某些部分指出,声明为返回非void类型的函数缺少return语句具有x行为,其中x是定义或未定义的实现…但事实上,5.1.2.2记录了特定情况下的替代行为(即main入口点)。
- @SEB:你似乎相信C语言可以通过解释标准中的自然语言文本来理解,就好像它是完全严格的一样。这根本不可能。规范包含错误,当一个简单的脚注澄清他们已经知道自己犯了一个错误,但读者可以理解时,WG14不会浪费时间重写东西。
- 从信息上讲,在atexit(3)注册的gcc函数中,无论您是调用exit(3)还是发出返回,都会运行。
Does exit() do anything special that 'return' doesn't?
对于一些不常见平台的编译器,exit()可能会将其参数转换为程序的退出值,而从main()返回的值可能会直接传递给主机环境而不进行任何转换。
在这些情况下,标准要求相同的行为(具体来说,它说返回与main()兼容的int应该等同于使用该值调用exit()。问题是不同的操作系统在解释退出值时有不同的约定。很多(很多!)系统,0意味着成功,任何其他都是失败。但在虚拟机上,零碎的价值观意味着成功,偶而的价值观意味着失败。如果从main()返回0,vms用户将看到有关访问冲突的恶意消息。实际上没有访问冲突——这只是与失败代码0相关联的标准消息。
后来,安西来了,祝福以东十一〔6〕和以东十一〔7〕作为可以传给以东十一〔0〕的论据。该标准还指出,exit(0)的行为应该与exit(EXIT_SUCCESS)相同,因此大多数实现将EXIT_SUCCESS定义为0。
因此,标准将您置于VM上的绑定中,因为它没有留下返回值为0的失败代码的标准方法。
因此,20世纪90年代早期的vax/vms c编译器没有解释main()的返回值,它只是将任何值返回到主机环境。但是,如果使用exit(),它将按照标准要求执行:将EXIT_SUCCESS或0转换为成功代码,将EXIT_FAILURE转换为一般故障代码。使用EXIT_SUCCESS时,必须传给exit(),不能从main()退回。我不知道该编译器的现代版本是否保留了这种行为。
一个可移植的C程序,用于如下显示:
1 2 3 4 5 6 7 8 9 10
| #include <stdio.h>
#include <stdlib.h>
int main() {
printf("Hello, World!
");
exit(EXIT_SUCCESS); /* to get good return value to OS */
/*NOTREACHED*/ /* to silence lint warning */
return 0; /* to silence compiler warning */
} |
旁白:如果我记得正确的话,退出值的vms约定比奇数/偶数更微妙。它实际上使用像低位三位这样的东西来编码严重性级别。但是,一般来说,奇数严重级别表示成功或杂项信息,偶数严重级别表示错误。
- 一些旧的pre-ansi编译器可能处理main的值returned与传递给exit的值不同,但标准特别指出,"如果main函数的返回类型是与int兼容的类型,则从最初调用main函数的返回相当于以main函数返回的值作为参数调用exit函数"。这是c11;c89/c90的措辞几乎相同。
- 的确。尽管如此,一些ansi-era编译器没有获得这种权利,需要显式使用exit来获得返回到主机环境的正确返回值。由于标准(即使在那时)要求0被视为与EXIT_SUCCESS相同,因此无法返回值为0的特定于平台的故障状态,这可能是时代的一些编译器将main和exit()的返回视为不同的原因。
- 你有这方面的引证吗?另一个问题是当前的编译器是否有那个特定的bug。你的答案是现在时态的短语。
- 这是个公平的批评。我已经修改了措辞,将范围限定在我所知道的具体案例中。
我强烈支持R关于使用exit()的评论,以避免在程序实际结束之前回收main()中的自动存储。main()中的return X;语句并不完全等同于对exit(X);的调用,因为当main()返回时,main()的动态存储消失了,但如果调用exit()则不会消失。
此外,在C语言或任何C语言中,return语句强烈地提示读者,执行将继续在调用函数中进行,而如果计算调用main()函数的C启动例程,这种执行的继续在技术上通常是正确的,但当您打算结束t时,它并不完全是您的意思。他处理。
毕竟,如果您想从除main()以外的任何其他函数中结束程序,必须调用exit()。在main()中持续这样做也会使代码更易于阅读,而且也使任何人都更容易重新考虑代码;即,从main()复制到其他函数的代码不会因为意外的return语句而出现错误行为,这些语句本应是exit()调用。
因此,将所有这些观点结合起来,得出的结论是,使用return语句结束main()中的程序是一个坏习惯,至少对于C来说是这样。
- 你可能会发现C标准的5.1.2.2.3P1很有趣…
- 你读过我在SEB上写的吗?
- 这个答案对于C程序是值得仔细考虑的,如答案中上下文所示。对于C++的使用,需要仔细权衡所有前面提到的警告。对于C++,我建议一般避免EDOCX1 5,但是如果EDCOX1,6,EDCX1,7个替代方案在特定的上下文中不工作,则使用它。但主要避免使用exit(),将回归作为典型的实践。
我总是使用return,因为main()的标准原型表示它确实返回了int。
也就是说,一些版本的标准对main进行了特殊处理,如果没有明确的return声明,则假定返回0。给出以下代码:
1 2
| int foo() {}
int main(int argc, char *argv[]) {} |
g++只为foo()生成警告,忽略main缺少的返回:
1 2 3
| % g++ -Wall -c foo.cc
foo.cc: In function ‘int foo()’:
foo.cc:1: warning: control reaches end of non-void function |
- 我不知道C,但是C++标准规定,如果不返回主值,则假定返回0。
- 似乎C99是一样的:faq.cprogramming.com/cgi-bin/&hellip;
- 如果没有返回语句,C99和C++返回0,C90则不返回。
- 仅仅因为函数被声明为具有返回值,并不意味着您必须使用return来结束它的执行。调用exit()也是结束任何函数执行的有效方法,有时也是必要的。事实上,正如我和其他人在其他地方所描述的那样,即使是从main()调用exit(),也传达了退出整个过程的更明确意图,保留了自动存储,直到过程退出,并使得在将来重构代码时更容易维护。对于c来说,在main()中使用return的目的是结束这个过程,因此可以说是一种不好的做法。
- @格雷加.伍兹,这是你的意见,但几乎不值得投反对票!我上面写的内容与标准完全一致,而您的论点只是语义。
- 相反,您的书面回答缺乏关于良好编程实践的建议。建议严格遵守标准和定义,尤其是在有明显的角落情况下,不定义的行为可能会被触发,以及在可维护性受到不利影响时,这不是一个好建议。
- 我从来没有遇到过这样一种情况,即"必须"调用exit(),而不是使用return。另一方面,我在给使用exit()的main()进行不必要的调用时遇到了一些问题。这里绝大多数的答案和评论似乎不同意你的观点,即在main()中使用return是"一种糟糕的做法"。
- "……"如果没有显式的返回语句,则假定它返回0。那有什么问题吗?你的意思是如果我使用exit()而有一些错误需要返回非零,那么程序将返回0?这就是你的意思吗?