Search code examples
c++cinitializationunions

What's the proper format for a union initializer list?


Say I have a class/struct with a single union in it... something like:

struct Box { union { AS128 intr; struct { AS32 a, b, c, d; }; }; };

What's the proper way to do an initializer list for this kind of data type?

Box iMyBoxA = { 10, 10, 100, 100 };
Box iMyBoxB = ( Box ){ 10, 10, 100, 100 };

The 'A' option above works in many cases, but isn't completely portable... giving the error "no known conversion for argument 1 from 'brace-enclosed initializer list'". And the second doesn't compile with "Cannot omit braces around initialization of subobject..."

I've tried a few other cases and can't seem to get it to compile across multiple platforms.


Solution

  • The original version of the structure and union definition in the question was:

    struct Box { union { AS128 intr; AS32 a, b, c, d; } };
    

    This is a little unusual. Assuming the types AS128 and AS32 are defined, then it is equivalent to:

    struct Box1  // Renamed for convenience of testing
    {
        union
        {
            AS128 intr;
            AS32  a;
            AS32  b;
            AS32  c;
            AS32  d;
        };
    };
    

    That is, it is a union of an AS128 called intr and 4 separate values of type AS32 called a, b, c and d (with the latter 4 all occupying the same space).

    Presumably, what you have in mind is:

    struct Box2
    {
        union
        {
            AS128 intr;
            struct
            {
                AS32  a;
                AS32  b;
                AS32  c;
                AS32  d;
            } s;
        };
    };
    

    This uses the anonymous members feature of C11 (listed in the Foreword of ISO/IEC 9899:2011 as a new feature compared to C99). [The question has now been fixed to reflect more or less this notation (using an anonymous structure). Note that you can't directly designate an anonymous union member, so the s above is a good idea, at least. The structure without the s can be used, but I don't think you can initialize the structure unless you can designate it — you can only designate named elements of a union.]

    With the first variant (struct Box1), you can initialize the first member (the AS128 intr; member) with no name:

    struct Box1 b1 = { { 1234 } };
    

    The outer braces are for the structure; the inner braces are for the (anonymous) union.

    Or you can specify which member to initialize using a designated initializer:

    struct Box1 b2 = { { .a = 1234 } };
    

    Note that you can't do:

    struct Box1 b3 = { { .a = 1234, .b = 2341 } };
    

    The compiler gives a warning (see the example code below).

    With the second variant (struct Box2), with the nested structure, you can still use the undesignated initializer or the designated initializers, but this time, you could specify all 4 of the members of the structure:

    struct Box2 b4 = { { .s = { .a = 32, .b = 65, .c = 48, .d = 97 } } };
    

    Example code

    Using funny types for the AS128 and AS32 type names.

    typedef long AS128;
    typedef char AS32;
    
    struct Box1
    {
        union
        {
            AS128 intr;
            AS32  a;
            AS32  b;
            AS32  c;
            AS32  d;
        };
    };
    
    struct Box2
    {
        union
        {
            AS128 intr;
            struct
            {
                AS32  a;
                AS32  b;
                AS32  c;
                AS32  d;
            } s;
        };
    };
    
    struct Box1 b1a = { { 1234 } };
    struct Box1 b1b = { { .intr = 1234 } };
    struct Box1 b1c = { { .a = 32 } };
    //struct Box1 b1d = { { .b = 29, .c = 31 } }; // Invalid double initialization
    //error: initialized field overwritten [-Werror=override-init]
    
    struct Box2 b2a = { { 1234 } };
    struct Box2 b2b = { { .intr = 1234 } };
    struct Box2 b2c = { { .s = { .a = 29, .b = 30, .c = 31, .d = 32 } } };