在主函数(main())中,return语句和exit()函数都可以用来终止程序的执行。它们的作用是相似的,但在使用上有一些细微的差别。 return语句是用来从函数中返回一个值,并将程序的控制权返回给调用该函数的地方。在主函数中,return语句可以用来指定程序的退出状态码,并将该状态码返回给操作系统。这样,操作系统就可以根据状态码来判断程序的执行情况。 而exit()函数则是直接终止程序的执行,并且不会返回任何值。它可以在任何地方被调用,不仅限于主函数。当程序执行到exit()函数时,会立即退出,并且不会执行后续的代码。 在使用上,return语句更常用于函数的正常返回,而exit()函数更常用于程序的异常终止。如果程序遇到了无法处理的错误或者需要立即退出的情况,可以使用exit()函数来终止程序的执行。 总之,return语句和exit()函数都可以用来终止程序的执行,但在使用上需要根据具体的情况来选择合适的方式。

228
在使用exit()或者只是return语句在main()函数中有什么区别吗?
个人而言,我更喜欢使用return语句,因为我觉得它就像阅读其他函数一样,代码的流程控制在我阅读时更加顺畅(在我看来)。而且,即使我想重构main()函数,使用return似乎比exit()更好的选择。 exit()有什么特殊功能,return没有吗?
8个回答

311

实际上,有一个细微的区别。这对C++来说更有意义,但这些差异很重要。

当我在main()中调用return时,我的局部作用域对象将被调用析构函数。如果我调用exit(),则不会为我的局部作用域对象调用析构函数! 请重新阅读一遍。 exit() 不会返回。 这意味着一旦我调用它,就没有“后悔药”。您在该函数中创建的任何对象都不会被销毁。通常这没有影响,但有时会有影响,比如关闭文件(您肯定希望将所有数据刷新到磁盘上吧?)。

请注意,即使您调用exit(),静态对象也将被清除。最后请注意,如果使用abort(),则不会销毁任何对象。也就是说,没有全局对象、静态对象和局部对象将调用其析构函数。

在喜欢exit而不是return时,请小心谨慎。

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


2
abort()以错误状态(非零退出代码)退出并可能甚至会导致核心转储。如果需要退出而不调用静态析构函数,请使用_exit。 - user3458
8
@Mike:C库文件缓冲区与C ++文件流对象之间有区别。由于exit()是C库的一部分,因此旨在与前者协调并刷新其内容,但可以绕过后者:即使是标准C ++ fstream内容也不会刷新到磁盘(试试吧 - 我试过了,在Linux/GCC下失败了),显然,具有缓冲I/O的用户定义类型也不能期望刷新。 - Tony Delroy
11
注意:在C++11中,与当前线程关联且具有线程存储期的对象将被销毁,因此不再适用于语句:“我的局部作用域对象不会调用析构函数!”。参考链接:http://www.cplusplus.com/reference/cstdlib/exit/ - Ilendir
10
意思是,thread_local对象的析构函数将被调用。其他本地对象的析构函数仍然不会被调用。http://ideone.com/Y6Dh3f - HolyBlackCat
4
顺便说一句,并且只是为了追求严谨,因为这个回答可能仍会让使用C的读者感到困惑:对于C而言,“exit()”函数能够干净地关闭文件的问题实际上是错误的。唯一可能没有刷新数据的情况是相反的情况:也就是如果从“main()”返回并且已经使用“setbuf()”或“setvbuf()”来设置在“main()”中声明为自动存储的缓冲区,则数据可能不会被刷新(正如R的回答所讨论的那样)。很遗憾,这个问题标记了C和C++(以及编码风格——这不是一个风格问题!)。 - Greg A. Woods
显示剩余3条评论

30

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

int main() { return 0; }

不过如果要使用exit,你需要引入一个头文件:

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

此外,这增加了一个额外的假设:从main调用exit具有与返回零相同的副作用。正如其他人指出的那样,这取决于您正在构建哪种可执行文件(即谁在调用main)。您是编写使用C运行时的应用程序吗?Maya插件?Windows服务?驱动程序?每种情况都需要研究以查看exit是否等效于return。在我看来,当您真正意味着return时,使用exit只会使代码更加混乱。但是,如果您真的意味着exit,那么务必使用它。


