我注意到我不太了解C中的某些行为。如果我定义的函数没有为参数指定类型,并且不带任何参数调用它,则代码会编译,但会发出警告,指出参数的类型默认为int
。
void f(i) {
printf("%d", i);
}
int main(void) {
f();
}
如果在godbolt上运行此命令,则会收到预期的编译器警告,并且执行将1打印到stdout
。
但是,如果我定义相同的方法,但将参数显式声明为int
,则编译器将为使用较少参数的函数调用引发错误。
void f(int i) {
printf("%d", i);
}
int main(void) {
f();
}
在godbolt编译此产生error: too few arguments to function 'f'
。
此行为在gcc和clang的所有现代版本中都是一致的。为什么指定参数的类型会使它成为编译器错误?另外,是否定义了第一个示例的行为?gcc和clang总是为i
1的事实使我认为这是故意的。如果是这样,为什么?
第二段代码相对简单。该函数被定义为接受一个参数,并且函数调用中未main
传递任何参数。那是一个明显的错误。
第一部分代码更有趣。这可以追溯到旧式的函数定义,在该函数定义中,参数列表仅包含参数名称,并且类型出现在终止于参数列表的右括号和函数主体开始之前的右括号之间。正确定义的此类函数如下所示:
void f(i)
int i;
{
printf("%d", i);
}
这种指定函数参数的样式可以追溯到原始的K&R C,但现在已被弃用。C的这种变体还具有以下属性:可以省略变量的类型,在该变量中,类型的默认类型为int
。
但是,即使你确实如上面的示例中那样在参数列表后指定了类型,也仍然不是错误。那为什么呢?
关键部分来自C标准的6.5.2.2节,涉及函数调用:
2 如果表示被调用函数的表达式的类型包括原型,则参数的数量应与参数的数量一致。 每个自变量的类型应使其值可以分配给具有相应参数类型的非限定版本的对象。
...
8不会隐式执行其他转换;特别是, 不将参数的数量和类型与不包含函数原型声明符的函数定义中的参数进行比较。
以及关于功能定义的第6.9.1p7节:
函数定义中的声明符指定要定义的函数的名称及其参数的标识符。 如果声明符包括参数类型列表,则该列表还将指定所有参数的类型;否则,列表将指定所有参数的类型。这样的声明器还充当函数原型,以供以后在同一翻译单元中调用同一函数。 如果声明器包括一个标识符列表,则参数类型应在随后的声明列表中声明。在这两种情况下,每个参数的类型均按照6.7.6.3中关于参数类型列表的描述进行调整;结果类型应为完整的对象类型
在使用K&R样式函数定义的情况下,将参数指定为标识符列表,而不是参数类型列表。这意味着函数定义也没有指定函数原型,这意味着在调用函数时不检查参数的数量和类型。