Search code examples
carrayspointersstructdynamic-memory-allocation

Dynamic array of pointer to structs - do I really need to explicitly allocate space for ea element?


I have a struct defined like this:

typedef struct
{
    int num;
    char letter;
}* Foo;

And an array like this:

Foo* items = malloc(sizeof(Foo) * 4);

From my understanding and from the (accepted) answer to this question Dynamic array of pointers to structs, I would expect the above line to only reserve the memory for 4 Foo items, but doesn't initialize it - i.e., if I try to access items[i]->num, I should get some kind of error. Also, in order to insert item into this array, I should do this:

items[0] = malloc(sizeof(*items[0]));

However, I did a little test, and seems like the following code prints 1 and a just fine:

Foo* items = malloc(sizeof(Foo) * 2);
items[0]->num = 4;
items[0]->letter = 'a';
printf("items[0] = {num=%d, char=%c}\n", items[0]->num, items[0]->letter);

I'm confused. Is this the expected behavior?


Solution

  • Your initial malloc:

    Foo* items = malloc(sizeof(Foo) * 4);
    

    Is creating an array of 4 pointers, since Foo is a pointer type. So your second malloc:

    items[0] = malloc(sizeof(*items[0]));
    

    Makes sense, since you're allocating a struct to that pointer.

    However, the assignment you're doing leads to undefined behavior because you didn't do the second malloc and therefore no space has been allocated to items[0] yet. C won't prevent you from writing to a memory location you shouldn't be writing to. And once you do that, anything can happen.

    One thing that's a bit confusing here is that you used typedef to define a pointer type. That can lead to a lot of confusion since it's not apparent by looking at the type that it's a pointer. And in this case, because of how you defined Foo, you had an extra layer of pointer indirection you probably don't need.

    So if you define your struct like this:

    typedef struct
    {
        int num;
        char letter;
    } Foo;
    

    Then this can be done safely:

    Foo* items = malloc(sizeof(Foo) * 2);
    items[0].num = 4;
    items[0].letter = 'a';
    printf("items[0] = {num=%d, char=%c}\n", items[0].num, items[0].letter);
    

    Now the malloc creates an array of structs instead of an array of pointers to structs, so an additional layer of mallocs is no longer necessary.