How does the following code work even though the signature of the function in the declaration doesn't match with the definition? The function declaration has empty parameter list, yet the definition has one parameter. Why the compiler doesn't give error?
#include <stdio.h>
double f(); //function declaration
int mai开发者_Python百科n(void)
{
printf("%f\n", f(100.0));
}
double f(double param) //function definition
{
return 5 * param ;
}
It compiles and runs fine (ideone).
But if I change the type of the parameter in the definition, from double
to float
, it gives the following error (ideone):
prog.c:7: error: conflicting types for ‘f’
prog.c:8: note: an argument type that has a default promotion can’t match an empty parameter name list declaration prog.c:2: error: previous declaration of ‘f’ was hereWhat is wrong with float
? Why does it give error with float
but not with double
?
Here is the list of pairs of declaration and definition, along with which pair works, and which not:
Works (ideone)
double f(); //declaration double f(double param); //definition
Does not work (ideone)
double f(); //declaration double f(float param); //definition
Works (ideone)
float f(); //declaration float f(double param); //definition
Does not work (ideone)
float f(); //declaration float f(float param); //definition
So as it seems, whenever the parameter-type is float
, it doesn't work!
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
?
I tried understanding the section §6.5.2.2 (C99), but the language is so cryptic that I couldn't clearly understand. 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). In C language the
double f();
declaration does not fully declare the function, i.e. it does not introduce the prototype. It only announces the fact that function f
exists and that its return type is double
. It says absolutely nothing about the number and the types of f
s arguments. The arguments can be absolutely anything. In that sense the declaration in your example does match the definition (i.e. it doesn't contradict the definition, which is good enought for C compiler).
If you really wanted the declare a function that takes no arguments, you'd have to specify an explicit void
in the parameter list
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. 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
.
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. In the original case you call it with a double
argument and the function is declared with a double
parameter. So everything is matching. But when you call it with a double
argument and declare it with a float
parameter, you get a mismatch.
Also note, that when a function is declared without a prototype, float
arguments are always promoted to double
arguments. For this reason, it is not possible to pass a float
argument to a function declared with ()
parameter list. If you want to have float
arguments, always use prototypes (the same applies to char
and short
arguments as well).
C allows the function declaration to be empty. From 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.
This is distinct from a void
parameter list, which is explicitly stating that the function has no arguments. From 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.
Note also that if the types aren't declared, then your declaration is not a prototype. From 6.2.1/2:
A function prototype is a declaration of a function that declares the types of its parameters.
The second question is indeed related to 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 todouble
.
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). 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. 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.
Such functions, when called with a floating-point number, implicitly take it as a double
. (And integral parameters as int
.)
So when you call foo(100.0), you are calling it with a 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. 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.
Bottom line: It is very bad style to declare functions with empty parameter lists in modern C. Declare the function properly, and if it to be referenced by multiple translation units, put the declaration in a header file.
[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
. (Or switch to C++...)
In C, an empty function declaration is like using ...
in C++. That is it matches any number and type of arguments. The problem with using a float
instead of a double
is that float
automatically promotes to double
. When calling f(...)
(to borrow C++ notation), it doesn't know what type is expected, so it gets promoted to double. Later, when you redeclare f
to take a float
argument, that conflicts with the implicit declaration of f
as f(double)
.
精彩评论