return语句vs main()中的exit()


197

我应该使用exit()还是仅使用return语句main()?就我个人而言,我喜欢这些return语句,因为我感觉就像在读取代码时读取任何其他函数和流程控制一样(我认为)。即使我想重构该main()函数,return似乎也比拥有更好的选择exit()

exit()没有特别之处return吗?

Answers:


277

其实,有 有差别,但它是微妙的。它对C ++具有更多含义,但是区别很重要。

当我调用returnmain(),将为本地作用域的对象调用析构函数。如果我调用exit(),则不会为本地作用域的对象调用析构函数!重新阅读。exit() 不返回。这意味着一旦我调用它,就不会有“后援”。您在该函数中创建的所有对象都不会被破坏。通常,这没有任何意义,但有时却可以,例如关闭文件(确定要将所有数据刷新到磁盘吗?)。

请注意,static即使您调用,对象也会被清理exit()。最后请注意,如果使用abort(),则不会破坏任何对象。也就是说,不会调用全局对象,静态对象和本地对象的析构函数。

当倾向于退出而不是收益时,请谨慎行事。

http://groups.google.com/group/gnu.gcc.help/msg/8348c50030cfd15a


1
abort()以错误条件(非零退出代码)退出,甚至可能是核心。如果您需要退出而无需调用静态析构函数,请使用_exit。

7
@Mike:C库文件缓冲区和C ++文件流对象之间存在差异。exit()-作为C库的一部分-旨在与前者进行协调并刷新后者,但可以绕过后者:即使是标准C ++ fstream内容也不会刷新到磁盘(尝试-我这样做了,它在Linux /下失败了GCC),显然也不能期望用户定义的类型具有缓冲的I / O。
托尼·德罗伊

9
注意:声明:我的本地作用域对象不会调用任何析构函数!对于C ++ 11不再适用:-销毁了与具有线程存储持续时间的当前线程关联的对象(仅C ++ 11)。cplusplus.com/reference/cstdlib/exit
Ilendir 2014年

7
这意味着thread_local将调用对象的析构函数。其他本地对象的析构函数仍未调用。ideone.com/Y6Dh3f
HolyBlackCat 2014年

3
顺便说一句,只是为了求知,因为这个答案对于使用C的读者仍然会造成混淆:对于C,关于exit()干净关闭文件的问题实际上是错误的。唯一可能不刷新数据的情况是相反的情况:即,如果一个用户使用returnfrom main()并且一个已调用setbuf()setvbuf()使用一个声明为自动存储在其中的缓冲区main()(如下面R.的答案所述)。这个问题同时用C和C ++标记(以及编码样式,这不是样式问题!),真是太糟糕了。
格雷格·伍兹

25

另一个区别: exit是标准库函数,因此您需要包括标头并与标准库链接。为了说明(在C ++中),这是一个有效的程序:

int main() { return 0; }

但是要使用,exit您需要包含:

#include <stdlib.h>
int main() { exit(EXIT_SUCCESS); }

此外,这还增加了一个附加假设:exitfrom main的调用与返回零具有相同的副作用。正如其他人指出的那样,这取决于您正在构建哪种可执行文件(即谁在调用main)。您是否正在编写使用C运行时的应用程序?Maya插件?Windows服务?一个司机?每个案例都需要进行研究,看是否exit等同于return。恕我直言exit,您的意思 return是使代码更加混乱。OTOH,如果您确实是故意的 exit,那么务必使用它。


16

至少有一个理由要使用exit:如果您的任何atexit处理程序在中引用了自动存储持续时间数据main,或者您使用setvbufsetbuf将的自动存储持续时间缓冲区分配给其中一个标准流main,则从mainProduces 返回未定义的行为,但调用exit有效。

另一个潜在的用法(但是通常保留给玩具程序使用)是从递归调用退出程序main


1
@Seb没什么特别的main()-它只是一个与其他函数一样的函数。另一方面,由于该标准中有特别提及,因此该标准在定义方式main()以及与之接近和珍贵的方面必须相当谨慎。但是最后,尽管该标准没有(也不得)要求编译器对中的自动存储做任何特殊的事情main()。请注意阅读注释中引用的段落下方的脚注11
格雷格·伍兹

1
@ GregA.Woods有趣。似乎有些规范性文本与某些信息性文本相矛盾。根据ISO / IEC指令,该规范性引用被认为是“必不可少的”,而-信息性仅被视为补充性的。根据上述文件(附件H)。总之,内容翔实的文本肯定是无效的。
自闭症2015年

2
@Seb:显然没有超越自动存储行为的要求,并且显然在脚注中对此进行了说明。是的,C标准中的措词不准确,不正确。任何阅读它的人都知道这一点。我们也知道,委员会通常不会解决此类问题,因为其意图已经很明显了。
R .. GitHub停止帮助ICE 2015年

1
@Seb:这不是辩论或法庭案件,不能证明您是对的。目标应该是清楚地了解实际的C语言(预期和实现的)是什么,并在对读者有用的答案中表达出来。规范性文本在本质上是由脚注固定的,因此是一种微妙的错误(与它应该表达的意图相反)。如果您对此不满意,请提交缺陷报告,但不要期望得到答复。WG14就是这样滚动的……
R .. GitHub停止帮助ICE 2015年

3
@Seb:您似乎相信可以通过解释标准的自然语言文本来理解C语言,就像它完全严格一样。这根本不可能。该规范包含错误,当一个简单的脚注说明他们已经知道自己犯了错误,但读者可以理解时,WG14不会浪费时间来重写内容。
R .. GitHub停止帮助ICE

5

我经常使用,return因为的标准原型main()表示它确实返回了int

也就是说,某些版本的标准提供了main特殊待遇,并假设如果没有明确的return声明,它将返回0 。给出以下代码:

int foo() {}
int main(int argc, char *argv[]) {}

G ++仅针对生成警告,foo()而忽略来自的缺失回报main

% 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/...
杰森·贝克

2
如果没有return语句,则C99和C ++返回0,而C90没有。
2009年

仅仅因为一个函数被声明为具有返回值并不意味着您必须使用它return来结束其执行。调用exit()也是结束任何函数执行的有效且有时是必要的方式。确实,正如我和其他人在其他地方所描述的那样,exit()即使从调用也main()传达了退出整个流程的明确意图,保留自动存储直到流程退出,并在将来的代码重构中简化维护。因此,对于C而言returnmain()在意图将要结束时使用in 是一种不好的做法。
Greg A. Woods

@ GregA.Woods是您的意见,但几乎不值得一票!我上面写的内容与standard完全一致,而您的论点仅仅是语义。
Alnitak

5

强烈支持 R.关于使用exit()的评论,以避免main()在程序实际结束之前回收自动存储。return X;in中的语句main()并不完全等同于对的调用exit(X);,因为动态存储main()main()返回时消失,但如果exit()改为调用,它就不会消失。

此外,在C或任何类似C的语言中,一条return语句强烈地向读者暗示执行将继续在调用函数中进行,而从技术上讲,这种连续执行通常是正确的,如果您算上调用了main()函数的C启动例程,则不是到底是什么,你的意思是,当你的意思是结束该进程。

毕竟,如果要从除main()您之外的任何其他函数中结束程序必须调用exit()。一致地执行此操作main()还可以使您的代码更具可读性,并且任何人都可以更轻松地重构您的代码。即从复制的代码main(),因为偶然的一些其他功能不会胡作非为return,发言exit()调用。

因此,将所有这些观点结合在一起,得出的结论是,这是一个坏习惯至少对于C语言来说,使用return语句在中结束程序main()


您可能会发现有趣的C标准5.1.2.2.3p1 ...
自闭症2015年

正如答案中的上下文所示,对于C程序,此答案值得仔细考虑。为了与C ++一起使用,需要仔细权衡上述所有警告。对于C ++,我建议exit()一般避免使用,但如果a throwabort()替代方法在特定情况下不起作用,请使用它。但是特别要避免exit()以main为主要做法,而改为以main为回报。
Eljay

5

exit()会执行“返回”所没有的特殊功能吗?

