Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursively freeing C structs

Tags:

c

malloc

struct

I have a struct that only contains pointers to memory that I've allocated. Is there a way to recursively free each element that is a pointer rather than calling free on each one?

For example, let's say I have this layout:

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;

And in my code I malloc each of the structs to create a model:

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

It's straightforward to free each pointer as so:

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

Is there a way that I can recursively iterate through the pointers in mdl rather than calling free on each element?

(In practice it's barely any work to just write free() for each one, but it would reduce code duplication and be useful to learn from)

like image 910
Kai Avatar asked Jun 03 '09 01:06

Kai


4 Answers

Not really - although you can write a method to do all six frees so that you never miss one.

void freeModel( model* md1 ) {
    free (mdl->vertices);
    free (mdl->normals);
    free (mdl->uv_coords);
    free (mdl->quads);
    free (mdl->triangles);
    free (mdl);
}
like image 106
Nathaniel Flath Avatar answered Nov 14 '22 17:11

Nathaniel Flath


Such functionality is not built in to C, but you can cheat a little bit by abusing the macro preprocessor:

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

To allocate:

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

To free:

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

The nasty bit is that the definition of struct model and the definition of XX_MODEL_POINTERS can become mutually inconsistent, and there's no way to catch it. For this reason it's often better to generate the definition of XX_MODEL_POINTERS by parsing a .h file somewhere.

Metaprogramming in C is never easy.

like image 8
Norman Ramsey Avatar answered Nov 14 '22 15:11

Norman Ramsey


There is no way in the C language to do this, nor would it be desirable - C doesn't know that each member is a distinct pointer allocated via malloc and C contains no run-time type info support to do this - at runtime the compiled code to access the struct is just using offsets off of a base pointer for each member access.

The simplest approach would be to write a "FreeModel" function:

void FreeModel(model* mdl)
{
   free(mdl->vertices);
   ... // Other frees
   free(mdl);
}
like image 7
Michael Avatar answered Nov 14 '22 15:11

Michael


Take a look at talloc http://talloc.samba.org/ if you do:

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

you can then:

talloc_free(mdl);

and talloc will take care of free'ing all the other blocks you called talloc with mdl as the first argument at allocation time (and it will do this recursively you can do talloc(mdl->vertices, ...) and talloc_free(mdl); will get that too)

as an aside there is a slight overhead to using talloc because it needs to track what stuff recurse over, but it's not very much.

like image 4
Spudd86 Avatar answered Nov 14 '22 15:11

Spudd86