Compiler: Bug of feature (struct/union initialization)

Hello,

I observed a strange behaviour which I would rate as a Compiler bug but it could be also a language feature which I didn't know until now.

I have the following defines:

typedef union 
{
    uint32_t u32;
    uint16_t u16[2];
    uint8_t  u8[4];
    float    f;
} uMsgDataValue;

#define NFC_WATCH_MODE_IMPEDANCE            0xF7
#define NFC_WATCH_MODE_SENSITIVITY          8
#define NFC_WATCH_MODE_POLLINGINTERVAL      300

When I create now an instance of the union and initialize it, the first two initialization values are overwritten with 0. But only, when I use u8 and u16 in the initialization list. When I use only u16 or initiaize the value later via normal assignment it works.

   uMsgDataValue msgDataValue = {.u8[0] = NFC_WATCH_MODE_IMPEDANCE,
                                  .u8[1] = NFC_WATCH_MODE_SENSITIVITY,
                                 .u16[1] = NFC_WATCH_MODE_POLLINGINTERVAL};
    print("msgDataValue.u32=0x%X\n",msgDataValue.u32);
    //This prints 0x012C0000. So u8[0] and u8[1] are zero
    
    uMsgDataValue msgDataValue2 = {.u16[0] = ((uint16_t)(NFC_WATCH_MODE_SENSITIVITY)<<8) + (NFC_WATCH_MODE_IMPEDANCE),
                                 .u16[1] = NFC_WATCH_MODE_POLLINGINTERVAL};
    print("msgDataValue2.u32=0x%X\n",msgDataValue2.u32);
    //This gives the correct value of 0x012C08F7
    
    msgDataValue.u8[0] = NFC_WATCH_MODE_IMPEDANCE;
    msgDataValue.u8[1] = NFC_WATCH_MODE_SENSITIVITY;
    msgDataValue.u16[1] = NFC_WATCH_MODE_POLLINGINTERVAL;
    print("msgDataValue.u32=0x%X\n",msgDataValue.u32);
    //This prints also the correct value

Is it not allowed in c to use different integer types in the list?

I'm using SDK Version 1.9.1

Regards
Erwin

  • Hi,

    In C unions there is a single memory location for the union, which is scaled to the largest member (so while it looks like a struct it actually is something very different).  And you should only use a single member of a single instance of the union. In the "failing" case here you are using two members of the same instance. The first two elements of the u8 does not overlap with the second element of the u16, so you could think that this should work, and probably it will with some compilers with different optimization configuration. But I do not think you can call it a compiler bug when the compiler does not behave as you would want when your code itself does not comply with the language.

  • Ok. With different words: when initializing a union I'm just allowed to use one item of the union. It's not allowed to use two different items in the same initialization list. Correct?

    I searched before in the internet and haven't found this explanation/limitation. Or I simply didn't understand the text correctly.

    But this limitation seems to be specified inside c and therefore it's not a compiler bug. It might also not be so strictly specified so that some other compilers might support this mixed initialization. I will remember this limitation now for the future.

  • ErwinCes said:
    Ok. With different words: when initializing a union I'm just allowed to use one item of the union. It's not allowed to use two different items in the same initialization list. Correct?

    Yes, that is right. A struct, has all the members. A union is different, and can only hold one of the members. This can be very useful sometimes, particularily when making more generic APIs, but you should not confuse it with a struct.

  • Way worse: Using different members of a union is undefined behavior in C.

    With the GCC compiler its actually defined behavior in some cases when using the uint8_t byte types - at least it was when I red the docs last time.

    You can also try using memory barriers between the union value setting and the print() statements.

Related