Dois-je appliquer realloc vérifier si la nouvelle taille du bloc est inférieure à la première?

StackOverflow https://stackoverflow.com/questions/2541215

  •  23-09-2019
  •  | 
  •  

Question

Peut realloc échouer dans ce cas?

int *a = NULL;

a = calloc(100, sizeof(*a));
printf("1.ptr: %d\n", a);

a = realloc(a, 50 * sizeof(*a));
printf("2.ptr: %d\n", a);

if(a == NULL){
    printf("Is it possible?\n");
}

return (0);

}

La sortie dans mon cas est:

1.ptr: 4072560
2.ptr: 4072560

'a' des points à la même adresse. Alors, dois-je appliquer realloc vérifier?

modifier plus tard :

  • Utilisation de compilateur MinGW sous Windows XP.
  • Le comportement est similaire avec gcc sous Linux?

modifier plus tard 2: Est-il acceptable pour vérifier cette façon?

int *a = NULL, *b = NULL;

a = calloc(100, sizeof(*a));
b = realloc(a, 50 * sizeof(*a));

if(b == NULL){
    return a;
}
a = b;
return a;
Était-ce utile?

La solution

Oui, vous devriez toujours appliquer un contrôle sur realloc, ou toute autre allocation de mémoire pour cette question.

Le comportement actuel de réutilisant la même adresse est un détail de mise en œuvre qui ne devrait pas être invoquée. Cela est tout simplement vous ouvrir des bugs quand soit les commutateurs de la bibliothèque, il est la mise en œuvre ou vous déplacer à une nouvelle plate-forme.

Est-il probable que ce ne sera jamais un échec? Probablement pas, je serais étonné si vous pouviez trouver un cas qu'il fait. Cependant, cela ne signifie pas qu'il ne sera pas. Emballage realloc dans une fonction qui effectue automatiquement le contrôle pour chaque opération est assez simple qu'il n'y a aucune raison de ne pas le faire.

void* xrealloc(void* ptr, size_t size) {
  ptr = realloc(ptr, size);
  if ( !ptr ) {
    exit(EXIT_FAILURE);
  }
  return ptr;
}

Autres conseils

Il serait surprenant que realloc échoué lorsqu'il est passé d'une taille plus petite que l'allocation initiale, mais rien dans la norme C (7.20.3.4) garantit qu'elle réussira toujours:

  

La fonction realloc libère le   ancien objet pointé par ptr   renvoie un pointeur vers un nouvel objet   a la taille spécifiée par size . le   contenu du nouvel objet sont   le même que celui de l'ancien objet   avant DEALLOCATION, jusqu'à la   moindre des nouvelles et anciennes tailles. Tout   octets dans le nouvel objet au-delà de la   taille de l'ancien objet ont   des valeurs indéterminées.

     

Si ptr est un pointeur NULL, realloc   fonction se comporte comme le malloc   fonction de la taille spécifiée.   Dans le cas contraire, si ptr ne correspond pas à une   pointeur précédemment retourné par la    calloc , malloc ou realloc fonction ,   ou si l'espace a été désallouée   par un appel à la free ou realloc   fonction, le comportement est indéfini.   Si la mémoire pour le nouvel objet ne peut pas être   alloué, l'ancien objet n'est pas   désallouée et sa valeur est   inchangé.

     

retour

     

La fonction realloc renvoie un pointeur   au nouvel objet (qui peut avoir   même valeur en tant que pointeur vers l'ancien   objet), ou un pointeur null si le nouveau   ne pouvait être attribué objet.

Une implémentation conforme très simple de realloc serait ceci:

void *realloc(void *ptr, size_t size)
{
    void *new_ptr= malloc(size);
    if (new_ptr && ptr)
    {
        size_t original_size= _get_malloc_original_size(ptr);
        memcpy(new_ptr, ptr, min(original_size, size));
        free(ptr);
    }

    return new_ptr;
}

Sous des conditions de mémoire (ou des conditions dans lesquelles malloc reviendrait NULL), ce retour serait NULL.

Il serait également une optimisation très simple de revenir le même pointeur si la taille de l'allocation initiale est supérieure ou égale à la taille requise. Mais rien dans la norme C dicte.

Il est bon de vérifier la valeur de retour de realloc dans tous les cas (la spécification ne vous dit pas plus en sécurité si vous réduisez votre bloc de mémoire que si vous développez elle). Mais vous devez faire attention à ne pas perdre le pointeur initial (que vous faites, dans votre cas), comme vous le feriez alors être totalement incapable de le libérer.

La norme C99 §7.20.3.4 (realloc) dit:

  

La fonction realloc désalloue l'ancien objet pointé par ptr et renvoie une   pointeur vers un objet qui a la taille spécifiée par la taille. Le contenu du nouveau   objet est identique à celui de l'ancien objet avant DEALLOCATION, jusqu'à la moindre des   les nouveaux et les anciens formats. Tous les octets dans le nouvel objet au-delà de la taille de l'objet ancien ont   des valeurs indéterminées.

     

Si ptr est un pointeur NULL, la fonction realloc se comporte comme la fonction malloc pour la   taille spécifiée. Dans le cas contraire, si PTR ne correspond pas à un pointeur plus tôt retourné par la   calloc, fonction malloc ou realloc, ou si l'espace a été désallouée par un appel   à la fonction libre ou realloc, le comportement est indéfini. Si la mémoire du nouveau   objet ne peut pas être attribué, l'ancien objet n'est pas désallouée et sa valeur ne change pas.

     

retour

     

La fonction realloc renvoie un pointeur vers le nouvel objet (qui peut avoir le même   valeur en tant que pointeur vers l'ancien objet), ou un pointeur NULL si le nouvel objet ne pouvait pas être   alloué.

Notez que l'ancien objet est désalloué; le nouvel objet peut arriver à pointer vers le même emplacement que l'ancien. Et il pourrait y avoir des problèmes. Il est assez peu probable, mais il est beaucoup plus simple d'aller avec une règle « toujours » que d'avoir des exceptions étranges.

Le contre-argument est normale « si cela ne peut pas échouer, cela signifie que j'ai un chemin d'erreur que je ne peux pas tester ». Jusqu'à un certain point, cela est vrai. Toutefois, il pourrait être qu'il ya eu un certain piétinement de la mémoire afin que l'allocation ne peut réussir - car les informations de contrôle a été corrompu. Il est plus probable que vous aurez juste une décharge de base, mais peut-être le code est assez robuste pour être en mesure d'éviter cela. (Je suppose que les 100 et pré-programmée 50 sont à des fins de poser la question;. Le code de la vie réelle ne serait pas surallocations quand il sait combien il a vraiment besoin)

Lorsque les deux appels à « realloc () » sont adjacentes, comme ici, il y a très peu de place pour quoi que ce soit à aller mal. Cependant, le code de travail réel de la vie aurait certaines opérations entre les deux -. Et que le code pourrait amener le second 'realloc () à l'échec

En ce qui concerne votre 'Edit 2' ...

Le code peut être mieux écrit:

if (b != NULL)
    a = b;
return a;

Mais le concept de base est OK. Notez que la norme indique explicitement que l'allocation initiale est sûre si le nouveau ne peut pas être créé.

Le temps qu'il faut pour faire le contrôle est si faible par rapport au temps passé dans realloc () que je ne vois même pas pourquoi ce serait un problème. Ou voulez-vous réduire le nombre de lignes de code?

realloc() peut revenir NULL assez facilement sur la réduction de la taille.

void *ptr = malloc(10);
ptr = realloc(ptr, 0);
if (ptr == NULL) {
  puts("Failure because return value is NULL? - not really");
}

realloc(any_pointer, 0) pourrait revenir NULL ou peut-être un pointeur not-NULL, il est défini par l'implémentation.

C'est pourquoi l'échec de realloc()/malloc() ne doit pas être un simple test de if (ptr == NULL) mais

void *ptr = malloc(newsize); // or realloc(..., newsize)
if (ptr == NULL && newsize > 0) {
  exit(0); // Handle OOM;
}

En raison de cette ambiguïté, devrait Code vouloir faire un wrapper realloc(), recommander quelque chose comme:

void *xrealloc(void *ptr, size_t newsize, bool *falure) {
  *failure = 0;
  if (newsize > 0) {
    void *tmp = realloc(ptr, newsize);
    if (tmp == NULL) {
      *failure = 1;
      return ptr;  // old value
    }
    return tmp;  // new value
  } 
  free(ptr);
  return NULL; // new value
  }

Obtenir NULL sur un realloc() avec une taille réduite est donc pas vraiment un échec et donc cette réponse ne s'applique tangentiellement, mais la question de l'OP a été » ... Faire respecter realloc vérifier si la nouvelle taille du bloc est plus petit que la première? » puis utilisé le paradigme if (ptr == NULL) moins digne de confiance.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top