Lee Jun Wei
Lee Jun Wei

Reputation: 320

How to copy hex representation of uint_64 to unsigned char array in hex, in C?

I'm not sure how to put what I am trying to achieve in words, but I'll just put an example below, hope you guys can lend me a hand!

Basically I'm trying to copy the hex representation of uint64_t value into an unsigned char array, in hex.

What I have:

uint64_t src;
unsigned char destination[8];
/* codes omitted, some codes that will change the value of src */
printf("src: %"PRIx64"\n", src); //this prints out src: 3132333435363738

How do I copy the values from src into destination array in a way that:

destination[0] = 0x31;
destination[1] = 0x32;
destination[2] = 0x33;
//and so on...

Thanks!

EDIT

I'm sorry If my question is unclear, I'm very new to programming and I'm struggling to explain myself. Basically I'm just trying to store whatever that prints out from that printf() into the unsigned char array as hex.

e.g, printf() outputs a string of "3132333435363738", I want to take 31 and store it in dst[0] where the value of dst[0] will be 0x31, and so on, where dst[1] will be 0x32.

Please bear with me, thanks!

Upvotes: 0

Views: 2100

Answers (4)

Matteo Italia
Matteo Italia

Reputation: 126787

The easy and portable way to extract the bytes that make up an integer in a given order is to just use bit shifts (and possibly masks); in your case, you seem to want to extract your bytes in big-endian order (first byte in destination => most significant byte in src), so that would be:

uint64_t src;
unsigned char destination[8];
for(int i=0; i<8; ++i) {
    destination[i] = src>>((7-i)*8);
} 

At each iteration, src is shifted to the right by (7-i)*8 bits, because we want the desired byte to go "at the bottom" of the result

First iteration:

src = 0x123456789abcdef0;
i = 0
(7-i) = 7
src >> ((7-i)*8) = 0x12

so, we got 0x12 and we put it into destination[0]; at the next iteration, we have

i = 1
(7-i) = 6
src >> ((7-i)*8) = 0x1234

Now, we could mask the result with 0xFF to take only the bottom 8 bit (namely, 0x34), but that's not important in our case, since each element of destination is an unsigned byte, so when assigning a value larger than 255 (as in this case) it does unsigned overflow, which is well defined to take only the lower bytes that "fit" the target; so, destination[1]=0x34.

Repeat this for i up to 7 and you get the required result.

Upvotes: 3

ad absurdum
ad absurdum

Reputation: 21314

This is essentially the same as the solution given by @Matteo Italia, but using a mask and an explicit cast to the fixed width type uint8_t instead of unsigned char. These solutions should work regardless of endianness, because the bitwise operators work on the value of the left operand, not its underlying representation.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main(void)
{
    uint64_t src = 0x3132333435363738;
    uint8_t destination[8];
    uint64_t mask = 0xff << 56;
    size_t i;

    for (i = 0; i < 8; i++) {
        destination[i] = (uint8_t) ((src & mask) >> (56 - i * 8));
        mask >>= 8;
    }

    printf("src: 0x%" PRIx64 "\n", src);

    for (i = 0; i < 8; i++) {
        printf("destination[%zu] = 0x%" PRIx8 "\n", i, destination[i]);
    }

    return 0;
}

Output is:

src: 0x3132333435363738
destination[0] = 0x31
destination[1] = 0x32
destination[2] = 0x33
destination[3] = 0x34
destination[4] = 0x35
destination[5] = 0x36
destination[6] = 0x37
destination[7] = 0x38

Upvotes: 0

Lundin
Lundin

Reputation: 213809

Solutions using pointer casts + pointer arithmetic, or solutions using unions will not be portable.

Here is a simple way to do this portably, no matter endianess. There may be more effective ways.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>

int main(void)
{
  uint64_t src;
  uint8_t destination[8] = {0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38};

  const uint16_t dummy = 1;
  const bool is_little_endian = *(const uint8_t*)&dummy == 1;

  src=0;
  for(size_t i=0; i<8; i++)
  {
    size_t shift = i*8;

    if(is_little_endian)
    {
      shift = 64-8-shift;
    }

    src |= (uint64_t)destination[i] << shift;
  }

  printf("%" PRIx64, src);

  return 0;
}

Upvotes: 0

mtrw
mtrw

Reputation: 35088

You can set a pointer to point to the uint64_t itself:

uint64_t src;
unsigned char *view;

view = (unsigned char *)&src;
for (size_t i = 0; i < sizeof(src); ++i) {
  printf("%x", view[i]);
}
printf("\n");

If you need to copy the value, not just have another view of it, the same principle applies:

uint64_t src;
unsigned char dst[sizeof(src)];
memcpy((void *)&dst, (void *)&src, sizeof(src));
for (size_t i = 0; i < sizeof(src); ++i) {
  printf("%x", dst[i]);
}
printf("\n");

Note that this will inherit the native endianness of the platform. If you want to convert endianness, you can use the library function uint64_t htobe64(uint64_t x) (Linux,BSD) or OSSwapHostToBigInt64(x) (OS X) on src before viewing/copying the string. I don't know the equivalents for Windows but I assume they have them. See I used unsigned char instead of char to avoid any sign extension issues.

Upvotes: 0

Related Questions