Du bon usage de realloc()
Cet article provient de mon ancien site Internet.
realloc()
realloc
est une fonction de la bibliothèque standard du C, pour
l’utiliser il suffit d’inclure l’en‑tête stdlib.h
. Sa principale
fonction est de modifier la taille d’un bloc mémoire alloué dynamiquement.
Le prototype de la fonction est simple. Elle prend l’adresse de la zone à modifier et la nouvelle taille à lui donner. Malheureusement, je vois encore trop souvent à mon goût une mauvaise utilisation de cette fonction (en particulier dans la gestion des erreurs, ce qui provoque des fuites de mémoire).
Ce qu’il ne faut pas faire (mais que je vois souvent…)
Voilà un exemple de mauvaise utilisation de realloc
(code trouvé
dans un tuto’ quelconque sur Internet).
1 2 3 4 5 6 7 8 9 10 11 | tab = realloc(tab, 6000*sizeof(unsigned long)); if(tab != NULL) { // Utilisation de l’espace mémoire ré-alloué // ... // /!\ Penser à libérer la mémoire !!! } else { // Echec de la ré-allocation mémoire } |
Nan, mais il ne faut pas rigoler : j’ai vu des gars qui ont un niveau pas trop mauvais en C qui pondent des conneries du genre…
Je crois que les gens qui produisent ce genre de code ne lisent pas le putain
de manuel avant d’utiliser une fonction. En effet, man 3 realloc
nous dit :
[…]
The realloc() function returns a pointer to the newly allocated memory, which is suitably aligned for any kind of variable and may be different from ptr, or NULL if the request fails. If size was equal to 0, either NULL or a pointer suitable to be passed to free() is returned. If realloc() fails the original block is left untouched; it is not freed or moved.
[…]
Donc si realloc
se vautre, il renvoie NULL
et là il
faut que les cadors du code crado m’expliquent comment ils libèrent leur bloc
mémoire maintenant qu’ils ont écrasé son adresse avec le retour
de realloc
.
La bonne façon d’utiliser realloc()
La solution est toute simple : passer par une variable intermédiaire pour
récupérer le retour de realloc
. Voilà un exemple de code
(imparfait je le sais, je ne teste pas le risque d’overflow sur
new_size
alors qu’il faudrait le faire).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static char* enlarge_your_buffer(char** buff, size_t* size) { size_t new_size = (size_t)(*size * 1.5); char* tmp = realloc(*buff, new_size); if(tmp != NULL) { *buff = tmp; *size = new_size; } else { fprintf(stderr, "error realloc failed: %s\n", strerror(ENOMEM)); free(*buff); *buff = NULL; } return *buff; } |
De cette manière on peut faire le traitement adapté en fonction du retour de
realloc
: on libère la zone mémoire et on signale l’erreur en cas
d’échec, sinon on met le pointeur à jour.