2009-06-03 7 views
3

J'ai une structure qui contient uniquement des pointeurs vers la mémoire que j'ai alloués. Existe-t-il un moyen de libérer récursivement chaque élément qui est un pointeur plutôt que d'appeler gratuitement sur chacun d'eux?Libération récursive des structures C

Par exemple, disons que j'ai cette mise en page:

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; 

Et dans mon code, je malloc chacun des struct pour créer un modèle:

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

Il est facile de libérer chaque pointeur comme si:

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

Est-il possible que je puisse récursive itérer les pointeurs dans mdl Rathe r qu'appeler gratuitement sur chaque élément?

(Dans la pratique, il est à peine tout travail juste écrire libre() pour chacun d'eux, mais il réduirait la duplication de code et être utile pour apprendre)

Répondre

6

Cette fonctionnalité n'est pas intégré à C, mais vous pouvez tricher un peu en abusant de la préprocesseur macro:

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

Allouer:

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

Pour gratuit:

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

Le bit méchant est que la définition de struct model et la définition de XX_MODEL_POINTERS peut devenir mutuellement incohérent, et il n'y a aucun moyen de l'attraper. Pour cette raison, il est souvent préférable de générer la définition de XX_MODEL_POINTERS en analysant un fichier .h quelque part.

La métaprogrammation en C n'est jamais facile.

+1

Ah intelligent, merci! Bien que cette solution semble sale, elle évite la duplication de code sans pénalité de performance et c'est une approche que je n'avais jamais vue auparavant. Je ne l'utiliserais probablement pas en pratique, mais il répond à ma question par une implémentation intéressante plutôt que de simplement réorganiser le code. – Kai

+0

Ne pourriez-vous pas faire l'opération pour effectuer un argument à la macro, plutôt que d'avoir à le "re-définir" à chaque fois? –

+0

Je pense que vous abusez du mot "abus" ici. Ce genre de truc est fait pour le préprocesseur, je pense! abus -> utiliser :) – Gewure

18

Pas vraiment - mais vous pouvez écrire une méthode pour faites les six libres pour ne jamais en manquer un.

void freeModel(model* md1) { 
    free (mdl->vertices); 
    free (mdl->normals); 
    free (mdl->uv_coords); 
    free (mdl->quads); 
    free (mdl->triangles); 
    free (mdl); 
} 
2

Lancer tous les libres dans une fonction?

0

Je ne crois pas qui est possible dans toute forme de C.

Vous pouvez coder une fonction distincte pour cette structure spécifique où vous passeriez les pointeurs et les libérer là-bas.

EDIT: Ups, trop tard, n'a jamais vu ces réponses ...

0

pas avec ces structures. Vous pouvez ajouter une autre entrée à votre structure 'model' de niveau supérieur contenant une liste de pointeurs à libérer, et parcourir cette liste. Mais je doute que la complexité ajoutée et la compréhension réduite de cette solution en valent la peine. (Sauf si vous avez un ensemble d'entrées beaucoup plus grand et plus profondément imbriqué à libérer dans la structure 'modèle' de niveau supérieur que vous montrez ici.)

7

Il n'y a aucun moyen dans le langage C de faire cela C ne sait pas que chaque membre est un pointeur distinct alloué via malloc et C ne contient pas de support d'informations de type runtime pour le faire - au moment de l'exécution, le code compilé pour accéder à la structure n'utilise que des offsets d'un pointeur de base pour chaque accès membre.

L'approche la plus simple serait d'écrire une fonction « FreeModel »:

void FreeModel(model* mdl) 
{ 
    free(mdl->vertices); 
    ... // Other frees 
    free(mdl); 
} 
3

Vous pouvez calculer la taille nécessaire pour tous ensemble et faire une grande malloc

sizeof (modèle) + sizeof (sommet) * nVertices ... etc.

affecter le résultat à Mdl, résultat + sizeof (modèle) à Model-> sommets ...

Puis libre, il est juste un fre e.

Vous pouvez avoir à vous soucier des problèmes d'alignement (en fonction de votre plate-forme), mais cela ne devrait pas être trop difficile à comprendre.L'autre problème est que c'est un morceau beaucoup plus grand qui peut présenter un problème si dans un environnement contraint par la mémoire.

3

Jetez un oeil à talloc http://talloc.samba.org/ si vous le faites:

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

vous pouvez alors:

talloc_free(mdl); 

et talloc prendra soin de free « ing tous les autres blocs que vous avez appelé talloc avec mdl comme le premier argument au moment de l'allocation (et il le fera récursivement, vous pouvez faire talloc(mdl->vertices, ...) et talloc_free(mdl); obtiendra cela aussi)En aparté, il y a un léger surcoût à l'utilisation de talloc car il a besoin de suivre ce qui se recurve, mais ce n'est pas beaucoup.

Questions connexes