c - c99中的func()vs func(void)

标签 c c99

void func()
实际上,空参数表示接受任何参数。
void func(void)不接受任何参数。
但在标准C99中,我发现这样的行:
6.7.5.3 Function declarators (including prototypes)
14标识符列表仅声明函数参数的标识符。作为函数定义一部分的函数声明符中的空列表指定该函数没有参数。函数声明符中的空列表不是该函数定义的一部分,它指定不提供有关参数数量或类型的信息。
根据标准,func()func(void)是一样的吗?

最佳答案

TL;博士
在声明中,

void func1();     // obsolescent
void func2(void);

行为完全不同。第一个声明了一个没有任何原型的函数-它可以接受任意数量的参数!而后者使用原型声明一个函数,它没有参数,也不接受参数。
在定义中
void func1() { }     // obsolescent


void func2(void) { }

前者声明并定义了一个没有参数和原型的函数
后者使用没有参数的原型声明并定义一个函数func1
这两个函数的行为很明显,尽管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显式定义的行为。
此外,空括号被认为是过时的特征:
使用带空括号的函数声明符(不是原型格式参数类型声明符)是一个过时的特性。

使用具有单独参数标识符和声明列表(不是原型格式参数类型和标识符声明符)的函数定义是过时的特性。
详细
有两个相关但截然不同的概念:参数和参数。
参数是传递给函数的值。
参数是函数中的名称/变量,在函数输入时设置为参数的值
在以下摘录中:
int foo(int n, char c) {
    ...
}

...

    foo(42, ch);

func2n是参数。c42是参数。
引用的摘录只涉及函数的参数,但没有提到函数的原型或参数。
声明ch意味着函数void func1()可以用任意数量的参数调用,即没有指定参数数量的信息(作为单独的声明,C99将其指定为“没有参数规范的函数”),而声明func1意味着函数void func2(void)根本不接受任何参数。
问题中的引号表示在函数定义中,func2void func1()都表示没有参数,即在输入函数时设置为参数值的变量名。void func2(void)void func() {}相反,前者声明void func();确实不接受任何参数,而后者是一个函数func的声明,没有为其指定参数或其类型(没有原型的声明)。
然而,他们在这方面的定义不同
定义func不声明原型,而void func1() {}声明原型,因为void func2(void) {}不是参数类型列表,而()是参数类型列表(6.7.5.3.10):
void类型的未命名参数作为列表中的唯一项的特殊情况指定该函数没有参数。
进一步6.9.1.7
如果声明符包含参数类型列表,则
list还指定了所有参数的类型;这样的声明符还用作函数原型,以便以后在同一个转换单元中调用同一个函数。如果声明符包含标识符列表,则应在下面的声明列表中声明参数的类型。在这两种情况下,每个参数的类型都将按照6.7.5.3中所述的参数类型列表进行调整;结果类型应为对象类型。
(void)的函数定义的声明符不包含参数类型列表,因此该函数没有原型。
func1仍然可以用任意数量的参数调用,但是用任意参数调用void func1() { ... }是编译时错误(6.5.2.2):
如果表示被调用函数的表达式具有包含原型的类型,则参数的数量应与参数的数量一致。每个参数都应有一个类型,以便可以将其值赋给具有其相应参数类型的非限定版本的对象。
(强调我的)
这是一个约束,根据标准,一致性实现必须至少显示一条关于此问题的诊断消息。但由于void func2(void) { ... }没有原型,因此不需要一致的实现来生成任何诊断。
但是,如果参数的数目不等于参数的数目,则行为是未定义的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);
}

当用func1编译上述程序时,输出为:
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) { }
      ^~~~~

也就是说,对于定义中声明不是原型函数(gcc -std=c99 test.c -Wall -Werror)的函数的参数,根本不检查这些参数,而GCC认为为原型函数(test)指定任何参数是一个编译时错误;任何符合条件的实现都必须诊断这一点,因为这是一种约束冲突。

关于c - c99中的func()vs func(void),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54665613/

相关文章:

不能在 C 中的中流声明上有标签?

c - 引用 fgets,\0 如何合并到普通文本文件中

java - FFmpeg 命令帮助和文档指针

C 结构体和 union 在一起

c - 我的 exe 文件中的所有垃圾信息是怎么回事?

gcc - 添加两个 float

c - 为什么 C 中的 for 循环只通过一次?

关于看似兼容的函数指针赋值的编译器警告(const vs no-const)

c - ftell 位于超过 2GB 的位置

c - 修改只读内存会发生什么?