【问题标题】:Why don't we use (void) in main?为什么我们不在 main 中使用 (void)?
【发布时间】:2011-03-10 13:02:45
【问题描述】:

人们使用void main() /*empty parens ()*/

有人教我写void main(void)

任何想法有什么区别?

【问题讨论】:

  • 你也不应该使用。 main 的正确签名(通常)是 int main(int argc, char* argv[]) ;-)
  • 另外,main 应该返回 int
  • 许多嵌入式系统编译器都期望void main(void)。例如,HiTech's C compiler。毕竟,返回值要去哪里?
  • @Nicholas Knight - 该特定编译器具有其他非标准扩展。但是他们还应该怎么称呼它呢? SuperHappyMagic 语言编译器?到底谁会发现呢?将其称为“一个 C 编译器,但与标准有偏差”可能会冒犯某些人的标准意识,但至少这意味着人们大致知道会发生什么。
  • @尼古拉斯。抱歉,但 C99 标准对 main 有这样的说法:“它应以 int 的返回类型且不带参数 ... 或两个参数 ... 或 以其他一些实现定义的方式定义 ”。 (第 5.1.2.2.1 节)因此,您可以以任何您喜欢的方式很好地定义 main,并且仍然被称为 C。

标签: c


【解决方案1】:

我不确定现在的标准是什么,但在传统的 ANSI C 中,使用空括号表示该函数可以采用 任何 个参数。另一方面,声明void 参数表示函数只接受零个参数。在这种情况下(以及许多其他情况),这真的没有太大关系。

如果你想要严格,最好定义void 参数。当然,main 函数也可以定义为 int main(int argc, const char* argv[]) - 这是完全有效的,但如果您不关心参数,通常是不必要的。

【讨论】:

  • @fahad:除非您使用的是稍微不标准的 C 编译器,就像前面提到的那样,否则您不应该这样做。 main 返回int。如果您声明它void,那么相关寄存器中剩下的任何内容都将被视为您的程序的退出代码,这通常不是一件好事。正如 Nicholas 所说,如果你不使用参数,你可以写 int main(void) 而不是完整的声明。
  • @Dummy00001:所以,很久很久以前(在计算机方面)。 :-) 20 多年。
  • @Dummy00001 在 ANSI C 之前,“标准”K & R C 中没有 void 关键字。假定没有返回类型的函数返回 int。
  • @ninjalj 定义便携!你完全错了。主原型必须与启动代码假定的原型相匹配。 main(void) 表示没有参数,不会改变启动代码调用 main 的方式,所以对我来说更合乎逻辑main()。如果启动代码将被编译然后链接,int main(void) 将引发 错误(不是警告!),请参阅我添加到答案中的代码
  • 我认为mainreal 定义还有第三个环境参数,但几乎所有人都忽略了它。我错了吗?
【解决方案2】:

没有区别,但通常 main 应该返回 int。一些编译器会给你一个警告(至少是 GNU 编译器 - gcc):

$ cat x.c
void main(void){}

$ gcc x.c
x.c: In function `main':
x.c:1: warning: return type of 'main' is not `int'

如前所述,main的原型是(按标准):

int main(int argc, const char* argv[])

【讨论】:

  • 但是您是否尝试过使用 gcc 版本或为特定环境制作的其他编译器进行交叉编译?如果标准要求必须使用两个参数调用 main 并且必须返回“退出值”,那么这是有道理的。我不确定,但我认为不是(如果确实如此,那么存在不标准的地方是有意义的)
  • @ShinTakezou:标准要求每个提供“托管”环境的编译器都必须支持 int main(void) 和 int main(int argc, char *argv[])。提供托管环境的编译器可以支持 main 的其他声明,提供独立环境的编译器可以调用他们在启动时选择的任何函数。
  • 所以,我是对的,该标准确实没有强制要求:int main()int main(void)int main(int argc, char **argv)void main()void main(void) 等等都是可能的,根据到环境。并且由于 main 和其他名称一样,启动代码可以调用另一个名称,只要它愿意。所以OP的Q的答案比较宽松,还是他要指定环境
  • int main(void) 是标准明确允许的,说双参数版本是“main 的原型”是错误的。
【解决方案3】:

这些 main() 的原型都是非标准的。

可以在 comp.lang.c 常见问题解答中找到有关该问题的精确度:http://c-faq.com/decl/main.html

编辑:将“错误”更改为“非标准”,因为规范允许实现定义的原型。

【讨论】:

  • 我不同意常见问题解答中所说的内容。 Main 由启动代码调用。如果启动代码忽略返回代码,则将 void main()void main(void) 作为 main 的原型是有意义的。
  • 它们在某些情况下可能有意义,但根据语言规范 (ISO/IEC 9899-1999),它们是不合法的。
  • @ShinTakezou:如果编译器文档明确表明支持void main()。如果不是,那么main 应该返回int。有一些平台(我相信 Acorn 就是其中之一)要么无法加载程序,要么在程序终止时崩溃,因为如果键入 main 时堆栈帧设置不正确 void
  • @ninjalj - 不,你错了。标准并不强迫它成为你所说的(参见另一个人所做的关于 std 关于 main 原型的评论)。谁期望 GNU/Linux 的代码可以移植到一个不起眼的“控制器”/嵌入式设备上?!
  • @John Bode,“这就是我所说的”我主要提到了我的答案。
【解决方案4】:

main 是一个函数,就像其他函数一样。几乎。无论如何,作为一个函数,它被其他一些代码(启动代码)调用。通常(阅读:几乎总是)int main() 是正确的,但实际上什么是真正正确的取决于您正在使用的平台。因为,如上所述,主函数可以由根本不传入任何参数的启动代码调用,并且在特定寄存器中不期望返回值(因此void main(void) 是正确的)。

