问题
I was studying about default argument promotions and got stuck at one point. In C 2011 (ISO/IEC 9899:2011), the relevant part seem to be:
§6.5.2.2 Function calls
¶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. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases:
— one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types;
— both types are pointers to qualified or unqualified versions of a character type or void.
In the last three lines of paragraph it talks about the function type that does not include a prototype while defining it.
It says if the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined.
Now i have a very silly doubt that if both the function declaration and function definition does not include a prototype as mentioned in this paragraph, so about which parameters they are talking about in last three lines of paragraph. And what is the meaning of "parameters after promotion" here as i have only studied about argument promotions. What is "parameter promotions"?
Also can you give example of the exceptional cases mentioned in the last. If someone can explain this with a proper example that would be really appreciable.
回答1:
Before C was standardized (aka before C89), functions were defined differently. The style is still supported in C11 for backwards-compatibility. Don't use it unless the whole purpose is to have fun:
int add_ints(); //forward-declaration has no parameters
add_ints(a, b)
//implicit type for return and parameters is int, this only works in pre-standard C or C89/C90
//int a, b; //remove this comment in C99/C11 for it to compile (also add return type int)
{
return a + b; //side note: old K&R compilers required parantheses around the return expression
}
In a way, these functions have parameters that behave like varargs. The caller doesn't know what parameters the function expects (same as with varargs). It is able to pass it any parameters and any number of them. However, it is of course undefined behavior if the number of parameters in the call statement doesn't match the number of parameters in the declaration.
Of course, there is a problem that arises from this. If the caller wants to pass a short
, how will it know whether the function is expecting a short
(and pass it directly) or an int
(and needs to convert it)? It cannot, so a common ground was reached. It has been decided that:
char
andshort
get promoted toint
float
gets promoted todouble
This happens for all functions defined this way (K&R style) and for varargs parameters. This way, a K&R function will never expect a short
parameter, thus the compiler will always promote short
parameters to int
.
Of course, as @aschepler said, you can still define the function like:
short add_shorts(a, b)
short a, b;
{
return a + b;
}
This means that the parameters are first converted to int
and passed to the function and only then does the function convert them to short
and add them.
Be careful with functions like printf()
:
printf("%.f", 3); //passes an int: UB and also wrong answer (my compiler prints 0)
printf("%.f", 3.0); //correct
printf("%.f", (double)3); //correct
You may actually see K&R functions quite often, especially if the author didn't pay attention to add the void
keyword to a function that takes no parameters:
int f1() //K&R function
{
return 0;
}
int f2(void) //Standard function
{
return 0;
}
int main(void) //Don't forget void here as well :P
{
int a = f1(); //Returns 0
int b = f2(); //Returns 0
int c = f1(100); //UB - invalid number of parameters, in practice just returns 0 :)
int d = f2(100); //Compiler error - parameter number/types don't match
//A good compiler would give a warning for call #3, but mine doesn't :(
}
EDIT: Not sure why, but cppreference classifies functions defined like f1() as their own type of function (parameter-less without void
), instead of K&R functions. I don't have the standard in front of me, but even if the standard says the same thing, they should behave the same and they have the history I mentioned.
Default argument promotions
Function declarations in C
来源:https://stackoverflow.com/questions/61835509/default-argument-and-parameter-promotions-in-c