According to the C standard (6.5.2.2 paragraph 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.
Thus, in general, there is nothing wrong with passing an int
to a variadic function that expects an unsigned int
(or vice versa) as long as the value passed fits in both types. However, the specification for printf
reads (7.19.6.1 paragraph 9):
If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
No exception is made for signed/unsigned mismatch.
Does this mean that printf("%x", 1)
invokes undefined behavior?
printf("%d",(char)1);
. The description ofprintf
doesn't say that it's the argument after integer promotions which must be the correct type, it says the argument itself must be. Should we conclude that it's an exception to that part of 6.5.2.2/6 as well? – Eldestprintf
if it hasn't been prototyped, and your quote concerns calls made where there is no prototype. The same argument promotions are applied to the arguments of varargs, though, according to 6.5.2.2/7, although that doesn't say anything about signed/unsigned compatibility. So maybe you're absolutely right, and signed/unsigned compatibility is only stated to apply to calls made with no prototype, not to varargs calls in general, let aloneprintf
in particular. – Eldestvoid foo();
, then dofoo(1)
, "the expression that denotes the called function" isfoo
, and its type does not include a prototype. The definition offoo
will introduce a prototype, perhaps in a different translation unit, butfoo
doesn't have one at the call point. – Eldest,...
prototype, the only part of 6.5.2.2/6 that is relevant is the description of default argument promotions. The mismatched arguments exceptions are not applicable. (In any case, the function must be defined with a matching prototype and the...
parameters don't have a known type.) The corresponding requirements for accessing vargs are in 7.15.1.1 which describes the use of theva_arg
macro. Here you are allowed to useva_arg
to access (e.g.) anint
as anunsigned int
providing the value is in the correct range. – Drediprintf
, i.e. if it's intended to mean that the value passed must be a valid value for the specified type (prior to default promotions and possible signedness mismatch in the resulting type). Of course that goes more with the other question. – Impetuosityunsigned short x = 1; printf("%hu\n", x);
would also appear to be UB due to the unsigned / signed mismatch introduced by integer promotions, even though most people reading it would probably not expect it. – Dancette%hu
expects an argument whose type after default promotions is the type ofunsigned short
with default promotions applied, which (assumingint
wider thanshort
) is not an unsigned type. – Impetuosity