On my way to computing ranges of char, short, int, and long variables, both signed and unsigned, I took help of the following solutions:
According to solution 1, I expected (unsigned short)~0
in the code below to output -1 and 65535, assuming its behaviour is the same as (unsigned int)~0
code for the two format specifiers.
// the two statements below produce different results
printf("Value of unsigned int is %d\n", (unsigned int)~0); // outputs -1
printf("Value of unsigned int is %u\n", (unsigned int)~0); // outputs 4294967295
// whereas, the two statements below produce the same result. Why?
printf("Value of short unsigned int is %d\n", (unsigned short)~0); // outputs 65535, expected -1
printf("Value short unsigned int is %u\n", (unsigned short)~0); // outputs 65535
Why is there a difference in the behaviour of (unsigned short)~0
and (unsigned int)~0
?
Why is there a difference in the behaviour of
(unsigned short)~0
and(unsigned int)~0
?
The behavior of those expressions is analogous. Supposing two's complement representation of type int
, each computes the largest representable value of its (unsigned) type.
However, variable arguments to a variadic function such as printf
are subject to default argument promotions. This affects unsigned short
, promoting it to int
if int
can represent all unsigned short
values, as is the case in your example (and to unsigned int
otherwise). It does not affect arguments of type int
or unsigned int
, or wider integer types.
The key problem with the code presented is that ...
printf("Value of unsigned int is %d\n", (unsigned int)~0);
... exhibits undefined behavior on account of the %d
directive not being correctly type matched to the corresponding argument. %d
must be matched to a signed int
, but you have associated it with an unsigned int
. In practice, the UB is manifesting as interpreting the bit pattern of the argument as if it were a signed
int
instead of an unsigned one, but in principle, the program could have done anything at all within its power.
Note well that this also has undefined behavior because of type mismatch:
printf("Value short unsigned int is %u\n", (unsigned short)~0);
The directive %hu
would be the best match to the corresponding actual argument, but %d
is acceptable because of the aforementioned automatic type promotion. %u
does not match. In this case, however, the UB manifested is identical to the behavior you expected -- at least as far as the output indicates. In practice, the bit pattern of the non-negative signed int
argument has been interpreted as an unsigned int
.