简体   繁体   English

function 定义中浮动的奇怪行为。 声明-定义不匹配,但它有效,如何?

[英]Strange behavior of float in function definition. And declaration-definition mismatch, yet it works, how?

How does the following code work even though the signature of the function in the declaration doesn't match with the definition?即使声明中 function 的签名与定义不匹配,以下代码如何工作? The function declaration has empty parameter list, yet the definition has one parameter. function 声明的参数列表为空,但定义只有一个参数。 Why the compiler doesn't give error?为什么编译器不报错?

#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 ; 
}

It compiles and runs fine ( ideone ).它编译并运行良好( ideone )。

But if I change the type of the parameter in the definition, from double to float , it gives the following error ( ideone ):但是,如果我将定义中的参数类型double更改为float ,则会出现以下错误( ideone ):

prog.c:7: error: conflicting types for 'f' prog.c:7:错误:“f”的类型冲突
prog.c:8: note: an argument type that has a default promotion can't match an empty parameter name list declaration prog.c:8:注意:具有默认提升的参数类型与空参数名称列表声明不匹配
prog.c:2: error: previous declaration of 'f' was here prog.c:2: 错误: 'f' 的先前声明在这里

What is wrong with float ? float有什么问题? Why does it give error with float but not with double ?为什么它会给出错误float而不是double

