17

很多时候,我在论坛上看到臭名昭著void main()的问题,并且几乎立即在问题之后发表评论,告诉用户永远不要使用void main()(我完全同意)。但它的起源在void main()哪里?

为什么我仍然看到新人养成了main什么都不退货的坏习惯,而正确的方法是退货int。我理解为什么这种方法是错误的,正如这个问题和其他许多人所解释的那样,但我不知道这种声明方法是如何产生的,main甚至不知道为什么它仍然教给一些学生。

4

7 回答 7

10

甚至 Bjarne Stroustrup 也用void mainC++编写了void main. 当然,Bjarne 也写过它void main从未成为 C 或 C++ 的一部分。然而,对于后一种说法(在他的常见问题解答中),至少从 C99 开始,Bjarne 看起来好像是错误的,因为 C99 标准的 N869 草案在其 §5.1.2.2.3/1 中说

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

更早前,在其 §5.1.2.2.1/1 中,它说明了 的签名main

“或以其他一些实现定义的方式。”

例如,返回类型“与 不兼容int”可以是void

因此,虽然这不是一个完整的答案(我怀疑网络上是否有关于此的历史资料),但至少它在某种程度上有助于纠正问题的假设void main在 C 和 C++ 中,这种情况并非完全令人厌恶。但在 C++ 中它是无效的:它是托管 C++ 实现中不支持的 C 事物。

于 2012-12-19T07:09:51.703 回答
4

我一直是这个问题的受害者,所以我想我可以告诉你为什么会这样()方法。

但是由于他们不想让学生感到困惑,也不想在 C 编程课程的一开始就涉及到教授返回类型和返回语句的复杂性,所以他们使用(并且还要求我们使用)void main()并告诉我们假设这是默认类型,直到我们详细研究函数和返回类型。

因此,这会导致我们在 C 编程的第一堂课中养成使用 void main() 的错误习惯。

希望这能很好地解释为什么大多数计算机程序员,尤其是较新的程序员会选择这种不好的做法。

干杯,马扬克

于 2012-12-19T07:17:23.977 回答
2

我个人认为是这样的:K&R C 不需要指定返回类型并隐式假定它是 int并且同时 K&R 中的示例没有使用返回值。

例如,K&R 第一版中的第一个代码如下:

#include <stdio.h>

main() 

{
   printf("Hello World\n");
}

因此,难怪人们后来(在某些编译器将 void 类型作为扩展添加到语言中之后)认为 main 实际上有一个 void return 语句。我会做同样的事情。

实际上K&R后来确实说:

为简单起见,到目前为止,我们从主要函数中省略了 return 语句,但我们将在后面包含它们,以提醒程序应该将状态返回到它们的环境。

所以这只是另一个例子,当你编写不正确的代码并在稍后包含免责声明时,假设人们会在做愚蠢的事情之前阅读所有内容;)

于 2012-12-19T07:10:54.053 回答
2

作为众多其他作家中的一位,赫伯特·希尔特(Herbert Schildt)写了一些流行但不一定是高质量的书籍来支持这一想法。

一个令人震惊的例子是他的 The Annotated C Standard。他在左页引用了 ISO/IEC 9899:1990 标准,并在右页提供了注释。当他引用第 5.1.2.2.1 节程序启动时,它说:

程序启动时调用的函数名为main. 实现没有声明这个函数的原型。它可以不带参数定义:

int main(void) { /* ... */ }

或使用两个参数(...):

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

这不包括添加到 C99 中的“或以某种其他实现定义的方式”子句。

然后,在注释中,他说:

main()有趣的是,编译器没有声明原型。因此,您可以根据程序的要求自由声明main()。例如,以下是三种常用的声明方法main()

void main(void)  /* no return value, no parameters */

int main(void)   /* return a value, no parameters */

/* return a value and include command-line parameters */
int main(int argc, char *argv[])

C90 标准不允许第一个变体,尽管他这么说,但无辜的读者可能会感到困惑。

请注意,第 5.1.2.2.3 节程序终止说:

对函数的初始调用的返回main等效于以exit函数返回的值main作为参数调用函数。如果main函数执行没有指定值的返回,则返回到托管环境的终止状态是未定义的。

由于您会发现它exit需要一个int参数,因此很明显返回类型main应该是int.

评论说:

