Search code examples
cstructcompound-literals

How to initialize array of structures after declaration in C


Is there a way to initialize the whole array of structures (maybe using compound literals) after it is being declared?

typedef struct
{
    int a;
    int b;
} tStruct;

/* Array of structures in declared in global scope, but not initialized */
tStruct myStruct[3];

void init()
{
  /* We want to initizlize the array with specific values*/
  
  /* using compound literals to initialize the whole array doesn't work */
  myStruct = (tStruct[])
  {
      {1, 2},
      {3, 4},
      {5, 6}
  }; 
}

Solution

  • Arrays aren't R values, so you can't copy them via assignemnt. But you can use memcpy

    #include <string.h>
    void init(void)
    {
        memcpy(&myStruct,(tStruct[]) { {1, 2}, {3, 4}, {5, 6} }, sizeof(myStruct)); 
    }
    

    The code generated by an optimizing compiler shouldn't be much worse than what you'd get with

    void init2(void)
    {
        myStruct[0] = (tStruct){1,2};
        myStruct[1] = (tStruct){3,4};
        myStruct[2] = (tStruct){5,6};
    }
    

    or

    void init3(void)
    {
        myStruct[0].a = 1, myStruct[0].b = 2;
        myStruct[1].a = 3, myStruct[1].b = 4;
        myStruct[2].a = 5, myStruct[1].b = 6;
    }
    

    Gcc and clang are well capable of eliding an unnecessary compound variable like that in favor of assigning individual components directly.

    https://gcc.godbolt.org/z/j9f37j

    The biggest downside of the memcpy approach is that it's a bit brittle and type-unsafe (and can result in out-of-bounds reads/writes if the unenforced type compatibility is violated).

    If your C dialect has __typeof, then with some macro trickery you should be able to almost get around this C language limitation:

    #include <string.h>
    #define assignAnything(DestPtr,SrcPtr) ((void)((__typeof(SrcPtr)){0} = (__typeof(DestPtr)){0}) /*typecheck*/, memcpy(DestPtr, SrcPtr, sizeof(*(DestPtr))))
    
    /* A Ptr-less version:
    #define assignAnything2(Dest,Src) ((void)((__typeof(&(Src))){0} = (__typeof(&(Dest))){0}), memcpy(&(Dest), &(__typeof(Src)){Src}, sizeof(Dest)))
    doesn't always work, unfortunately */
    
    int main()
    {
        int d[3][2][1]={0};
        int const s[3][2][1] = {0};
        assignAnything(&d,&s); //works
        #if NOPE
        assignAnything(&s,&d); //will (correctly) compile-time fail because s has more qualifs (const) than d
        #endif
    }