Search code examples
cmallocrealloc

malloc() of struct array with varying size structs


How does one malloc an array of structs correctly if each struct contains an array of strings which vary in size?

So each struct might have a different size and would make it impossible to

realloc(numberOfStructs * sizeof(structName))

after

malloc(initialSize * sizeof(structName)

How does one allocate memory for this and keep track of what is going on?


Solution

  • I am making some guesses here, based on the information you have provided. The only reason I can see for wanting to realloc an array of structs is if you want to add more structs to that array. That's cool. There are plenty of reasons to want that kind of dynamic storage. The best way to handle it, especially if the structures are themselves dynamic, is to keep an array of pointers to these structures. Example:

    1. Data structure:

    typedef struct {
        int numberOfStrings;
        char ** strings;
    }
    stringHolder;
    
    typedef struct {
        int numberOfStructs;
        stringHolder ** structs;
    }
    structList;
    

    2. Managing dynamic arrays of strings:

    void createNewStringHolder(stringHolder ** holder) {
        (*holder) = malloc(sizeof(stringHolder));
        (*holder)->numberOfStrings = 0;
        (*holder)->strings = NULL;
    }
    
    void destroyStringHolder(stringHolder ** holder) {
        // first, free each individual string
        int stringIndex;
        for (stringIndex = 0; stringIndex < (*holder)->numberOfStrings; stringIndex++)
        { free((*holder)->strings[stringIndex]); }
        // next, free the strings[] array
        free((*holder)->strings);
        // finally, free the holder itself
        free((*holder));
    }
    
    void addStringToHolder(stringHolder * holder, const char * string) {
        int newStringCount = holder->numberOfStrings + 1;
        char ** newStrings = realloc(holder->strings, newStringCount * sizeof(char *));
        if (newStrings != NULL) {
            holder->numberOfStrings = newStringCount;
            holder->strings = newStrings;
            newStrings[newStringCount - 1] = malloc((strlen(string) + 1) * sizeof(char));
            strcpy(newStrings[newStringCount - 1], string);
        }
    }
    

    3. Managing a dynamic array of structures:

    void createNewStructList(structList ** list, int initialSize) {
        // create a new list
        (*list) = malloc(sizeof(structList));
        // create a new list of struct pointers
        (*list)->numberOfStructs = initialSize;
        (*list)->structs = malloc(initialSize * sizeof(stringHolder *));
        // initialize new structs
        int structIndex;
        for (structIndex = 0; structIndex < initialSize; structIndex++)
        { createNewStringHolder(&((*list)->structs[structIndex])); }
    }
    
    void destroyStructList(structList ** list) {
        // destroy each struct in the list
        int structIndex;
        for (structIndex = 0; structIndex < (*list)->numberOfStructs; structIndex++)
        { destroyStringHolder(&((*list)->structs[structIndex])); }
        // destroy the list itself
        free((*list));
    }
    
    stringHolder * addNewStructToList(structList * list) {
        int newStructCount = list->numberOfStructs + 1;
        size_t newSize = newStructCount * sizeof(stringHolder *);
        stringHolder ** newList = realloc(list->structs, newSize);
        if (newList != NULL) {
            list->numberOfStructs = newStructCount;
            list->structs = newList;
            createNewStringHolder(&(newList[newStructCount - 1]));
            return newList[newStructCount - 1];
        }
        return NULL;
    }
    

    4. Main program:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main (int argc, char * argv[]) {
        structList * allHolders;
        createNewStructList(&allHolders, 10);
    
        addStringToHolder(allHolders->structs[4], "The wind took it");
        addStringToHolder(allHolders->structs[4], "Am I not merciful?");
        addStringToHolder(allHolders->structs[7], "Aziz, Light!");
    
        printf("%s\n", allHolders->structs[4]->strings[0]);  // The wind took it
        printf("%s\n", allHolders->structs[4]->strings[1]);  // Am I not merciful?
        printf("%s\n", allHolders->structs[7]->strings[0]);  // Aziz, Light!
    
        stringHolder * newHolder = addNewStructToList(allHolders);
        addStringToHolder(newHolder, "You shall not pass!");
        printf("%s\n", newHolder->strings[0]);               // You shall not pass!
        printf("%s\n", allHolders->structs[10]->strings[0]); // You shall not pass!
    
        destroyStructList(&allHolders);
        return 0;
    }