Search code examples
carraysstructdynamic-allocation

How to store data in a dynamic array of structs?


I have these structs with which I would like to implement a map

typedef struct {
    const char *name;
    int number;
}   Entry;

typedef struct {
    int available;
    int guard;
    Entry *entries;
} Map;

and code to work to initialise and put elements in it:

Map *map_init() {
    Map *res = (Map *) malloc(sizeof(Map));

    res->available = 4;
    res->guard = 0;
    res->entries = (Entry *) malloc(4 * sizeof(Entry));

    return res;
}

int map_put(Map *map, const char *name, int nr) {
    Entry entry;
    int i = 0;

    for (i = 0; i < map->guard; ++i) {
        entry = map->entries[i];
        printf("entry (  x , %u) at %p (%p)\n", entry.number, &entry, entry.name);

        if (!strcmp(entry.name, name))        // Segmentation fault here
            return 0;
    }

    entry = map->entries[map->guard++];
    entry.name = name;
    entry.number = nr;

    printf("entry (%s, %u) at %p (%p)\n", entry.name, entry.number, &entry, entry.name);

    return 1;
}

when I run my main method

int main(int argc, char **argv) {
    printf("Initialising...\n");
    Map *map = map_init();

    printf("Putting...\n");
    map_put(map, "test", 2);
    map_put(map, "some", 1);

    // ...

    free(map->entries);
    free(map);
    return 0;
}

I get as output

Initialising...
Putting...
entry (test, 2) at 0x7fff50b32a90 (0x10f0cdf77)
entry (  x , 0) at 0x7fff50b32a90 (0x5000000000000000)
Segmentation fault: 11

from which I could derive that the segmentation fault is due to the fact that entry.name does not point to a string anymore (also the number is lost, but this does not lead to unauthorised memory access). After I set the data in the first invocation of map_put, everything seems to be stored in the correct places.

Anyone an idea where these entries could be overwritten or why the values are not stored?


Solution

  • The problem is this:

    entry = map->entries[map->guard++];
    

    Here you copy the data from the array into the entry structure instance. Then you modify the data of entry and discard those modifications. The (original) structure data in the array is still unmodified.

    That will of course lead to undefined behavior when you in the next call to map_put use the uninitialized structures in the array.

    Either modify the array structure instance directly and increase map->guard separately. Or make entry a pointer and make it point to the array element.