Here is the list of pairs of declaration and definition, along with which pair works, and which not:这是声明和定义对的列表,以及哪些对有效,哪些无效:

  • Works ( ideone )作品( ideone

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

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

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

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

So as it seems, whenever the parameter-type is float , it doesn't work!所以看起来,只要参数类型是float ,它就不起作用!


So I've basically two questions:所以我基本上有两个问题:

  • Why does the first example work even though there is a mismatch in the declaration and the definition?为什么即使声明和定义不匹配,第一个示例仍然有效?
  • Why does it not work when the parameter-type is float ?为什么当参数类型为float时它不起作用?

I tried understanding the section §6.5.2.2 (C99), but the language is so cryptic that I couldn't clearly understand.我尝试理解第 §6.5.2.2 (C99) 部分,但该语言非常神秘,以至于我无法清楚地理解。 I don't even know if I read the correct section.我什至不知道我是否阅读了正确的部分。 So please explain these behaviors in simple words.所以请用简单的话来解释这些行为。

Your assumption that declaration does not match the definition is incorrect.您认为声明与定义不匹配的假设是不正确的。 (That would be the case in C++, but not in C). (在 C++ 中会出现这种情况,但在 C 中不会出现这种情况)。 In C language the在 C 语言中

double f();

declaration does not fully declare the function, ie it does not introduce the prototype .声明没有完全声明function,即没有介绍原型 It only announces the fact that function f exists and that its return type is double .它只宣布 function f存在并且它的返回类型是double的事实。 It says absolutely nothing about the number and the types of f s arguments.它完全没有说明f s arguments 的数量和类型。 The arguments can be absolutely anything. arguments 绝对可以是任何东西。 In that sense the declaration in your example does match the definition (ie it doesn't contradict the definition, which is good enought for C compiler).从这个意义上说,您示例中的声明确实与定义相匹配(即,它与定义不矛盾,这对于 C 编译器来说已经足够了)。

If you really wanted the declare a function that takes no arguments, you'd have to specify an explicit void in the parameter list如果你真的想要声明一个没有 arguments 的 function,你必须在参数列表中指定一个明确的void

double f(void);

That would indeed contradict the definition.这确实与定义相矛盾。 What you have originally does not.你原来拥有的东西没有。

When you call a function that has been declared with empty parameter list () , it is your responsibility to supply the proper number of arguments of proper type.当您调用已用空参数列表()声明的 function 时,您有责任提供正确类型的正确数量的 arguments。 If you make a mistake, the behavior is undefined.如果您犯了错误,则行为是未定义的。 This is what the compiler warns you about when you change the actual parameter type to float .这是当您将实际参数类型更改为float时编译器会警告您的内容。

Your analysis of "pairs" of declaration and definition is not entirely correct.您对声明和定义的“对”的分析并不完全正确。 It is misguided.这是被误导的。 It is not really about the declaration and definition.这与声明和定义无关。 It is really about the definition and the way you call your function.这实际上是关于定义和调用function 的方式。 In the original case you call it with a double argument and the function is declared with a double parameter.在原始情况下,您使用double参数调用它,并且使用double参数声明 function。 So everything is matching.所以一切都是匹配的。 But when you call it with a double argument and declare it with a float parameter, you get a mismatch.但是当你用一个double参数调用它并用一个float参数声明它时,你会得到一个不匹配的结果。

Also note, that when a function is declared without a prototype, float arguments are always promoted to double arguments.另请注意,当 function 被声明为没有原型时, float arguments 始终提升为double arguments。 For this reason, it is not possible to pass a float argument to a function declared with () parameter list.因此,无法将float参数传递给使用()参数列表声明的 function。 If you want to have float arguments, always use prototypes (the same applies to char and short arguments as well).如果您想要float arguments,请始终使用原型(同样适用于charshort arguments)。

C allows the function declaration to be empty. C 允许 function 声明为空。 From C99 6.7.5.3/14:从 C99 6.7.5.3/14 开始:

The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied. function 声明符中的空列表不是该 function 定义的一部分,它指定不提供有关参数数量或类型的信息。

This is distinct from a void parameter list, which is explicitly stating that the function has no arguments.这与void参数列表不同,后者明确指出 function 没有 arguments。 From 6.7.5.3/10:从 6.7.5.3/10 开始:

The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters. void类型的未命名参数作为列表中的唯一项的特殊情况指定 function 没有参数。

Note also that if the types aren't declared, then your declaration is not a prototype.另请注意,如果未声明类型,则您的声明不是原型。 From 6.2.1/2:从 6.2.1/2 开始:

A function prototype is a declaration of a function that declares the types of its parameters. function 原型是声明其参数类型的 function 的声明。

The second question is indeed related to C99 6.5.2.2/6:第二个问题确实和C99 6.5.2.2/6有关:

If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double .如果表示被调用的 function 的表达式具有不包含原型的类型,则对每个参数执行 integer 提升,并将具有float类型的 arguments 提升为double精度。

So in your case, float gets promoted to double whenever the function is called, and a double is put on the call stack (or in eax or whatever).因此,在您的情况下,每当调用 function 时, float都会被提升为double ,并且在调用堆栈(或eax或其他任何东西)中放置一个double But of course, if your function definition takes a float , it would read a float off the call stack, which would lead to a binary incompatibility.但是,当然,如果您的 function定义采用float ,它将从调用堆栈中读取一个float ,这将导致二进制不兼容。 The compiler knows this, hence the error message.编译器知道这一点,因此会出现错误消息。

In C, an empty parameter list in the declaration means the function can be called with 0 or more arguments.在 C 中,声明中的空参数列表意味着 function 可以与 0 个或多个 arguments 一起调用。

Such functions, when called with a floating-point number, implicitly take it as a double .此类函数在使用浮点数调用时,会隐式将其视为double (And integral parameters as int .) (和整数参数为int 。)

So when you call foo(100.0), you are calling it with a double.所以当你调用 foo(100.0) 时,你是用一个 double 调用它。 If you try to call it with a float, the argument will be converted to double at the time of the call.如果您尝试使用浮点数调用它,则参数将在调用时转换为双精度。

This will not work if you define the function to take a float, because the way doubles and floats are passed is different.如果您将 function 定义为浮点数,这将不起作用,因为双精度数和浮点数的传递方式不同。 Hence the compiler is helpfully giving you an error.因此,编译器很有帮助地给你一个错误。

Be glad you made this mistake in 2011 and not 1985, because compilers used to be pretty stupid and this was a nightmarish kind of bug to track down.很高兴你在 2011 年而不是 1985 年犯了这个错误,因为编译器过去非常愚蠢,这是一个难以追踪的噩梦般的错误。

Bottom line: It is very bad style to declare functions with empty parameter lists in modern C.底线:在现代 C 中声明具有空参数列表的函数是非常糟糕的风格。 Declare the function properly, and if it to be referenced by multiple translation units, put the declaration in a header file.正确声明 function,如果要被多个翻译单元引用,则将声明放入 header 文件中。

[edit] [编辑]

As detly points out in a comment, if you actually want to declare a function to take zero arguments, declare it to take void .正如评论中详细指出的那样,如果您真的想声明 function 为零 arguments,请声明它为void (Or switch to C++...) (或切换到 C++...)

In C, an empty function declaration is like using ... in C++.在 C 中,一个空的 function 声明就像在 C++ 中使用... That is it matches any number and type of arguments.也就是说,它匹配任何数量和类型的 arguments。 The problem with using a float instead of a double is that float automatically promotes to double .使用float而不是double的问题是float会自动提升为double When calling f(...) (to borrow C++ notation), it doesn't know what type is expected, so it gets promoted to double.当调用f(...) (借用 C++ 表示法)时,它不知道预期的类型,所以它被提升为 double。 Later, when you redeclare f to take a float argument, that conflicts with the implicit declaration of f as f(double) .稍后,当您重新声明f以采用float参数时,这与 f 的隐式声明为f f(double)冲突。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM