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?
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;
}