Рекурсивно освобождающие структуры C - PullRequest
4 голосов
/ 03 июня 2009

У меня есть структура, которая содержит только указатели на память, которую я выделил. Есть ли способ рекурсивно освободить каждый элемент, являющийся указателем, вместо вызова free для каждого?

Например, допустим, у меня есть такой макет:

typedef struct { ... } vertex;
typedef struct { ... } normal;
typedef struct { ... } texture_coord;

typedef struct
{
    vertex* vertices;
    normal* normals;
    texture_coord* uv_coords;
    int* quads;
    int* triangles;
} model;

И в своем коде я размещаю каждую структуру для создания модели:

model* mdl = malloc (...);
mdl->vertices = malloc (...);
mdl->normals = malloc (...);
mdl->uv_coords = malloc (...);
mdl->quads = malloc (...);
mdl->triangles = malloc (...);

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

free (mdl->vertices);
free (mdl->normals);
free (mdl->uv_coords);
free (mdl->quads);
free (mdl->triangles);
free (mdl);

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

( На практике практически нет необходимости просто писать free () для каждого, но это уменьшит дублирование кода и будет полезно учиться у )

Ответы [ 8 ]

19 голосов
/ 03 июня 2009

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

void freeModel( model* md1 ) {
    free (mdl->vertices);
    free (mdl->normals);
    free (mdl->uv_coords);
    free (mdl->quads);
    free (mdl->triangles);
    free (mdl);
}
7 голосов
/ 03 июня 2009

Такая функциональность не встроена в C, но вы можете немного обмануть, злоупотребив макрос-препроцессором:

#define XX_MODEL_POINTERS do { \
  xx(vertices); xx(normals); xx(uv_coords); xx(quads); xx(triangles); \
} while(0)

Выделить:

model *mdl = malloc(sizeof(*mdl));
assert(mdl);
#define xx(N) mdl->N = malloc(sizeof(*mdl->N)); assert(mdl->N)
XX_MODEL_POINTERS;
#undef xx

Чтобы бесплатно:

assert(mdl);
#define xx(N) free(mdl->N); mdl->NULL
XX_MODEL_POINTERS;
#undef xx
free(mdl);
mdl = NULL;

Самое неприятное в том, что определение struct model и определение XX_MODEL_POINTERS могут стать взаимно непоследовательными, и нет способа поймать его. По этой причине часто лучше генерировать определение XX_MODEL_POINTERS, анализируя где-то файл .h.

Метапрограммирование на С никогда не бывает легким.

7 голосов
/ 03 июня 2009

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

Самый простой подход - написать функцию "FreeModel":

void FreeModel(model* mdl)
{
   free(mdl->vertices);
   ... // Other frees
   free(mdl);
}
4 голосов
/ 18 июня 2010

Взгляните на talloc http://talloc.samba.org/, если вы делаете:

model* mdl = talloc (NULL, ...);
mdl->vertices = talloc (mdl, ...);
mdl->normals = talloc (mdl, ...);
mdl->uv_coords = talloc (mdl, ...);
mdl->quads = talloc (mdl, ...);
mdl->triangles = talloc (mdl, ...);

тогда вы можете:

talloc_free(mdl);

и talloc позаботятся о free, используя все остальные блоки, которые вы назвали talloc с mdl в качестве первого аргумента во время выделения (и это будет делать это рекурсивно, вы можете сделать talloc(mdl->vertices, ...) и talloc_free(mdl); тоже это получит)

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

3 голосов
/ 03 июня 2009

Вы можете рассчитать размер, необходимый для всех их вместе и сделать один большой malloc

sizeof (модель) + sizeof (вершина) * nVertices ... и т. Д.

назначить результат для mdl, result + sizeof (model) для model-> vertices ...

Тогда, чтобы освободить это просто один бесплатно.

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

2 голосов
/ 03 июня 2009

Бросить все свободные функции?

0 голосов
/ 03 июня 2009

Не с этими структурами. Вы можете добавить еще одну запись в структуру «model» верхнего уровня, содержащую список указателей, которые должны быть освобождены, и выполнить итерацию этого списка. Но я сомневаюсь, что добавленная сложность и уменьшенная понятность этого решения стоили бы того. (Если у вас нет гораздо большего и более глубоко вложенного набора записей для освобождения в структуре 'model' верхнего уровня, чем вы показываете здесь.)

0 голосов
/ 03 июня 2009

Я не верю , что возможно в любой форме С.

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

РЕДАКТИРОВАТЬ: Ups, слишком поздно, никогда не видел эти ответы ...

...