虚空主体的起源是什么
What is the origin of void main?
我经常在论坛上看到臭名昭著的void main()
,问题后几乎立即有评论告诉用户永远不要使用void main()
(我完全同意)。但是void main()
的起源在哪里呢?
为什么我仍然看到更新的人养成了让main
什么都不回的坏习惯,而正确的方法是返回int
。我理解为什么这个问题和其他许多问题中解释的这种方法是错误的,但我不知道这种声明main
的方法是如何产生的,甚至不知道为什么它仍然被教授给一些学生。
甚至Bjarne Stroustrup也用C++编写了void main
,所以它确实是一个常见的反模因,而且是一个古老的反模因为,早于Java和其他支持void main
的当代语言。当然,Bjarne也写过void main
从来都不是C或C++的一部分。然而,对于后一种说法(在他的常见问题解答中),至少从C99开始,Bjarne似乎是错误的,因为C99标准的N869草案在其§5.1.2.2.3/1中规定
“如果
main
函数的返回类型是与int
兼容的类型,则从初始调用到main
函数的返回相当于以main
函数返回的值作为参数调用exit
函数;到达终止CCD_ 16功能的CCD_。如果返回类型与int
不兼容,则返回到主机环境的终止状态未指定”
之前,在其§5.1.2.2.1/1中,它说明了main
的签名,
“或者以某种其他实施方式定义的方式”
返回类型“与int
不兼容";例如可以是void
。
因此,虽然这不是一个完整的答案(我怀疑网上是否有关于这一点的历史来源),但至少它在一定程度上纠正了问题的假设。CCD_ 21在C和C++中并不是一个完全可憎的东西。但在C++中,它是无效的:它是托管C++实现中不支持的C东西。
我一直是这个问题的受害者,所以我想我可以告诉你为什么会发生这种情况。在我们的C课程中,教师必须使用示例程序(可能是"Hello World")开始我们的课程,为此他们必须使用main()方法。
但是,由于他们不想混淆学生,也不想在C编程课程一开始就陷入教授返回类型和返回语句的复杂性,他们使用(也要求我们使用)void main(),并告诉我们在详细研究函数和返回类型之前,假设这是默认类型。
因此,这导致我们养成了一个错误的习惯,即在C编程的第一堂课上使用void main()。
希望这能很好地解释为什么大多数计算机程序员,尤其是那些新来的程序员会采用这种糟糕的做法。
干杯,马扬克
我个人认为如下:K&RC不需要指定返回类型,并隐式地假设它是int和,同时K&R没有使用返回值。
例如,K&R第一版如下:
#include <stdio.h>
main()
{
printf("Hello Worldn");
}
因此,难怪后来读到这篇文章的人(在一些编译器将void类型作为扩展添加到语言中之后)认为main实际上有一个void返回语句。。我也会做同样的事。
实际上K&R稍后会说:
为了简洁起见,我们省略了到目前为止,我们的主要功能,但我们将包括它们此后,作为程序应返回其状态的提醒环境
所以这只是另一个例子,说明当你写了不正确的代码,并假设人们在做愚蠢的事情之前会阅读所有内容,随后会包含免责声明时会发生什么;)
作为众多作家中的一位,Herbert Schildt写了一些流行但不一定是高质量的书,这些书都支持这一想法。
一个令人震惊的例子是他的注释C标准。他在左侧页面引用了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
函数的初始调用返回相当于用main
函数返回的值作为参数调用exit
函数。如果main
函数执行的返回没有指定任何值,则返回到托管环境的终止状态是未定义的。
由于您会发现exit
采用int
参数,因此可以清楚地看出,main
的返回类型应该是int
。
评论说:
在大多数实现中,
main()
的返回值(如果有)会返回到操作系统。请记住,如果您没有显式返回main()
中的值,那么从技术上讲,传递给操作系统的值是未定义的。尽管当没有指定其他返回值时(即使main()
被声明为void
),大多数编译器都会自动返回0,但您不应该依赖这一事实,因为标准并不能保证这一点。
这些评论中有很多是牛粪,我并不是唯一一个持这种观点的人。这本书唯一的优点是,它包含了几乎所有的C90标准(fprintf
的描述中少了一页——同一页打印了两次),远远低于该标准的成本。有人认为,价格的差异代表了评论中的价值损失。关于C的一些信息,请参阅Lysor,以及Clive Feather对注释C标准的评论。
他的另一本书是《C:完整参考》,该书至少出版了第4版。第三版广泛使用CCD_ 39;这可能在第4版中得到了澄清,但令人遗憾的是,花了这么多版本才纠正了这样一个根本问题。
在裸机上运行的嵌入式程序,也就是说,在没有操作系统的情况下,永远不会返回。上电时,重置向量间接跳到main
(首先发生一些内存初始化),在main
内部,有一个无限的while (1){}
循环。从语义上讲,main
的返回值没有意义。
可能的原因:
- Java程序员习惯于编写
public static void main(...)
- 缺少return语句可能会导致一些人认为
main
不会返回,尽管它隐式返回0
- 在C中,您可以编写不带返回类型的
main()
,默认情况下为int
。也许有些人认为缺少的返回类型等同于void
- 坏书/老师
从C++的角度来看,存在3个混淆源:
- 原教旨主义PC/台式机程序员,他们狂热而盲目地宣扬
int main()
,而实际上自己并不了解标准的全貌。C和C++对于如何在独立系统中声明main()有完全不同的规则(当编程裸金属嵌入式系统或操作系统时) - 与C++相比,C语言在历史上有着不同的规则。在C中,main()的规则随着时间的推移而发生了变化
- 来自黑暗时代的遗留编译器和编码标准,包括20世纪80年代的编程教师
我将在这个答案中解决每一个困惑的来源。
PC/桌面程序员有问题,因为他们认为托管系统是唯一存在的系统,因此传播关于main()的正确形式的不正确/不完整的宣传,教条地说你必须使用int main()
,在这样做的时候错误地引用了标准(如果有的话)。
C和C++标准总是列出两种类型的系统:独立系统和托管系统。
在独立实现中,void main (void)
在C中一直是允许的。在C++中,独立实现略有不同:独立实现可能不命名入口函数main(),或者它必须遵循返回int
的声明形式。
即使是Bjarne Stroustrup也无法引用标准或正确/完整地解释这一点,所以难怪普通程序员会感到困惑!(他引用了托管环境子章节,但没有引用其中的所有相关部分)。
这一切都在这里详细讨论,并参考了标准,Bjarne和其他人请阅读。
关于托管系统中的void main (void)
,这源于ISO C标准之前黑暗时代的某个地方,在那里一切都是允许的。
我怀疑它背后的罪魁祸首是Borland Turbo C编译器,它在1990年发布ISO C时已经是市场领导者。此编译器允许void main (void)
。
需要注意的是,用于托管实现的void main (void)
在C90中被隐式禁止。对于托管系统,不允许任何实现定义的形式。因此Turbo C从来都不是一个严格符合要求的实现。然而,它仍然在学校中使用(尤其是在印度)!从头开始教每个学生不正确的编程标准。
自从C99以来,void main (void)
和其他形式在C中被允许使用,因为添加了一句奇怪的话:"或者以其他实现定义的方式"。这也在上面的链接答案中进行了讨论,参考了C99的基本原理和C标准的其他部分,这些部分假设托管系统main()可能不会返回int。
因此,在C中,void main (void)
(可以说)目前是托管实现的一种允许形式,因为编译器记录了它的功能。但请注意,由于这是实现定义的行为,是编译器决定是否允许这种形式,而不是程序员
在C++中,void main (void)
是不允许的形式。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- C++避免重复声明的语法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- C++中名称篡改的目的是什么
- 在 c++ 中拥有一组结构的正确方法是什么?
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 是什么阻止DOMTimerCoordinator::NextID进入无休止的循环
- 派生类销毁的最佳实践是什么
- 这个语法std::class<>{}(arg1, arg2) 在C++中是什么意思?
- 通过JNI传递数据数组的最快方法是什么
- "using namespace std;"在C++的作用是什么?
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- 文件系统:复制功能的速度秘诀是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 是什么原因导致它无法编译?它是声明签名还是在函数本身的实现中
- 虚空主体的起源是什么