int main() { }(没有 "void")在 ISO C 中是否有效且可移植?
Is int main() { } (without "void") valid and portable in ISO C?
C 标准为 main
指定了两种形式的定义
托管实施:
int main(void) { /* ... */ }
和
int main(int argc, char *argv[]) { /* ... */ }
它的定义方式可能与上述 "equivalent" 相同(例如
例如,您可以更改参数名称,将 int
替换为 typedef
名称定义为int
,或将char *argv[]
写为char **argv
).
也可以定义"in some other implementation-defined manner"
-- 这意味着像 int main(int argc, char *argv[],
char *envp)
这样的东西是有效的 如果 实现记录了它们。
"in some other implementation-defined manner" 子句不在
1989/1990标准;它是由 1999 标准添加的(但
早期的标准允许扩展,因此实现可以
仍然允许其他形式)。
我的问题是:鉴于当前 (2011) ISO C 标准,是
表格定义
int main() { /* ... */ }
对所有托管实施有效且可移植?
(请注意,我不是在解决 void main
或使用
int main()
在 C++ 中没有括号。这只是关于
ISO C 中 int main(void)
和 int main()
的区别。)
没有
根据标准的规范性写法,一个定义
使用没有 void
关键字的空括号不是
必须接受的形式,严格来说是行为
这样的程序是未定义的。
参考:
N1570
第 5.1.2.2.1 节。 (2011 年发布的 ISO C 标准,不是
免费提供,与 N1570 草案的措辞相同。)
第 1 段说:
The function called at program startup is named main
. The implementation declares no
prototype for this function. It shall be defined with a return type of int
and with no
parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc
and argv
, though any names may be
used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent; or in some other implementation-defined manner.
在约束之外使用 "shall" 一词意味着任何
违反它的程序具有未定义的行为。因此,例如,如果我写:
double main(unsigned long ocelots) { return ocelots / 3.14159; }
不需要符合标准的编译器来打印诊断信息,但它是
也不需要编译程序,或者如果它确实编译
它,让它以任何特定的方式表现。
如果int main()
等价于到int main(void)
,那么它
对于任何符合要求的托管实现都是有效的和可移植的。
但这并不等同。
int main(void) { }
提供了 声明(在本例中为原型)和
定义。声明通过使用 void
关键字指定该函数没有参数。定义指定了相同的东西。
如果我改写:
int main() { }
然后我使用旧式声明和定义。 (这样的
声明和定义过时,但它们仍然
语言定义的一部分,所有符合标准的编译器都必须
还是支持他们。)
作为声明,它没有指定参数的数量或类型
函数所期望的。作为定义,它不定义任何参数,
但编译器不需要使用该信息来诊断不正确的调用。
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.
结论:按照标准的措辞,一个
允许实施记录 int main() { }
是
允许。如果它没有记录它,它仍然被允许接受
它毫无怨言。但是符合标准的编译器也可能 拒绝
int main() { }
,因为它不是允许的形式之一
标准,因此它的行为是未定义的。
但仍有一个悬而未决的问题:这是作者的意图吗
的标准?
在 1989 年 ANSI C 标准发布之前,void
关键字不存在。 Pre-ANSI (K&R) C 程序将定义 main
作为
main()
或
int main()
ANSI 标准的一个主要目标是添加新功能(包括
prototypes) without breaking existing pre-ANSI code.说明
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%。
是的。
int main() { /* ... */ }
相当于
int main(void) { /* ... */ }
N1570 5.1.2.2.1/1
The function called at program startup is named main. The implementation declares no
prototype for this function. It shall be defined with a return type of int and with no
parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be
used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent; or in some other implementation-defined manner.
6.7.6.3/14
An identifier list declares only the identifiers of the parameters of the function. An empty
list in a function declarator that is part of a definition of that function specifies that the
function has no parameters. The empty list in a function declarator that is not part of a
definition of that function specifies that no information about the number or types of the
parameters is supplied.
(强调我的)
正如标准明确指出的那样,定义 int main() { /* ... */ }
确实 指定函数 main
没有参数。而且我们都很清楚,这个函数定义确实指定函数main
的return类型是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() {}
,因为 "The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature." (6.11.6),并且由于这种形式的定义不包含函数原型声明符,因此编译器不会检查参数的数量和类型是否正确
N1570 6.5.2.2/8
No other conversions are performed implicitly; in particular, the number and types of
arguments are not compared with those of the parameters in a function definition that
does not include a function prototype declarator.
(强调我的)
一个强烈的迹象表明 int main()
是有效的,无论标准是否准确地给出了使其有效的措辞,事实上 int main()
偶尔在标准中使用而没有任何人提出任何异议。虽然示例不是规范的,但它们确实表明了意图。
6.5.3.4 The sizeof and _Alignof operators
8 EXAMPLE 3 In this example, the size of a variable length array is computed and returned from a function:
#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 Function declarators (including prototypes)
20 EXAMPLE 4 The following prototype has a variably modified parameter.
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;
}
无效。尽管如此,该标准在规范性文本中明确指出可以使用任何名称,这意味着 int main (int argc, char *argv[])
和 int main (int x, char *y[])
就 5.1.2.2.1 的目的而言是等效的。 “等效”一词的严格英语含义并不是它的意思。
基思·汤普森 (Keith Thompson) 在他的回答中建议对该词进行更宽松的解释。
对这个词的一个同样有效甚至更宽松的解释确实允许 int main()
:int main(void)
和 int main()
都将 main
定义为返回 int
和不带参数。
标准和任何官方 DR 目前都没有回答预期使用哪种解释的问题,因此该问题无法回答,但示例强烈表明最后的解释。
C 标准为 main
指定了两种形式的定义
托管实施:
int main(void) { /* ... */ }
和
int main(int argc, char *argv[]) { /* ... */ }
它的定义方式可能与上述 "equivalent" 相同(例如
例如,您可以更改参数名称,将 int
替换为 typedef
名称定义为int
,或将char *argv[]
写为char **argv
).
也可以定义"in some other implementation-defined manner"
-- 这意味着像 int main(int argc, char *argv[],
char *envp)
这样的东西是有效的 如果 实现记录了它们。
"in some other implementation-defined manner" 子句不在 1989/1990标准;它是由 1999 标准添加的(但 早期的标准允许扩展,因此实现可以 仍然允许其他形式)。
我的问题是:鉴于当前 (2011) ISO C 标准,是 表格定义
int main() { /* ... */ }
对所有托管实施有效且可移植?
(请注意,我不是在解决 void main
或使用
int main()
在 C++ 中没有括号。这只是关于
ISO C 中 int main(void)
和 int main()
的区别。)
没有
根据标准的规范性写法,一个定义
使用没有 void
关键字的空括号不是
必须接受的形式,严格来说是行为
这样的程序是未定义的。
参考: N1570 第 5.1.2.2.1 节。 (2011 年发布的 ISO C 标准,不是 免费提供,与 N1570 草案的措辞相同。)
第 1 段说:
The function called at program startup is named
main
. The implementation declares no prototype for this function. It shall be defined with a return type ofint
and with no parameters:int main(void) { /* ... */ }
or with two parameters (referred to here as
argc
andargv
, though any names may be used, as they are local to the function in which they are declared):int main(int argc, char *argv[]) { /* ... */ }
or equivalent; or in some other implementation-defined manner.
在约束之外使用 "shall" 一词意味着任何 违反它的程序具有未定义的行为。因此,例如,如果我写:
double main(unsigned long ocelots) { return ocelots / 3.14159; }
不需要符合标准的编译器来打印诊断信息,但它是 也不需要编译程序,或者如果它确实编译 它,让它以任何特定的方式表现。
如果int main()
等价于到int main(void)
,那么它
对于任何符合要求的托管实现都是有效的和可移植的。
但这并不等同。
int main(void) { }
提供了 声明(在本例中为原型)和
定义。声明通过使用 void
关键字指定该函数没有参数。定义指定了相同的东西。
如果我改写:
int main() { }
然后我使用旧式声明和定义。 (这样的 声明和定义过时,但它们仍然 语言定义的一部分,所有符合标准的编译器都必须 还是支持他们。)
作为声明,它没有指定参数的数量或类型 函数所期望的。作为定义,它不定义任何参数, 但编译器不需要使用该信息来诊断不正确的调用。
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.
结论:按照标准的措辞,一个
允许实施记录 int main() { }
是
允许。如果它没有记录它,它仍然被允许接受
它毫无怨言。但是符合标准的编译器也可能 拒绝
int main() { }
,因为它不是允许的形式之一
标准,因此它的行为是未定义的。
但仍有一个悬而未决的问题:这是作者的意图吗 的标准?
在 1989 年 ANSI C 标准发布之前,void
关键字不存在。 Pre-ANSI (K&R) C 程序将定义 main
作为
main()
或
int main()
ANSI 标准的一个主要目标是添加新功能(包括
prototypes) without breaking existing pre-ANSI code.说明
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%。
是的。
int main() { /* ... */ }
相当于
int main(void) { /* ... */ }
N1570 5.1.2.2.1/1
The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent; or in some other implementation-defined manner.
6.7.6.3/14
An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.
(强调我的)
正如标准明确指出的那样,定义 int main() { /* ... */ }
确实 指定函数 main
没有参数。而且我们都很清楚,这个函数定义确实指定函数main
的return类型是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() {}
,因为 "The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature." (6.11.6),并且由于这种形式的定义不包含函数原型声明符,因此编译器不会检查参数的数量和类型是否正确
N1570 6.5.2.2/8
No other conversions are performed implicitly; in particular, the number and types of arguments are not compared with those of the parameters in a function definition that does not include a function prototype declarator.
(强调我的)
一个强烈的迹象表明 int main()
是有效的,无论标准是否准确地给出了使其有效的措辞,事实上 int main()
偶尔在标准中使用而没有任何人提出任何异议。虽然示例不是规范的,但它们确实表明了意图。
6.5.3.4 The sizeof and _Alignof operators
8 EXAMPLE 3 In this example, the size of a variable length array is computed and returned from a function:
#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 Function declarators (including prototypes)
20 EXAMPLE 4 The following prototype has a variably modified parameter.
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;
}
无效。尽管如此,该标准在规范性文本中明确指出可以使用任何名称,这意味着 int main (int argc, char *argv[])
和 int main (int x, char *y[])
就 5.1.2.2.1 的目的而言是等效的。 “等效”一词的严格英语含义并不是它的意思。
基思·汤普森 (Keith Thompson) 在他的回答中建议对该词进行更宽松的解释。
对这个词的一个同样有效甚至更宽松的解释确实允许 int main()
:int main(void)
和 int main()
都将 main
定义为返回 int
和不带参数。
标准和任何官方 DR 目前都没有回答预期使用哪种解释的问题,因此该问题无法回答,但示例强烈表明最后的解释。