【问题标题】:return statement vs exit() in main()main() 中的 return 语句与 exit()
【发布时间】:2010-10-02 11:09:58
【问题描述】:

我应该在main() 中使用exit() 还是只使用return 语句?我个人喜欢return 语句,因为我觉得它就像阅读任何其他函数一样,并且当我阅读代码时流程控制很流畅(在我看来)。即使我想重构main() 函数,拥有return 似乎比exit() 更好。

exit() 会做一些 return 没有做的特别事情吗?

【问题讨论】:

    标签: c++ c coding-style return exit


    【解决方案1】:

    其实,有的区别,但很微妙。它对 C++ 的影响更大,但差异很重要。

    当我在main() 中调用return 时,将为我的本地范围对象调用析构函数。如果我调用exit()将不会为我的本地范围对象调用任何析构函数!重新阅读。 exit() 不返回。这意味着,一旦我称它为,就“没有后盾”。您在该函数中创建的任何对象都不会被销毁。通常这没有任何影响,但有时确实如此,例如关闭文件(您确定要将所有数据刷新到磁盘吗?)。

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

    选择退出而不是返回时要谨慎行事。

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

    【讨论】:

    • abort() 以错误条件退出(非零退出代码),甚至可能是核心。如果您需要在不调用静态析构函数的情况下退出,请使用 _exit 。
    • @Mike:C 库文件缓冲区和 C++ 文件流对象之间存在差异。 exit() - 作为 C 库的一部分 - 旨在与前者协调并刷新,但可以绕过后者:即使是标准 C++ fstream 内容也不会刷新到磁盘(尝试一下 - 我这样做了,它在 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++ 标记(以及编码风格——这不是风格问题!)。
    【解决方案2】:

    另一个区别: exit 是标准库 功能,所以你需要包括 标头和与标准的链接 图书馆。为了说明(在 C++ 中), 这是一个有效的程序:

    int main() { return 0; }
    

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

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

    另外,这增加了一个额外的假设:从main 调用exit 与返回零具有相同的副作用。正如其他人指出的那样,这取决于您正在构建什么样的可执行文件(即,谁在调用main)。您是否正在编写使用 C 运行时的应用程序?玛雅插件? Windows 服务?一个司机?每个案例都需要研究以查看exit 是否等同于return。恕我直言,当您真的是说 return 时使用exit 只会使代码更加混乱。 OTOH,如果你真的是意思exit,那么一定要使用它。

    【讨论】:

    • ISO C 保证当 main 返回时发生的任何事情都等同于 main 的调用者将返回值传递给 exit()Return vs Exit from main function in C 有一些引用标准的答案。如果您的程序没有作为独立进程运行,则第一个函数可能不称为main。如果是这样,您正在做一些奇怪的巫术,而不是在 ISO C 领域了。
    【解决方案3】:

    首选exit 至少有一个原因:如果您的任何atexit 处理程序引用main 中的自动存储持续时间数据,或者如果您使用setvbufsetbuf 分配给标准流之一是 main 中的自动存储持续时间缓冲区,然后从 main 返回会产生未定义的行为,但调用 exit 是有效的。

    另一种潜在用途(不过,通常保留给玩具程序)是通过递归调用 main 退出程序。

    【讨论】:

    • @Seb main() 没有什么特别之处——它只是一个和其他函数一样的函数。另一方面,由于它在标准中特别提到,标准必须相当小心它如何定义main() 以及它附近和亲爱的东西。然而最终,尽管标准没有(并且不得)要求编译器对main() 中的自动存储做任何特别的事情。请注意阅读您在评论中引用的段落下方的Footnote #11
    • @GregA.Woods 很有趣。似乎有一些规范性文本与一些信息性文本相矛盾。根据the ISO/IEC directives,规范性参考被认为是“必不可少的”,而信息性仅被认为是补充性的……此外,使用“意志”一词来传达要求是无效的;根据上述文件(附件 H)。总之,信息性文本肯定是无效的。
    • @Seb:其目的显然不是要覆盖对自动存储行为的要求,而脚注显然是为了澄清这一点。是的,C 标准中的措辞不准确,措辞不佳。读过它的人都知道这一点。我们也知道委员会一般不会解决这样的问题,因为意图已经很明显了。
    • @Seb:这不是证明你是对的辩论或法庭案件。目标应该是清楚地了解实际的 C 语言(按预期和已实现)是什么,并在对读者有用的答案中表达出来。规范性文本以一种基本上由脚注固定的方式存在微妙的错误(与它应该表达的意图相反)。如果您对此不满意,请提交缺陷报告,但不要期待回复。这就是 WG14 滚动的方式...
    • @Seb:您似乎相信 C 语言可以通过解释标准的自然语言文本来理解,就好像它完全严格一样。这根本不可能。规范包含错误,当一个简单的脚注澄清他们已经知道他们犯了错误但读者可以理解时,WG14 不会浪费时间重写内容。
    【解决方案4】:

    我总是使用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++ 标准规定,如果您在 main 中不返回值,则假定返回 0。
    • C99 和 C++ 如果没有返回语句则返回 0,C90 没有。
    • 仅仅因为函数被声明为具有返回值并不意味着您必须使用return 来结束其执行。调用exit() 也是一种有效的、有时是必要的方式来结束任何函数的执行。事实上,正如我和其他人在其他地方所描述的那样,即使从 main() 调用 exit() 也传达了退出整个过程的更明确的意图,保留自动存储直到过程退出,并且在将来的代码重构期间更容易维护。对于 C 在 main() 中使用 return 的目的是结束进程,因此可以说是一种不好的做法。
    • 从来没有遇到过这样一种情况,即“有必要”调用exit() 而不是在main 中使用return。另一方面,我在包装对main() 的调用时遇到了问题,该调用不必要地使用了exit()。这里的绝大多数答案和评论似乎不同意您的断言,即在main() 中使用return 是“一种不好的做法”。
    【解决方案5】:

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

    此外,在 C 或任何类似 C 的语言中,return 语句强烈暗示读者将在调用函数中继续执行,虽然如果计算 C 启动例程,这种继续执行通常在技术上是正确的调用了您的 main() 函数,当您打算结束进程时,这并不是 的意思。

    毕竟,如果您想在除main() 之外的任何其他函数中结束您的程序,您必须调用exit()。在main() 中始终如一地这样做也会使您的代码更具可读性,并且也使任何人都可以更轻松地重构您的代码;即,从main() 复制到其他函数的代码不会因为意外的return 语句应该exit() 调用而行为不端。

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

    【讨论】:

    • 你可能会觉得5.1.2.2.3p1 of the C standard很有趣...
    • 这个答案对于 C 程序来说值得仔细考虑,如答案中的上下文所示。对于与 C++ 一起使用,需要仔细权衡前面提到的所有警告。对于 C++,我建议一般避免使用 exit(),但如果 throwabort() 替代方案在特定上下文中不起作用,请使用它。但尤其要避免在 main 中使用 exit(),并在 main 中使用 return 作为典型做法。
    【解决方案6】:

    exit() 有没有做任何“return”没有做的特殊事情?

    对于不常见平台的一些编译器,exit() 可能会将其参数转换为程序的退出值,而从main() 返回可能只是将值直接传递给主机环境而无需任何转换。

    在这些情况下,该标准要求相同的行为(具体来说,它说从main() 返回与int 兼容的东西应该等同于使用该值调用exit())。问题是不同的操作系统有不同的解释退出值的约定。在许多(很多!)系统上,0 意味着成功,其他任何东西都是失败。但是在 VMS 上,奇数表示成功,偶数表示失败。如果您从main() 返回 0,VMS 用户会看到一条关于访问冲突的令人讨厌的消息。实际上并没有访问冲突——这只是与失败代码 0 相关的标准消息。

    然后 ANSI 出现并祝福 EXIT_SUCCESSEXIT_FAILURE 作为参数传递给 exit()。该标准还规定exit(0) 的行为应与exit(EXIT_SUCCESS) 相同,因此大多数实现将EXIT_SUCCESS 定义为0

    因此,该标准将您置于 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 退出值的约定比奇数/偶数更细微。它实际上使用像低三位这样的东西来编码严重性级别。然而,一般来说,奇数严重级别表示成功或杂项信息,偶数表示错误。

    【讨论】:

    • 一些旧的 pre-ANSI 编译器可能对 returned 的值 main 与传递给 exit 的值不同——但标准明确指出,“如果main函数是与int兼容的类型,从初始调用返回main函数相当于调用exit 函数,将 main 函数返回的值作为其参数”。那是C11; C89/C90 的措辞几乎相同。
    • 确实如此。然而,一些 ANSI 时代的编译器并没有做到这一点,并且需要显式使用 exit 才能将正确的返回值返回给主机环境。由于标准(即便如此)要求将 0 视为与 EXIT_SUCCESS 相同,因此无法返回值为 0 的特定于平台的 failure 状态,这可能是某些那个时代的编译器对 return-from-main 和 exit() 的处理方式不同。
    • 你有引用吗?一个单独的问题是是否有任何 current 编译器具有该特定错误。你的答案是现在时。
    • 这是一个中肯的批评。我已经更改了措辞,将范围限制在我所知道的具体案例中。
    【解决方案7】:

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

    C standard 的第 5.1.2.2.3 节指出:

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

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

    【讨论】:

      【解决方案8】:

      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。

      【讨论】:

        最近更新 更多