为什么在定义后更改函数原型是合法的?

Why is it legal to change function prototype after its definition?

int test();
int test(){
 return 5;
}
int test(int in);

int main() {
   return test();
}

我知道一个函数可以有多个(和类似的)声明,而只有一个定义。

我的问题与自己定义后的声明有关。编译器会选择它遇到的最后一个原型作为真正的原型吗?即使它是父亲的完整定义?

编译时出现以下错误: error: too few arguments to function 'test'

这是不符合 C 标准的无效代码。

根据 C 标准(6.7.6.3 函数声明符(包括原型))

  1. For two function types to be compatible... If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier....

即在提供的代码中声明了两个不兼容的函数。

你的示例代码中有3个test的声明:

  1. int test(); 是声明,不是原型1 或定义2.
  2. int test() { return 5; }是函数定义的声明。
  3. int test(int in); 是作为函数原型的声明。

第 1 个简单声明不完整。3它没有说明是否有任何参数或它们的类型是什么。该声明可以由声明 2 或声明 3 完成,但不能同时由两者完成。如果 2 和 3 都存在,编译器应该给您警告或错误消息。4

具体来说,回答您的问题“为什么在函数定义后更改函数原型是合法的?”:在不完整的声明之后,您可以通过添加更多信息来更改已知的函数类型,使用旧的-风格定义或现代原型。除了添加更多信息外,您无法进行更改。5(原型或定义后,函数类型已完成,您无法进行任何更改。)

声明 2 匹配旧式函数定义,其中参数在关闭 ) 之后和打开 { 之前定义:

<i>return-type</i> <i>name-of-function</i>(<i>names-of-parameters</i>)
<i>declarations of parameters</i>
{
    <i>body-of-function</i>
}

例如:

int test(n, x)
int n;
double x;
{
    …
}

当这种风格的定义中没有参数时,表示该函数不带参数。注意区别:

  • int test();没有说参数是什么,连有多少也没有说。
  • int test() { return 5; }表示没有参数。

如您所见,这些是兼容的:不说明有多少参数与说没有参数兼容。6

声明 3 是一种现代声明,表示有一个参数,类型为 int。与 2 一样,声明 1 和 3 是兼容的:不说明有多少个参数或它们可能具有的类型与说有一个 int.

类型的参数兼容。

但是,声明 2 和 3 不兼容:

  • 声明 2 说没有参数。
  • 声明 3 说只有一个参数。

脚注

1 函数原型声明其参数的类型。由于 () 为空,因此没有声明参数类型。 (由于 C 语言的历史,函数声明中的 () 意味着没有参数,而 (void) 是一个原型,表示没有参数。)

2 函数定义包括函数体,大括号 复合语句 包含函数执行的代码。

3 这里使用的“不完整”是普通英语意义上的,而不是 C 标准适用于对象类型的意义上的。

4 这是 C 2018 6.7 4 中的约束所要求的:“同一范围内引用同一对象或函数的所有声明应指定兼容的类型。”

5 可能会有一些细微的变化。例如,可以在参数中添加或删除诸如 const 之类的限定符,尽管出于兼容性目的这不会更改函数类型。

6函数类型兼容性的具体规则在C 2018 6.7.6.3 15.