Question

I recently read that

unsigned char x=1;
printf("%u",x);

invokes undefined behaviour since due to the format specifier %u, printf expects an unsigned int. But still I would like to understand what is going on in this example.

I think that the integral promotion rules apply with the expression printf("%u",x) and the value represented by x.

A.6.1 Integral Promotion

A character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type, may be used in an expression wherever an integer may be used. If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. This process is called integral promotion.

What does "may be used" mean here? Does it mean 'is syntactically correct' or 'is defined behaviour'?

And how is x promoted in this example? I have read that it is promoted to an int, but if printf("%u", (int x)) is still undefined behaviour then I don't really understand why...

Was it helpful?

Solution

Since printf uses a variable argument list, the integer promotions are applied to its integer arguments. In any normal C implementation, the integer promotions convert an unsigned char to an int. Then you are formatting an int With a specifier for unsigned int, so the behavior is undefined.

There is no conflict between saying that a character may be used where an integer may be used and the fact that your statement has behavior not defined by the C standard. Although you may use a character in place of an integer, the rules about what may be printed with %u still apply. If using a character results in an integer appropriate for the specifier, the behavior is defined. If using a character results in an integer inappropriate for the specifier, the behavior is not defined by the C standard.

Discussion elsewhere on Stack Overflow concluded that an exotic C implementation might in theory conform to the C standard while having char types (plain, signed, and unsigned) as wide as int types. In such an implementation, and int could not represent all values of an unsigned char, so an unsigned char would have to be promoted to an unsigned int. However, such an implementation would be exotic and troublesome (notably with handling EOF), and you may ignore it in practice.

OTHER TIPS

If your platform's int can represent all values than an unsigned char can, then the promotion is to int, otherwise to unsigned int. So it depends on your platform.

As to "why", that's because you're passing x as a variable argument, and the rules of variable arguments say that the standard promotions take place (presumably so as to simplify the implementation).

Since printf uses a variable argument list, it will be unpacked via va_arg. C++ refers to the C standard for va_arg rules. The C99 Standard says the following:

The va_arg macro expands to an expression that has the specified type and the value of the next argument in the call. The parameter ap shall have been initialized by the va_start or va_copy macro (without an intervening invocation of the va_end macro for the same ap). Each invocation of the va_arg macro modifies ap so that the values of successive arguments are returned in turn. The parameter type shall be a type name specified such that the type of a pointer to an object that has the specified type can be obtained simply by postfixing a * to type. If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:

  • one type is a signed integer type, the other type is the corresponding unsigned integer type, and the value is representable in both types;
  • one type is pointer to void and the other is a pointer to a character type.

Clearly, integer promotions are taken into account when determining whether the actual and expected type match. And signed vs unsigned mismatch is covered by the first bullet point.

Since x = 1 is certainly a value representable by unsigned int, and promotion of unsigned char generates either signed int (if INT_MAX >= UCHAR_MAX) or unsigned int (if INT_MAX < UCHAR_MAX), this is perfectly legal.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top