I am trying to write a function to deallocate/free a double pointer in C. I have a struct that has an allocated char, and a double pointer to another struct I have. I need to create a function free of that memory. The structs I have:
typedef struct {
char *name; // allocated
int age;
int height;
} student;
typedef struct {
char *name; // allocated
char *location; // allocated
double area;
int population;
student **students; // fully allocated, NOT a reference array
} school;
I need to dispose the school function, I do have a function already but I'm not sure it's right
void dispose_school(school *r)
{
free(r->name);
free(r->location);
free(r->students[i]);
free(r);
}
If someone can shine some light, thank you!
CodePudding user response:
You will have to iterate through all the elements of students
, free them. And then after your loop completes, free students
as well. See this code-
for(int i = 0;i < r->population;i ) {
free(r->students[i]->name); // do this if your students[i]->name is dynamically allocated
free(r->students[i]);
}
free(r->students);
CodePudding user response:
Freeing the double pointer is straightforward. You have the pointers you allocate to hold each student
, you have the struct you allocate to hold each students values (name
, age
, height
), and finally you have the name
you allocated storage for to hold the student's name. This specifies the order in which you would have needed to allocate the memory.
To free()
a complex set of allocations, you simply take the order in reverse. Here you have some number of students (the population
), so you would loop school->population
times freeing:
- the block of memory allocated to hold the students
name
; - the block of memory for the student struct holding the name, age and height; and finally
After the loop completes, then you free all of the pointers students
.
That will free all the memory you have allocated for students
, but you are not done. You will have also allocated storage for the school name
, location
and perhaps the school struct itself (unless you declared the base school struct with automatic-storage-duration).
Presuming you have allocated for the school struct and the rest of the pointers, you can write a free function that takes a pointer to the school and free all memory with something similar to the following (assuming you have used school->population
as the student counter):
void destroy_school (school *s)
{
/* loop over each student and free storage for (1) name and (2) struct */
while (s->population--) {
free (s->students[s->population]->name);
free (s->students[s->population]);
}
/* free storage for student pointers */
free (s->students);
/* free school name, location and school struct */
free (s->name);
free (s->location);
free (s); /* remove if s has automatic-storage-duration */
}
A Short Example
Putting the pieces together to allocate and free()
all the needed information, you can write a short program to do so. Splitting the code into functions to create_school()
, add_student()
, print_school()
and destroy_school()
keeps the logic straight. Using the population
as your student counter, you could do something similar to:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name; // allocated
int age;
int height;
} student;
typedef struct {
char *name; // allocated
char *location; // allocated
double area;
int population;
student **students; // fully allocated, NOT a reference array
} school;
school *create_school (const char *name, const char *loc, double area)
{
school *s = NULL; /* create school ptr init NULL */
size_t len = strlen (name); /* get lenght of name parameter */
if (!(s = malloc (sizeof *s))) { /* allocate/VALIDATE struct school */
perror ("malloc-s"); /* handle error on allocation fail */
return NULL; /* return failure */
}
if (!(s->name = malloc (len 1))) { /* allocate/VALIDATE for name */
perror ("malloc-s.name"); /* handle error on allocation fail */
free (s); /* clean up previous allocations */
return NULL;
}
memcpy (s->name, name, len 1); /* copy name to s->name */
len = strlen (loc);
if (!(s->location = malloc (len 1))) { /* allocate/VALIDATE for loc */
perror ("malloc-s.loc"); /* handle error */
free (s->name); /* clean up prior allocations */
free (s);
return NULL;
}
memcpy (s->location, loc, len 1); /* copy location to school */
s->area = area; /* set area and pop */
s->population = 0;
s->students = NULL; /* init students ptr NULL */
return s; /* return allocated school struct */
}
int add_student (school *s, const char *name, int age, int height)
{
size_t len = strlen (name); /* length of name */
void *tmp;
/* reallocate/VALIDATE pointer for next student
* ALWAYS realloc() using a temporary pointer so if realloc() fails
* you don't overwrite your original pointer with NULL creating a
* memory leak.
*/
if (!(tmp = realloc (s->students, (s->population 1) *
sizeof *s->students))) {
fprintf (stderr, "error: realloc student: %d\n", s->population);
return 0;
}
s->students = tmp; /* assign realloc'ed block to students */
/* allocate/VALIDATE storage for struct student */
if (!(s->students[s->population] = malloc (sizeof **s->students))) {
perror ("malloc-s->students[s->population");
return 0;
}
/* allocate/VALIDATE storage for student name */
if (!(s->students[s->population]->name = malloc (len 1))) {
perror ("malloc-s->students[population]->name");
return 0;
} /* copy name to allocated space */
memcpy (s->students[s->population]->name, name, len 1);
s->students[s->population]->age = age; /* assign age and height */
s->students[s->population]->height = height;
s->population = 1; /* increment pop count */
return 1; /* return success */
}
void print_school (school *s)
{
printf ("\nSchool : %s\n"
"Location : %s\n"
"Area : %.2f\n"
"N-Students : %d\n\n",
s->name, s->location, s->area, s->population);
for (int i = 0; i < s->population; i ) {
printf (" d %-16s M M\n",
i,
s->students[i]->name,
s->students[i]->age,
s->students[i]->height);
}
}
void destroy_school (school *s)
{
/* loop over each student and free storage for (1) name and (2) struct */
while (s->population--) {
free (s->students[s->population]->name);
free (s->students[s->population]);
}
/* free storage for student pointers */
free (s->students);
/* free school name, location and school struct */
free (s->name);
free (s->location);
free (s);
}
int main (void) {
/* allocate/initialize struct school, population == 0, students == NULL */
school *RidgeMontHigh = create_school ("Ridgemont High",
"Hollywood, CA", 2357.8);
if (!RidgeMontHigh) { /* validate creation of school */
return 1;
}
/* add students to school (can validate each, but population will do) */
add_student (RidgeMontHigh, "Mickey Mouse", 103, 48);
add_student (RidgeMontHigh, "Minnie Mouse", 99, 44);
add_student (RidgeMontHigh, "Pluto (the dog)", 97, 47);
add_student (RidgeMontHigh, "Daffy Duck", 102, 46);
if (RidgeMontHigh->population == 0) { /* validate non-zero student pop */
return 1;
}
print_school (RidgeMontHigh); /* print school followed by students */
destroy_school (RidgeMontHigh); /* free all memory involved */
}
Example Use/Output
Compiling and running the code will produce the following:
./bin/schoolpop
School : Ridgemont High
Location : Hollywood, CA
Area : 2357.80
N-Students : 4
00 Mickey Mouse 103 48
01 Minnie Mouse 99 44
02 Pluto (the dog) 97 47
03 Daffy Duck 102 46
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/schoolpop
==18986== Memcheck, a memory error detector
==18986== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18986== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==18986== Command: ./bin/schoolpop
==18986==
School : Ridgemont High
Location : Hollywood, CA
Area : 2357.80
N-Students : 4
00 Mickey Mouse 103 48
01 Minnie Mouse 99 44
02 Pluto (the dog) 97 47
03 Daffy Duck 102 46
==18986==
==18986== HEAP SUMMARY:
==18986== in use at exit: 0 bytes in 0 blocks
==18986== total heap usage: 16 allocs, 16 frees, 1,290 bytes allocated
==18986==
==18986== All heap blocks were freed -- no leaks are possible
==18986==
==18986== For lists of detected and suppressed errors, rerun with: -s
==18986== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.