Reputation: 320
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
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
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
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
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