grim7reaper

Un artisan du code

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

Un mauvaise utilisation de realloc()
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).

Utilisation correcte de realloc()
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.