为什么C和C++中主函数的类型由用户定义?

19

为什么main()是用户定义的函数?

何时使用void main()int main()


每个非库函数的类型都由用户定义。 main 不同之处在于选择集更窄。 - Keith Thompson
6个回答

40

编辑:这个答案并不是非常完整,因为它没有真正解释奇怪的句子"或以某种实现定义的方式"。我已经写了一个更完整的答案,它还涵盖了C90、C11和C++。 编辑结束

以下是C标准所说的(ISO C 9899:1999):

5.1.2.1 自由环境

在自由环境中(程序不受操作系统影响执行的情况),调用程序启动时的函数的名称和类型是由实现定义的。 / .. / 在自由环境中,程序终止的效果是由实现定义的。

5.1.2.2 托管环境

托管环境可有可无,但如果存在,必须符合以下规范。

5.1.2.2.1 程序启动

在程序启动时调用的函数名为 main。 实现不为此函数声明原型。 它应该具有返回类型int且没有参数:

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

或具有两个参数(这里称为argc和argv,但可以使用任何名称,因为它们是在声明它们的函数中局部的):

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

C ++标准中的文本基本相同。 请注意,“程序启动”在文本中是托管环境的“子条款”。

这意味着:

  • 如果您的程序运行在无主机环境下(您的程序是嵌入式系统或操作系统),它可以有任何返回类型。void main() 是最常见的。

  • 如果您的程序运行在托管环境中(在操作系统之上),main() 必须返回 int,并且可能具有其他参数。


6
对于有效的答案点赞。但也许需要更多的解释。在独立环境中,main 函数的返回类型是实现定义的:这并不意味着由程序员决定,而是被平台强制规定的。编译器手册应该会说明这一点。在托管环境中,参数的选择确实只有两个给定的备选项。如果存在参数,则必须恰好为两个,并且具有给定的类型。顺便说一下,我认为你漏掉了 argv 的一个 * - Jens Gustedt
2
在检查标准时,您实际上跳过了该部分最后一句话的续写:“或等效方式;9)或以其他一些实现定义的方式。” 这意味着编译器实现可能会再次具有不同的调用约定。 一些编译器例如允许第三个参数“char * envp []”。 但是这也必须在编译器文档中指定。 - Jens Gustedt
2
C标准允许main函数的返回类型为除int以外的其他类型。5.1.2.1的措辞存在歧义,但是5.1.2.2.3开头写道:“如果main函数的返回类型与int兼容…” - Keith Thompson
1
@KeithThompson 5.1.2.1指定了一个独立环境,因此与5.1.2.2.3无关,后者仅适用于托管环境。是的,标准允许实现定义的方法来声明main,以及返回int类型之外的其他类型时未指定的行为。但这些仍然是由编译器实现者而不是程序员做出的决策,因此我认为最好将所有这些特殊情况都从答案中排除掉。 - Lundin
2
@Lundin:我的观点很简单,你的回答中有一个事实上不正确的陈述。符合标准的C语言实现可以记录并允许void main(void)。(而且你所描述的行为并不会使Turbo C不符合标准;如果实现没有记录它,定义void main()就会产生未定义的行为,但它并不需要诊断。) - Keith Thompson
显示剩余10条评论

7

Lundin 关于 C 的说法是正确的,但在 C++ 中措辞已经足够明确以产生差异:

[C++11: 3.6.1/1]: 程序必须包含一个名为 main 的全局函数,该函数是程序的指定起点。在自由环境中是否需要定义 main 函数是实现定义的。

[C++11: 3.6.1/2]: 实现不应预定义 main 函数。该函数不得重载。它应该返回类型为 int 的返回值,但其余部分的类型是实现定义的 [..]

第一个加粗的段落不会覆盖或取消第二个段落。

main 在 C++ 中始终返回 int


4
你可以使用 void……但也仅仅只限于此。 - Bartek Banachewicz
1
然后3.6.1/1继续说:“注意:在独立环境中,启动和终止是实现定义的”。我不知道为什么C++11中的措辞发生了变化,但这只能合理地解释为“独立实现可以完全按照自己的意愿声明main函数”。对于没有任何操作系统的程序或操作系统本身来说,返回int是没有意义的。他们会把int返回给谁呢? - Lundin
@Tim 但是在C++11中还有更多的内容。例如,如果您定义了main函数,不仅必须返回int,还必须允许argv argc,这在独立环境中也是完全无意义的。而且这是规范性文本,没有任何绕过的方法。显然,在发布C++11期间,C++委员会被狂热的PC程序员劫持了。由于3.6.2中所有这些教条主义的废话,该语言在嵌入式系统中变得不那么有用。 - Lundin
@Lundin 这个标准引用片段并没有做出除了其返回类型为int之外的任何声明,它明确表示在自由环境(即嵌入式)中甚至不需要main函数。 - Tim Seguine
@Lundin 我刚刚读了它。它要求两种形式的main可用(但程序员不必使用其中之一),其中之一是不接受参数的函数。它还允许编译器允许其他签名。正如我已经解释过的,返回int并不是问题(即使对于嵌入式系统也是如此)。如果您的编译器强制要求参数和返回值在启动代码中占用存储空间,那么这是您的编译器的问题,而不是标准的问题。 - Tim Seguine
显示剩余6条评论

5
< p > 对于 < code > main 的返回类型是由实现确定而不是程序员。检查编译器文档以查看 < code > main 的合法签名。不要假设 < code > void main() 是其中之一。在托管环境中,main通常返回int。在独立环境中,入口点甚至可能没有命名为main,但其返回类型仍然由实现而非程序员确定。

3
有三种情况:
  1. 自由实现
  2. 符合标准的托管实现,没有扩展
  3. 带有扩展的托管实现
在 1 中,完全不需要有名为 main 的函数。实现定义了程序如何启动。 在 2 中,程序从一个名为 main 的函数开始执行,该函数使用以下两个“签名”之一定义:int main(void)int main(int argc, char **argv) 在 3 中,程序从一个名为 main 的函数开始执行,该函数按实现允许的方式定义。此函数必须返回 int 才能符合标准。例如: int main(int argc, char **argv, char **envp)int main(wchar_t**)。请注意,使用这些形式的程序并不一定在所有托管实现中都有效(如果实现更改,则可能对原始作者无效)。

1
在第三种情况下,void main(void) 仍然是无效的。标准允许托管实现接受实现定义的参数到 main,但不允许接受不同的返回类型。 - R.. GitHub STOP HELPING ICE
1
@R..:C++要求main函数返回int类型;而C语言允许实现定义的形式返回其他类型。 - Keith Thompson
实现可能定义任意数量的标准之外的行为,只要它们不与符合 C 程序的编译或错误报告冲突。例如,gcc 语句表达式 ({...}) 似乎不会与语言的任何要求冲突,但即使它是针对通常使用 gcc 的平台而设计的,我也会犹豫地称使用它们的程序为“有效的 C”。 - R.. GitHub STOP HELPING ICE

1

最初,在C语言中,没有void这种类型,因此函数必须返回int

实际上,返回int允许您从您的进程中运行另一个进程(使用forkexec),如果您可以从该进程获取返回结果,则会知道它是否起作用。


-1

许多编译器不支持void main(),因此您应该始终使用int main()。


8
这是错误的。如果您的程序是嵌入式系统或操作系统,它将使用void main(),这在C/C++标准中是完全可以的。请查看我下面更详细的答案。 - Lundin
这不是错因,而是因为编译器支持某些内容与此无关。 - Lightness Races in Orbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接