int main() 是正确的,因为通常启动代码需要一个返回值,并传入两个参数。通过说int main(void),您是在说 main 根本不接受任何参数,在大多数情况下这是错误的。对于(),你说有参数(一、二、三,你不在乎),但你对它们不感兴趣,所以你对说它们是什么以及它们是哪种类型不感兴趣。

正如我在代码中看到的那样,当您忽略传递的 int argc, char **argv 参数时,“正常”环境(没有嵌入式设备或其他可以以不同方式调用 main 的“奇怪”环境)的最常用原型是 int main()。 (GCC 抱怨,因为我们使用的是适合环境的 gcc 版本;在启动代码不传递任何参数且不期望返回值的环境之一使用跨 GCC 版本对其进行测试)

编辑

只是为了善待持怀疑态度的人;在调用 main 函数的环境中,使用两个参数,如下

int func()
{
  return 0;
}

int func2(void)
{
  return 1;
}

int main(void)
{
  int a;
  a = func(a, a); /* A */
  a = func2(a);   /* B */
  return 0;
}

说 A 没有错误,而 B 说 too many arguments to function ‘func2’,用 gcc -std=c99 -pedantic 编译。将 int main(void) 更改为 int main() 没有任何区别,也没有警告。

在其他环境中(我现在无法进行实际测试),void main(void) 没问题,而在这种情况下它会发出警告。警告不仅仅是因为标准,而只是因为在使用的环境中 main 的原型不匹配。标准似乎允许 main 的任何其他“配置”。

在 OP 情况下,考虑到“正常”环境(例如 GNU/Linux 之类的操作系统),其中两个 args 被传递给 main,并且预期返回值,int main() 更可取(参数被推送无论您是否说int main(void),启动代码的堆栈,所以int main()对我来说更有意义)

编辑

还有一个注意事项,总是给持怀疑态度的人。正如已经证明的那样,B 引发了一个错误,因为我说过它是int func2(void),但我称之为传递参数。然后,假设我们可以编译启动代码并链接它,就像任何其他代码一样。在某个地方,它会调用 main,就像

retval = main(argc, argv);

如果我们使用int main(void),编译器将停止并给出错误,因为启动代码(在此环境中)试图用两个参数调用 main。如果我们使用int main(),则不会发生任何事情,并且代码会正确编译。

所以,int main()int main(void)优越(在我们期望两个参数主要可能的环境中)

编辑

电话更可能是这样的

retval = main(_argc, _argv, environ);

在许多系统上,但这不会改变之前的演讲。

最终编辑

有没有人发现,在使用int main(void) 构建命令行工具(即在int argc, char ** 有意义的系统上)时,所选的编译器/链接器链接了一个启动代码,其中main 在没有参数的情况下被调用(无论调用约定是),而当使用int main(int argc, char **argv) 构建时,启动代码是不同的,实际上使用这两个参数调用main(即使main 本身不使用它们)?

【讨论】:

  • int main() 是一个历史性的意外,从 1989 年就应该被弃用了。
  • 正如别人的回答所说,function() 的意思是,我不关心参数,如果有的话(任何数量的参数)。而 void 指定调用者 passes no args,你知道(如果在某处发生,则会发生错误)。在另一条评论中,有人报告了一段 C99 标准,对我来说很明显@987654354 @是合理的。还有:你写得更频繁int function() { ... } 还是int function(void) { ... }?如果我们分析大量代码,我们会更频繁地发现int func(void) ..int func() ..?此外,如果它是标准的一部分,-std=c99 -pedantic >
  • @ninjalj ...> -std=c99 -pedantic options 应该说些什么,但它没有。所以,告诉我们哪里说int main() 已被弃用。
  • 注意我写的是“应该是”,而不是“已经是”。当然,它被认为(正确地,IMNSHO)与遗留代码兼容比强制类型检查更重要。我仍然建议在编译 C 代码时使用 gcc 的 -Wstrict-prototypes -Wmissing-prototypes。
  • 即使使用-Wall 也没有警告。我在another answer 中重复了我的观点。为什么应该被弃用?我认为这是有道理的。特别是对于 main,其中int main() 表示“我不会使用传入的参数”,而int main(void) 表示“main 没有参数”;它们是不同的句子,这是我关注的重点,应该在传递 argc 和 argv 的地方使用int main()(因为它们无论如何都通过了)
【解决方案5】:

来自 C99 标准:

5.1.2.2.1 程序启动

程序启动时调用的函数名为main。实现声明没有 这个函数的原型。它应定义为返回类型为 int 并且没有 参数:

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

或带有两个参数(此处称为 argc 和 argv,尽管可以是任何名称) 使用,因为它们在声明它们的函数中是本地的):

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

或等价物;或以其他一些实现定义的方式。

When main is defined without parameters, will argc and argv still be present on the stack?

【讨论】:

  • «或以其他一些实现定义的方式。»由于OP没有指定实现,他的void main是可以的。虽然实现声明没有原型是错误的。确实它没有声明“公开可用”原型,但我挖掘启动代码,当用 C 本身编写时,它们确实声明原型(当然)。您的链接给出 404;无论如何,如果启动代码确实如此,答案是 yes所以;所以当您的意思是 int main(int argc, char **argv) 但您对 args 不感兴趣时​​,int main() 更合乎逻辑。
  • 很多人误解了最后一部分:或以其他一些实现定义的方式。我强烈建议阅读 Stroustrup 以了解这一点:请参阅 www2.research.att.com/~bs/bs_faq2.html#void-main
猜你喜欢
  • 2010-10-01
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多