int main(){}(无“ void”)在ISO C中有效且可移植吗?

问题描述 投票:22回答:3

C标准为main指定了两种定义形式托管的实现:

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

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

它的定义方式可以与上述“等效”(对于例如,您可以更改参数名称,用typedef替换int名称定义为int,或将char *argv[]写为char **argv)。

也可以“以其他实现定义的方式定义”-这表示int main(int argc, char *argv[], char *envp)之类的内容有效if该实现记录了它们。

“以其他实现定义的方式”子句不在1989/1990标准;它是按1999年标准添加的(但较早的标准允许扩展,因此实现可以仍然允许其他形式)。

我的问题是:鉴于当前(2011年)的ISO C标准,表格的定义

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

对于所有托管实现有效且可移植吗?

(请注意,我不是在讲void main或使用int main()在C ++中不带括号。这只是关于ISO C中int main(void)int main()之间的区别。)

c language-lawyer c99 c11
3个回答
24
投票

编号

根据标准的规范用语,定义使用不带void关键字的空括号不是其中一种必须接受的表格,严格来说是这样的程序是不确定的。

参考:N15705.1.2.2.1节。 (已发布的2011 ISO C标准免费提供,其措词与N1570草案相同。)

第1段说:

在程序启动时调用的函数命名为main。实现声明为否此功能的原型。它的返回类型应定义为int且不包含参数:

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

或具有两个参数(此处称为argcargv,尽管可以使用任何名称使用,因为它们对于声明它们的函数是局部的):

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

或同等;或以其他实现定义的方式。

在约束之外使用“应”一词意味着违反它的程序具有未定义的行为。因此,例如,如果我写:

double main(unsigned long ocelots) { return ocelots / 3.14159; }

不需要符合要求的编译器即可打印诊断,但是也不需要编译程序,或者如果编译的话使其具有任何特定方式。

如果int main()int main(void)相等,则它对于任何符合标准的托管实施都是有效且可移植的。但这不是等效的。int main(void) { }

同时提供

declaration

(在本例中为原型)和定义。该声明通过使用void关键字指定该函数没有参数。该定义指定相同的内容。如果我改为写:

int main() { }

然后我使用的是

old-style

声明和定义。 (这样声明和定义是obsolescent,但它们仍然语言定义的一部分,所有符合标准的编译器必须仍然支持他们。)
作为声明,它没有指定参数的数量或类型该功能所期望的。作为定义,它没有定义任何参数,但是编译器不需要使用该信息来诊断错误的调用。

