ccharsignedinteger-promotioninternal-representation

I learned that in C language char type ranges from -128 to 127, but it doesn't seem like that


This might be a very basic problem, but I couldn't manage to. Here is what I am working with.

#include <stdio.h>

int main(void)
{
    char c1, c2;
    int s;
    c1 = 128;
    c2 = -128;

    s = sizeof(char);

    printf("size of char: %d\n", s);
    printf("c1: %x, c2: %x\n", c1, c2);
    printf("true or false: %d\n", c1 == c2);
}

The result is like this.

size of char: 1
c1: ffffff80, c2: ffffff80
true or false: 1

i assigned the value 128 to signed(normal) char type, but it didn't overflow.

In additon, c1 and c2 both seems to hold 4bytes, and -128 and 128 are the same value.

How can I understand these facts? I need your help. Thank you very much.


Solution

  • In c1 = 128;, 128 does not fit in the signed eight-bit char that your C implementation uses. 128 is converted to char per C 2018 6.5.16.1 2: “the value of the right operand is converted to the type of the assignment expression…”

    The conversion is implementation-defined, per 6.3.1.3 3: “Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.” Your C implementation converted 128, which is 100000002 as an unsigned binary numeral, to −128, which is represented with the same bits when using two’s complement for signed binary. Thus, the result is that c1 contains the value −128.

    In printf("c1: %x, c2: %x\n", c1, c2);, c1 is converted to an int. This is because the rules for calling functions with ... parameters are to apply the default argument promotions to the corresponding arguments, per 6.5.2.2 7: “The default argument promotions are performed on trailing arguments.”

    The default argument promotions include the integer promotions, per 6.5.2.2 6. When the range of char is narrower than int, as it is in most C implementations, the integer promotions convert a char to an int, per 6.3.1.1 2: “If an int can represent all values of the original type…, the value is converted to an int…”

    Thus, in printf("c1: %x, c2: %x\n", c1, c2);, an int value of −128 is passed as the second argument. Your C implementation uses 32-bit two’s complement for int, in which −128 is represented with the bits 11111111111111111111111110000000, which we can express in hexadecimal as ffffff80.

    The format string specifies a conversion using %x. The proper argument type for %x is unsigned int. However, your C implementation has accepted the int and reinterpreted its bits as an unsigned int. Thus, the bits 11111111111111111111111110000000 are converted to the string “ffffff80”.

    This explains why “ffffff80” is printed. It is not because c1 has four bytes but because it was converted to a four-byte type before being passed to printf. Further, the conversion of a negative value to that four-byte type resulted in four bytes with many bits set.

    Regarding c1 == c2 evaluating to true (1), this is simply because c1 was given the value −128 as explained above, and c2 = -128; also assigns the value −128 to c2, so c1 and c2 have the same value.