How to correctly and safely free() all memory used

2019-08-10 20:11发布

问题:

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?

回答1:

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 students 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


回答2:

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.