5

即使声明中的函数签名与定义不匹配,以下代码如何工作?函数声明的参数列表为空,但定义只有一个参数。为什么编译器不报错?

#include <stdio.h>
double f(); //function declaration
int main(void)  
{ 
   printf("%f\n", f(100.0)); 
}
double f(double param) //function definition
{
   return 5 * param ; 
}

它编译并运行良好(ideone)。

但是,如果我将定义中的参数类型doubleto更改为float,则会出现以下错误(ideone):

prog.c:7:错误:“f”的类型冲突<br> prog.c:8:注意:具有默认提升的参数类型无法匹配空参数名称列表声明
prog.c:2:错误: 之前的 'f' 声明在这里

有什么问题float?为什么它会给出错误float而不是 with double

这是声明和定义对的列表,以及哪些对有效,哪些无效:

  • 作品(ideone

    double f();              //declaration
    double f(double param);  //definition
    
  • 不起作用(ideone

    double f();              //declaration
    double f(float param);   //definition
    
  • 作品(ideone

    float f();               //declaration
    float f(double param);   //definition
    
  • 不起作用(ideone

    float f();               //declaration
    float f(float param);    //definition
    

所以看起来,只要参数类型是float,它就不起作用!


所以我基本上有两个问题:

  • 为什么即使声明和定义不匹配,第一个示例仍然有效?
  • 为什么当参数类型为 时它不起作用float

我尝试理解第 §6.5.2.2 (C99) 部分,但该语言非常神秘,以至于我无法清楚地理解。我什至不知道我是否阅读了正确的部分。所以请用简单的话来解释这些行为。

4

4 回答 4

6

您认为声明与定义不匹配的假设是不正确的。(在 C++ 中就是这种情况,但在 C 中不是)。在 C 语言中

double f();

声明没有完全声明函数,即它没有引入原型。它只宣布函数f存在并且它的返回类型是double. f它完全没有说明s 参数的数量和类型。论点可以是任何东西。从这个意义上说,您示例中的声明确实与定义相匹配(即它与定义不矛盾,这对于 C 编译器来说已经足够好了)。

如果你真的想要声明一个不带参数的函数,你必须void在参数列表中指定一个显式

double f(void);

这确实与定义相矛盾。你原来拥有的东西没有。

当您调用已使用空参数列表声明的函数时,()您有责任提供适当数量的适当类型的参数。如果您犯了错误,则行为是未定义的。当您将实际参数类型更改为float.

您对声明和定义的“对”的分析并不完全正确。这是被误导的。这与声明和定义无关。这实际上是关于定义和调用函数的方式。在原始情况下,您使用参数调用它,并且使用double参数声明double函数。所以一切都是匹配的。但是当你用参数调用它并用double参数声明它时float,你会得到一个不匹配的结果。

还要注意,当函数声明时没有原型,float参数总是被提升为double参数。因此,不可能将参数传递给使用参数列表float声明的函数。()如果你想有float参数,总是使用原型(同样适用于charshort参数)。

于 2011-06-05T16:14:52.857 回答
4

C 允许函数声明为空。从 C99 6.7.5.3/14 开始:

不属于该函数定义的函数声明器中的空列表指定不提供有关参数数量或类型的信息。

这与参数列表不同,void参数列表明确声明函数没有参数。从 6.7.5.3/10 开始:

作为列表中唯一项的类型未命名参数的特殊情况void指定该函数没有参数。

另请注意,如果未声明类型,则您的声明不是原型。从 6.2.1/2 开始:

函数原型是声明其参数类型的函数的声明。

第二个问题确实和C99 6.5.2.2/6有关:

如果表示被调用函数的表达式的类型不包含原型,则对每个参数执行整数提升,并将具有类型的参数 float提升为double.

因此,在您的情况下,无论何时调用该函数,float都会将其提升为,并将 a放入调用堆栈(或其中或其他)。但是当然,如​​果您的函数定义采用 a ,它将从调用堆栈中读取 a ,这将导致二进制不兼容。编译器知道这一点,因此会出现错误消息。doubledoubleeaxfloatfloat

于 2011-06-05T16:08:06.863 回答
3

在 C 中,声明中的空参数列表意味着可以使用 0 个或多个参数调用函数。

此类函数在使用浮点数调用时,会隐式将其视为double. (和积分参数为int。)

所以当你调用 foo(100.0) 时,你是用一个 double 调用它。如果您尝试使用浮点数调用它,则参数将在调用时转换为双精度。

如果您将函数定义为采用浮点数,这将不起作用,因为双精度数和浮点数的传递方式不同。因此,编译器很有帮助地给你一个错误。

很高兴你在 2011 年而不是 1985 年犯了这个错误,因为编译器过去非常愚蠢,这是一个难以追踪的噩梦般的错误。

底线:在现代 C 中声明具有空参数列表的函数是非常糟糕的风格。正确声明函数,如果要被多个翻译单元引用,请将声明放在头文件中。

[编辑]

正如评论中详细指出的那样,如果您真的想声明一个函数以接受零参数,请将其声明为 take void。(或切换到 C++ ......)

于 2011-06-05T16:11:00.593 回答
1

在 C 中,空函数声明就像...在 C++ 中使用。也就是说,它匹配任意数量和类型的参数。使用 afloat而不是 a的问题double是它会float自动提升为double. 调用时f(...)(借用 C++ 表示法),它不知道预期的类型,因此它被提升为 double。稍后,当您重新声明f接受参数时,这与asfloat的隐式声明冲突。ff(double)

于 2011-06-05T16:11:57.563 回答