很多时候,我在论坛上看到臭名昭著void main()
的问题,并且几乎立即在问题之后发表评论,告诉用户永远不要使用void main()
(我完全同意)。但它的起源在void main()
哪里?
为什么我仍然看到新人养成了main
什么都不退货的坏习惯,而正确的方法是退货int
。我理解为什么这种方法是错误的,正如这个问题和其他许多人所解释的那样,但我不知道这种声明方法是如何产生的,main
甚至不知道为什么它仍然教给一些学生。
甚至 Bjarne Stroustrup 也用void main
C++编写了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 事物。
我一直是这个问题的受害者,所以我想我可以告诉你为什么会这样()方法。
但是由于他们不想让学生感到困惑,也不想在 C 编程课程的一开始就涉及到教授返回类型和返回语句的复杂性,所以他们使用(并且还要求我们使用)void main()并告诉我们假设这是默认类型,直到我们详细研究函数和返回类型。
因此,这会导致我们在 C 编程的第一堂课中养成使用 void main() 的错误习惯。
希望这能很好地解释为什么大多数计算机程序员,尤其是较新的程序员会选择这种不好的做法。
干杯,马扬克
我个人认为是这样的:K&R C 不需要指定返回类型并隐式假定它是 int并且同时 K&R 中的示例没有使用返回值。
例如,K&R 第一版中的第一个代码如下:
#include <stdio.h>
main()
{
printf("Hello World\n");
}
因此,难怪人们后来(在某些编译器将 void 类型作为扩展添加到语言中之后)认为 main 实际上有一个 void return 语句。我会做同样的事情。
实际上K&R后来确实说:
为简单起见,到目前为止,我们从主要函数中省略了 return 语句,但我们将在后面包含它们,以提醒程序应该将状态返回到它们的环境。
所以这只是另一个例子,当你编写不正确的代码并在稍后包含免责声明时,假设人们会在做愚蠢的事情之前阅读所有内容;)
作为众多其他作家中的一位,赫伯特·希尔特(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 版清理了,但很遗憾花了这么多版本来纠正这样一个基本问题。
在没有操作系统的裸机上运行的嵌入式程序永远不会返回。上电时,复位向量间接跳转(首先发生一些内存初始化)到main
和内部main
,有一个无限while (1){}
循环。从语义上讲, for 的返回值main
没有意义。
可能的原因:
public static void main(...)
.main
返回,尽管它隐式返回0
。main()
没有返回类型,并且int
默认情况下是这样。也许有些人认为缺少的返回类型等同于void
.从 C++ 的角度来看,存在 3 个混淆来源:
int main()
,但自己并不真正了解标准的全貌。C 和 C++ 对于如何在独立系统中声明 main() 有完全不同的规则(在编程裸机嵌入式系统或操作系统时)。我将在这个答案中解决每个混淆的来源。
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)
不是允许的形式。