Какие основные вещи следует учитывать при освобождении памяти в C? - PullRequest
1 голос
/ 04 декабря 2010

Я попытался найти какой-нибудь учебник, который явно объясняет, что нужно учитывать при освобождении памяти. Но я не смог найти такие вещи. Может ли кто-нибудь дать мне знать, какие основные вещи нужно иметь в виду программисту при освобождении памяти в C. В настоящее время я имею дело со связанными списками. В некоторых случаях возникает новый список связанных создается с использованием 2 или более существующих связанных списков. Например:

 list l1;
 list l2 
 list l3 = list_append(l1,l2)
 list l4 = list_append(l3,l1)
 list l5 = list_append(l3,l4)

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

здесь list_append - это функция, которая возвращает копию списка.

Ответы [ 6 ]

3 голосов
/ 04 декабря 2010

При использовании семейства функций malloc / free необходимо соблюдать два правила:

  1. Вы можете только free действительная память, возвращаемая распределителем семейства malloc,и освобождение его делает его недействительным (поэтому двойное освобождение является ошибкой, поскольку освобождение памяти не получено из malloc).
  2. Ошибка доступа к памяти после ее освобождения.

И вот важная часть: распределитель предоставляет нет средств, чтобы помочь вам соблюдать эти правила .

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

Позвольте мне предложить два достаточно безопасных паттерна:

Распределить и освободить в одном контексте

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

Учитывая, что у вас есть список с узлами, которые нужно добавить и удалить, вы можете рассмотреть шаблон владения структурой, поэтому помните: как только вы дадите узел к структуре вы только обращаетесь к нему через структуру.

2 голосов
/ 04 декабря 2010

Может кто-нибудь дать мне знать, каковы Основные вещи, которые программист следует иметь в виду, занимаясь память в кл.

Основные принципы довольно просты: любая память, выделенная с использованием семейства функций * alloc, включая malloc, calloc или realloc, должна быть освобождена соответствующим вызовом free().

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

2 голосов
/ 04 декабря 2010

Вопрос в общем не имеет смысла, единственный разумный ответ кажется довольно очевидным:

  1. Что память была выделена динамически в первом случае
  2. Что после освобождения вы не делаетепопытайтесь использовать память снова
  3. что вы сохраняете хотя бы один указатель на выделение, пока вам не потребуется освободить его (т.е. не позволяйте вашей единственной ссылке на блок выходить из области видимости или уничтожаться).

Этому второму требованию можно помочь, установив указатель на NULL или ноль после освобождения, но указатель может храниться в другом месте, так как он не защищен от ошибок.

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

1 голос
/ 04 декабря 2010

Первый принцип:

Все, что вы выделяете (с помощью calloc / malloc), вам необходимо в конечном итоге освободить.

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

0 голосов
/ 04 декабря 2010

Ну, лучший ответ - это вопрос ... какого чёрта ты пишешь код на С вместо того, чтобы использовать язык более высокого уровня с лучшим управлением памятью?

0 голосов
/ 04 декабря 2010

Используйте valgrind (1), чтобы показать вам, в вашем конкретном случае, какие объекты еще существуют при завершении, а затем измените свой код, чтобы освободить ненужные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...