Search code examples
cstructc89strict-aliasing

Casting small fields in a structure to a larger variable


I have a situation in a legacy code with a large field of a structure being split into two sub-fields. For example, a uint32 is split into two uint16's:

typedef struct
{
    uint16 myVar_H;
    uint16 myVar_L;
} MyStruct;

Until now these fields were read from a uint32 variable via shifting to read each uint16 portion. Now we want to optimize this so that we read the entire uint32 by casting the first field's address to a uint32:

MyStruct s;
*((uint32*)(&s.myVar_H)) = myValue;

This works for my compiler. The question is if this is legal and defined in C89 (or other C standards)?

Note: changing the structure is another option, but since this is an ancient legacy code based on a proprietary protocol I'd rather not touch the structures definition.

Edit: I am using a big-endian MCU (a ColdFire), so this works correctly in the endianess regard.


Solution

  • There's a few problems with your "optimized" solution:

    1. It violates the strict aliasing rule.
    2. It will not work on little-endian machines (the immense majority of computers nowadays), because you are not reordering correctly the high and low fields.
    3. There is no guarantee that myVar_H and myVar_L are consecutive in memory: the compiler is allowed to add padding between the two fields.

    The legal and platform-independant way of achieving what you want is to keep your previous solution with shifting. It shouldn't bring any performance issues.

    MyStruct s;
    // Reading
    uint32 u = (uint32)s.myVar_H << 16 | s.myVar_L; // The cast is important
    // Writing
    s.myVar_H = myValue >> 16;
    s.myVar_L = myValue % 0xFFFF;
    

    Edit following comment: you say you work on big endian machines so memory order is not a problem. In that case, the "most legal" thing you may do is make your struct a union with an anonymous struct (not actual C89, it's a gcc extension). This way you don't break strict aliasing and the new definition doesn't break any existing code.

    typedef union // Your type is now a union
    {
        struct
        {
             uint16 myVar_H;
             uint16 myVar_L;
        }; // Anonymous struct, legal C11 but gcc extension in C89
    
        uint32 myVar;
    } MyStruct;
    
    s.myVar = myValue; // Safe