TL;DR
在声明中,
void func1(); // obsolescent
void func2(void);
行为完全不同。第一个声明了一个没有任何原型的函数——它可以接受任意数量的参数!而后者声明了一个带有原型的函数,它没有参数并且不接受任何参数。
在定义中
void func1() { } // obsolescent
和
void func2(void) { }
这两者的行为截然不同,而 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
但是,两个调用在严格符合程序中都是非法的,因为根据6.5.2.2p6,它们是明确未定义的行为。
此外,空括号被认为是过时的功能:
使用带空括号的函数声明符(不是原型格式的参数类型声明符)已过时。
和
使用具有单独参数标识符和声明列表(不是原型格式的参数类型和标识符声明符)的函数定义已过时。
详细说明
有 2 个相关但不同的概念:参数和参数。
摘录如下:
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 的声明,既不参数也不 em> 指定了它们的类型(没有原型的声明)。
然而,它们在定义上却有所不同
-
定义void func1() {} 没有声明原型,而void func2(void) {} 声明了原型,因为() 不是参数类型列表,而(void) 是参数类型列表(6.7.5.3.10):
类型为 void 的未命名参数作为列表中唯一项的特殊情况指定该函数没有参数。
还有6.9.1.7
如果声明符包含参数类型列表,则
list 还指定了所有参数的类型;这样的声明器还可以用作函数原型,以便以后调用同一翻译单元中的同一函数。如果声明器包含标识符列表,则参数的类型应在以下声明列表中声明。在任何一种情况下,每个参数的类型都会按照 6.7.5.3 中对参数类型列表的描述进行调整;结果类型应为对象类型。
func1 的函数定义声明符不包含参数类型列表,因此该函数没有原型。
-
void func1() { ... } 仍然可以使用任意数量的参数调用,而使用任何参数调用 void func2(void) { ... } 是编译时错误 (6.5.2.2):
如果表示被调用函数的表达式的类型包含原型,则参数的数量应与参数的数量一致。每个参数都应该有一个类型,这样它的值就可以被分配给一个具有其对应参数类型的非限定版本的对象。
(强调我的)
这是一个约束,根据标准规定,符合标准的实现必须至少显示一条关于此问题的诊断消息。但由于func1 没有原型,因此不需要符合要求的实现来生成任何诊断信息。
但是,如果参数的数量不等于参数的数量,则行为未定义 6.5.2.2p6:
如果表示被调用函数的表达式的类型不包含原型,[...] 如果参数的数量不等于参数的数量,则行为未定义。
因此,理论上,在这种情况下,符合 C99 的编译器也可以出错或诊断出警告。 StoryTeller 提供了clang might diagnose this 的证据;但是,我的 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);任何符合要求的实现必须诊断这是因为它违反了约束。