Search code examples
cbit-manipulationunsigned-integer

Use a uint32_t to store four separate uint8_t values


I want to use a uint32_t to store 4 separate uint8_t values, and be able to read/write each of them individually.

Is it safe to do either of the following to set the value of each 8-bit range, and if so, which is better (faster, more portable)? I am not necessarily trying to set them all at once, I am only illustrating how to set each 8-bit value at any point in time.

uint32_t x = ...;

Option 1:

((uint8_t *)(&x))[0] = a;
((uint8_t *)(&x))[1] = b;
((uint8_t *)(&x))[2] = c;
((uint8_t *)(&x))[3] = d;

Option 2:

x = (x & 0xFFFFFF00) | (uint32_t) a;
x = (x & 0xFFFF00FF) | (uint32_t) b << 8;
x = (x & 0xFF00FFFF) | (uint32_t) c << 16;
x = (x & 0x00FFFFFF) | (uint32_t) d << 24;

Solution

  • Your initial revision had a correct albeit roundabout approach for option 2, which was

    // a, b, c, and d are of initialized and of type uint8_t
    uint32_t x = ...;
    x = (x & 0xFFFFFF00) | (uint32_t) a;
    x = (x & 0xFFFF00FF) | (uint32_t) b << 8;
    x = (x & 0xFF00FFFF) | (uint32_t) c << 16;
    x = (x & 0x00FFFFFF) | (uint32_t) d << 24;
    

    This revision for option 2 is wrong:

    uint32_t x = ...;
    x |= (uint32_t) a;
    x |= (uint32_t) b << 8;
    x |= (uint32_t) c << 16;
    x |= (uint32_t) d << 24;
    

    Even when x is initialized it's still wrong because you're not setting the 8 bit ranges, you're ORing them.

    The correct approach would be

    // a, b, c, and d are of initialized and of type uint8_t
    uint32_t x = (uint32_t) a;
    x |= (uint32_t) b << 8;
    x |= (uint32_t) c << 16;
    x |= (uint32_t) d << 24;
    

    Or more succinctly

    // a, b, c, and d are of initialized and of type uint8_t
    uint32_t x =
        (uint32_t) a
      | (uint32_t) b << 8
      | (uint32_t) c << 16
      | (uint32_t) d << 24;
    

    The issue with option 1 is that it assumes the endianness of uint32_t to be LSB first and is therefore not a portable solution.


    After receiving clarification about the question you're asking, your initial revision (the first code block in this answer) is the correct approach. It leaves the remaining 24 bits untouched while setting a particular 8 bit range to the uint8_t value on the RHS.