21

My question is how do you convert a UINT32 value to a UINT8 array[4] (C/C++) preferably in a manner independent of endianness? Additionally, how would you reconstruct the UINT32 value from the UINT8 array[4], to get back to where you started?

Chris
  • 211
  • 1
  • 2
  • 3
  • 1
    How do you want to do the conversion? Big-endian or little-endian? Specifically: If your input is 0x12345678, do you want array[0] to be 0x12 or 0x78? You have to decide this for yourself. Then we can help you. – TonyK Jun 28 '11 at 15:16

5 Answers5

40

You haven't really said what you mean by independent of endianness - it's unclear since the byte array must have some endianness. That said, one of the below must answer your requirements:

Given UINT32 v and UINT8 a[4]:

"Host" endian

(use the machine's native byte order):

UINT8 *vp = (UINT8 *)&v;
a[0] = vp[0];
a[1] = vp[1];
a[2] = vp[2];
a[3] = vp[3];

or:

memcpy(a, &v, sizeof(v));

or:

*(UINT32 *)a = v;

Big endian

(aka "network order"):

a[0] = v >> 24;
a[1] = v >> 16;
a[2] = v >>  8;
a[3] = v;

Little endian

a[0] = v;
a[1] = v >>  8;
a[2] = v >> 16;
a[3] = v >> 24;
Community
  • 1
  • 1
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • Do you have a solution for move semantics? Say you want to move the value of v to the byte array without copying v. – jiggunjer Jul 15 '15 at 07:28
  • 1
    @jiggunjer that makes no sense. Primitive values can't get "moved" like that. – Alnitak Jul 15 '15 at 10:20
  • 1
    @jiggunjer Yes. If you're talking about C++11 "move semantics" there's nothing to "move" in a 32-bit variable. – Alnitak Jul 15 '15 at 11:46
  • I realise this is an old answer (it got liked to by an EE.SE question). But `*(UINT32 *)a = v;` is a very dangerous assumption - if `a` is not initialised aligned to a `UINT32` word boundary this will likely cause an unaligned access error. – Tom Carpenter Sep 26 '20 at 09:28
14

E.g. like this:

UINT32 value;
UINT8 result[4];

result[0] = (value & 0x000000ff);
result[1] = (value & 0x0000ff00) >> 8;
result[2] = (value & 0x00ff0000) >> 16;
result[3] = (value & 0xff000000) >> 24;

Edit: added parenthesis (>> seems to have higher precedence than &)

Patrick
  • 23,217
  • 12
  • 67
  • 130
  • The masks for `result[0]` and `result[3]` do not seem strictly necessary. – Pascal Cuoq Jun 27 '11 at 21:36
  • Remove the &'s. They're useless and a bad compiler might not eliminate them. Simply `result[0]=value; result[1]=value>>8; ...` is all you need. – R.. GitHub STOP HELPING ICE Jun 27 '11 at 21:54
  • 2
    @R.: I disagree. They express the symmetry (I normally have a `>> 0` for exactly this purpose), and it would have to be a pretty terrible compiler that wouldn't optimise them away. – Oliver Charlesworth Jun 27 '11 at 21:56
  • 4
    Whatever the aesthetic merits of the `&` operators, the fact is that they kill the code. `>>` has higher precedence than `&`. – TonyK Jun 27 '11 at 21:58
  • @TonyK is exactly right; this is incorrect because of operator precedence. The masks are unnecessary anyway -- all you need are the shifts. – Stephen Canon Jun 27 '11 at 22:05
  • 1
    @R.: Actually, I take that back. I overlooked the fact that we're converting to `uint8`, so of course, all masks are unnecessary, as @Stephen points out. – Oliver Charlesworth Jun 27 '11 at 22:07
  • To all: I agree with the precedence problem. I forgot the parenthesis (should have checked this). Sorry for that. – Patrick Jun 28 '11 at 06:41
  • 4
    And regarding the use of &: You don't only write code for the compiler. You also write code for the one reading the code after you. In my opinion, adding & makes it clear that you take this specific byte, and you don't rely on any behind-your-back truncation. I still think that the compiler is smarter than the one reading the code. So don't make the code readable for the compiler; make it readable for the reader. – Patrick Jun 28 '11 at 06:43
  • 2
    You should note that this is little endian. – Albert Jun 03 '13 at 12:41
3

If you don't want to code it yourself, you can use the C library function htonl() to convert the 32-bit int to network byte order. There is also the function ntohl() to convert them back to host order.

Once they're in network byte order, it's simply a matter of accessing the int/long as a byte array.

All in all that's are probably the most portable and tested way of achieving your goal.

Timo Geusch
  • 24,095
  • 5
  • 52
  • 70
1

One could also do it with pointers. (This is little endian, but if you use the same reconstruction method it won't matter)

uint32 in = 0x12345678;
uint8 out[4];
*(uint32*)&out = in;

This assigns the value of the uint32 to the 4 bytes after the memory address of the uint8, doing exactly what you need.

To go the other way:

uint8 in[4] = {0x78, 0x56, 0x34, 0x12};
uint32 out;
out = *(uint32*)&in
amrelk
  • 11
  • 2
  • I realise this is an old answer (it got liked to by an EE.SE question). But `*(uint32*)&out = in;` is a very dangerous assumption - if `a` is not initialised aligned to a `uint32` word boundary this will likely cause an unaligned access error on some processors. – Tom Carpenter Sep 26 '20 at 09:38
-4

use a Union consisting of an Array with 4 time uint8 and an uint32.

So it sorts automatically by c inherent pointer Magic (Arrays are pointers to start of array)

luja
  • 1