即使声明中函数的签名与定义不匹配,以下代码如何工作?函数声明有空参数列表,但定义有一个参数。为什么编译器不会出错?
#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)。
但是,如果我更改定义中参数的类型,从double
到float
,则会出现以下错误(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)部分,但语言是如此神秘,以至于我无法清楚地理解。我甚至不知道我是否阅读了正确的部分。所以请用简单的词语解释这些行为。
答案 0 :(得分:6)
您认为声明与定义不匹配是不正确的。 (在C ++中就是这种情况,但在C中却不是这样)。
double f();
声明没有完全声明该函数,即它没有引入原型。它只宣布函数f
存在且返回类型为double
的事实。它绝对没有说明f
s参数的数量和类型。争论绝对可以是任何事情。从这个意义上说,你的例子中的声明确实与定义匹配(即它与定义不矛盾,这对于C编译器来说是很好的。)
如果你真的想要声明一个不带参数的函数,你必须在参数列表中指定一个显式的void
double f(void);
这确实与定义相矛盾。你原来没有。
当您调用已使用空参数列表()
声明的函数时,您有责任提供正确类型的适当数量的参数。如果您犯了错误,则行为未定义。当您将实际参数类型更改为float
时,编译器会向您发出警告。
您对声明和定义“对”的分析并不完全正确。这是错误的。它并不是关于声明和定义的。它实际上是关于你调用你的函数的定义和方式。在原始情况下,您使用double
参数调用它,并使用double
参数声明该函数。所以一切都很匹配。但是,如果使用double
参数调用它并使用float
参数声明它,则会出现不匹配。
另请注意,当声明函数时没有原型时,float
参数始终会提升为double
个参数。因此,无法将float
参数传递给使用()
参数列表声明的函数。如果您想拥有float
个参数,请始终使用原型(同样适用于char
和short
参数。)
答案 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)
冲突。