0
votes

Type libre Cast dans la liste liée générique

Pourquoi la fonction LIST_DESTROY, GRATUITE COURROI-> DATA STROND> Deux fois si sa chaîne (puisque libère deux fois une erreur), mais cela ne le fait pas. liste-> freefn appelle une fonction qui est un type libre caste à caractère *, mais alors libres (actuelles-> données) est appelé à nouveau sans type coulé, alors celui-ci ne fonctionne pas?

Pourquoi devons-nous taper Casting Gratuit pour Cordes (Char *) mais pas INT, supposerait-il de le faire à tous les types? P>

Didacticiel original: https : //pseudomuto.com/2013/05/Implementaling-a-generic-linked-list-in-c/ p>

void list_destroy(list* list)
{
    listNode* current;
    while (list->head != NULL) {
        current = list->head;
        list->head = current->next;

        if (list->freeFn) {
            list->freeFn(current->data);
        }

        free(current->data);
        free(current);
    }
}

void free_string(void* data)
{
    free(*(char**)data);
}


void list_new(list* list, int elementSize, freeFunction freeFn)
{
    assert(elementSize > 0);
    list->logicalLength = 0;
    list->elementSize = elementSize;
    list->head = list->tail = NULL;
    list->freeFn = freeFn;
}

void main()
{
    ...
    list list;
    list_new(&list, sizeof(char*), free_string);
    ...
    list_destroy(&list);
}


1 commentaires

Vous ne montrez pas tout, mais probablement actuel-> data et actuel indique à différents malloc Mémoire ED.


3 Réponses :


-1
votes

Le résultat de freefn (actuel-> données) code> est gratuit (* (* (* * *)) code> qui est différent de gratuit (actuel-> données) code>; Bien que soit douteux. Notez que la différence provient de la déséroférance de l'argument Data (& Current-> Data).

Le tutoriel est faux, car les données de membre ne sont pas attribuées. Une solution possible serait de changer: p> xxx pré>

à: p> xxx pré>

et modifier: p>

void free_string(void* data)
{
    free(*(char**)data);
    *(char **)data = 0;
}


1 commentaires

Vous avez tort - lorsqu'un nœud de liste est créé, l'espace est attribué pour les données.



0
votes

Je pense que vous pouvez le voir si vous faites votre propre intacte. Prenez la ligne de free_string et mettez-la dans la liste_Destroy. Ce que vous avez, c'est que vous faites des données gratuites deux fois, mais les données ont déjà été libérées. Une fois que l'option est de modifier le free_string sur ce qui suit (la coulée n'est pas requise): xxx

et l'appelez avec list-> freefn (& actuel-> données);


4 commentaires

Je pense que cela rendrait la mise en œuvre de la liste moins générique. A void * peut pointer sur un objet de n'importe quel type, mais un void ** peut seulement pointer sur un vide * .


Est-ce que ça importe? Free est déclaré comme gratuit (vide *); , nous avons donc un vide * de toute façon.


Le point est que le rappel freefn doit pouvoir être utilisé pour nettoyer n'importe quelle valeur. Par exemple, si la liste a été utilisée pour stocker des valeurs struct FOO avec le type défini comme struct foo {char * a; Char * b; } avec le A et B Membres pointant vers des chaînes allouées dynamiquement, le rappel freefn serait quelque chose comme: vide free_foo (void * données) { struct foo * p = données; gratuit (p-> a); Gratuit (p-> B); } . Voir ce que je veux dire par "générique"?


Je ne pense pas que cela vaut la peine de -1, mais c'est ce que c'est.



1
votes

L'implémentation de la liste générique stocke les valeurs d'élément d'une taille fixe fournie à list_new () avec un pointeur sur une fonction de rappel en option pour libérer la valeur de l'élément lorsqu'un nœud de liste est détruit. La taille de l'élément est stockée dans le membre ELSIZE de la liste et le pointeur de fonction de rappel (qui est null si aucune fonction de rappel doit être appelée) est stockée dans le freefn membre.

list_append () et list_prepend () Ajouter un nouveau nœud à la liste et est fourni avec un pointeur à la valeur de l'élément à ajouté à la liste. Ils allouent un nouveau nœud de liste et allouer un bloc de mémoire pointé par nœud-> données pour maintenir la valeur d'élément, qui est copié dans le bloc de mémoire alloué à l'aide de memcpy () .

list_destroy () libère tous les nœuds de la liste, appelant la fonction freefn Fonction de rappel (si fourni) avec les données Pointeur pour chaque valeur d'élément. Il appartient à la fonction de rappel pour définir ce qu'il faut faire avec la valeur de l'élément pour la libérer. La fonction de rappel ne libère pas la mémoire pointée par le pointeur Data . Il utilise la valeur indiquée par le pointeur . Il est list_destroy () qui libère le bloc de mémoire alloué par list_append () ou list_prepend () < /code >.


loget> Entier, la valeur à stocker est juste une valeur de type int . Le membre éléments de la liste est Tailleof (int) . Le pointeur de la fonction de rappel freefn est null car rien ne doit être fait pour libérer un int - il n'est qu'un chiffre. L'exemple appelle list_append () avec un pointeur sur la valeur int à stocker. list_append () attribue un bloc de taille Tailleof (int) , pointé par noeud-> données et copie le int < / CODE> VALEUR EN IT.

Dans l'exemple de chaîne, la valeur à stocker est un char * qui pointe sur un tampon à chaîne alloué de manière dynamique allouée par STRUP () . Ce tampon doit être libéré lorsque le nœud de la liste est détruit. Par conséquent, l'exemple de chaîne fournit un pointeur à la fonction free_string pour libérer le tampon de chaîne attribué par STRUP () . L'exemple d'appels list_append () avec un pointeur sur la valeur Char * Valeur à stocker. list_append () attribue un bloc de taille Tailleof (Char *) , pointé par noeud-> données et copie le * la valeur. (Remarque: il copie la valeur du pointeur Char * , pas le contenu de la chaîne. Le contenu de la chaîne a été copié par STRUP () .)

Le free_string () La fonction de rappel reçoit un indicateur Void (code> VOID * / code> sur une copie de la valeur pointée dans l'appel à list_append () . Il pointe de la valeur char * , la première chose à faire est de convertir le pointeur Generic Void * sur un Char ** Pointeur . Cela fait-il avec un opérateur de type de type: (char * **) données . Ensuite, il déréférences le Char ** Pour obtenir un Char * Valeur: * (Char **) Données . Ce Char * Pointe de la valeur sur la mémoire allouée par STRUP () et qui doit être libéré: gratuit (* (* (* **) données); .

S'il est plus facile de voir ce que free_string () fait, il aurait pu définir certaines variables locales pour contenir les valeurs de pointeur intermédiaires, comme ceci: xxx

Comme vous pouvez le voir, free_string ne libère pas le tampon pointé par le pointeur "/ code>. Il ne fait que libérer la mémoire tampon indiquée par la valeur char * que le pointeur pointer sur.


0 commentaires