函数定义中float的奇怪行为。和声明定义不匹配,但它的工作原理如何?

时间:2011-06-05 16:01:14

标签: c definition function-declaration function-prototypes

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

#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)。

但是,如果我更改定义中参数的类型,从doublefloat,则会出现以下错误(ideone):< / p>

prog.c:7:错误:'f'的冲突类型 prog.c:8:注意:具有默认促销的参数类型不能与空参数名称列表声明
匹配 prog.c:2:错误:先前'f'的声明在这里

float有什么问题?为什么它会导致float错误而不会导致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 个答案:

答案 0 :(得分:6)

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

double f();

声明没有完全声明该函数,即它没有引入原型。它只宣布函数f存在且返回类型为double的事实。它绝对没有说明f s参数的数量和类型。争论绝对可以是任何事情。从这个意义上说,你的例子中的声明确实与定义匹配(即它与定义不矛盾,这对于C编译器来说是很好的。)

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

double f(void);

这确实与定义相矛盾。你原来没有。

当您调用已使用空参数列表()声明的函数时,您有责任提供正确类型的适当数量的参数。如果您犯了错误,则行为未定义。当您将实际参数类型更改为float时,编译器会向您发出警告。

您对声明和定义“对”的分析并不完全正确。这是错误的。它并不是关于声明和定义的。它实际上是关于你调用你的函数的定义和方式。在原始情况下,您使用double参数调用它,并使用double参数声明该函数。所以一切都很匹配。但是,如果使用double参数调用它并使用float参数声明它,则会出现不匹配。

另请注意,当声明函数时没有原型时,float参数始终会提升为double个参数。因此,无法将float参数传递给使用()参数列表声明的函数。如果您想拥有float个参数,请始终使用原型(同样适用于charshort参数。)

答案 1 :(得分:4)

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

  

a中的空列表   函数声明符不是其中的一部分   该函数的定义   指定没有关于的信息   参数的数量或类型   提供。

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

  

一个未命名的特例   类型为void的参数为唯一   列表中的项指定了   功能没有参数。

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

  

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

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

  

如果表达式表示   被调用的函数有一个类型   不包括原型,整数   每个都进行促销活动   参数和具有类型的参数   float被提升为double

因此,在您的情况下,只要调用函数,float就会被提升为double,并且double会被置于调用堆栈上(或eax或其他任何内容)。但是,当然,如果您的函数 definition 采用float,它将从调用堆栈中读取float,这将导致二进制不兼容。编译器知道这一点,因此错误消息。

答案 2 :(得分:3)

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

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

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

如果定义函数以获取浮点数,则此方法无效,因为传递双精度数和浮点数的方式不同。因此,编译器会帮助您解决错误。

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

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

[编辑]

正如在评论中明确指出的那样,如果你真的想要声明一个函数来接受零参数,请声明它取void。 (或者切换到C ++ ......)

答案 3 :(得分:1)

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