[DR #317包括C标准委员会2006年的一项裁决,即()的定义不能提供与(void)相同的原型(感谢hvd查找该参考)。

C允许递归调用main。假设我写:

int main(void) { if (0) { main(42); } }

[可见的原型int main(void)指定main没有争论。试图传递一个或多个参数的调用违反约束,需要编译时诊断。

或者假设我写:

int main() { if (0) { main(42); } }

如果执行了调用main(42),它将具有未定义的行为-但它没有违反约束,因此不需要诊断。由于它受到if (0)的保护,因此通话永远不会发生,并且未定义的行为永远不会真正发生。如果我们假设int main()有效,那么任何人都必须接受该程序符合要求的编译器。但是正因为如此,它证明了int main()

not

等同于int main(void),因此不在5.1.2.2.1范围内。

结论:

按照标准的措词,允许将int main() { }记录为允许的。如果没有记录,仍然可以接受它没有抱怨。但是,合格的编译器也可能rejectint main() { },因为它不是以下格式允许的形式之一该标准,因此其行为是不确定的。
但是仍然存在一个未解决的问题:是作者的意图吗?的标准?

[在1989年ANSI C标准发布之前,void关键字不存在。 ANSI前(K&R)C程序将定义main要么作为>

main()

或作为

int main()

ANSI标准的主要目标是添加新功能(包括原型)

without

破坏现有的ANSI前代码。说明int main()不再有效,将违反该目标。
我怀疑C标准的作者没有

打算

使int main()无效。但是书面的标准并没有反映该意图;它至少permits合格的C编译器拒绝int main()

实际上

来说,您几乎可以肯定会摆脱它。我曾经尝试过的每个C编译器都可以接受int main() { return 0; }
无投诉,行为等同于

int main(void) { return 0; }

但是由于多种原因:

    同时遵循本标准的宗旨和意图;
  • 避免使用过时的功能(将来的标准可能会删除旧式的函数定义);
  • 保持良好的编码习惯(()(void)之间的差异对于main以外的其他功能实际上要调用的功能很重要。]
  • 我建议始终写int main(void)而不是int main()。它可以更清楚地说明意图,您可以100%确保编译器将接受它,而不是99.9%。

int main()是有效的有力说明,无论标准是否准确给出了使之生效的措辞,都是这样的事实,即int main()偶尔会在标准中使用而没有任何人提出异议。尽管示例不是规范性的,但它们确实表明了意图。

6.5.3.4 sizeof和_Alignof运算符

8例3,在此例中,计算可变长度数组的大小并从函数返回它:

#include <stddef.h> size_t fsize3(int n) { char b[n+3]; // variable length array return sizeof b; // execution time sizeof } int main() { size_t size; size = fsize3(10); // fsize3 returns 13 return 0; }

6.7.6.3函数声明符(包括原型)

20例4以下原型具有可变修改的参数。

void addscalar(int n, int m, double a[n][n*m+300], double x); int main() { double b[4][308]; addscalar(4, 2, b, 2.17); return 0; } void addscalar(int n, int m, double a[n][n*m+300], double x) { for (int i = 0; i < n; i++) for (int j = 0, k = n*m+300; j < k; j++) // a is a pointer to a VLA with n*m+300 elements a[i][j] += x; }

关于标准的实际规范性文本,我认为太多内容被理解为“等效”。应该很清楚

int main (int argc, char *argv[]) { (void) argc; (void) argv; return 0; }

有效,并且那个

int main (int x, char *y[]) { (void) argc; (void) argv; return 0; }

无效。但是,该标准在规范性文本中明确指出可以使用任何名称,这意味着[5.1]中int main (int argc, char *argv[])int main (int x, char *y[])等同。 “等价”一词的严格英语含义不是应理解的含义。

[Keith Thompson在回答中对这个词的解释有些松散。

对单词的同样有效甚至更松散的解释的确允许int main()int main(void)int main()都将main定义为返回int且不带参数的函数。

[标准或任何正式DR目前都没有回答要解释的问题,因此该问题无法回答,但是这些示例强烈建议最后解释。

是。

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

相当于

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

N1570 5.1.2.2.1 / 1

在程序启动时调用的函数称为main。实现声明为否此功能的原型。

应使用int的返回类型定义,并且不使用参数

int main(void) { /* ... */ }
或带有两个参数(这里称为argc和argv,尽管任何名称都可以是使用,因为它们对于声明它们的函数是局部的):

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

或同等;或以其他实现定义的方式。

6.7.6.3/14

标识符列表仅声明函数参数的标识符。

一个空作为该函数定义一部分的函数声明器中的列表指定函数没有参数。

函数声明符中的空列表不属于该函数的定义指定没有关于其数量或类型的信息提供了参数。(重点是我的)

如标准明确指出的那样,定义int main() { /* ... */ }

确实指定功能main没有参数。我们所有人都清楚,此函数定义does

指定函数main的返回类型为int。并且,由于5.1.2.2.1不需要声明main才具有原型,因此我们可以安全地确认定义int main() { /* ... */ }满足标准(It [the main funtion] shall be defined with a return type of int and with no parameters, or [some other forms] .)提出的所有要求。
尽管如此,您永远不要在代码中使用int main() {},因为“使用带有空括号的函数声明符(不是原型格式的参数类型声明符)是过时的功能。” (6.11.6),并且由于此定义形式不包含函数原型声明器,因此编译器将不会检查参数的数量和类型是否正确。

N1570 6.5.2.2/8

没有其他转换隐式执行;尤其是

在函数定义中,参数未与参数的参数进行比较不包含函数原型声明符]]。

(重点是我的)

13
投票
int main()是有效的有力说明,无论标准是否准确给出了使之生效的措辞,都是这样的事实,即int main()偶尔会在标准中使用而没有任何人提出异议。尽管示例不是规范性的,但它们确实表明了意图。

7
投票
是。
© www.soinside.com 2019 - 2024. All rights reserved.