Search code examples
cgccinitializationdesignated-initializer

Using designated initializers with unnamed nested data types


I'm wondering if it is possible to use designated initializers in unnamed data members of structs... (Yikes, a mouthful, but yes, it is the cleanest way to do what I'm trying to do...). If I have:

typedef struct MainStruct {
    union {
         uint8_t   a8[16];
         uint64_t  a64[2];
    };
    uint64_t       i64;
} MainStruct_t;

typedef struct OtherStruct {
    MainStruct_t main;
    int          otherval;
} OtherStruct_t;

OtherStruct_t instance = { .main.a64 = { 0, 0 }, .otherval = 3 };

and I try to compile, I get the error:

tst3.c:16: error: unknown field ‘a64’ specified in initializer

I've also tried using .main..a64, but I'm getting other issues. This is with gcc 4.4. Unfortunately, the MainStruct is used all over the code, so naming the union would involve changing hundreds of files, so I'd like to avoid that. I'd also like to avoid any assumptions about position of MainStruct within OtherStruct if possible.


Solution

  • You need to change the syntax a bit, initializing .main within the initializer for instance:

    typedef struct MainStruct {
        union {
             uint8_t   a8[16];
             uint64_t  a64[2];
        };
        uint64_t       i64;
    } MainStruct_t;
    
    typedef struct OtherStruct {
        MainStruct_t main;
        int          otherval;
    } OtherStruct_t;
    
    OtherStruct_t instance = { .main = {.a64 = { 0, 0 }}, .otherval = 3 };
    

    Here is a working test program:

    #include <stdio.h>
    #include <stdint.h>
    
    typedef struct MainStruct {
        union {
             uint8_t   a8[16];
             uint64_t  a64[2];
        };
        uint64_t       i64;
    } MainStruct_t;
    
    typedef struct OtherStruct {
        MainStruct_t main;
        int          otherval;
    } OtherStruct_t;
    
    OtherStruct_t instance = { .main = {.a64 = { 5, 10 }}, .otherval = 3 };
    
    int main(void)
    {
        printf("%d, %d\n", (int) instance.main.a64[0], (int) instance.main.a64[1]);
        printf("%d\n", instance.otherval);
    
    }
    

    Compiled with gcc -std=c11 -Wall -Wextra -Wpedantic, here is the program output:

    5, 10
    3
    

    Update

    This use of designated initializers should also work with at least C99, though C99 does not support unnamed structures or unions. Here is an example:

    #include <stdio.h>
    
    struct Inner {
        int x;
        int arr[2];
    };
    
    struct Outer {
        char id[100];
        struct Inner state;
    };
    
    int main(void)
    {
        struct Outer instance = { .id = "first",
                                  .state = {.x = 5, .arr[0] = 1, .arr[1] = 2 }};
    
        printf("instance id: %s\n", instance.id);
        printf("instance state.x = %d\n", instance.state.x);
        printf("instance state.arr[0] = %d\n", instance.state.arr[0]);
        printf("instance state.arr[1] = %d\n", instance.state.arr[1]);
    
        return 0;
    }
    

    Compiled with gcc -std=c99 -Wall -Wextra -Wpedantic, here is the program output:

    instance id: first
    instance state.x = 5
    instance state.arr[0] = 1
    instance state.arr[1] = 2
    

    Final Note

    It turns out that OP's original syntax of:

    OtherStruct_t instance = { .main.a64 = { 0, 0 }, .otherval = 3 };
    

    should also work on both C99 and C11, but is not supported in older standards which do not allow initialization of subobjects.

    Unnamed unions are not supported in C99, but are available as a GNU extension. Further investigation has turned up this bug report which suggests that designated initializers for unnamed structs and unions were fixed in gcc 4.6. As workaround, it was suggested at this link to enclose the offending initializer in braces; it is also mentioned that this workaround is a bit finicky and position dependent, which may explain why it does not work here for OP.