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
?
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 affectsunsigned short
, promoting it toint
ifint
can represent allunsigned short
values, as is the case in your example (and tounsigned int
otherwise). It does not affect arguments of typeint
orunsigned int
, or wider integer types.The key problem with the code presented is that ...
... exhibits undefined behavior on account of the
%d
directive not being correctly type matched to the corresponding argument.%d
must be matched to a signedint
, but you have associated it with anunsigned int
. In practice, the UB is manifesting as interpreting the bit pattern of the argument as if it were asigned
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:
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-negativesigned int
argument has been interpreted as anunsigned int
.