При использовании семейства функций malloc
/ free
необходимо соблюдать два правила:
- Вы можете только
free
действительная память, возвращаемая распределителем семейства malloc
,и освобождение его делает его недействительным (поэтому двойное освобождение является ошибкой, поскольку освобождение памяти не получено из malloc). - Ошибка доступа к памяти после ее освобождения.
И вот важная часть: распределитель предоставляет нет средств, чтобы помочь вам соблюдать эти правила .
Вы должны сами управлять этим.Это означает, что вы должны организовать логику вашей программы, чтобы гарантировать, что эти правила всегда соблюдаются.Это с, и огромное количество утомительной и сложной ответственности ложится на ваши плечи.
Позвольте мне предложить два достаточно безопасных паттерна:
Распределить и освободить в одном контексте
//...
{
SomeData *p = malloc(sizeof SomeData);
if (!p) { /* handle failure to allocate */ }
// Initialize p
// use p various ways
// free any blocks allocated and assigned to members of p
free p;
}
//...
Здесь вы знаете, что данные, на которые указывает p, распределяются один раз, освобождаются один раз и используются только между ними.Если инициализация и освобождение содержимого SomeData нетривиальны, вы должны заключить их в пару функций, чтобы уменьшить это до
//...
{
SomeData *p = NewSomeData(i,f,"name"/*,...*/); // this handles initialization
if (!p) { /* handle failure to allocate */ }
// use p various ways
ReleaseSomeData(p) // this handles freeing any blocks
// allocated and assigned to members of p
}
//...
Назовите это «Владение областью».Вы заметите, что он не сильно отличается от локальных автоматических переменных и предоставляет вам только несколько опций, недоступных для автоматических переменных.
Вызовите второй вариант «Структурное владение»: здесь ответственность за удаление выделенного блокапередается в большую структуру:
List L = NewList();
//...
while (something) {
// ...
Node n= NewNode(nodename);
if (!n) { /* handle failure to allocate */ }
ListAdd(L,n); // <=== Here the list takes ownership of the
// node and you should only access the node
// through the list.
n = NULL; // This is not a memory leak because L knows where the new block is, and
// deleting the knowledge outside of L prevents you from breaking the
// "access only through the structure" constraint.
//...
}
// Later
RemoveListNode(L,key); // <== This routine manages the deletion of one node
// found using key. This is why keeping a separate copy
// of n to access the node would have been bad (because
// your separate copy won't get notified that the block
// no longer valid).
// much later
ReleaseList(L); // <== Takes care of deleting all remaining nodes
Учитывая, что у вас есть список с узлами, которые нужно добавить и удалить, вы можете рассмотреть шаблон владения структурой, поэтому помните: как только вы дадите узел к структуре вы только обращаетесь к нему через структуру.