void func()
实际上,参数为空表示接受任何参数。
void func(void)
不接受任何参数。
但在标准C99中,我找到了以下内容:
6.7.5.3 函数声明符(包括原型)
14 标识符列表仅声明函数参数的标识符。函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数。 函数声明符中的空列表不是该函数定义的一部分,指定未提供有关参数数量或类型的信息。
根据标准,func()
和func(void)
是相同的吗?
void func()
实际上,参数为空表示接受任何参数。
void func(void)
不接受任何参数。
但在标准C99中,我找到了以下内容:
6.7.5.3 函数声明符(包括原型)
14 标识符列表仅声明函数参数的标识符。函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数。 函数声明符中的空列表不是该函数定义的一部分,指定未提供有关参数数量或类型的信息。
根据标准,func()
和func(void)
是相同的吗?
在声明中,
void func1(); // obsolescent
void func2(void);
行为差异很大。第一个声明的函数没有任何原型 - 它可以接受任意数量的参数!而后者声明了一个带有原型的函数,该函数不带参数且不接受任何参数。
在定义中
void func1() { } // obsolescent
和
void func2(void) { }
前者声明并定义了一个没有参数和原型的函数func1
后者声明并定义了一个没有参数但有原型的函数func2
这两个函数的行为不同,因为当使用错误数量的参数调用具有原型的函数时,C编译器必须打印诊断消息,但是在调用没有原型的函数时,它不需要这样做。
也就是说,给定上述定义:
func1(1, 2, 3); // need not produce a diagnostic message
func2(1, 2, 3); // must always produce a diagnostic message
// as it is a constraint violation
此外,空括号被认为是一种过时的特性:
使用带有空括号的函数声明符(不是原型格式参数类型声明符)是一种过时的特性。
和
使用具有单独的参数标识符和声明列表的函数定义(不是原型格式参数类型和标识符声明符)是一种过时的特性。
有两个相关但不同的概念:参数和参数值。
参数是传递到函数中的值。
参数是函数内的名称/变量,在函数输入时将其设置为参数值的值
在以下摘录中:
int foo(int n, char c) {
...
}
...
foo(42, ch);
n
和c
是参数。 42
和ch
是参数的实际值。
引用摘录仅涉及函数的参数,但未提及函数的原型或参数。
声明 void func1()
表示函数 func1
可以被调用并带有任意数量的参数,即没有关于参数数量的信息被指定(在 C99 中,作为一个单独的声明,它被指定为“没有参数规范的函数”),而声明 void func2(void)
则表示函数 func2
根本不接受任何参数。
你提问中的引语意味着,在函数定义中,void func1()
和 void func2(void)
都表明它们没有参数,即当进入函数时,没有将变量名设置为参数值的情况。 void func() {}
与 void func();
相对比,前者声明了 func
确实不带参数,而后者是一个函数 func
的声明,其中既没有指定参数,也没有指定它们的类型(没有原型的声明)。
但是,它们在定义方面仍然有所不同。
定义 void func1() {}
没有声明原型,而 void func2(void) {}
声明了原型,因为()
不是参数类型列表,而(void)
是一个参数类型列表(6.7.5.3.10):
如果参数列表中只有一个类型为 void 的未命名参数,则指定函数没有参数。
进一步信息请参考6.9.1.7
如果声明符包括参数类型列表, 那么该列表同时也指定所有参数的类型;这样的声明符也作为同一翻译单位中对该函数以后调用的函数原型。 如果声明符包括标识符列表,则在以下的声明列表中必须声明参数的类型。 在两种情况下,每个参数的类型都要按照6.7.5.3的说明进行调整,针对参数类型列表;生成的类型应为对象类型。
func1
函数定义的声明符中没有参数类型列表,因此该函数没有原型。
void func1() { ... }
仍可用任意数量的参数调用,而使用任何参数调用void func2(void) { ... }
会导致编译时错误(6.5.2.2):
如果表示调用的函数的表达式具有包括原型的类型,则参数数量必须与参数数量相同。每个参数的类型都应该是其对应参数的未限定类型的对象可赋值的类型。
(强调我的)
这是一个约束条件,根据标准规定,符合要求的实现必须显示关于此问题的至少一条诊断消息。但由于func1
没有原型,符合要求的实现不需要生成任何诊断信息。
因此,理论上符合 C99 标准的编译器也可以在这种情况下报错或发出警告。StoryTeller 提供了clang 可能会诊断此问题的证据;但是,我的 GCC 似乎没有这样做(这可能也是为了与一些旧的晦涩代码兼容所必需的):如果表示调用函数的表达式具有不包括原型的类型,[...] 如果参数的数量与实参的数量不相等,则行为未定义。
void test() { }
void test2(void) { }
int main(void) {
test(1, 2);
test2(1, 2);
}
gcc -std=c99 test.c -Wall -Werror
编译上述程序时,输出如下:test.c: In function ‘main’:
test.c:7:5: error: too many arguments to function ‘test2’
test2(1, 2);
^~~~~
test.c:3:6: note: declared here
void test2(void) { }
^~~~~
test
)中,参数根本不会被与函数参数进行任何检查,而GCC认为为有原型声明的函数(test2
)指定任何参数是编译时错误;任何符合规范的实现都必须将其诊断为约束违规。void
即可) - TonioElGringo_function definition_
作为 void func1() { }
是一个不赞成的特性。也许您认为 6.11.6函数声明符 适用于 _函数定义_? - chux - Reinstate Monicavoid func1() { }
。但它却适用于 void func3(a,b) int a; int b; { }
。 - chux - Reinstate Monicafoo(5)
的形式调用int foo() {...};
不会有任何要求,但是一些实现可以并且确实定义了这样的调用具有有用的行为,特别是如果函数的代码使用了内联汇编或其他实现定义的扩展。将这样的调用视为约束违规将阻止此类实现提供有用的功能。 - supercat6.7.5.3 函数声明符(包括原型)14 标识符列表仅声明函数参数的标识符。在函数声明符中,如果空列表是该函数定义的一部分,则指定该函数没有参数。但是,在不是该函数定义的一部分的函数声明符中,空列表指定未提供有关参数数量或类型的信息。
因此,在带有函数体的函数中,当参数列表为空时,它们是相同的。但如果只是函数的声明,则不同。
void function1(); // No information about arguments
void function2(void); // Function with zero arguments
void function3() {
// Zero arguments
}
void function4(void) {
// Zero arguments
}
int func() {..}
(没有原型),在这种情况下仍然可以接受参数,该定义也充当声明。 - P.P()
不指定原型,因此函数3没有原型 - 它也没有任何参数,但是不会检查参数数量或它们的类型。 - Antti Haapala -- Слава Україніvoid
定义函数时,如果参数的数量与参数(以及其类型)不匹配,则由于约束违规(§6.5.2.2/2),它将无法编译。这种情况需要符合标准的编译器发出诊断消息。#include <stdio.h>
void func1(void) { puts("foo"); }
void func2() { puts("foo"); }
int main(void)
{
func1(1, 2); // constraint violation, it shouldn't compile
func2(3, 4); // may or may not compile, UB when called
return 0;
}
-01
编译上述代码(不包括func1
的调用)的方式。main: # @main
push rax ; align stack to the 16-byte boundary
call func2 ; call func2 (no arguments given)
xor eax, eax ; set zero as return value
pop rcx ; restore previous stack position (RSP)
ret
void funct()
和void func(void)
是不同的。但当它们作为定义的一部分时,它们是相同的。void func() { ... }
和void func(void) { ... }
都不带参数。 - haccks