1

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?

Nulik
  • 6,748
  • 10
  • 60
  • 129
  • `elements` is a pointer to an array of pointers, not an array of structs. – Barmar Sep 24 '16 at 00:05
  • Arrays and pointers are not the same thing. Arrays decay to pointers in certain contexts, but array literals don't decay in initialization lists. – Barmar Sep 24 '16 at 00:07
  • @Barmar if you understood what kind of hierarchy I want to build in memory, and you know the answer, then please share it. TIA – Nulik Sep 24 '16 at 01:20

3 Answers3

3

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • thanks! that's exactly what I needed. What book do you recommend me to read to understand C at such deep level ? – Nulik Sep 24 '16 at 14:44
  • You were almost there, as I said. I'd recommend [C: A Reference Manual](http://http://www.careferencemanual.com/) as a good book to read to fill in some of the holes, but you're already familiar with designated initializers, so the gap you missed was how to use a compound literal in conjunction with them. – Jonathan Leffler Sep 24 '16 at 23:16
1

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.

Andy Schweig
  • 6,597
  • 2
  • 16
  • 22
1

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]

Gilbert
  • 3,740
  • 17
  • 19