Reputation: 19
I have to make a function to delete an entire dynamic array, and I'm having trouble thinking what to do with the data that is stored, because if I just use free() I'd be losing memory.
I couldn't find any help in other issues because they mostly talk about deleting a single element, but I want to destroy the array basically. Thanks
Upvotes: 0
Views: 693
Reputation: 164689
The rule of thumb is for every malloc
there should be a free
.
If it's an array, all the data is stored directly in the array's memory. Freeing the array frees all the elements.
For example, let's say I have a struct called person
.
typedef struct {
int id;
char name[20];
} person;
If I want to store 8 structs, I allocate an array large enough for 8 structs.
person *people = malloc(sizeof(person) * 8);
Each thing I assign to the array is copied to the array.
// `p` uses automatic stack memory which is managed for you
person p = { .id = 23, .name = "Yarrow Hock" };
people[0] = p; // p is copied to the array's heap memory
When I'm done, I free the array. This frees all the structs.
free(people);
One malloc, one free.
An array of pointers is different. If I want to store an array of 8 pointers to my struct, I allocate space for 8 pointers.
person **array = malloc(sizeof(person*) * 8);
Then I must allocate space for each element. Only the pointer is copied to the array.
for( int i = 0; i < 8; i++ ) {
person *p = malloc(sizeof(person));
p->id = i;
p->name = "Some One";
people[i] = p;
}
The allocated heap memory for the elements must be managed. How you do that depends on who you decide "owns" the elements.
If it's the array, then the array should free them before freeing itself.
for( int i = 0; i < 8; i++ ) {
free(people[i]);
}
free(people);
If someone else owns the elements, just free the array. Something else will manage the memory of the elements.
free(people);
Which you do depends on how your code is designed. For example, if I store a pointer to each person's name in an array.
char *names = malloc(sizeof(char*));
for( int i = 0; i < 8; i++ ) {
names[i] = people[i]->name;
}
// Free only the array.
free(names);
When I free names
I do not free the elements. They are part of memory names
does not "own", it has "borrowed" the memory. If I were to free the elements of names
then the persons in people
would be corrupted.
What if the struct itself has pointers?
typedef struct {
int id;
char *name;
} person;
They need to be free'd as well. This complicates managing the memory of the array's elements. You need to know details of how the array's elements are managed.
for( int i = 0; i < 8; i++ ) {
free(people[i]->name);
free(people[i]);
}
free(people);
Array implementations can deal with this by allowing you to register a function to handle deallocating the elements. For example, GLib Pointer Arrays.
void free_the_people(gpointer data) {
person *p = (person*)data;
printf("Deallocating %s\n", p->name);
free(p->name);
free(p);
}
int main() {
GPtrArray *people = g_ptr_array_new_full(8, free_the_people);
// Allocate memory for the struct.
person *p = malloc(sizeof(person));
// Allocate memory for the string.
p->name = malloc(40);
strcpy(p->name, "Yarrow Hock");
p->id = 23;
// Copy the pointer to the array.
g_ptr_array_add(people, p);
// Call free_the_people on each element, then deallocate the array.
g_ptr_array_unref(people);
}
Upvotes: 1