Search code examples
c++bitunions

How to use union in C++ correctly?


I have following union in C++

union MsgData {
      struct
      {
        uint32_t msb : 1;
        uint32_t reg_addr : 7;
        uint32_t data : 8;
        uint32_t fill : 3;
        uint32_t crc : 5;
      } bits;
      uint8_t bytes[3];
    };

In case I do following (address contains 26, data contains 255 - both in decimal):

  msg.bits.msb = 1;
  msg.bits.reg_addr = (address & 0x7F);
  msg.bits.data = data;
  msg.bits.fill = 0;
  msg.bits.crc = 0;

the byte array bytes inside the union MsgData contains:

msg.bytes[0]: 53
msg.bytes[1]: 255
msg.bytes[2]: 0

I have expected 154 value in the msg.bytes[0]. Can anybody give me an advice why I have got 53 instead of 154 value in the msg.bytes[0]?


Solution

  • The behaviour of your program is undefined because you read from an inactive member of the union.

    How to use union in C++ correctly?

    In general: By only reading from the active member of the union which is the one that was assigned last. Exceptions do exist. For example, reading from inactive member that has the same type as the active member is also allowed. There are no such exceptions that would apply to your program.

    Since you want to pun the type into array of bytes, there is another, well defined way: reinterpret_cast:

    struct
    {
        ...
    } bits;
    
    static_assert(std::is_same_v<uint8_t, unsigned char>);
    uint8_t* bytes = reinterpret_cast<uint8_t*>(&bits);
    

    Note that this reading through this bytes pointer is allowed specifically because unsigned char (along with a few other types) is special.


    Now, assuming that you use a language extension that defines the behaviour of union type punning or use the reinterpret_cast shown above:

    Can anybody give me an advice why I have got 53 instead of 154 value in the msg.bytes[0]?

    Because of this:

    reg_addr | msb | bit field
    7654321  | 0   | bit index in order of significance
    0011010  | 1   | bit value
    
    0b00110101 == 53
    

    It is unclear why you had expected otherwise. Relying on order of bit fields is not portable.