-1

I would like to know why printf statement displays the character value as 4 byte value (fffffffe) in the following code. Since the character is of 1 byte, I thought we should be getting result of only FE one byte value.

int main(void)
{
    unsigned char a4=1;
    char b4=1;
    printf("inverted a4 = %x\t b4= %x\n",~a4, ~b4);
    return(0);
}
Jon Wheelock
  • 263
  • 3
  • 16

6 Answers6

2

The operand of the ~ operator undergoes the integer promotions. An operand of type char or unsigned char is promoted to int, and the result is of type int.

char b4=1;

b4 is of type char.

~b4

The value of b4 is promoted to int, and the ~ operator yields the bitwise complement of 1. If int is 32 bits, the result is -2, assuming a 2's-complement representation for signed integers.

printf"%x\n", ~b4);

The int value -2 is printed as if it were of type unsigned char; the result is fffffffe.

If you want just the low-order 8 bits, you can use a bitwise mask:

printf("%X\n", ~b4 & 0xFF);

This will print FE.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
2

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.)

zwol
  • 135,547
  • 38
  • 252
  • 361
0

sizeof(~b4) was 4 in Wandbox.

To get only FE, use AND mask.
Also, change %x to %X to get FE, not fe.

#include <stdio.h>

int main(void)
{
    unsigned char a4=1;
    char b4=1;
    printf("inverted a4 = %X\t b4= %X\n",(~a4) & 0xFF, (~b4) & 0xFF);
    return(0);
}
MikeCAT
  • 73,922
  • 11
  • 45
  • 70
0

The parameter %X and %x expects int.

You providing char and it is casted to int.

The solution is as MikeCAT give:

printf("inverted a4 = %X\t b4= %X\n",(~a4) & 0xFF, (~b4) & 0xFF);
Nick
  • 9,962
  • 4
  • 42
  • 80
0

The reason for this is the way ...(ellipsis) functions work in C. When the char argument is provided to ellipsis function, it is 'expanded' to int (or unsigned int, depending whether char is signed or unsigned). Actually, everything which is smaller than int is expanded (correct term is promoted) to int. As a result of this promotion, when printf sees the value, it was already promoted. By the way, float is promoted to double in the same way.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
0

The compiler is promoting the character (0x01) to a 32-bit integer (0x00000001) before it inverts it, this gives the value (0xFFFFFFFE).

You could do a bitwise AND as the other answers mentioned, or add a type cast to convert the answer back to char type afterward:

unsigned char a4=1;
char b4=1;
printf("inverted a4 = %X\t b4= %X\n",(unsigned char)(~a4), (char)(~b4));
return(0);
R3K
  • 11
  • 3