web-dev-qa-db-fra.com

Réallocation sur un pointeur à valeur NULL (ou non défini)

Je lisais sur realloc et je me suis trompé sur un point mentionné ici. Considérez le code ci-dessous:

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

int main () {

    int* ptr = NULL;
    ptr = realloc(ptr, 10*sizeof(int));
    return 0;
}

Y a-t-il un danger à allouer de la mémoire avec realloc en utilisant le NULL initialement évalué ptr? Si au lieu de:

int* ptr = NULL;

J'avais ceci:

int* ptr; // no value given to ptr

serait-ce un problème d'appeler realloc en utilisant ptr?

33
user1607425

Y a-t-il un danger à allouer de la mémoire avec realloc en utilisant le ptr initialement à valeur NULL

None

7.22.3.5

Si ptr est un pointeur nul, la fonction realloc se comporte comme la fonction malloc pour la taille spécifiée.

Pour la deuxième partie:

int* ptr; // no value given to ptr

serait-ce un problème d'appeler realloc en utilisant ptr?

Si vous utilisez des pointeurs non initialisés, c'est un problème très grave car vous ne pouvez pas prédire quelle sera leur valeur. La fonction realloc ne fonctionne correctement que pour NULL ou les valeurs obtenues à partir de malloc/realloc.

Sinon, si ptr ne correspond pas à un pointeur précédemment renvoyé par une fonction de gestion de la mémoire [...] le comportement n'est pas défini

39
cnicutar

Avec le code spécifique affiché, il n'y a aucun problème avec l'utilisation du pointeur nul initialement.

Si la variable ptr n'est pas initialisée - non définie sur 0 ou NULL - alors tout appel à realloc() l'utilisant est dangereux; le comportement n'est pas défini et si vous êtes chanceux, le programme se bloquera, mais si vous n'avez pas de chance, il semblera fonctionner pendant un certain temps, jusqu'à ce que quelque chose se passe mal plus tard dans le programme où il sera difficile de détecter que le problème est dans le code exécuté il y a longtemps.

Il y a ceux qui soutiennent qu'il vaut mieux utiliser malloc() pour l'allocation initiale et realloc() par la suite. Il y a une certaine justice dans la suggestion, notamment parce que vous n'utiliseriez probablement pas ptr = realloc(ptr, 0); pour libérer de la mémoire, même si vous pourriez le faire (vous n'avez donc pas vraiment besoin de malloc() ou free() car realloc() peut effectuer les trois opérations). Mais la norme C90 requiert que realloc(0, new_size) fonctionne de manière équivalente à malloc(new_size), et je ne connais aucune bibliothèque C qui se comporte différemment (mais il pourrait y en avoir; je n'ai utilisé que quelques bibliothèques C , quoique principalement les plus largement utilisés).


Cependant, dans un cas plus général tel que le code suivant, il y a alors un problème subtil avec le code (mais cela n'a rien à voir avec le pointeur nul initial):

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

int main(void)
{
    char    *ptr = NULL;
    size_t   len = 0;
    char     buffer[256];

    while (fgets(buffer, sizeof(buffer), stdin))
    {
        size_t buflen = strlen(buffer) + 1;
        if (buflen > len)
        {
            if ((ptr = realloc(ptr, buflen)) == 0)  // Danger!
                // ... handle memory allocation failure ...
            len = buflen;
        }
        strcpy(ptr, buffer);
        // ... do something with ptr
    }
    free(ptr);
    return 0;
}

Quel est le danger? Le danger est que si la deuxième ou une allocation de mémoire suivante échoue et que ptr est le seul pointeur vers la mémoire allouée, vous venez d'écraser sa valeur précédente avec null. Cela signifie que vous ne pouvez plus libérer la mémoire allouée à l'aide de ptr - vous avez perdu de la mémoire. (Pour la première allocation, la valeur initiale était 0, la valeur remplacée était zéro et rien n'a changé; il n'y a pas de fuite de mémoire. C'est pourquoi la boucle a été ajoutée au code.)

Règle générale

  • N'écrivez pas ptr = realloc(ptr, newsize);

Enregistrez la nouvelle valeur dans une variable distincte jusqu'à ce que vous l'ayez testée.

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

int main(void)
{
    char    *ptr = NULL;
    size_t   len = 0;
    char     buffer[256];

    while (fgets(buffer, sizeof(buffer), stdin))
    {
        size_t buflen = strlen(buffer) + 1;
        if (buflen > len)
        {
            char *new_ptr = realloc(ptr, buflen);
            if (new_ptr == 0)
                // ... handle memory allocation failure ...
            ptr = new_ptr;
            len = buflen;
        }
        strcpy(ptr, buffer);
        // ... do something with ptr
    }
    free(ptr);
    return 0;
}

Ce code ne fuit pas la mémoire en cas d'échec d'allocation.

Recommandation auxiliaire: n'utilisez pas de variable appelée new; il sera difficile de compiler avec un compilateur C++. Même si vous n'avez pas l'intention de convertir en C++ (et même si vous finirez probablement par réécrire la gestion de la mémoire si vous le faites), il n'y a aucun avantage à utiliser le mot clé C++ new comme nom de variable C .. . sauf si vous souhaitez explicitement empêcher la compilation avec un compilateur C++.

8
Jonathan Leffler

Y a-t-il un danger à allouer de la mémoire en utilisant realloc en utilisant le ptr initialement de valeur NULL?

Non, ce serait exactement comme un malloc.

Si au lieu de:

int* ptr = NULL;

J'avais ceci:

int* ptr; // no value given to ptr

serait-ce un problème d'appeler realloc en utilisant ptr?

Oui, il y aurait un problème. Si realloc n'obtient pas de NULL, il essaiera d'étendre la mémoire à partir de cet emplacement, ou peut essayer de free et malloc une autre partie de la mémoire. Puisque les variables non initialisées peuvent avoir n'importe quelle valeur, les chances sont très élevées, ce n'est pas une valeur realloc likes. Si vous êtes chanceux, votre programme planterait immédiatement.

4
Shahbaz