Partage
  • Partager sur Facebook
  • Partager sur Twitter

Liste chainée, destruction tout est détruit ?

    23 avril 2024 à 14:59:14

    Bonjour,

    Je suis en train de travailler sur les listes chaînées du cours en C d'OpenClassroom et je code les dernières fonctions su les listes chaînées.

    Je suis sur celle de la destruction de la liste.

    Je pense avoir compris comment supprimer tous les éléments de la liste (voir code en dessous).

    Si des améliorations sont possibles dites-le moi (des conseils sont toujours les bienvenus !)

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Element Element;
    struct Element
    {
        int nombre;
        Element *suivant;
    };
    
    typedef struct Liste Liste;
    struct Liste
    {
        Element *premier;
    };
    
    Liste *initialisation()
    {
        Liste *liste = malloc(sizeof(*liste));
        Element *element = malloc(sizeof(*element));
    
        if(liste == NULL || element == NULL)
        {
            exit(EXIT_FAILURE);
        }
    
        element->nombre=0;
        element->suivant=NULL;
        liste->premier=element;
    
        return liste;
    }
    
    void insertion(Liste *liste, int nvNombre)
    {
        Element *nouveau = malloc(sizeof(*nouveau));
        if(liste == NULL || nouveau == NULL)
        {
            exit(EXIT_FAILURE);
        }
        nouveau->nombre = nvNombre;
    
        nouveau->suivant = liste->premier;
        liste->premier = nouveau;
    }
    
    void afficherListe(Liste *liste)
    {
        if(liste == NULL)
        {
            exit(EXIT_FAILURE);
        }
    
        Element *actuel = liste->premier;
    
        while (actuel !=NULL)
        {
            printf("%d, adresse -> ", actuel->nombre);
            printf("%p\n", actuel);
            actuel = actuel->suivant;
        }
        printf("NULL\n");
        
    }
    
    void destructionListe(Liste *liste)
    {
        if(liste==NULL)
        {
            exit(EXIT_FAILURE);
        }
    
        Element *elementToDelete = liste->premier;
        Element *elementSuivant = elementToDelete; // Element temporaire pour stocker l'adresse de l'élement suivant
        while (elementToDelete!=NULL)
        {
            elementSuivant = elementToDelete->suivant;
            free(elementToDelete);
            elementToDelete = elementSuivant;
        }
        
    }
    
    
    int main(int argc, const char* argv[])
    {
        int i=0;
    
        Liste *maListe = initialisation();
        // Insertion de plusieurs éléments dans ma liste
        insertion(maListe, 35);
        insertion(maListe, 42);
        insertion(maListe, 69);
        insertion(maListe, 13);
        insertion(maListe, 21);
        insertion(maListe, 27);
    
        afficherListe(maListe);
    
        printf("Destruction de la liste\n");
        destructionListe(maListe);
    
    
        Element *afficheElement;
        afficheElement = maListe->premier;
    
        printf("adresse => %p\n", afficheElement);
        printf("adresse suivante => %p\n", afficheElement->suivant);
        printf("nombre => %d\n", afficheElement->nombre);
    
    
        if(maListe==NULL)
        {
            printf("Ma liste est vide\n");
        }
    
        return 0;
    }

    Dans ce code, je fais ceci :

    • Création de la liste
    • Ajout d'éléments à la liste
    • Affichage de la liste (Nombre et adresse de l'élément actuel)
    • Destruction de la liste
    • Affichage de la liste de son adresse, l'adresse qu'elle pointe et le nombre qu'elle devrait avoir s'il y en a un
    • Boucle if pour voir si ma liste est toujours existante

    Et voici le résultat que cela m'affiche

    Ma question est que je ne saisis pas trop si la liste est bien supprimée ou si alors le premier élément est toujours là ?

    Je vois que je ne pointe sur plus rien donc plus de maillons après mais l'adresse de base est toujours là (0x7158F0)

    Dois-je aussi supprimer cette adresse ? Par exemple rajouter un free(liste) dans ma fonction destructionListe() ?

    Ou alors laisser tout tel quel et c'est suffisant ?

    • Partager sur Facebook
    • Partager sur Twitter
      23 avril 2024 à 18:08:59

      Bonjour ! Si tu veux savoir si la liste est bien détruite, il suffit de faire appel à la fonction afficherListe() : tu verras bien ce qu'elle affiche.
      • Partager sur Facebook
      • Partager sur Twitter
        23 avril 2024 à 18:26:51

        Si j'appelle la fonction afficherListe(), j'ai une belle erreur de segmentation :).

        Elle m'affiche un nombre complètement aléatoire (identique à celui que j'avais dans le résultat au-dessus) et l'adresse du premier élément de la liste chaînée (identique aussi).

        Donc on peut en déduire que tout est bien supprimé ?

        • Partager sur Facebook
        • Partager sur Twitter
          23 avril 2024 à 18:26:52

          Tous les éléments sont bien détruit, mais pas la liste créé avec malloc ligne 19. Donc on devrait plutôt dire que ta fonction vide la liste au lieu de la détruire. Si tu veux la détruire, il faudra aussi un free qui correspond à ce malloc. 

          PS : Est-ce bien utile de créer un élément à 0 lors de l'initialisation ? Tu aurais pu mettre liste->premier à NULL pour signifier qu'il n'y a pas d'élément dans la liste.

          EDIT :

          Terence01 a écrit:

          Si j'appelle la fonction afficherListe(), j'ai une belle erreur de segmentation :).

          Comme tu l'as vidée, il n'y a pas d'élément à afficher, et liste->premier pointe à un emplacement qui n'est plus valide, d'où l'erreur de segmentation.

          -
          Edité par rouIoude 23 avril 2024 à 18:33:00

          • Partager sur Facebook
          • Partager sur Twitter
          ...
            23 avril 2024 à 18:30:27

            Ok, je vais tester de faire un free de ce malloc aussi alors.

            Pour l'élément à 0, c'est comme cela que ça a été défini dans le cours, mais oui ça paraît logique de mettre tout à NULL.

            NULL et 0 ne sont pas censé être la même chose ?

            • Partager sur Facebook
            • Partager sur Twitter
              23 avril 2024 à 18:36:12

              Hello,

              Ta fonction de destruction est inutilement compliquée:

              void destructionListe(Liste *liste) {
                  while(liste->premier) {
                      Element *tmp=liste->premier->suivant;
              		printf("Suppression de %d\n", liste->premier->nombre);
              		free(liste->premier);
              		liste->premier=tmp;
                  }
              }

              Et encore, la ligne 4 n'est qu'informative. Ça a l'avantage que liste->premier sera NULL à la fin.

              rouIoude a écrit:

              PS : Est-ce bien utile de créer un élément à 0 lors de l'initialisation ? Tu aurais pu mettre liste->premier à NULL pour signifier qu'il n'y a pas d'élément dans la liste.

              Tu as entièrement raison, c'est le cours de oc qui n'est pas correct.



              -
              Edité par edgarjacobs 23 avril 2024 à 18:38:00

              • Partager sur Facebook
              • Partager sur Twitter

              On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                23 avril 2024 à 18:37:52

                Même si la plus part du temps NULL vaut 0,  On utilise NULL pour les pointeurs. C'est la règle.
                • Partager sur Facebook
                • Partager sur Twitter
                ...
                  23 avril 2024 à 19:25:32

                  Terence01 a écrit:

                  Si j'appelle la fonction afficherListe(), j'ai une belle erreur de segmentation :).

                  Il faudrait peut-être adapter cette fonction pour qu'elle gère correctement les listes vides.

                  -
                  Edité par robun 24 avril 2024 à 13:05:06

                  • Partager sur Facebook
                  • Partager sur Twitter
                    24 avril 2024 à 12:33:50

                    edgarjacobs a écrit:

                    Hello,

                    Ta fonction de destruction est inutilement compliquée:

                    void destructionListe(Liste *liste) {
                        while(liste->premier) {
                            Element *tmp=liste->premier->suivant;
                    		printf("Suppression de %d\n", liste->premier->nombre);
                    		free(liste->premier);
                    		liste->premier=tmp;
                        }
                    }

                    Et encore, la ligne 4 n'est qu'informative. Ça a l'avantage que liste->premier sera NULL à la fin.

                    rouIoude a écrit:

                    PS : Est-ce bien utile de créer un élément à 0 lors de l'initialisation ? Tu aurais pu mettre liste->premier à NULL pour signifier qu'il n'y a pas d'élément dans la liste.

                    Tu as entièrement raison, c'est le cours de oc qui n'est pas correct.



                    -
                    Edité par edgarjacobs il y a environ 15 heures

                    Bonjour,

                    Merci pour les réponses,

                    En effet ta fonction est nettement plus simple et compréhensible !

                    Je m'obstinais à vouloir créer des éléments pour bosser dessus alors que ce n'est pas utile.

                    Je me rends compte que ça m'obligeait aussi à rajouter la ligne "liste->premier=elementToDelete;" pour avoir ma liste à NULL à nouveau.

                    ça veut dire que mon premier élément dans liste ne changerait jamais et aurait gardé l'adresse de départ ?

                    Tu utilises dans ton code cette instruction, je ne connaissais pas ceci => liste->premier->suivant;

                    On ne le voit pas dans le cours d'OpenClassroom, ici ça veut dire qu'on pointe sur l'adresse suivante du premier élément ?

                    Maintenant que liste->premier = NULL, plus besoin de faire un free(liste->premier) à la toute fin c'est ça ?

                    On considère donc à ce moment que la liste est détruite ? ou juste vidé comme disait rouloude ? Pour moi il reste 1 élément encore même s'il est à NULL.

                    Est-ce qu'on peut rester dans cette état ou faut-il obligatoirement clôturer cette liste ? (Désolé si les questions sont chiantes, je préfère demander que de le laisser traîner des variables n'importe où)

                    J'ai corrigé aussi l'initialisation, j'ai mis uniquement liste->premier = NULL.

                    Je trouvais ça "normal" (bien que cela ne le soit pas) que Element->nombre= 0; dans le code vu qu'on remplit un int, je n'avais pas fait attention que c'était un pointeur.

                    Pour la fonction afficherListe(), je l'ai modifié aussi, désormais si liste->premier = NULL alors j'affiche que la liste est vide

                    void afficherListe(Liste *liste)
                    {
                        if(liste == NULL)
                        {
                            exit(EXIT_FAILURE);
                        }
                    
                        if(liste->premier==NULL)
                        {
                            printf("La liste est vide\n");
                        }
                    
                        Element *actuel = liste->premier;
                    
                        while (actuel !=NULL)
                        {
                            printf("%d, adresse -> ", actuel->nombre);
                            printf("%p\n", actuel);
                            actuel = actuel->suivant;
                        }
                        printf("NULL\n");
                        
                    }



                    • Partager sur Facebook
                    • Partager sur Twitter
                      24 avril 2024 à 13:40:54

                      Terence01 a écrit:

                      On considère donc à ce moment que la liste est détruite ? ou juste vidé comme disait rouloude ? Pour moi il reste 1 élément encore même s'il est à NULL.

                      Ici, elle est vide, mais pas détruite car le pointeur maListe pointe toujours sur un espace mémoire valide de type Liste (créé avec ton malloc ligne 19) Elle n'a plus d'élément car son champ premier est NULL, mais tu pourrais encore lui rajouter des éléments avec la fonction insertion

                      Terence01 a écrit:

                      Est-ce qu'on peut rester dans cette état ou faut-il obligatoirement clôturer cette liste ? (Désolé si les questions sont chiantes, je préfère demander que de le laisser traîner des variables n'importe où)

                       Ça c'est un choix, il n'y a aucune obligation, ça dépend de ce que tu veux en faire. Il faut bien sur nommer les fonction correctement ex : detruireListe , viderListe.

                      -
                      Edité par rouIoude 24 avril 2024 à 13:45:24

                      • Partager sur Facebook
                      • Partager sur Twitter
                      ...
                        24 avril 2024 à 15:51:11

                        Ok donc là, ma structure  est vide.

                        Je viens d'essayer de rajouter dans mon main un free(maListe)

                        Avant de faire ce free(maListe), maListe->premier vaut NULL, après le free, il prend une adresse en mémoire, pourquoi ?

                        Cela ne suffit pas pour détruire cette liste ? Comment doit-on si prendre ?

                        Vider la liste ça ok, c'est réglé mais juste pour la curiosité je veux comprendre comment la supprimer purement et simplement (pour éviter les fuites mémoires aussi)

                        int main(int argc, const char* argv[])
                        {
                            int i=0;
                        
                            Liste *maListe = initialisation();
                            // printf("Adresse maListe => %p\n", maListe->premier);
                            insertion(maListe, 35);
                            insertion(maListe, 42);
                            insertion(maListe, 69);
                            insertion(maListe, 13);
                            insertion(maListe, 21);
                            insertion(maListe, 27);
                        
                            printf("Il y a actuellement %d elements dans ma liste\n", maListe->i_nbElements);
                            printf("Il y a actuellement %d elements dans ma liste selon ma fonction Taille liste\n", TailleListe(maListe));
                        
                            afficherListe(maListe);
                        
                        
                            // Destruction de toute la liste
                            printf("Destruction de la liste\n");
                            destructionListe(maListe);
                            
                        
                            printf("Il y a actuellement %d elements dans ma liste selon ma fonction Taille liste\n", TailleListe(maListe));
                        
                            free(maListe);
                            printf("adresse => %p\n", maListe->premier);
                            printf("\n\n");
                            afficherListe(maListe);
                        
                            if(maListe->premier==NULL)
                            {
                                printf("Ma liste est vide\n");
                            }
                        
                            return 0;
                        }

                        Autre question aussi, dans ma structure Liste, j'ai rajouté un int i_nbElements pour compter le nombre d'éléments dans la liste.

                        typedef struct Liste Liste;
                        struct Liste
                        {
                            int i_nbElements;
                            Element *premier;
                        };

                        Lors de l'initialisation dans la fonction initialisation(), j'ai voulu l'initialiser à NULL aussi mais ça me sort un warning

                        warning: assignment to 'int' from 'void *' makes integer from pointer without a cast

                        Par contre si je l'initialise avec un 0, aucun warning.

                        J'avoue que je suis un peu perdu là car pour l'assignement j'utilise "->" donc je pointe bien sur ma structure avec ça. Dans les messages précédents, on me disait d'initialiser avec NULL mais là, le compilateur n'aime pas trop on dirait.

                        Liste *initialisation()
                        {
                            Liste *liste = malloc(sizeof(*liste));
                            Element *element = malloc(sizeof(*element));
                        
                            
                        
                            if(liste == NULL || element == NULL)
                            {
                                exit(EXIT_FAILURE);
                            }
                        
                            // element->nombre=NULL;
                            // element->suivant=NULL;
                            liste->premier=NULL;
                            // initialisation du nombre d'éléments à 0
                            liste->i_nbElements = NULL;
                        
                            return liste;
                        }





                        • Partager sur Facebook
                        • Partager sur Twitter
                          24 avril 2024 à 17:01:01

                              free(maListe);
                              printf("adresse => %p\n", maListe->premier);

                          Après avoir fait  free(maListe) le pointeur maListe n'est plus valide. Tu n'as plus le droit de le déréférencer, c'est à dire d’accéder où il pointe. Donc faire maListe->premier est une erreur qui peut provoquer un comportement indéterminé comme une erreur de segmentation ou autre...

                          Terence01 a écrit:

                          Avant de faire ce free(maListe), maListe->premier vaut NULL, après le free, il prend une adresse en mémoire, pourquoi ?

                          Une fois la mémoire libéré, les valeurs qu'elle contient peuvent être n'importe quoi.
                          Quand tu as vidé ta liste et que tu as fait le free(maListe) toutela mémoire que tu avais alloué est libéré, on peut considéré que ta liste est détruite.
                          Le pointeur, lui il existe toujours, mais il n'est plus valide, tu ne doit plus le déréférencer en l'état.
                          Une solution pour le marquer non valide est de le mettre à NULL après avoir libéré la mémoire sur laquelle il pointait, comme cela tu peut le tester avant de l'utiliser.  

                          Terence01 a écrit:

                          dans ma structure Liste, j'ai rajouté un int i_nbElements pour compter le nombre d'éléments dans la liste.

                          Lors de l'initialisation dans la fonction initialisation(), j'ai voulu l'initialiser à NULL aussi mais ça me sort un warning

                          i_nbElements est un entier de type int et un int ça initialise avec un entier : ici avec 0. NULL c'est pour les pointeurs (Je te l'ai dit plus haut). 

                          -
                          Edité par rouIoude 24 avril 2024 à 17:17:44

                          • Partager sur Facebook
                          • Partager sur Twitter
                          ...
                            24 avril 2024 à 17:02:10

                            Quand tu initialises ta liste, tu as un pointeur: liste, qui te donne accès aux éléments de ta structure.

                            Il faut alors initialiser les éléments en fonction de leur type:

                            • premier est de type pointeur, donc liste->premier est initialisé avec NULL (*)
                            • i_nbElement est de type int, donc liste->i_nbElement est initialisé avec 0 (zéro)

                            (*) qui est souvent un #define NULL ((void *)0)

                            -
                            Edité par edgarjacobs 24 avril 2024 à 17:12:48

                            • Partager sur Facebook
                            • Partager sur Twitter

                            On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                              24 avril 2024 à 18:44:40

                              Terence01 a écrit:

                              [...] je ne connaissais pas ceci => liste->premier->suivant;

                              [...] ça veut dire qu'on pointe sur l'adresse suivante du premier élément ?

                              Y a pas de "on" qui pointe.

                              Il y a une variable liste, qui dans ce contexte est en réalité un pointeur, de type struct Liste *. 

                              (Remarque : C'est un peu confusionnant de nommer "liste" un truc qui contient en fait l'_adresse_ d'une liste. Enfin bref on va faire avec)

                              Bref, la variable liste est un pointeur,  *liste désigne la zone mémoire pointée par liste, qui est - si le pointeur a bien été initialisé etc- un struct liste, qui a un champ (une donnée membre, dans le jargon officiel de C) qui s'appelle premier

                              On peut le noter de deux façons

                              • (*liste).premier
                              • liste->premier

                              Pour la première il faut mettre les parenthèses, parce que si on écrit *a.b,  ça se lit *(a.b) selon les règles de priorité des opérateurs, c'est à dire que a est une structure, b un de ses champs, qui est un pointeur, et *(a.b) c'est le truc désigné par ce pointeur.

                              Bref (bis) dans liste->premier->suivant

                              • liste->premier est lui aussi un pointeur (de type struct Element *)

                              • liste->premier->suivant est le champ "suivant" de ce qu'il pointe.

                              Conseil : Faut faire des dessins, ça évite de se prendre la tête. 

                              • Les boites représentent des zones mémoires (octets contigus), 
                              • une flèche indique que le contenu d'une boite est l'adresse d'une autre boîte, pour voir laquelle suivez la flèche.
                              • Et on met une croix quand c'est NULL (la constante qui vaut zéro et indique que le pointeur NE DÉSIGNE PAS une zone en mémoire)

                               

                              PS

                              PS : une des difficultés de la programmation est de bien choisir les noms de variables, des fonctions etc pour se simplifier la vie ensuite. 

                              Le nombre d'éléments d'une liste, c'est ce qu'on appelle sa taille, alors autant l'appeler comme ça

                              struct Liste {
                                 int             taille;
                                 struct Element *premier;
                              };
                              


                              (et normalement c'est jamais négatif, et ça pourrait être de type unsigned int)

                              ---

                              PS: ce cours est mal rédigé. Quand on voit par exemple

                              > Il suffit de faire pointer le dernier élément de la liste vers NULL, c'est-à-dire de mettre son pointeur suivant à NULL :

                              1) Ben non, le dernier élément ne pointe pas vers NULL.

                              a) parce que le dernier élément n'est pas un pointeur, mais une structure, et une structure ça pointe pas. C'est le champ suivant qui est un pointeur susceptible de pointer sur quelque chose


                              b) NULL n'est pas une donnée en mémoire, alors on ne peut pas pointer dessus (= contenir son adresse). C'est une valeur spéciale [1] que par convention on utilise pour signaler l'absence d'une adresse normale [2].

                              2) Ce qu'on veut dire, c'est que le dernier élément de la liste, par définition, n'a pas de successeur. Et pour représenter ça, on a dans le dernier pointeur la valeur spéciale NULL.

                              Notes:

                              [1] qui vaut 0, scoop.

                              [2] alors que l'adresse 0 existe en réalité. Sauf qu'elle se trouve (sur un système moderne) à un endroit de la mémoire où on ne peut en principe pas accéder (protections mémoire etc).

                              -
                              Edité par michelbillaud 25 avril 2024 à 12:17:52

                              • Partager sur Facebook
                              • Partager sur Twitter
                                25 avril 2024 à 14:57:41

                                Hello messieurs,

                                @rouloude Ok je comprends mieux maintenant, après le free(maListe), je le mets à NULL et je ne le touche plus, c'est mieux.

                                @edgarjacobs je vois ce que tu veux dire, en fait je me mélangeais les pinceaux au moment de l'initialisation, pour moi dans la fonction initalisation() quand j'utilisais "liste" vu que c'est un pointeur, on ne pouvait pas mettre de valeur à i_nbElement et donc on devait mettre NULL car je considérais du fait que "liste" soit un pointeur tout devait être à NULL mais non.

                                Si j'ai bien compris, liste sera un pointeur donc on utilisera "->" pour assigner les valeurs à chaque donnée membre et après on remplira ces données membres en fonction de leur type :

                                int, long, double ou autre = 0

                                pointeur = NULL

                                @michelbillaud wouah fallait pas se donner tant de mal :D, merci pour les schémas et le rappel sur les structures, c'est vrai que c'est plus parlant. je comprends mieux comment accéder à chaque variable/champ

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  25 avril 2024 à 16:11:40

                                  En fait, il n'y a pas aucune raison qui oblige à ce que les "struct Liste" soient allouées dynamiquement, comme c'est fait dans ce cours écrit par un pizzaïolo

                                  Liste *maListe = initialisation();


                                  On pourrait très bien écrire

                                  Liste maListe = LISTE_VIDE;
                                  

                                  avec

                                  const Liste LISTE_VIDE = {
                                       .premier = NULL;
                                  };
                                   

                                  et là, la variable maListe est de type Liste. Comme son nom l'indique. Pas un pointeur de Liste.

                                  ---

                                  Ensuite, pour agir sur cette liste, on passe son adresse aux fonctions inserer etc.

                                  ajouter_au_debut(& maListe, 33);
                                  

                                  puisque ça peut modifier la structure (changer le champ premier), et que C ne connaît que le passage par valeur.

                                  Bonus : la fonction qui vide une liste (en libérant tous les maillons)

                                  void vider_liste( Liste *l) 
                                  {
                                     while (l->premier != NULL) {
                                        enlever_premier(l);          // décomposer le travail...
                                     }
                                  }
                                  

                                  va laisse une structure propre, dont le champ premier est à NULL. Alors que sa fonction destruction_liste ne libère pas la structure Liste allouée lors de l'initialisation, et de toutes façons ne remet pas le pointeur maListe à NULL, le pointeur est donc invalide.

                                  Bref, c'est le souk.

                                  ---

                                  Les causes probables de cette erreur de conception du cours

                                  • le type a décidé que faire comme si on programmait en Java ou en C# (où les variables sont des références)
                                  • ça l'emmerde de mettre des &

                                  Dommage, parce que là on programme en C, et on est en plein dans un chapitre où il faut donner une vision claire de ce qu'est une donnée, une adresse, un pointeur, un déreférencement etc.  Et si on commence à les planquer sous le tapis, c'est pas comme ça qu'on va apprendre.

                                  En tout cas, si c'est un choix conscient, il aurait fallu l'expliciter.

                                  -
                                  Edité par michelbillaud 25 avril 2024 à 16:21:08

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    25 avril 2024 à 16:41:16

                                    J'ai repris ce cours de langage C, je l'avais déjà fait il y a quelques années sans le terminer mais c'est vrai que je l'ai trouvé différent et comme si certaines parties avaient été enlevées. Après je me suis dit autant terminer ce cours pour avoir une première base et ensuite fouiller un peu plus pour des contenus plus fournis.

                                    Après moi j'ai rien contre les pizzaïolos :D

                                    Juste je reviens sur ce code :

                                    const Liste LISTE_VIDE = {
                                         .premier = NULL;
                                    };
                                    Pas besoin de mettre le mot clé struct ?
                                    et ce "." devant premier, ça signifie quoi ? on ne met pas de type ici ?
                                    J'ai jamais vu la construction d'une structure comme ça.
                                    En fouillant rapidement sur internet, ce cours => https://www.mbillaud.fr/docs/Pointeurs/pointeurs.pdf est de vous ?
                                    Je crois que je vais le lire, ça m'aidera sûrement
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      25 avril 2024 à 17:31:17

                                      Terence01 a écrit:

                                      je reviens sur ce code :

                                      const Liste LISTE_VIDE = {
                                           .premier = NULL;
                                      };
                                      Pas besoin de mettre le mot clé struct ?
                                      et ce "." devant premier, ça signifie quoi ? on ne met pas de type ici ?

                                      Le type c'est Liste, tel qu'il est défini dans ton code. 

                                      Pas besoin de struct puisque tu a défini Liste avec un typedef (dans ton code)

                                      Ici on a donc affaire à l'initialisation d'une constante nommé LISTE_VIDE de type Liste.

                                      Le point suivi de premier veut dire que l'on initialise le champ premier (à NULL ici).

                                      /!\ Il y a une erreur dans ce code, il ne faut pas le point virgule après le NULL.

                                      Tu aurais pu l'initialiser de cette façon :

                                      const Liste LISTE_VIDE = {NULL};

                                      Mais là il faut initialiser les champs dans l'ordre. (ici il n'y en a qu'un).

                                      -
                                      Edité par rouIoude 25 avril 2024 à 17:38:19

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                      ...
                                        25 avril 2024 à 17:38:37

                                        Les messages se sont croisés

                                        > Il y a une erreur dans ce code,

                                        ah mince.

                                        ---

                                        > Pas besoin de mettre le mot clé struct ?

                                        Si avant on a déclaré

                                        typedef struct Liste Liste;

                                        ce n'est pas la peine.


                                        En analysant une déclaration

                                           Type var;

                                        le compilateur voit que ça ne commence pas par struct (ou union) et va regarder dans la table des typedef qui ont été déclarés.

                                        Exemple pathologique mais parfaitement valide :

                                        struct X { int i; };
                                        typedef char* X;
                                        
                                        void test() {
                                        	struct X n;
                                        	X s;
                                        }
                                        
                                        


                                        on définit  deux types distincts struct X et X. Distincts et incompatibles... (Une idée stupide dans la conception du langage C) Faut surtout pas faire ça, pour des raisons évidentes de lisibilité, mais c'est pour montrer que techniquement, les deux types sont distincts.

                                        La notation ci-dessus c'est pour l'initialisation des structures lors de la déclaration d'une variable. Les points indiquent des _désignateurs_ de champs

                                        struct Point {
                                           float x, y;
                                        };
                                        
                                        // à l'ancienne
                                        
                                        struct Point p1 = {  1.2, 3.4};
                                        
                                        // avec désignateurs 
                                        
                                        struct Point p2 = {  
                                            .x = 1.2,
                                            .y = 3.4
                                        };
                                        
                                        // dans le désordre si on veut
                                        
                                        struct Point p3 = {  
                                            .y = 3.4,
                                            .x = 1.2
                                        };
                                        
                                        // ceux qui ne sont pas cités sont à 0, 0.0 ou NULL selon type
                                        
                                        struct Point origine = {
                                           // .x = 0.0;
                                           .x = 0.0         // correction : pas de point virgule
                                        };
                                        

                                        On peut aussi s'en servir dans une affectation. Application

                                        Element *e;
                                        
                                        ...
                                        
                                        *e = (Element){ .nombre = n, .suivant = liste->premier};


                                        Lire : on met dans *e un Element dont le champ nombre contient n, et suivant (etc)

                                        > c'est de vous ?

                                        j'assume :)   C'est rédigé un peu raide, j'étais de mauvais poil ce jour-là, parce que j'en avais marre de voir de lire des tutoriels foireux. En gros, ça correspondait à des trucs que je racontais en 2ieme année d'IUT info dans un petit cours de C (dont je ne suis pas spécialiste), du temps où j'étais censé travailler pour être payé.

                                        On peut se tutoyer quand même.

                                        -
                                        Edité par michelbillaud 26 avril 2024 à 7:38:18

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          25 avril 2024 à 17:56:18

                                          @rouloude Ok merci pour les explications, donc le ".mavariable" permet d'initialiser la bonne variable avec la valeur souhaitée comme les paramètres dans les fonctions en soi.

                                          @michelbillaud Super les explications, oui on peut se tutoyer, ça sera plus simple

                                          je vois toutes les manières différentes d'initialiser une structure merci.

                                          Par contre comme disait rouloude, le ";" ne doit pas être présent ?

                                          Donc ici pour struct Point Origine ligne 25, le point-virgule devrait disparaître ?

                                          Pour le PDF, tant que c'est compréhensible, moi ça me va ^^

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            25 avril 2024 à 18:06:17

                                            Et on peut même utiliser cette écriture dans l'appel d'une fonction. Exemple: la fonction TTF_RenderText_Blended() attend en troisième paramètre une structure de type SDL_Color. Et moi, cette structure, elle m'enquiquine, car j'ai pris l'habitude de coder les couleurs rgb sous la forme 0xrrggbb. J'ai donc écrit trois macros R, G et B qui extraient la couleur voulue à partir de la couleur codée 0xrrggbb. Et pour l'appel à la fonction, je fais
                                            SDL_Surface *s=TTF_RenderText_Blended(...., ...., 
                                                           (SDL_Color){R(0xrrggbb), G(0xrrggbb), B(0xrrggbb), SDL_ALPHA_OPAQUE});

                                            Et je n'emploie pas la notation avec . car je n'ai aucune envie de m'encombrer l'esprit avec le nom des variables de la structure SDL_Color.

                                            Edit: la couleur est un #define, ex: #define BKG_COLOR 0xabcdef

                                            -
                                            Edité par edgarjacobs 25 avril 2024 à 18:12:13

                                            • Partager sur Facebook
                                            • Partager sur Twitter

                                            On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                                              25 avril 2024 à 18:28:36

                                              Ah sympa comme macro !

                                              Le SDL_Color entre parenthèses c'est ce qu'on appelle un cast c'est bien ça ? Pas besoin de mettre un nom de structure entre (SDL_Color) et les accolades {} ?

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                25 avril 2024 à 18:54:29

                                                Oui, c'est bien un cast. Il indique que ce qui va suivre doit être compilé comme étant une variable de type SDL_Color. Et SDL_Color est un typedef: voir ici

                                                Si les macros t'intéressent:

                                                #define R(x)	((x)>>16)
                                                #define G(x)	(((x)>>8) & 0xff)
                                                #define B(x)	((x) & 0xff)
                                                


                                                Ce qu'il y a de bien avec ce système, c'est que tout est réglé à la (pré)compilation, aucun calcul n'est fait à l'exécution (edit: si x est une constante, bien sur).

                                                -
                                                Edité par edgarjacobs 25 avril 2024 à 18:59:22

                                                • Partager sur Facebook
                                                • Partager sur Twitter

                                                On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                                                  26 avril 2024 à 10:32:20

                                                  On peut pousser l'usage des macros plus loin en définissant

                                                  #define to_SDL_Opaque_Color(color)      \
                                                         (SDL_Color){R(color),           \ 
                                                                     G(color),           \
                                                                     B(color),           \
                                                                     SDL_ALPHA_OPAQUE}
                                                  

                                                  et avec ça, dukou dukou, c'est la fête

                                                  SDL_Surface *s = TTF_RenderText_Blended(
                                                                        ...., 
                                                                        ....,  
                                                                        to_SDL_Opaque_Color(my_favorite_color)
                                                                   );
                                                  


                                                  Bon principe : on introduit des abstractions pour se simplifier la vie ensuite.

                                                  Histoire de chipoter [1], je dirais que c'est une déclaration de "compound literal" (une donnée composée définie et initialisée "sur place", en plein milieu d'une expression)  plutôt qu'un cast [2].

                                                  ref : brouillon du standard C23 p 78 https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf

                                                  6.5.3.6 Compound literals
                                                  Syntax
                                                  compound-literal:
                                                        (storage-class-specifiersopt  type-name)  braced-initializer

                                                  les storage-class-specifiers étant optionnels, on tombe sur

                                                              (type-name) braced_initializer

                                                  Soit : un type entre parenthèses, suivi d'un initialisateur avec des accolades.


                                                  Exemples significatifs

                                                  int *p = (int []){2, 4};

                                                  ou, avec une fonction drawline qui prend l'adresse de deux points

                                                  drawline( &(struct point){.x=1, .y=1},
                                                            &(struct point){.x=3, .y=4});
                                                  


                                                  PS: c'est hyper galère de retrouver quelque chose dans le standard :/


                                                  [1] une réputation de gros pénible, faut l'entretenir

                                                  [2] parce que bon, on ne peut pas "caster" un record, par exemple. Un cast, c'est forcément pour un type scalaire (nombre, pointeur...), page 83-84


                                                  6.5.5Cast operators
                                                     Syntax
                                                  cast-expression:
                                                                unary-expression
                                                         (type-name) cast-expression

                                                  Constraints
                                                    Unless the type name specifies a void type, the type name shall specify atomic, qualified, or unqualified scalar type, and the operand shall have scalar type.

                                                  -
                                                  Edité par michelbillaud 26 avril 2024 à 11:01:19

                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    26 avril 2024 à 12:23:15

                                                    Le décalage de bits pour avoir la bonne valeur pour la couleur c'est pas mal, je retiens !

                                                    Pour les compound literal intéressant, si je comprends bien on parle de :

                                                    - "compound literal" pour les structures définies et initialisés

                                                    - cast pour toute autre type de variable (int, long, double, float, char, string tout ça signed ou unsigned) déjà initialisés et dont on veut juste changer le type sur le moment

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      26 avril 2024 à 14:03:14

                                                      Faudrait regarder dans les discussions du comité de normalisation, ça serait pas étonnant que le "braced initializer" (introduit dans quelle version du langage) soit présenté comme une généralisation du cast (présent dans le C d'origine)

                                                      En tout cas, ça donne plusieurs manières de faire avec les scalaires

                                                      void fun() {
                                                      	float f;
                                                      	f = (float) 1;
                                                      	f = (float) {1};	
                                                      	f = (float) {1.23};
                                                      	int i;
                                                      	i = f;
                                                      	i = (float) f;
                                                      	i = (float) { f };
                                                      }



                                                      -
                                                      Edité par michelbillaud 26 avril 2024 à 14:04:30

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        26 avril 2024 à 17:16:46

                                                        michelbillaud a écrit:

                                                        On peut pousser l'usage des macros plus loin en définissant

                                                        #define to_SDL_Opaque_Color(color)      \
                                                               (SDL_Color){R(color),           \ 
                                                                           G(color),           \
                                                                           B(color),           \
                                                                           SDL_ALPHA_OPAQUE}
                                                        

                                                        J'aime bien l'idée. Merci à toi, ça va aérer le code et en faciliter la lecture. Que n'y ais-je pensé moi-même !

                                                        -
                                                        Edité par edgarjacobs 26 avril 2024 à 17:31:53

                                                        • Partager sur Facebook
                                                        • Partager sur Twitter

                                                        On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                                                        Liste chainée, destruction tout est détruit ?

                                                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                        • Editeur
                                                        • Markdown