Strict aliasing rule uint8_t buffer to structure

I had suggested that proposed approach does not follow strict aliasing rule

Correct. ptrMsg = (Message *)buffer means that you cannot access the data of ptrMsg without invoking undefined behavior.

You can prove your point with C17 6.5 §7 (cited here - What is the strict aliasing rule?). An lvalue expression such as ptrMsg->var1 = value does not access the stored value through a type compatible with the effective type of what's stored there, nor through any of the allowed expressions.

You can however go from Message to an array of uint8_t (assuming uint8_t is a character type) without violating strict aliasing.


The larger problem is however the presence of the bit-field in the first place, which is non-standard and non-portable. For example, you cannot know which part of the bit-field that is the MSB and LSB. You cannot know how the bits are aligned in the 64 bit type. Using a 64 bit type for a bit-field is a non-standard extension. It is endianess-dependent. And so on.


Assuming the 24 bits refer to bit 31 to 8 (we can't know by reading your code), then proper code without strict aliasing violations, bit-field madness and non-standard "struct padding killers" would look like this:

typedef union
{
   uint32_t var;
   uint8_t  bytes[4];
} Message;


uint8_t buffer[4];
Message* ptrMsg = (Message*)buffer;
uint32_t var1 = (ptrMsg->var >> 8);
uint8_t  var2 = (ptrMsg->var >> 4) & 0x0F;
uint8_t  var3 = (ptrMsg->var) & 0x0F;

Message being "a union type that includes one of the aforementioned types among its members". Meaning it contains a type compatible with uint8_t [4].

This code also contains no copying and the shifts will get translated to the relevant bit access in the machine code.


You are right.

C17 draft § 6.5:

  1. An object shall have its stored value accessed only by an lvalue expression that has one of the following types: 89)

    • a type compatible with the effective type of the object,

    • a qualified version of a type compatible with the effective type of the object,

    • a type that is the signed or unsigned type corresponding to the effective type of the object,

    • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,

    • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
    • a character type.

In this case the object is of type uint8_t[] and type of the lvalue expression is Message. None of the exceptions above apply.

Using memcpy over dereferences fixes this issue, or alternatively you can disable the strict aliasing in your compiler if you want to write in non-C language.

But even after this the code has a lot of problems and is not portable: Bitfields are extremely poorly defined in standard and overall structures are very clumsy way to handle serialization. You should take the option of deserializing each member manually.

Tags:

C