对于某些针对不常见平台的编译器,exit()可能会在执行以下操作的同时将其参数转换为程序的退出值:main()可能会将其值直接传递给主机环境,而不进行任何转换。

在这些情况下,该标准要求具有相同的行为(特别是,它说返回int兼容的东西main()应该等同于exit()使用该值进行调用)。问题在于,不同的操作系统具有不同的解释出口值的约定。在许多(MANY!)系统上,0表示成功,其他则表示失败。但是在VMS上,奇数表示成功,偶数表示失败。如果从返回0 main(),则VMS用户将看到有关访问冲突的令人讨厌的消息。实际上并没有访问冲突-仅仅是与故障代码0相关的标准消息。

然后ANSI出现并得到祝福EXIT_SUCCESSEXIT_FAILURE作为参数可以传递给ANSI exit()。该标准还说exit(0)应该表现得与之相同exit(EXIT_SUCCESS),因此大多数实现都将其定义EXIT_SUCCESS0

因此,该标准使您陷入VMS的困境,因为它没有标准的方法可以返回恰好值为0 的故障代码。

因此,1990年代初期的VAX / VMS C编译器没有解释来自 main(),它只是将任何值返回给主机环境。但是,如果使用exit()它,它将满足标准要求:将EXIT_SUCCESS(或0)转换为成功代码和EXIT_FAILURE通用失败代码。要使用它EXIT_SUCCESS,您必须将其传递给exit(),但不能从退还main()。我不知道该编译器的更现代版本是否保留了该行为。

一个可移植的C程序,看起来像这样:

#include <stdio.h>
#include <stdlib.h>

int main() {
  printf("Hello, World!\n");
  exit(EXIT_SUCCESS);  /* to get good return value to OS */
  /*NOTREACHED*/ /* to silence lint warning */
  return 0;  /* to silence compiler warning */
}

顺便说一句:如果我没记错的话,退出值的VMS约定比奇/偶更细微。实际上,它使用诸如低三位之类的东西来对严重性级别进行编码。但是,一般而言,奇数严重级别表示成功或其他信息,偶数严重级别表示错误。


某些旧的ANSI之前的编译器可能会将值与传递给值returned的方法main不同exit-但标准特别指出:“如果main函数的返回类型与兼容int,则从初始调用到main函数的返回为等效于以exit函数返回的值main作为参数来调用函数”。那是C11;C89 / C90的措词几乎相同。
基思·汤普森

确实。但是,某些ANSI时代的编译器没有正确地做到这一点,需要显式使用exit才能将正确的返回值返回给主机环境。由于标准(甚至是标准)要求将0与视为相同EXIT_SUCCESS,因此无法返回值为0 的特定于平台的故障状态,这可能就是为什么该时代的某些编译器将return-from-main视为和exit()不同的。
Adrian McCarthy 2015年

你有被引用吗?一个单独的问题是当前的编译器是否存在该特定错误。您的答案是现在时的措词。
基思·汤普森

这是公平的批评。我更改了措辞以将范围限制为我所了解的特定情况。
阿德里安·麦卡锡

0

在C语言中,从from main进行返回exit与使用相同值进行调用完全相同。

C标准的 5.1.2.2.3节规定:

如果main函数的返回类型是与int兼容的类型,那么从初始调用到main函数的返回等同于以main函数返回的值作为参数来调用exit函数 ; 11)到达终止主函数的}会返回值0。如果返回类型与int不兼容,则未指定返回到主机环境的终止状态。

如其他答案中所述,C ++的规则有些不同。


-1

实际上存在的差异exit(0),并return(0)main-当你的main函数被调用多次。

以下程序

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    return(0);
  printf("%d", main(argc - 1, argv));
}

运行为

./program 0 0 0 0

将导致以下输出:

00000

但是这个:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {
  if (argc == 0)
    exit(0);
  printf("%d", main(argc - 1, argv));
}

无论参数如何,都不会打印任何内容。

如果您确定没有人会main显式地调用您的代码,那么从技术上来说,这通常不会有太大的区别,但是维护更清晰的代码exit会更好。如果出于某些原因想要打电话main–您应根据需要进行调整。

谈到C。

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.