Reputation: 7360
How do I initialize an array of structs without creating intermediate array of list of pointers to these structs? Consider the following example code:
snippets $ cat a2p.c
struct shape {
int angles;
char shape_name[16];
};
typedef struct shape shape_t;
struct container {
char name[32];
shape_t **elements;
int num_elts;
};
typedef struct container container_t;
shape_t triangle = {
.angles = 3,
.shape_name = {"Triangle"}
};
shape_t rectangle = {
.angles = 4,
.shape_name = {"Rectangle"}
};
container_t c = {
.name = {"Case"},
.elements = {
&triangle,
&rectangle
},
.num_elts =2
};
int main(void) {
return 0;
}
I need the .elements member to point to an array of pointers to shape_t sturcts, but this code does not compile:
snippets $ gcc -c a2p.c
a2p.c:24:2: warning: braces around scalar initializer
.elements = {
^
a2p.c:24:2: note: (near initialization for ‘c.elements’)
a2p.c:25:3: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
&triangle,
^
a2p.c:25:3: note: (near initialization for ‘c.elements’)
a2p.c:26:3: warning: excess elements in scalar initializer
&rectangle
^
a2p.c:26:3: note: (near initialization for ‘c.elements’)
snippets $
However , if I add an intermediate array , like this:
shape_t *shapes[] = {
&triangle,
&rectangle
};
container_t c = {
.name = {"Case"},
.elements = shapes,
.num_elts =2
};
The code compiles ok. Can I avoid creating the shapes[] array as an intermediate step and directly initialize container_t with all data like on the first code snippet? What would be the correct initialization syntax then?
Upvotes: 1
Views: 2372
Reputation: 3776
If you move elements member to the END of the structure DEFINITION you can then use the following on C99, and later, compilers:
container_t c2 = { .name = {"Case2"}, .num_elts =3, .elements = { &(shape_t){ 3, "Triangle" } , &(shape_t){ 4, "Rectangle" }, &(shape_t){ 2, "Line" } } };
The variable size member must be the last member of the structure.
The part of me that hates places for easy bugs notes you can get rid of the need for a manually set element count by ending the list with a NULL pointers and dropping num_elts:
container_t c2 = { .name = {"Case2"}, .elements = { &(shape_t){ 3, "Triangle" } , &(shape_t){ 4, "Rectangle" }, &(shape_t){ 2, "Line" }, NULL } };
Now you just step through the list until NULL == container_list->elements[n]
Upvotes: 1
Reputation: 753705
You are almost there; you just need to ensure that the elements
initializer is a suitable pointer, like this:
struct shape {
int angles;
char shape_name[16];
};
typedef struct shape shape_t;
struct container {
char name[32];
shape_t **elements;
int num_elts;
};
typedef struct container container_t;
shape_t triangle = {
.angles = 3,
.shape_name = { "Triangle" }
};
shape_t rectangle = {
.angles = 4,
.shape_name = { "Rectangle" }
};
container_t c = {
.name = { "Case" },
.elements = (shape_t *[]) {
&triangle,
&rectangle,
},
.num_elts = 2,
};
int main(void) {
return 0;
}
Note the use of a compound literal for the array of pointers to shape_t
elements.
Upvotes: 3
Reputation: 6749
You have to do it exactly like you did it in your second snippet. The only way you can do it all in one initialization (as in your original code) is if you declare elements
as an array of shape_t *
, as in shape_t *elements[10]
, but you probably don't want to have a fixed size array there.
Upvotes: 1