ISO C保证,当main函数返回时发生的任何事情都等同于main的调用者将返回值传递给exit()从C语言的main函数中返回和退出提供了一些引用标准的答案。如果您的程序不作为独立进程运行,则第一个函数可能不叫做main。如果是这样,那么您正在进行一些奇怪的操作,而不是在ISO C领域内。 - Peter Cordes

17

至少有一个理由偏好使用 exit:如果您的 atexit 处理程序引用了 main 中的自动存储期数据,或者您使用了 setvbufsetbuf 来分配一个自动存储期缓冲区给 main 中的标准流之一,那么从 main 中返回将产生未定义行为,但调用 exit 是有效的。

另一个潜在的用途(通常仅用于玩具程序)是在具有递归调用 main 的情况下退出程序。


1
@Seb,main()函数并没有什么特别之处--它只是像其他任何函数一样。另一方面,由于它在标准中有特别的提及,标准必须非常小心地定义main()和与其相关的事物。然而,最终标准并不要求编译器对main()中的自动存储做任何特殊处理(也不能)。请务必仔细阅读您在评论中引用的段落下面的脚注#11 - Greg A. Woods
1
@GregA.Woods 有趣。似乎有一些规范性文本与一些信息性文本相矛盾。根据ISO/IEC指令,规范性参考被认为是“必不可少的”,而信息性参考仅被认为是补充...此外,使用“将”来传达要求是无效的;根据前述文件(附录H)。总之,信息性文本肯定是无效的。 - autistic
2
@Seb:显然,意图并不是要覆盖自动存储行为的要求,脚注显然是为了澄清这一点而写的。是的,在C标准中有不精确、措辞不当的问题。任何读过它的人都知道这一点。我们也知道,委员会通常不会修复这样的问题,因为意图已经很明显了。 - R.. GitHub STOP HELPING ICE
1
@Seb:这不是一场为了证明你正确的辩论或法庭案件。目标应该是获得对实际C语言(按照预期和实现)的清晰理解,并用对读者有用的答案来表达。规范文本在某种程度上是错误的(与其预期要表达的相反),但这个问题在注释中已经被修复了。如果你对此不满意,请提交缺陷报告,但不要期望得到回复。这就是WG14的做法... - R.. GitHub STOP HELPING ICE
3
您似乎认为可以通过解释标准文本来理解C语言,就好像它完全严谨一样。这是不可能的。该规范中包含错误,WG14并不会浪费时间重写内容,而只需要添加一个简单的脚注以澄清他们已经知道自己犯了错误,但读者仍能理解其含义。 - R.. GitHub STOP HELPING ICE
显示剩余5条评论

5
exit()和'return'有什么特别之处吗?
对于一些不常见平台的编译器,exit()可能会将其参数转换为程序的退出值,而从main()返回可能会直接将该值传递给主机环境而没有任何转换。标准要求在这些情况下行为相同(具体来说,它表示从main()返回与使用该值调用exit()应该是等价的)。问题在于不同的操作系统有不同的解释退出值的约定。在许多系统上,0表示成功,其他任何值都表示失败。但是,在VMS上,奇数值表示成功,偶数值表示失败。如果你从main()返回0,则VMS用户将看到一个关于访问冲突的不良消息。实际上并没有发生访问违规——这只是与失败代码0相关联的标准消息。
然后,ANSI出现了,并将EXIT_SUCCESS和EXIT_FAILURE作为可以传递给exit()的参数。标准还表示,exit(0)应该与exit(EXIT_SUCCESS)行为相同,因此大多数实现将EXIT_SUCCESS定义为0。
因此,标准在VMS上使你陷入困境,因为它没有标准方法返回一个恰好为0的失败代码。
因此,20世纪90年代早期的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 之前的编译器可能会将 main 函数返回的值与传递给 exit 函数的值不同对待,但标准明确指出:“如果 main 函数的返回类型与 int 兼容,则从初始调用 main 函数返回等效于使用 main 函数返回的值作为其参数调用 exit 函数”。这是 C11 的规定;C89/C90 几乎有相同的措辞。 - Keith Thompson
然而,一些ANSI时代的编译器并没有做到这一点,需要显式使用exit才能将正确的返回值返回给主机环境。由于标准(即使当时)要求0与EXIT_SUCCESS被视为相同,因此无法使用值为0来返回特定于平台的_失败_状态,这可能是该时代的一些编译器处理从主函数返回和exit()的差异的原因。 - Adrian McCarthy
你有这方面的引用吗?另一个问题是是否有任何当前的编译器存在那个特定的 bug。你的回答使用现在时态表述。 - Keith Thompson
这是一个公正的批评。我已经更改了措辞,将范围限制在我所知道的特定情况内。 - Adrian McCarthy

5
强烈支持R.关于使用exit()的评论,以避免在程序结束之前自动回收main()中的自动存储。在main()中的return X;语句与调用exit(X);不完全相同,因为当main()返回时,main()的动态存储会消失,但如果调用exit()则不会。
此外,在C或任何类似C的语言中,return语句强烈提示读者执行将继续在调用函数中进行,虽然这种执行通常在技术上是正确的,如果您计算了调用main()函数的C启动例程,但这并不是您希望结束进程时的确切含义。
毕竟,如果您想从除main()之外的任何其他函数中结束程序,则必须调用exit()。在main()中始终如一地这样做可以使您的代码更加可读,并且也使得任何人重构您的代码变得更加容易;即从main()复制到其他函数的代码不会因意外的return语句而表现不良,这些return语句应该被替换为exit()调用。
因此,结合所有这些观点,结论是,在C中,使用return语句结束main()中的程序是一个不好的习惯

你可能会发现C标准的5.1.2.2.3p1很有趣... - autistic
1
这个答案对于C程序来说值得仔细考虑,正如答案中所指示的那样。对于C++的使用,需要仔细权衡所有先前提到的注意事项。对于C++,我建议一般情况下避免使用exit(),但如果在特定情况下throwabort()替代方案无法工作,则可以使用它。但是特别要避免在main函数中使用exit(),而是使用返回语句作为典型实践。 - Eljay

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 functionint foo()’:
foo.cc:1: warning: control reaches end of non-void function

3
如果没有返回语句,C99和C++会返回0,但C90不会。 - d0k
仅仅因为一个函数被声明为具有返回值并不意味着您必须使用 return 来结束其执行。调用 exit() 也是一种有效的,有时是必要的方式来结束任何函数的执行。实际上,正如我和其他人在其他地方所描述的那样,即使从 main() 调用 exit(),也传达了更清晰的退出整个进程的意图,保留自动存储直到进程退出,并且在未来重构代码时更易于维护。因此,在 C 中,当意图结束进程时,在 main() 中使用 return 可能是一种不良实践。 - Greg A. Woods
@GregA.Woods 这只是你的观点,但并不值得被踩!我上面所写的完全符合标准,而你的论点只是纯粹的语义学。 - Alnitak
相反,你的回答缺乏关于良好编程实践的建议。严格遵守标准和定义,特别是在存在明显的角落情况可以触发未定义行为的情况下,并且在影响可维护性的情况下,这并不是一个好的建议。 - Greg A. Woods
1
我从未遇到必须调用 exit() 而不是在 main 中使用 return 的情况。另一方面,当包装一个不必要使用 exit()main() 调用时,我曾经遇到过问题。这里绝大多数的答案和评论似乎都不同意你的观点,即在 main() 中使用 return 是“不好的做法”。 - Alnitak
显示剩余3条评论

0

在C语言中,从main函数返回与使用相同值调用exit函数是完全相同的。

C标准的5.1.2.2.3节规定:

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

C++的规则与其他答案中提到的有些不同。


-1

main 函数中,exit(0)return(0) 之间确实存在区别——尤其当你的 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语言。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接