C semantics regarding conversion of integers is defined in terms of the mathematical values, not in terms of the bits. It is good to understand how values are represented as bits, but, when analyzing expressions, you should think about values.
In this statement:
printf("Signed : %d\n",x);
the signed char
x
is automatically promoted to int
. Since x
is −1, the new int
also has the value −1, and printf
prints “-1”.
In this statement:
printf("Unsigned : %d\n",(unsigned)x);
the signed char
x
is automatically promoted to int
. The value is still −1. Then the cast converts this to unsigned
. The rule for conversion to unsigned
is that UINT_MAX+1
is added to or subtracted from the value as needed to bring it into the range of unsigned
. In this case, adding UINT_MAX+1
to −1 once brings the value to UINT_MAX
, which is within range. So the result of the conversion is UINT_MAX
.
However, this unsigned
value is then passed to printf
to be printed with the %d
conversion. That violates C 2018 7.21.6.1 9, which says the behavior is undefined if the types do not match.
This means a C implementation is allowed to do anything. In your case, it seems what happened is:
- The
unsigned
value UINT_MAX
is represented with all one bits.
- The
printf
interpreted the all-one-bits value as an int
.
- Your implementation uses two’s complement for
int
types.
- In two’s complement, an object with all one bits represents −1.
- So
printf
printed “-1”.
If you had used this correct code instead:
printf("Unsigned : %u\n",(unsigned)x);
then printf
would print the value of UINT_MAX
, which is likely 4,294,967,295, so printf
would print “4294967295”.