您的第一个示例使用从 C 的过时方言继承的功能,该方言早于第一个 ANSI(1989)和 ISO(1990)标准:即,您可以编写一个不指定其返回类型的函数,并且在这种情况下,类型默认为int。
在早期的 C 中,void 关键字和相关类型不存在。当程序员想要编写过程(“具有副作用但不返回任何内容的函数”)时,他们会使用此功能对其进行模拟。他们编写了一个没有任何关键字指定返回类型的函数。他们允许函数在不返回值的情况下执行最后一条语句(或者,他们使用return; 从中间退出而不提供值),并且他们编写了对函数的调用,以便这些调用不会尝试使用返回值:
parse_input() /* similar to a procedure in Pascal, but fake! */
{
/* ... */
if (condition())
return; /* no value */
/* ... */
/* fall off end here */
}
int main()
{
parse_input(); /* no return value extracted, everything cool! */
return 0;
}
不幸的是,一些程序员也开始不关心程序的终止状态,并以这种程序风格编写main:
main()
{
/* do something */
/* fall off the end without returning a value */
}
(也存在混合样式:省略 int 声明符但返回整数值。)
这些未能返回值的程序具有不确定的终止状态。对于操作系统来说,它们的执行可能看起来成功或失败。 试图依赖这样一个程序的终止状态的脚本编写者有祸了!
然后事情变得更糟了。 C++ 出现并引入了 void,并被 C++ 采用。使用 C++ 中的 void 关键字,可以声明一个实际上不返回任何内容的函数(并且在任何其他语句中使用 return; 语句会成为错误一种功能)。以前写main 没有返回类型的傻瓜程序员变得更笨了,开始把这个新奇的、新鲜的C++ void 贴在前面:
void main() /* yikes! */
{
/* do something */
/* fall off the end without returning a value */
}
这时候他们忘记了,写main()的时候,其实是int main(),这使得函数的类型与环境调用的启动调用兼容(除了忽略返回一个价值)。现在他们实际上有一个与预期不同的函数类型,甚至可能没有成功调用!
现在的情况是,在 C++ 和最新的 C++ 标准中,main 仍然需要返回 int。但是这两种语言都为最初的虚拟程序员做出了让步:您可以让执行“下降”到 main 的末尾,并且行为就好像 return 0; 已经在那里执行了一样。所以这个琐碎的程序现在在 C99 和我认为 C++98(或者可能更早)中具有成功终止状态:
int main()
{
}
但是这两种语言都没有为第二代笨拙的程序员(以及阅读这些程序员在 1980 年代及以后编写的 C 书籍的其他所有人)做出让步。也就是说,void 不是 main 的有效返回声明符(除非平台记录它被接受,并且仅适用于这些平台,不适用于可移植语言)。
哦,在 C99 中从 C 中删除了缺少声明符的余量,因此 main() { } 在 C 的新方言中不再正确,并且不是有效的 C++。顺便说一句,C++ 在其他地方确实有这样的语法:即,类构造函数和析构函数不需要有返回类型说明符。
好的,现在关于 () 与 (void)。回想一下 C++ 引入了void。此外,虽然 C++ 引入了void,但它没有引入(void) 参数语法。 C++ 更严格的类型化引入了原型声明,并摒弃了非原型函数的概念。 C++ 改变了() C 语法的含义,赋予它声明的权力。在 C++ 中,int func(); 声明了一个没有参数的函数,而在 C 中,int func(); 没有做这样的事情:它声明了一个我们不知道参数信息的函数。当 C 采用 void 时,委员会有一个丑陋的想法:为什么我们不使用语法 (void) 来声明一个没有参数的函数,然后 () 语法可以保持向后兼容松散-goosey 遗留行为迎合无类型编程。
你可以猜到接下来会发生什么:C++ 人看了这个(void) hack,为了跨语言兼容性,举起手臂将其复制到 C++ 中。事后看来,当你看到今天的语言是如何分化的,并且基本上不再关心那种程度的兼容性时,这真是令人惊讶。所以(void) 在 C 和 C++ 中明确表示“声明为没有参数”。但是在 C++ 代码中使用它显然是纯 C++ 从来没有打算成为 C 是丑陋的,而且风格很差:例如,在类成员函数上!写class Foo { public: Foo(void); virtual ~Foo(void) /*...*/ };这样的东西没有多大意义
当然,当你定义像int main() { ... }这样的函数时,无论是哪种语言,定义的函数都没有参数。不同之处在于引入了哪些声明信息范围。在 C 语言中,我们可能会遇到一种荒谬的情况,即函数可以在同一程序文本单元中完全定义,但不声明!
当我们编写main 时,通常不会从程序内部调用它,因此定义声明的内容并不重要。 (在 C++ 中,main 不能从程序中调用;在 C 中可以)。因此,无论您使用的是 C 还是 C++,您是否编写 int main() 或 int main(void) 都无关紧要。调用main 的东西看不到它的任何声明(无论如何,你在程序中编写)。
所以请记住,如果你写:
int main() /* rather than main(void) */
{
}
那么,尽管它是完美的 C++ 和正确的 C,但作为 C,它有一点文体瑕疵:您正在编写一个旧式的 pre-ANSI-C 函数,它不能用作原型。尽管在 main 的情况下它在功能上并不重要,但如果您以某种方式使用某些编译器,您可能会收到警告。例如,使用 -Wstrict-prototypes 选项的 GCC:
test.c:1:5: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
因为-Wstrict-prototypes 是在 C 中编程时打开的非常有用的警告,以提高类型安全性(与 -Wmissing-prototypes 一起),并且我们努力从编译作业中消除警告,因此我们应该编写:
int main(void) /* modern C definition which prototypes the function */
{
}
这将使该诊断消失。
如果您希望main 接受参数,那么参数名称由您决定是int main(int argc, char **argv)。
在 C++ 中,您可以省略参数名称,因此这个定义是可能的,它很好地代替了main()。
int main(int, char **) // both arguments ignored: C++ only
{
}
由于参数向量是空指针终止的,因此您不需要argc,C++ 让我们可以在不引入未使用变量的情况下表达这一点:
#include <cstdio>
int main(int, char **argv) // omitted param name: C++ only
{
// dump the arguments
while (*argv)
std::puts(*argv++);
}