C标准为a main
指定了两种形式的定义
托管实施:
int main(void) { /* ... */ }
和
int main(int argc, char *argv[]) { /* ... */ }
它可以以与上述“等同”的方式定义(for
例如,您可以更改参数名称,用typedef替换int
名称定义为int
,或将char *argv[]
写为char **argv
)。
它也可以“以某种其他实现定义的方式”定义
- 这意味着int main(int argc, char *argv[],
char *envp)
之类的内容有效,如果实施文档记录它们。
“在其他一些实现定义的方式”条款中没有 1989/1990标准;它是由1999年标准添加的(但是 早期的标准允许扩展,因此实现可以 仍允许其他形式)。
我的问题是:鉴于目前(2011年)的ISO C标准,是一个 表格的定义
int main() { /* ... */ }
对所有托管实施都有效且可移植吗?
(请注意,我没有解决void main
或使用。{
{+ 1}}在C ++中没有括号。这只是关于
ISO C中int main()
和int main(void)
之间的区别。)
答案 0 :(得分:23)
没有
根据标准的规范性措辞,定义
使用没有void
关键字的空括号不是其中之一
必须接受的形式,严格来说是行为
这样的程序是未定义的。
参考: N1570 第5.1.2.2.1节。 (公布的2011年ISO C标准,不是 免费提供,与N1570草案具有相同的措辞。)
第1段说:
程序启动时调用的函数名为
main
。实施宣布否 这个功能的原型。它的定义应为int
的返回类型,而不是 参数:int main(void) { /* ... */ }
或有两个参数(此处称为
argc
和argv
,但任何名称都可能是 使用,因为它们是声明它们的函数的本地函数):int main(int argc, char *argv[]) { /* ... */ }
或同等的;或者以其他一些实现定义的方式。
使用"这个词应该"在约束之外意味着任何 违反它的程序有未定义的行为。所以,例如,如果我写:
double main(unsigned long ocelots) { return ocelots / 3.14159; }
打印诊断程序并不需要符合标准的编译器,但它是正确的 也不需要编译程序,或者如果它编译 它,让它以任何特定的方式表现。
如果int main()
等效到int main(void)
,那么它
对任何符合要求的托管实现都是有效且可移植的。
但它并不等同。
int main(void) { }
提供声明(在本例中为原型)和a
定义。声明使用void
关键字指定该函数没有参数。该定义指定了相同的内容。
如果我改为写:
int main() { }
然后我使用旧式声明和定义。 (这样 声明和定义是 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()
不等同于int main(void)
,因此
不适用于5.1.2.2.1。
结论:遵循标准的措辞,a
允许实现记录int main() { }
允许的。如果它没有记录,它仍然允许接受
没有抱怨。但是符合标准的编译器也可以拒绝
int main() { }
,因为它不是允许的形式之一
标准,因此其行为未定义。
但仍有一个悬而未决的问题:这是作者的意图吗? 标准?
在1989 ANSI C标准发布之前,void
关键字不存在。预ANSI(K& R)C程序将定义main
无论是
main()
或
int main()
ANSI标准的主要目标是添加新功能(包括
原型)没有破坏现有的ANSI前代码。说明
int main()
不再有效会违反该目标。
我怀疑C标准的作者并非打算
使int main()
无效。但是所写的标准没有
反映这种意图;它至少允许符合C的编译器
拒绝int main()
。
实际上说话,你几乎肯定可以逃脱它。 我曾尝试过的每个C编译器都会接受
int main() { return 0; }
没有抱怨,行为相当于
int main(void) { return 0; }
但出于各种原因:
()
和(void)
之间的差异对main
以外的功能非常重要
实际上是由其他函数调用的我建议始终写int main(void)
而不是int main()
。
它更清楚地表明了意图,你可以100%确定你的意图
编译器会接受它,而不是99.9%。
答案 1 :(得分:13)
强烈表明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; }
至于标准的实际规范性文本,我认为过多的东西正在被读入&#34;等同于#34;。应该很清楚
int main (int argc, char *argv[]) {
(void) argc; (void) argv;
return 0;
}
有效,而且
int main (int x, char *y[]) {
(void) argc; (void) argv;
return 0;
}
无效。尽管如此,该标准在规范性文本中明确指出可以使用任何名称,这意味着int main (int argc, char *argv[])
和int main (int x, char *y[])
在5.1.2.2.1中被视为等效。严格的英语含义&#34;等同于&#34;不是如何阅读。
Keith Thompson在他的回答中提出了一个稍微宽松的解释。
对该单词的同等有效甚至更宽松的解释允许int main()
:int main(void)
和int main()
定义main
作为返回int
的函数并且不参数。
标准或任何官方DR目前都没有回答哪个解释的意图,所以问题是无法回答的,但这些例子强烈建议最后的解释。
答案 2 :(得分:7)
是。
int main() { /* ... */ }
相当于
int main(void) { /* ... */ }
N1570 5.1.2.2.1 / 1
程序启动时调用的函数名为main。实施宣布否 这个功能的原型。 应使用返回类型int和no来定义 参数强>:
int main(void) { /* ... */ }
或有两个参数(这里称为argc和argv,但可能有任何名称 使用,因为它们是声明它们的函数的本地函数):
int main(int argc, char *argv[]) { /* ... */ }
或同等的;或者以其他一些实现定义的方式。
6.7.6.3/14
标识符列表仅声明函数参数的标识符。 空 函数声明符中的列表是该函数定义的一部分,用于指定 function没有参数。函数声明符中不属于a的空列表 该函数的定义指定没有关于数量或类型的信息 提供参数。
(强调我的)
正如标准明确指出的那样,定义int main() { /* ... */ }
确实指定功能main
没有参数。我们所有人都清楚,这个函数定义确实指定函数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() {}
,因为“使用带有空括号的函数声明符(而不是prototype-format参数类型声明符)是一个过时的功能。” (6.11.6),并且因为这种形式的定义不包含函数原型声明符,所以编译器不会检查参数的数量和类型是否正确。
N1570 6.5.2.2/8
不会隐式执行其他转换;特别是,的数量和类型 参数不与函数定义中的参数进行比较 不包括函数原型声明器。
(强调我的)