ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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


F
FreeMemory

其实还是有区别的,但是很微妙。它对 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++ 标记(以及编码风格——这不是风格问题!)。
j
jwfearn

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

int main() { return 0; }

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

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

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


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

首选 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 不会浪费时间重写内容。
A
Alnitak

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

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

此外,在 C 或任何类似 C 的语言中,return 语句强烈暗示读者将在调用函数中继续执行,虽然如果计算调用 { 2} 函数,当你打算结束进程时,这并不是 的意思。

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

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


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

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


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

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

C standard 的第 5.1.2.2.3 节规定:

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

正如其他答案中提到的,C++ 的规则有点不同。


r
radrow

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。