Allouer de la mémoire et les libérer. Devrions-nous les définir sur NULL?

gcc (GCC) 4.7.0 c89 

Bonjour,

Je me demande si je pense correctement ici. Quand j’alloue de la mémoire en utilisant malloc. malloc retournera un pointeur à une taille en mémoire.

Donc avant d’allouer ma mémoire, tous les pointeurs auront la valeur NULL.

En utilisant cet extrait de code:

 struct address *db_row = NULL; db_row = malloc(sizeof(struct address)); db_row->name = malloc(sizeof(char) * 10); db_row->email = malloc(sizeof(char) *10); free(db_row->name); free(db_row->email); free(db_row); 

Je l’ai fait dans le débogueur gdb pour db_row avant d’allouer de la mémoire:

 (gdb) p db_row $20 = (struct address *) 0x0 (gdb) p *db_row Cannot access memory at address 0x0 

Ce qui est correct, aucune adresse mémoire n’a été atsortingbuée. Après avoir alloué de la mémoire, je reçois ce qui suit quand je fais la même chose:

 (gdb) p db_row $25 = (struct address *) 0x602310 (gdb) p *db_row $26 = {id = 0, set = 0, name = 0x0, email = 0x0} 

Cependant, après avoir libéré la mémoire, je reçois toujours la même adresse mémoire. Ne doit-il pas être NULL comme dans le premier cas avant d’allouer de la mémoire?

Après avoir libéré la mémoire:

 (gdb) p db_row $28 = (struct address *) 0x602310 (gdb) p *db_row $27 = {id = 6300480, set = 0, name = 0x602330 "", email = 0x602350 " #`"} 

Comme vous pouvez le voir, il pointe toujours vers le même emplacement mémoire, est-ce correct?

Enfin, j’ai ajouté ceci à la fin pour voir si je pouvais faire un double libre:

 if(db_row != NULL) { free(db_row); } if(db_row != NULL) { free(db_row); } 

Je reçois un vidage de stack au deuxième appel pour libérer. Mais par mesure de sécurité, devriez-vous toujours vérifier pour vous assurer que vous n’essayez pas de faire un double libre?

Cela vaut-il la peine de mettre les pointeurs sur NULL après les avoir libérés?

 db_row = NULL; 

Merci beaucoup pour vos suggestions,

Cependant, par mesure de sécurité, devez-vous toujours vérifier que vous n’essayez pas de libérer un pointeur NULL?

Appeler free() avec un pointeur NULL est sécurisé et rien ne se produit. L’appel de free() n’annule pas le pointeur, vous pouvez le faire explicitement. NULL utiliser un pointeur après avoir appelé free() empêcherait le double libre sur la même variable de pointeur:

 /* Assuming 'db_row' is referring to a valid address at this stage the following code will not result in a double free.*/ free(db_row); db_row = NULL; free(db_row); 

Si une autre variable de pointeur pointe vers la même adresse et est transmise à free() un double free survient toujours.

Tout simplement parce qu’un pointeur n’est pas – NULL ne garantit pas qu’il pointe vers une adresse valide. Il incombe au programmeur de s’assurer que les doubles free() ne se produisent pas. NULL variables de pointeur NULL après un appel à free() aident mais ne fournissent aucune garantie, car plusieurs variables de pointeur peuvent pointer vers la même adresse.


Mais par mesure de sécurité, devriez-vous toujours vérifier pour vous assurer que vous n’essayez pas de faire un double libre?

Il n’y a aucun moyen d’interroger une variable de pointeur pour déterminer si la mémoire à l’adresse qu’elle contient a déjà été free() d. Une variable de pointeur contient une adresse, rien de plus.

Il est recommandé de définir un pointeur sur null après null avoir libéré, sauf si vous vous trouvez à la fin de la scope de la variable. et oui il est normal que free ne free fasse pas.

Si le pointeur est null , appeler free sur lui est sécurisé et tout access à celui-ci générera un vidage de la base, ce qui est beaucoup plus facile à déboguer que la corruption de la mémoire ou la double libération qui pourrait se produire si vous utilisez un pointeur libéré non défini sur null .

Après avoir libéré la mémoire, je reçois toujours la même adresse mémoire. Ne doit-il pas être NULL comme dans le premier cas avant d’allouer de la mémoire?

Parce que free() ne libère que la mémoire qui vous a été allouée. Il ne définit pas le pointeur sur NULL .

Cependant, par mesure de sécurité, devez-vous toujours vérifier que vous n’essayez pas de libérer un pointeur NULL?

Si le pointeur passé à free() est un pointeur nul, aucune action n’est exécutée ( C99, section 7.20.3.2 ).

Cela vaut-il la peine de mettre les pointeurs sur NULL après les avoir libérés?

Il n’y a absolument aucun mérite à le faire, car le plus souvent, cela finira par cacher vos problèmes, ce qui soulèvera la tête sous une forme ou une autre.

C’est un style de programmation défensif et douteux, qui évite les pointeurs en suspens ou les problèmes de double libre, dans lequel vous essayez de vous protéger contre les appels free() sur le même pointeur. En réalité, le plus souvent ou non le problème de double libre se produit lorsque vous dans la même mémoire et ces différents pointeurs sont passés à free() .

La pratique consistant à rendre le pointeur NULL ne fait rien dans le deuxième scénario de bogue plus commun. Toutes ces situations ne peuvent être évitées qu’en écrivant des programmes en y ajoutant des reflections bien pensées et des révisions périodiques du code.

Pour essayer d’être clair ici, je vais citer la page de manuel malloc:

The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behavior occurs. If ptr is NULL, no operation is performed.

Donc, pour récapituler:

  1. Free prend un pointeur renvoyé par une fonction d’allocation de mémoire (NULL ou valide)
  2. Si c’est NULL, rien ne se passe
  3. Si c’est un pointeur valide, cela libère de la mémoire

Vous remarquerez ici que rien ne dit de changer l’adresse indiquée par ptr . C’est à vous de faire. Donc c’est très sûr de faire:

 int *p = malloc(sizeof(int)); *p = 5; printf("p=%d\n",*p); free(p); p = NULL; 

Vous pouvez maintenant appeler free() toute la journée sur votre pointeur p et tout va bien. Si vous craignez des erreurs lors d’appels double free() , sur les versions récentes de Linux libc (ultérieure à 5.4.23) et GNU libc (2.x), un mécanisme de MALLOC_CHECK_ nommé MALLOC_CHECK_ en place MALLOC_CHECK_

Au lieu d’obtenir un crash de stack, vous verrez un message tel que: *** glibc detected *** ./a.out: free(): invalid pointer: 0x0804b008 ***

Vous pouvez l’utiliser de différentes manières:

  1. export MALLOC_CHECK_=0 pour désactiver malloc check et (éventuellement) laisser votre programme se bloquer
  2. export MALLOC_CHECK_=1 vous aurez le message d’avertissement ci-dessus
  3. export MALLOC_CHECK_=2 abort (1) et vous remettra une trace de votre code
  4. export MALLOC_CHECK_=3 n’est que l’option 1 | 2 ensemble