Home > Mobile >  Is this union legal?
Is this union legal?

Time:06-02

I'm reworking and cleaning up an ex-coworker's code. I created a union to simplify the "parsing" and "construction" of a message by a UART transmission protocol he designed.

Example message: !cx:000:2047

The first character indicates whether the parameter (defined by the second and third character, in this case cx) should be set (!) or returned (?) to the sender of the message. If needed, the address part of the message (000) defines the index in an array where the value specified (in this case 2047) should be written to. The Command, the address and the value are separated using the : character.

I could've used strtok() to separate the message using the separation characters but the receive buffer's size is fixed to 12 characters, the sender function makes sure that the message's format is right and I found this method to be the easiest to read and understand.

But now, that I've seen this thread, I'm not really sure whether folowing union is legal or not:

typedef union UartMessage
{
    char msg[12];
    struct
    {
        char dir;           //[0]:      set/get
        char param[2];      //[1-2]:    parameter to set/get
        char separator1;    //[3]:      ':' separator
        char addr[3];       //[4-6]:    memory array index (used for current and speed arrays)
        char separator2;    //[7]:      ':' separator
        char value[4];      //[8-11]:   value to set
    };
} uartMsg_t;

CodePudding user response:

C and C are different when it comes to "union type punning". In C we might write to one union member and read from another, after which the binary value is converted to the new type. Assuming there are no issues with alignment and that the new type can be represented based on the values, this is well-defined1) in C. Doing the same in C invokes undefined behavior.

As for if your union works or not when compiled in C, it depends on alignment. A struct/union consisting solely of char members (or arrays of them) shouldn't reasonably have any padding, although in theory the compiler is free to insert it. Best practices involves doing a compile-time check to ensure this isn't a problem:

_Static_assert(sizeof(uartMsg_t) == sizeof((uartMsg_t){0}.msg), 
               "Padding detected!");

In other situations when you do have padding, you may have to explicitly disable it using non-standard #pragma pack or similar. Maximum portable programs that use structs to represent data protocols need to call explicit seralization/deserialization routines (portability over performance).


1) Source: C17 6.5.2.3 note 97), C17 6.2.6

CodePudding user response:

It is legal since C11 where anonymous structs were added.

However, I suggest adding an assert if no extra padding was introduced into the embedded struct.

_Static_assert(offsetof(uartMsg_t, value[4]) == 12, "extra padding");
  •  Tags:  
  • c
  • Related