I have four different layers of struct nested. The code is as follows:
typedef struct System system;
typedef struct College college;
typedef struct Student student;
typedef struct Family family;
#define MAX_COLLEGES 10
#define MAX_NAME_LEN 32
#define MAX_STUDENTS 10
struct System {
college *Colleges[MAX_COLLEGES];
};
struct College {
char name[MAX_NAME_LEN];
student *Students[MAX_STUDENTS];
};
struct Student {
char name[MAX_NAME_LEN];
int id;
family *fam; //was typo familiy
};
struct Family {
char fatherName[MAX_NAME_LEN];
char motherName[MAX_NAME_LEN];
};
And I allocated memory to all of them (I'm not sure if I allocated all of them correctly), as follows:
system *collegeSys = malloc(sizeof(system));
college *colleges = malloc(sizeof(college));
student *students = malloc(sizeof(student));
family *fam = malloc(sizeof(family));
// then the following is initialization
...
...
...
Now, I need to delete the collegeSys
structure and anything associated with it. So, I don't know if I can just free the first collegeSys
struct without freeing any other structs, like this:
free(collegeSys);
Or in order to "delete anything associated with it", I have to free everything bottom-up, like this:
free(fam);
free(students);
free(colleges);
free(collegeSys);
Or to that end, I even have to free anything included inside each struct and free them bottom-up, like this:
free (fam -> fatherName);
free (fam -> motherName);
free (fam);
free (students -> name);
free (students -> id);
free (students -> fam);
free (students)
.
. till
.
free (collegeSys -> colleges);
free (collegeSys);
Which one is the correct and safe way to free the memory? Or none of them is?
I don't really understand point of having array of pointers, it could be done with pointer.
Definition:
struct System {
college *Colleges;
};
struct College {
char name[MAX_NAME_LEN];
student *Students;
};
struct Student {
char name[MAX_NAME_LEN];
int id;
familiy *fam;
};
struct Family {
char fatherName[MAX_NAME_LEN];
char motherName[MAX_NAME_LEN];
};
Allocation and initialization :
system *collegeSys = malloc(sizeof(*collegeSys));
collegeSys->colleges = malloc(MAX_COLLEGES * sizeof(*(collegeSys->colleges)));
collegeSys->colleges->students = malloc(MAX_STUDENTS * sizeof(*(collegeSys->colleges->students)));
collegeSys->colleges->students->fam = malloc(sizeof(*(collegeSys->colleges->students->fam)));
Freeing:
free(collegeSys->colleges->students->fam);
free(collegeSys->colleges->students);
free(collegeSys->colleges);
free(collegeSys);
Update:
Like I want to have struct student A, B, C, D under a struct college
collegeSys->colleges->students[0] = A;
collegeSys->colleges->students[1] = B;
collegeSys->colleges->students[2] = C;
collegeSys->colleges->students[3] = D;
Should do it.
If yo have array of student
s you can use memcpy
or copy in loop
.
struct student stud[MAX_STUDENTS] = {...};
memcpy(collegeSys->colleges->students[2], stud, MAX_STUDENTS);
or
for (int i = 0; i< MAX_STUDENTS; i++)
collegeSys->colleges->students[i] = stud[i];
Note:
You can assign the array to collegeSys->colleges->students
in that case you don't need dynamic memory allocation or freeing.
// collegeSys->colleges->students = malloc(MAX_STUDENTS * sizeof(*(collegeSys->colleges->students))); //Leaks memory
collegeSys->colleges->students = stud;
//free(collegeSys->colleges->students); //wrong
When you allocate a structure then set all pointers in it to NULL.
For example to allocate your College structure you need to set all the students to NULL:
struct College* CollegeAlloc( char name[MAX_NAME_LEN] ) {
struct College* college = malloc( sizeof(struct College) );
if ( college ) {
for ( int i = 0; i < MAX_STUDENTS; ++i )
college->Students[i] = NULL;
memcpy( college->name, name, MAX_NAME_LEN );
}
return college;
}
Alternatively you could add a count field in the structures for each array, in order to count the number of array elements that are actually used.
If you set the array element to NULL when not used then you can free from the bottom up first.
void FamilyFree( struct Family *fam ) {
free( fam );
}
void StudentFree( struct Student *student ) {
if ( student ) {
FamilyFree( student->fam );
free( student );
}
}
void CollegeFree( struct College *college ) {
if ( college ) {
for ( int i = 0; i < MAX_STUDENTS; ++i )
StudentFree( college->Students[i] );
free( college );
}
}
void SystemFree( struct System *sys ) {
if ( sys ) {
for ( int i = 0; i < MAX_COLLEGES; ++i )
CollegeFree( sys->Colleges[i] );
free( sys );
}
}
Note this assumes that there is no sharing of pointers, e.g. the same student being at more than one college (when the implementation has allocated just one structure for each student), or when there are two siblings who share the same family structure. (the family structure does not model families very well, e.g. single parents, divorced, remarried, gay parents, legal guardians).
When structures can be shared then you can put a reference count in the structure and free only when it is reduced to zero.