Home > Software design >  Why does converting string to hex need to be done with 0xff in C?
Why does converting string to hex need to be done with 0xff in C?

Time:09-29

Following on this question How to convert a string to hex and vice versa in c??

I run the following code:

#include <stdio.h>

int main (int argc, char *argv[])
{
    char str[] = { 0x58, 0x01, 0x02, 0x20, 0x22, 0x00, 0xC5};
    char hex[32] = {0}, hex2[32] = {0};

    for (int i = 0; i < sizeof(str); i  ) {
        sprintf(hex   i*2, "X", str[i]);
    }

    for (int i = 0; i < sizeof(str); i  ) {
        sprintf(hex2   i*2, "X", str[i] & 0xff);
    }

    printf("hex = %s\nhex2 = %s\n", hex, hex2);
    return 0;
}

I get this result:

hex = 580102202200FFFFFFC5
hex2 = 580102202200C5

I wonder why there is more FFFFFF without 0xFF?

CodePudding user response:

There are many small pieces to the puzzle that needs to be put together.

To begin with it's implementation-defined if char is signed or unsigned.

If it's signed, then because of the common two's complement the value 0xc5 will actually be negative (it will be the decimal value -59).

And when a small integer type (like char) is used as an argument to a variable-argument function like sprintf, then it will be promoted to int. If the type is signed and the value is negative, it will be sign extended. So assuming the common 32-bit int, the signed and negative value 0xc5 will be promoted and sign-extended to 0xffffffc5.

To solve this problem, the simplest solution is to simply use an explicit unsigned type like uint8_t (which basically is an alias for unsigned char).

Besides that I also recommend you use the hh format type prefix to explicitly say that the argument is a byte.

Put together change to

uint8_t str[] = { 0x58, 0x01, 0x02, 0x20, 0x22, 0x00, 0xC5};

and

sprintf(hex   i*2, "hhX", str[i]);

As for the difference made by str[i] & 0xff, it's because then you mask out the top 24 bits of the int value. So 0xffffffc5 & 0xff becomes 0x000000c5.

CodePudding user response:

Before you go believing some of the things you've been told, stop and consider...

sprintf( dst, "X", str[i] & 0xff);

could be written

sprintf( dst, "X", ( str[i] & 0xff)  );

Now, since str[i] has been defined as a (signed) char, it should be apparent that there are no extra higher bits involved in this operation.

Instead, the magic of the 0xff is that hexadecimals are unsigned values.

If the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type is converted to the type of the operand with unsigned integer type.

The compiler will convert the signed byte to unsigned and perform the masking (which is superfluous). This unsigned value 0x## will be promoted to an unsigned integer value passed to the variadic function printf as 0x000000##...

Careful to whom you confer the term 'advisor'.

The 1st comment explains the solution to your overabundant 'F's...
You've mixed hex notation into a (signed) char array... Easy to fix...

  • Related