Search code examples
ctypesxor

unsigned char and char XOR operation


Is there anyone can help me to explain what is the difference between unsigned char and char in XOR operation?

#include <stdio.h>

int main() {
    char a[2] = { 0x56, 0xa5 };  // a[0]  0101 0110   
                                 // a[1]  1010 0101 
    a[0] = a[0] ^ a[1];          // a[0]  1111 0011    f3   
    printf("%02x", a[0]);
    puts("");

    unsigned char b[2] = { 0x56, 0xa5 }; // b[0]  0101 0110
                                         // b[1]  1010 0101 
    b[0] = b[0] ^ b[1];                  // b[0]  1111 0011    f3   
    printf("%02x", b[0]);
    puts("");
}

result:

fffffff3
f3
[Finished in 0.0s]

Another example:

#include <stdio.h>

int main() {
    char a[2] = { 0x01, 0x0a };

    a[0] = a[0] ^ a[1];          
    printf("%02x", a[0]);
    puts("");

    unsigned char b[2] = { 0x01, 0x0a };       

    b[0] = b[0] ^ b[1];             
    printf("%02x", b[0]);
    puts("");
}

result:

0b
0b
[Finished in 0.0s]

Solution

  • In the first case, your code

    printf("%02x", a[0]);
    

    Passes a char value to a variadic function printf. The char value is promoted to int type and passed as such. The value of a[0] is -13 because the char type happens to be signed by default on your environment, The value is preserved by the promotion as int and printf receives it as an int.

    The format %02x expects an unsigned int value. printf was passed an int value, an incorrect type invoking undefined behavior. Since int and unsigned int use the same parameter passing convention on your platform, this negative value of -13 is interpreted as an unsigned int with the same bit pattern, with value 0xFFFFFFF3 because the int on your platform has 32 bits and negative values are represented in 2s complement. The string produced by printf is fffffff3. This behavior is not actually guaranteed by the C Standard.

    In the second example, b[0] is an unsigned char with value 243 (0xf3). Its promotion to int preserves the value and the int passed to printf is 243. The same undefined behavior is invoked as printf is passed an int instead of an unsigned int. In your particular case, the conversion performed by printf yields the same value, which printed as hex with at least 2 digits padded with leading 0s gives f3.

    To avoid this ambiguity, you should either cast the operand as unsigned char:

    printf("%02x", (unsigned)(unsigned char)a[0]);
    

    Or specify its actual type as unsigned char:

    printf("%02hhx", (unsigned char)a[0]);