When you do any arithmetic or bit operation on a char
quantity, it is silently converted to int
first. This is called integer promotion. It affects short
(and signed char
, unsigned char
, and unsigned short
) as well. (Technically, if sizeof(unsigned short) == sizeof(unsigned int)
then unsigned short
would promote to unsigned int
rather than int
, but you're not likely to trip over a system with that characteristic anymore, even though it's still allowed by the C standard.)
This also happens to char
and short
when they are passed through the anonymous arguments to printf
(or any other variadic function).
So, your code
unsigned char a4=1;
char b4=1;
printf("inverted a4 = %x\t b4= %x\n",~a4, ~b4);
is equivalent to
...
printf("inverted a4 = %x\t b4= %x\n", (int) ~(int)a4, (int) ~(int)b4);
(The cast after the ~
doesn't do anything but I have written it anyway to emphasize that conversion could happen both because of the arithmetic and because of the argument passing.)
There is no way to turn this behavior off. To get the effect you wanted, I would write
static_assert(CHAR_BIT == 8);
printf("inverted a4 = %02x\tb4 = %02x\n",
(~(unsigned int)a4) & 0xFFu,
(~(unsigned int)b4) & 0xFFu);
The difference between this and the code suggested by MikeCAT and Nick is that all bit operations are done on unsigned quantities. Some bit operations have undefined behavior when applied to signed quantities, and I can never remember exactly what the rules are, so I just avoid doing any of them to signed quantities. (The change from %x
to %02x
is just for aesthetics.)
Systems on which the static_assert
fails are vanishingly rare nowadays, but again, still allowed by the C standard. I mostly include it to document the program's expectations. (The 0xFFu
masks and %02x
formatters are wrong on a system with a different value for CHAR_BIT
.)