Default argument and parameter promotions in C

余生长醉 提交于 2021-02-08 14:24:54

问题


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 and short get promoted to int
  • float gets promoted to double

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!