Освобождение памяти, возвращаемой из функций C - PullRequest
5 голосов
/ 26 мая 2009

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

  • Предоставляет функцию "деструктор", которая инкапсулирует вызов free ().
  • Требовать, чтобы пользователи самостоятельно () возвращали указатель.

Например, чтобы открыть и закрыть файл, который мы делаем:

FILE* f = fopen("blah", "w");
fclose(f);

Является ли это предпочтительным для:

FILE* f = fopen("blah", "w");
fclose(f);
free(f);

Предупреждение: Не вызывайте free () для указателя FILE. Я использую здесь только гипотетическую реализацию.

А как насчет случаев, когда локальные переменные указывают на возвращенную память? Вредно ли free () здесь? (или, возможно, этого никогда не следует делать)

FILE f = &fopen("blah", "w");
fclose(&f);

Ответы [ 10 ]

14 голосов
/ 26 мая 2009

Лучший вариант для выделения и освобождения памяти - делать это симметрично. Т.е., если вызывающая сторона выделяет память, пусть вызывающая сторона освобождает ее. Если ваш API выделяет память (вызываемый), то ваш API должен освободить ее.

Пример вызова / выделения памяти:

int * mymem = (int *)malloc(20 * sizeof(int));
...
a_func_to_call(mymem);
...
free(mymem);

Пример вызова / освобождения вызываемого абонента:

FILE* f = fopen("blah", "w"); // allocs a FILE struct
fclose(f); // The implementation of fclose() will do what's necessary to 
           // free resources and if it chooses to deallocate any memory
           // previously allocated
10 голосов
/ 26 мая 2009

Вы никогда не должны освобождать файл - fclose обрабатывает освобождение ресурсов должным образом. Как правило, только бесплатные указатели, которые были выделены непосредственно malloc. Большинство других указателей будут иметь свои собственные функции очистки ресурсов.

Это, как говорится, относительно вашего первоначального вопроса:

Я считаю, что предоставление функции деструктора обычно является лучшей практикой по трем причинам.

1) Во многих случаях бесплатное использование неуместно, и это может быть неочевидно для вашего конечного пользователя. ФАЙЛ * хороший пример этого - вам не следует звонить free(f); выше ...

2) Если вы используете это в DLL, в зависимости от времени выполнения, инкапсулируя свободную функциональность, можно обойти многие, многие тонкие ошибки из-за смешивания времени выполнения, особенно на платформе Windows. Попытка использовать DLL, скомпилированную в VS2005 из VS2008, может вызвать проблемы, если вам случится освободить память в коде одной платформы, который был выделен в другой. Наличие функции-обертки для управления памятью решает эту важную проблему.

3) Многие функции API C работают таким образом - например, FILE * с использованием fopen / fclose. Это не удивительно для пользователя вашей библиотеки.

5 голосов
/ 26 мая 2009

Вы не можете освободить ФАЙЛ * он не был выделен malloc.
Поскольку вы не несете ответственности за его распределение, вам не следует освобождать его.

2 голосов
/ 26 мая 2009

FILE* f - указатель на объект FILE, который используется для идентификации потока при всех дальнейших операциях, связанных с файлом. Вы не должны использовать free(f), так как память не выделена на malloc().

fclose достаточно, чтобы закрыть файл, связанный с потоком.

По поводу вашего вопроса о предоставлении функции деструктора для освобождения памяти: Я чувствую, что уместно предоставить функцию типа деструктора, если функция делает больше, чем освобождает память.

wrapperFree(Pointer* p)
{
 //do some additional work [ other cleanup operations ]
 free(p);
}

Кроме того, для WrapperFree необходимо указать связанный с ним WrapperAllocate.

В противном случае, я думаю, что thumbrule будет для каждого malloc() вызова free() явно.

0 голосов
/ 27 мая 2009

Мне нравятся не только функции-деструкторы, но и соглашение Дейва Хансона, которое берет адрес указателя и обнуляет указатель при освобождении памяти:

Thing_T *Thing_new(void);
void Thing_dispose(Thing_T **p);   // free *p and set *p = NULL
0 голосов
/ 26 мая 2009

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

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

0 голосов
/ 26 мая 2009

Нет лучшего опыта без контекста ...

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

Функция открытия / выделения должна возвращать непрозрачный дескриптор некоторого вида, который закрывается / освобождается функцией закрытия.

ФАЙЛ * можно рассматривать как непрозрачную ручку.

0 голосов
/ 26 мая 2009

Как говорит mgb, нет необходимости free() a FILE *. Но при обычном использовании есть библиотечные вызовы и ваш собственный код, который выделит память и сделает вас ответственным. Вот несколько простых рекомендаций:

  • Создайте функцию "деструктор", когда что-то должно произойти, кроме free памяти. Материал FILE * является хорошим примером: он должен иметь дело с самим файлом, а также с памятью.
  • Используйте голые free(), когда это просто кусок памяти. Все остальное излишне.
0 голосов
/ 26 мая 2009

Полагаю, вы имели в виду malloc(), а не fopen(). malloc () выделяет память и возвращает адрес выделенной памяти. Это должно быть выпущено, используя free() вызов.

0 голосов
/ 26 мая 2009

fopen возвращает указатель на структуру библиотеки FILE, управляемую изнутри C, которая освобождается (необязательно free d) при вызове fclose.

...