1

The meanings of the phrases trailing arguments and default argument promotion are unclear to me as used in the following excerpts, where the two paragraphs seem almost contradictory, leading me to be unclear about when default promotions should be expected.

ISO/IEC 9899:201x section 6.5.2.2 Function calls:

  • para 6: If the expression that denotes the called function has a type that does not include a prototype,..., and arguments that have type float are promoted to double. These are called the default argument promotions.
  • para 7: "...The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments."

From paragraph 6, (a type that does not include a prototype) seems to suggest that only arguments 4.0 and 5.0 will undergo default promotions. Then in para. 7 it says promotion stops after the last declared parameter. (I believe that is b). Seeming to suggest that a and b will undergo promotions, but nothing following them in the argument list will be promoted. But then it goes on to say default promotions are performed on trailing arguments. Trailing means at the end of, indicating those allowed by the ellipsis.

So what exactly gets promoted when calling f(), and Why?

 int f(float a, float b, ...); int main(void) { float a = 1.0; float b = 2.0; int res = f(a, b, 4.0, 5.0); return 0; } int f(float a, float b, ...) { ... } 
17
  • 1
    para 7 says "argument type conversion" stops (according to the prototype I assume), not "promotion stops". I think that's a difference Commented Sep 11, 2020 at 15:20
  • 2
    The arguments 4.0 and 5.0 are both of type double and undergo no promotion. Paragraph 7 says that the normal conversion of actual argument type to the type demanded by the prototype stops when there are no more named parameters because the arguments are passed 'to the ellipsis' part of the function signature. If you passed 1.0 in place of a, then the double value would be converted to float because of the prototype (assuming f was declared correctly before it was called). If you passed 4.0F instead of 4.0, the float value would be default promoted to double. Commented Sep 11, 2020 at 15:24
  • 3
    "argument type conversion" means converting to the declared type of the corresponding parameter. "default argument promotion" refers to converting to a default type because there's no declared parameter. Commented Sep 11, 2020 at 15:25
  • 1
    No — the phrases are different because they mean different things. Argument type conversion is the process that allows sqrt(2) to work correctly — converting the integer argument to double because of the prototype. Default argument promotions occur when there is no prototype or when the argument is passed 'to the ellipsis' part of a prototyped function. Commented Sep 11, 2020 at 15:26
  • 2
    If I read this correctly, your excerpt from 6 relates to function calls without a known prototype, while 7 related to those with a prototype. Commented Sep 11, 2020 at 15:26

2 Answers 2

5

None of the conditions in Paragraph 6 apply to your example. A function without a prototype refers to a function declared using the archaic K&R syntax:

int f(); 

When you call a function with this type of declaration, all arguments undergo default promotions.

Paragraph 6 also describes other situations where there's a prototype and the types in the call are not compatible to with the types in the prototype, but your types are compatible (they're the same).

Paragraph 7 says that argument conversion is performed for the first two arguments; they're converted to float as specified in the prototype. Since a and b are already float, no conversion is necessary.

The remaining arguments undergo default argument promotion (as described in Paragrah 6), since they correspond to the ellipsis in the prototype. The literals 4.0 and 5.0 have type double, so no promotion is necessary.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the clarification regarding prototype or no prototype. I had been assuming that the text If the expression that denotes the called function has a type that does not include a prototype, referred to the variables allowed by the ellipsis. (i.e. arguments 3 and 4 in the example code. )
"expression that denotes the called function" refers to f.
Thanks, I've updated to clarify that none of the other conditions in 6 apply, either.
2

According to the C11 standard §6.5.2.2 Function calls (and I don't think this changed in C18), the full quotes for paragraphs 6 and 7 are:

¶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.

¶7 If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

Given the code from the question:

int f(float a, float b, ...); int main(void) { float a = 1.0; float b = 2.0; int res = f(a, b, 4.0, 5.0); return 0; } int f(float a, float b, ...) { ... } 

There are no argument type conversions or promotions in the call to f.

  • The arguments a and b are both of type float, the same as in the prototype and definition of the function, so there is no conversion required.
  • The arguments 4.0 and 5.0 are both of type double and do not undergo default argument promotion. These values are passed after the last declared parameter, so they are eligible for default argument promotion, but since the type is already double, no promotion is required.
  • If the argument was 4.0F instead of 4.0, then the float value would undergo default argument promotion to double.

If the call was written as:

int res = f(4.0, 5.0, a, b); 

then:

  • The double arguments 4.0 and 5.0 would be "implicitly converted, as if by assignment" to float because that's what the prototype requires.
  • The float arguments a and b would be promoted to type double because they undergo default argument promotion.

2 Comments

One question, toward the bottom of your answer you say The double arguments 4.0 and 5.0- Did you mean _The float arguments 4.0 and 5.0?
I meant "the double arguments 4.0 and 5.0". Without a qualifying suffix, floating point constants are of type double. To create a float constant, add the suffix F (or f); to create a long double constant, add the suffix L (or l, but don't use the lower-case letter for either suffix).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.