Как правильно и безопасно освободить () всю память, использующую вложенную структуру в C? - PullRequest
1 голос
/ 05 июля 2019

У меня есть четыре различных слоя структуры. Код выглядит следующим образом:

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

И я выделил всем им память (я не уверен, правильно ли я их выделил) следующим образом:

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

Теперь мне нужно удалить структуру collegeSys и все, что с ней связано. Итак, я не знаю, смогу ли я просто освободить первую collegeSys структуру, не освобождая другие структуры, например:

free(collegeSys);

Или, чтобы «удалить все, что с ним связано», я должен освободить все снизу вверх, например:

free(fam);
free(students);
free(colleges);
free(collegeSys);

Или для этого мне даже нужно освободить все, что включено в каждую структуру, и освободить их снизу вверх, например:

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);

Какой из них является правильным и безопасным способом освобождения памяти? Или никто из них не является?

Ответы [ 2 ]

2 голосов
/ 05 июля 2019

Я не совсем понимаю смысл наличия массива указателей, это можно сделать с помощью указателя.

Определение:

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

Распределениеи инициализация:

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)));

Освобождение:

free(collegeSys->colleges->students->fam);
free(collegeSys->colleges->students);
free(collegeSys->colleges);
free(collegeSys);

Обновление:

Как я хочу, чтобы структура студентов, A, B, C, D была в структуре колледжа

 collegeSys->colleges->students[0] = A;
 collegeSys->colleges->students[1] = B;
 collegeSys->colleges->students[2] = C;
 collegeSys->colleges->students[3] = D;

Должен это сделать.

Если у вас есть массив students Вы можете использовать memcpy или скопировать в loop.

struct student stud[MAX_STUDENTS] = {...};

memcpy(collegeSys->colleges->students[2], stud, MAX_STUDENTS);

или

for (int i = 0; i< MAX_STUDENTS; i++)
     collegeSys->colleges->students[i] = stud[i];

Примечание:

Вы можете присвоить массиву collegeSys->colleges->students, в этом случае вам не нужно динамическое выделение или освобождение памяти.

 // collegeSys->colleges->students = malloc(MAX_STUDENTS * sizeof(*(collegeSys->colleges->students)));  //Leaks memory

 collegeSys->colleges->students = stud;

//free(collegeSys->colleges->students); //wrong
1 голос
/ 05 июля 2019

Когда вы выделяете структуру, тогда установите все указатели в ней на NULL.Например, чтобы выделить структуру вашего колледжа, вам нужно установить для всех студентов значение 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;
}

В качестве альтернативы вы можете добавить поле подсчета в структуры для каждого массива, чтобы подсчитать количество элементов массива, которыефактически используются.

Если вы установите для элемента массива значение NULL, когда оно не используется, вы можете сначала освободиться снизу вверх.

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

Обратите внимание, что это предполагает, что нет совместного использования указателей, например, один и тот же учащийся находится в более чем одном колледже (когда реализация выделила только одну структуру для каждого студента),или когда есть два родных брата, которые имеют ту же самую структуру семьи.(структура семьи не очень хорошо моделирует семьи, например, одинокие родители, разведенные, вступившие в повторный брак, гомосексуальные родители, законные опекуны).Когда структуры могут использоваться совместно, вы можете поместить счетчик ссылок в структуру и освободить его, только когда он уменьшен до нуля.

...