在大多数实现中,来自 的返回值main()(如果有的话)被返回给操作系统。请记住,如果您没有显式返回一个值,main()那么从技术上讲,传递给操作系统的值是未定义的。尽管大多数编译器在没有指定其他返回值时会自动返回 0(即使main()声明为void),但您不应依赖这一事实,因为标准不保证这一点。

这篇评论中有些是牛粪,我并不是唯一一个持有这种观点的人。这本书的唯一优点是它包含了几乎所有的 C90 标准(描述中缺少一页fprintf——同一页被打印了两次),而且成本远低于标准的成本。有人认为,价格差异代表评论价值的损失。有关C 的一些信息,请参阅Lysator ,以及Clive Feather对 The Annotated C Standard 的评论。

他的另一本书是C: The Complete Reference,它至少出版到了第 4 版。第三版被广泛使用void main();这可能已经被第 4 版清理了,但很遗憾花了这么多版本来纠正这样一个基本问题。

于 2015-07-10T14:32:23.443 回答
2

在没有操作系统的裸机上运行的嵌入式程序永远不会返回。上电时,复位向量间接跳转(首先发生一些内存初始化)到main和内部main,有一个无限while (1){}循环。从语义上讲, for 的返回值main没有意义。

于 2015-07-10T15:04:20.147 回答
1

可能的原因:

  • Java 程序员习惯于编写public static void main(...).
  • 缺少 return 语句可能有一些假设不会main返回,尽管它隐式返回0
  • 在 C 中,您可以编写main()没有返回类型,并且int默认情况下是这样。也许有些人认为缺少的返回类型等同于void.
  • 坏书/老师?
于 2012-12-19T06:50:13.027 回答
1

从 C++ 的角度来看,存在 3 个混淆来源:

  • 原教旨主义 PC/桌面程序员,他们狂热而盲目地宣扬int main(),但自己并不真正了解标准的全貌。C 和 C++ 对于如何在独立系统中声明 main() 有完全不同的规则(在编程裸机嵌入式系统或操作系统时)。
  • 与 C++ 相比,C 语言在历史上有着不同的规则。在 C 中,main() 的规则随着时间的推移而改变。
  • 来自黑暗时代的遗留编译器和编码标准,包括停留在 1980 年代的编程教师。

我将在这个答案中解决每个混淆的来源。

PC/桌面程序员是有问题的,因为他们认为托管系统是唯一存在的系统,因此传播了关于 main() 正确形式的不正确/不完整的宣传,武断地声明你必须使用int main(),在这样做的时候错误地引用了标准,如果一点也不。

C 和 C++ 标准都一直列出两种系统:独立式和托管式。

在独立实现中,void main (void)在 C 中一直是允许的。在 C++ 中,独立实现略有不同:独立实现可能不会将入口函数命名为 main(),或者它必须遵循 return 的规定形式int

甚至 Bjarne Stroustrup 都没有设法引用标准或正确/完整地解释这一点,所以难怪普通程序员会感到困惑!(他引用了托管环境子章节,并没有引用它的所有相关部分)。

一切都在此处参考标准进行了详细讨论,Bjarne 和其他人请阅读。

关于void main (void)托管系统,这可以追溯到很久以前,在 ISO C 标准之前的黑暗时代,一切都被允许。

我怀疑它背后的罪魁祸首是 Borland Turbo C 编译器,它在 1990 年发布 ISO C 时已经是市场领导者。这个编译器允许void main (void).

并且应该注意,void main (void)对于托管系统,C90 中隐式禁止托管实现,不允许实现定义的形式。所以 Turbo C 从来就不是一个严格遵守的实现。然而它仍然在学校中使用(尤其是在印度)!从头开始教每个学生不正确的编程标准。

由于 C99void main (void)和其他形式在 C 中被允许,因为添加了一个奇怪的句子:“或以某种其他实现定义的方式”。这也在上面的链接答案中进行了讨论,参考了 C99 基本原理和 C 标准的其他部分,这些部分假设托管系统 main() 可能不会返回 int。

因此,在 C 中,void main (void)(可以说)当前是托管实现的允许形式,因为编译器记录了它的作用。但请注意,由于这是实现定义的行为,因此决定是否允许这种形式的是编译器,而不是程序员!

在 C++ 中,void main (void)不是允许的形式。

于 2015-07-10T10:58:49.960 回答