Why char specified as integer in printf gets printed correctly

Viewed 720

Why does the following code work?

char c = 'A';
printf("%d - size: %d", c, sizeof(c));

Prints out:

65 - size: 1

Why is the output not garbage, since an int would usually be 4 bytes long and we can clearly see that the char is 1 byte long. Does the compiler do the implicit conversation?

2 Answers

Any integer type with a rank lower than int is promoted to either int or unsigned int anytime it is used in an expression. This is specified in section 6.3.1.1p2 of the C standard:

The following may be used in an expression wherever an int or unsigned int may be used:

  • An object or expression with an integer type (other than int or unsigned int ) whose integer conversion rank is less than or equal to the rank of int and unsigned int .
  • A bit-field of type _Bool , int , signed int ,or unsigned int .

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int ; otherwise, it is converted to an unsigned int . These are called the integer promotions .

All other types are unchanged by the integer promotions

That's what is happening in this case, since the printf function doesn't implicitly know the type of its parameters at compile time. So the char argument is promoted to int, and using %d to format it is valid.

There is a special rule for functions with variable-length argument lists, like printf. In the variable-length portion of the argument list, all integral arguments smaller than int are promoted to int, and float is promoted to double. So it turns out it's perfectly fine to print a character (or a short) using %d.

These default argument promotions end up accounting for a number of anomalies in printf. You might think that the correct format specifiers for char, short, int, float, and double are %hhd, %hd, %d, %f, and %lf, respectively. But in fact you can get away with %d, %d, %d, %f, and %f. printf basically ignores the l modifier for floating point, and it seems to ignore the h modifier for integers. (Actually h can make a difference in obscure cases, as chux explains in a comment.)

Related