32 votes

Libération automatique des variables de pile en C

Malheureusement, en C il n'y a pas de pointeurs intelligents.. mais est-il possible de créer une macro qui enveloppent la déclaration de la variable et appeler les appels de fonction à cette variable comme une variable d'entrée au moment de quitter le champ où la variable a été déclarée ?

Désolé pour le long terme, mais je suis en train de travailler sur le noyau xnu où vous avez de nombreux éléments qui ont intégré dans les compteurs de référence, et on ne doit pas oublier de unref cet élément lorsque le fait de l'utiliser pour éviter les fuites de mémoire.

Par exemple, si j'ai le type suivant d' proc_t:

struct proc;
typedef struct proc * proc_t;

Je veux déclarer une pile qui varient en fonction de ce type dans un champ, par exemple :

{
    proc_t_release_upon_exit proc_t proc_iter = proc_find(mypid);
    //the rest of the code in this scope 
}

Après préprocesseur analyser le macro et avant la compilation, le code suivant je m'attends à être généré est :

{ 
    proc_t myproc = proc_find(mypid)
    //the rest of the code in scope
    proc_rele(myproc);
}

Est-il possible de définir une macro comme en C ?

39voto

Maciej Malinowski Points 306

Vous pouvez utiliser le nettoyage de la variable attribut dans GCC. Veuillez jeter un oeil à ceci: http://echorand.me/site/notes/articles/c_cleanup/cleanup_attribute_c.html

Exemple de code:

#include <stdio.h>
#include <stdlib.h>

void free_memory(void **ptr)
{
    printf("Free memory: %p\n", *ptr);
    free(*ptr);
}

int main(void)
{
    // Define variable and allocate 1 byte, the memory will be free at
    // the end of the scope by the free_memory function. The free_memory 
    // function will get the pointer to the variable *ptr (double pointer
    // **ptr).
    void *ptr  __attribute__ ((__cleanup__(free_memory))) = malloc(1);
    return 0;
}

Si vous enregistrez le code source dans un fichier nommé principal.c, vous pouvez le compiler avec cette commande:

gcc main.c -o main

et de vérifier s'il y a des fuites de mémoire par:

valgrind ./main

Exemple de sortie de valgrind:

==1026== Memcheck, a memory error detector
==1026== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==1026== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==1026== Command: ./main
==1026== 
Free memory: 0x51ff040
==1026== 
==1026== HEAP SUMMARY:
==1026==     in use at exit: 0 bytes in 0 blocks
==1026==   total heap usage: 1 allocs, 1 frees, 1 bytes allocated
==1026== 
==1026== All heap blocks were freed -- no leaks are possible
==1026== 
==1026== For counts of detected and suppressed errors, rerun with: -v
==1026== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

18voto

Leushenko Points 2079

C fournit un moyen pour placer le code syntaxiquement avant tout autre code qui sera exécuté en premier: l' for bloc. Rappelez-vous que la clause 3 for structure peut contenir une expression arbitraire, et court toujours après l'exécution du bloc principal.

Vous pouvez donc créer une macro qui fait un prédéterminés d'appel après un bloc d' suivants du code en l'enveloppant d'un for bloc dans une macro:

#define M_GEN_DONE_FLAG() _done_ ## __LINE__ 

#define M_AROUND_BLOCK2(FLAG, DECL, BEFORE, AFTER) \
  for (int FLAG = (BEFORE, 0); !FLAG; ) \
    for (DECL; !FLAG; FLAG = (AFTER, 1))

#define M_AROUND_BLOCK(DECL, BEFORE, AFTER) M_AROUND_BLOCK2(M_GEN_DONE_FLAG(), DECL, BEFORE, AFTER)

#define M_CLEANUP_VAR(DECL, CLEANUP_CALL) M_AROUND_BLOCK(DECL, (void)0, CLEANUP_CALL)

...et vous pouvez l'utiliser comme ceci:

#include <stdio.h>

struct proc;
typedef struct proc * proc_t;

proc_t proc_find(int);
void proc_rele(proc_t);

void fun(int mypid) {
  M_CLEANUP_VAR (proc_t myproc = proc_find(mypid), proc_rele(myproc))
  {
    printf("%p\n", &myproc); // just to prove it's in scope
  }
}

Le truc c'est qu'un for bloc accepte une déclaration suivante, mais si nous ne sommes pas en mettre qu'énoncé dans la définition de la macro, on peut suivre la macro invocation avec un bloc de code et il sera "comme par magie" appartiennent à notre nouveau l'étendue de contrôle de la structure de la syntaxe, tout simplement parce qu'à la suite de la élargi for.

Toute l'optimiseur de dollars à l'aide permettra de supprimer la boucle de drapeau à son niveau le plus bas des paramètres d'optimisation. Notez que le nom des affrontements avec le drapeau n'est pas une préoccupation énorme (c'est à dire que vous n'avez pas vraiment besoin d'un gensym - ce parce que le drapeau est l'étendue du corps de la boucle, et toutes les boucles imbriquées en toute sécurité les masquer, si ils utilisent le même nom d'option.

La prime ici, c'est que la portée de la variable de nettoyage est limité (il ne peut pas être utilisé en dehors de l'enceinte immédiatement après sa déclaration) et visuellement explicite (à cause du dit composé).

Pour:

  • c'est la norme C sans extensions
  • le flux de contrôle est simple
  • c'est en fait (en quelque sorte) moins verbeux que __attribute__ __cleanup__

Inconvénients:

  • il ne fournit pas "plein" RAII (c'est à dire de ne pas protéger contre goto ou les exceptions C++: __cleanup__ est généralement mis en œuvre avec le C++ machines sous le capot, donc il est plus complet). Plus sérieusement, elle ne protège pas contre les premiers return (merci @Voo). (Vous pouvez au moins se prémunir contre un éventuel break - si vous voulez - par l'ajout d'une troisième ligne, switch (0) default: à la fin de l' M_AROUND_BLOCK2.)
  • pas tout le monde est d'accord avec la syntaxe de l'extension de macros (mais considérez que vous êtes l'extension de C de la sémantique, donc...)

4voto

Dan Points 4105

Je sais que ce n'est pas ce que vous voulez entendre, mais je vous exhorte à ne pas le faire.

Il est parfaitement acceptable que le style C ait un point de retour unique, avant lequel tout est nettoyé. Comme il n'y a pas d'exception, c'est facile à faire et facile à vérifier en regardant la fonction.

L'utilisation de "fonctionnalités" de macro-hackery ou de compilateur n'est pas acceptée comme style C. Après vous, ce sera un fardeau pour tout le monde de lire et de comprendre. Et finalement, cela ne vous rapporte pas vraiment grand chose.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X