1

I have the following struct

typedef struct __attribute__((packed)) word {
    uint16_t value;
    uint8_t flag
} Word;

I want to convert it to a hex string. For example if value = 0xabcd and flag = 0x01 I want to get the string 01abcd

if I do some pointer juggling

Word word;
word.value = 0xabcd;
wordk.flag = 0x01;

printf("word: %X\n", *(int *)&word);

I get the output that I want (word: 1ABCD) but this doesn't seem safe and when I tried to do this after looking at some of the answer here

char ptr[3];
memcpy(ptr, word, 3);
printf("word: %02X%02X%02X\n", ptr[2], ptr[1], ptr[0]);

I got word: 01FFFFFFABFFFFFFCD, for some reason the first two bytes are being extended to a full int

4 Answers 4

3

There's no real gain from messing around with pointers or type-punning, if all you want is to output the values of the two structure members. Just print them individually:

printf("word: %02x%04x\n", (unsigned int)word.flag, (unsigned int)word.value);
Sign up to request clarification or add additional context in comments.

4 Comments

%x expects an unsigned int parameter so you should add casts ro avoid UB
@AdrianMole: No, they will not. uint8_t will be promoted to int. uint16_t will be promoted to or already is unsigned int if that is 16 bits. Otherwise, it will be promoted to int.
@EricPostpischil Oops. excellent point ... edited. (Those integer promotion rules can still bite me!)
The gain from "messing around with pointers" would be massively improved execution speed, much smaller binary size and no stdio.h format string/va arg/non-existent type safety hazards present in the program (as illustrated by the above comments). How exactly is faster, smaller and safer "no real gain"?
2

Use a simple sprintf to convert to a string:

int main(void)
{
    Word v = { 0xabcd, 0x01 };
    
    char s[10];
    
    sprintf(s, "%02x%04x", v.flag, v.value);
    
    puts(s);
}

Comments

1

I want to get the string 01abcd

So you want to print the binary representation of the struct on a little endian machine backwards. There's no obvious advantage of using sprintf for this apart from it being quick & dirty to implement.

If you want something with performance, then hacking this out manually isn't rocket science - simply iterate over the struct byte by byte and convert each nibble to the corresponding hex character:

void stringify (const uint8_t* data, size_t size, char* dst)
{
  for(size_t i=0; i<size; i++)
  {
    uint8_t byte = data[size-i-1]; // read starting from the end of the data
    dst[i*2]   = "0123456789ABCDEF"[ byte >> 4  ]; // ms nibble
    dst[i*2+1] = "0123456789ABCDEF"[ byte & 0xF ]; // ls nibble
  }
  dst[size*2] = '\0';
}

This will give "01ABCD" on a little endian machine and "01CDAB" on a big endian machine.

(Optionally add restrict to data and dst parameters but I doubt it will improve performance since the const correctness already blocks aliasing at some extent.)

Comments

0

Could join them mathematically first.

printf("word: %06lx\n", wordk.flag * 0x10000L + word.value);

Using long in case we are on a 16-bit int machine.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.