简体   繁体   中英

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}
  }; 
}

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
}

No, that does not work as you cannot assign one array to another.

But you can use memcpy to get around it

void init()
{
   tStruct data[3] = {
      {1, 2},
      {3, 4},
      {5, 6}
   };
   memcpy(mystruct, data, sizeof(mystruct));
}

But , be careful as the size of the data must match exactly.

For arrays, compound literals only work on the statement declaration. You have to use individual assignments, loops, memcpy() or memset() to initialize arrrays and structs after that.

Example, to obtain same result:

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

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

void init()
{
  /* Initizlize the array with specific values*/
  int i;
  const tStruct second_element = { 3, 4 };

  // member by member...
  myStruct[0].a = 1;
  myStruct[0].b = 2;

  // using struct copy.
  myStruct[1] = second_element;

  // or a loop...
  for (i = 2; i < 3; ++i)  // I know, there are only 3 elmments, but you get 
                           // the idea.. 
  {
      myStruct[i].a = (2 * i) + 1;
      myStruct[i].b = myStruct[i].a + 1;
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM