2010-10-05 3 views
1

J'écris une fonction en C qui accepte une liste chaînée et un prédicat et retourne un tableau contenant toutes les valeurs de la liste chaînée satisfaisant cette condition. Voici la fonction:C Erreur Realloc - "Assertion` ptr == alloc_last_block 'a échoué! "

void **get_all_that(list_t *l, int (*pred)(const void *)) { 
    void **vals = NULL; 
    int i = 0; // Number of matches found 
    const size_t vps = sizeof(void *); 
    node_t *n = l->first; 
    while (n) { 
     if (pred(n->value)) { 
      vals = (void **)realloc(vals, i*vps); // (*) 
      vals[i] = n->value; 
      i++; 
     } 
     n = n->next; 
    } 
    if (vals != NULL) { 
     vals = (void **)realloc(vals, i*vps); 
     vals[i] = NULL; // NULL-terminate array 
    } 
    return vals; 
} 

je suis passé un prédicat qui retourne toujours 1 (à savoir get_all_that est essentiellement to_array), et je me fais une erreur à la ligne à l'itération étoilée où i = 4. L'erreur sur le backtrace (qui a été automatiquement imprimé à partir d'un SIGABRT) est "*** glibc détecté *** ~/list/test: realloc(): invalide taille suivante: 0x0804c0e8 ***"

J'ai ouvert GDB lui disant de casser à droite avant d'appeler realloc quand i = 4. J'ai ensuite essayé d'appeler realloc (vals, i * vps) manuellement à partir de GDB et j'ai reçu le message d'erreur: "Incohérence détectée par ld.so: dl-minimal.c: 138: realloc: Assertion` ptr == alloc_last_block 'a échoué! "

Quelqu'un sait ce qu'il se passe?

Répondre

2

Votre realloc alloue un élément de trop. Essayez de remplacer i par i+1. Vous devriez également vérifier l'échec de realloc avant de remplacer le pointeur que vous lui avez transmis, sinon vous obtiendrez une fuite de mémoire (sans parler de crash car vous ne parvenez pas à vérifier NULL) en cas d'échec, et en supprimant les lancers inutiles et laids de la la valeur de retour de realloc serait bien aussi.

+0

C'est fait. Bonne prise. Pourquoi les moulages ne sont-ils pas nuls ** nécessaires? – Nick

+0

@Nick: Parce que 'void *' est implicitement converti vers et à partir de tout autre type de pointeur (en C, pas en C++). – caf

+1

Le point entier des pointeurs 'void' (que' malloc' et 'realloc' retournent) est qu'ils se convertissent automatiquement en n'importe quel type de pointeur (non-function-). Pourquoi c'est une mauvaise idée (en termes de style, maintenabilité, et les erreurs de masquage) est un sujet sur lequel vous pouvez faire une recherche. –

0

Et votre premier realloc appelle la longueur avec 0, qui est un free. Donc, le conseil de mettre i+1 est doublement important.

+0

Ce n'est pas un problème en soi, car l'appel suivant passerait à nouveau le pointeur renvoyé par l'appel de taille 0 à 'realloc' dans' realloc'. Si 'realloc' avec la taille 0 renvoie un pointeur' NULL' ou un pointeur unique valide (le standard le permet), le programme fonctionnerait comme prévu. –

+0

Il convient également de noter que le comportement GNU 'malloc' du renvoi d'un pointeur non nul unique sur l'allocation de taille zéro a rendu plus difficile la recherche du bogue et a modifié ce qui aurait été un pointeur nul facile à attraper. déréférencer segfault dans un bug de corruption de mémoire légèrement difficile à trouver. –

Questions connexes