Pile et listes chaînées

Bonjour je voulais savoir comment on fait pour empiler une chaine de caractère dans une pile
Exemple: chaine={AGFGDG}
Je veux empiler ceci sur une pile mais je sais pas comment faire, pouvez vous m'aider svp

Je sais comment empiler un caractère mais un bloc contenant une chaine de caractère ,je sais pas

9 réponses

  • Votes
  • Date
  • Date décroissante
Résumé de la discussion

L’objectif est d’empiler une chaîne de caractères dans une pile en C alors que la pile existante gère des caractères simples et qu’une liste chaînée est déjà utilisée. Les échanges proposent soit d’étendre la pile pour accepter des chaînes soit de stocker des pointeurs sur char ou des structures copiables, avec des choix entre copie, référence et gestion mémoire. Des approches génériques avec typedef Donnee et fonction empiler_donnee, ou l’ajout d’un type étiqueté pour différencier chaînes, caractères et nombres, permettent d’adapter le code sans remodeler entièrement la pile. En parallèle, des précisions sur le dépilage et la gestion du type lors du dépôt montrent l’importance d’un design cohérent pour éviter les erreurs.

Bobot (l’IA à votre service)
  1. Contributeur
    Bonjour Theo_0055,

    Une "pile" peut être implémentée de plusieurs façons en C.

    Si tu disposes d'un code que tu as réalisé et qui gère une pile pour un caractère, il est possible que ton code puisse être adapté pour gérer des chaînes de caractères.

    Utilises-tu une liste chaînée, un simple tableau, autre chose ?

    Dal
    1. J’utilise une liste chaîné et une pile

      Les caractères et chaînes de caractère sont dans ma liste chaîné et je veux empiler cela sur ma pile
      1. Contributeur
        Dans ton code, tu dois donc certainement définir une struct, et avec ce type struct que tu attribues aux maillons de ta liste chaînée, tu dois stocker :
        • un moyen d'accéder à l'élément contenu dans le maillon
        • un pointeur sur l'élément suivant


        Peux-tu poster ton type struct avec lequel tu gères une pile de char, stp ?

        Tu dois aussi avoir des fonctions de manipulation de la pile, qui te permettent d'ajouter un élément, de retirer, retourner le nombre d'éléments de la pile, etc. Mais voyons déjà quelle tête a ton type.
    2. typedef struct Element Element;
      struct Element{
          int nombre;
          Element *suivant;
      };
      
      typedef struct Pile Pile;
      struct Pile{
          Element *sommet;
      };
      
      void empiler_char(Pile *pile,char&  carac){
         Element *nouveau = malloc(sizeof(*nouveau));
          if (pile == NULL || nouveau == NULL)
              exit(EXIT_FAILURE);
              
          nouveau->nombre = carac;
          nouveau->suivant = pile->sommet;
          pile->sommet = nouveau;
      }
      
      
      1. Maintenant,je veux créer une fonction void empiler_chaineCarac qui empile une chaine de caractere
        1. J’ai une petite piste esceque on doit faire tant que on a pas fini de parcourir la chaîné de caractère,on ajoute le caractère dans la pile en faisant appel à empiler_char?
          1. Contributeur
            Salut Theo_0055,

            OK, 2 remarques sur ton code avant d'entrer dans le vif du sujet : ton membre nombre est de type int, alors que tu veux y stocker des char ; deuxièmement dans ton prototype
            void empiler_char(Pile *pile,char&  carac)
            la partie
            char&  carac
            n'est pas du C (cela ressemble à une référence C++).

            Tu peux rendre ton code plus générique en mettant le type géré dans un typedef.

            Ton idée dans ce que tu postes est de stocker la donnée dans le maillon, mais en utilisant directement le type char dans les paramètres de ta fonction. En faisant cela, ton code d'ajout devient spécifique aux données traitées.

            Une autre approche serait de rendre ton code plus générique pour que ta pile puisse traiter n'importe quel type de données avec un minimum de changements.

            typedef char Donnee; 
            
            typedef struct Element Element;
            struct Element{
                Donnee donnee;
                Element *suivant;
            };
            
            void empiler_donnee(Pile *pile, Donnee donnee){
                if (pile == NULL)
                    exit(EXIT_FAILURE);
                Element *nouveau = malloc(sizeof(*nouveau));
                if (nouveau == NULL)
                    exit(EXIT_FAILURE);
                    
                nouveau->donnee = donnee;
                nouveau->suivant = pile->sommet;
                pile->sommet = nouveau;
            }
            


            Ce code devrait gérer une pile de char en copiant le char passé en tant que type Donnee dans la pile.

            Ensuite, il suffit de changer
            typedef char Donnee; 
            en
            typedef char * Donnee; 
            pour gérer une liste de pointeurs sur char où tu stockes des pointeurs sur char passés à la fonction empiler_donnees().

            Cela fonctionne car, dans
            empiler_donnee
            , l'opérateur d'affectation dans
            nouveau->donnee = donnee;
            va simplement affecter un pointeur.

            Si tu veux que ta liste gère une copie des données passées, stockée sous la forme de tableau de char dans la pile, alors tu peux le faire aussi, mais il faut ruser car l'opérateur d'affectation
            =
            ne permet pas l'affectation de types tableaux en langage C. En revanche, il peut gérer les struct. Donc, en rusant, tu peux réutiliser le même code en changeant ton type Donnee comme ceci :

            #define MAX_LEN 500
            typedef struct Donnee Donnee; 
            struct Donnee {
                char str[MAX_LEN];
            };
            


            Dans
            empiler_donnee()
            , l'opérateur d'affectation à la ligne
            nouveau->donnee = donnee;
            va alors copier le contenu de la struct (en faisant une opération équivalente à un memcpy()).
            1. 1.Pour le char& carac en fait c’était une erreur de frappe c’était char carac tout court sans &

              2.Si j’ai bien compris
              typedef char Donnee donnee =définition du type Donnee (qui est un char)

              3.votre dernier Donnee il est censé gérer les chaînes de caractères c’est ça?

              4.J’ai essayé plusieurs fois d’écrire un code pour gérer n’importe quel type de Donnee (char,int,double,..) mais le problème je sais pas comment m’y prendre
              Car créer à chaque fois une fonction qui dépile int,une autre des char.. c’est un peu répétitif et j’aimerais créer une seule qui gère tous les types
              1. Contributeur
                2.Si j’ai bien compris
                typedef char Donnee donnee =définition du type Donnee (qui est un char)


                oui

                3.votre dernier Donnee il est censé gérer les chaînes de caractères c’est ça?

                J'en au proposé deux qui sont susceptibles permettre au code gérant la liste de gérer des chaînes de caractères. Le dernier est l'un d'eux.

                4.J’ai essayé plusieurs fois d’écrire un code pour gérer n’importe quel type de Donnee (char,int,double,..) mais le problème je sais pas comment m’y prendre
                Car créer à chaque fois une fonction qui dépile int,une autre des char.. c’est un peu répétitif et j’aimerais créer une seule qui gère tous les types


                Je viens de t'indiquer une façon de faire, le seul changement à faire étant sur le typedef qui définit le type de tes données.
              2. @[Dal]Par exemple si je met
                typedef char Donnee
                typedef int Donnee


                Ça peut générer des char et des int en même temp

                Par exemple si empile_donnee ,on lui donne un int en 2 eme paramètre ça va marcher,de même que pour char?
              3. Contributeur
                @Theo_0055Tu ne peux pas écrire cela, car cela revient à tenter de redéfinir un alias de type. Ma suggestion d'une modification minimale de ton code pour gérer un autre type de données que le char était que tu définisses ton alias de type Donnee avec le type de données que tu veux gérer dans ta pile. Ton code générique gérera ce type de données.

                Maintenant tu sembles avoir un besoin différent. Si dans chaque maillon tu veux stocker un char et un int, tu peux créer un type Donnee qui soit une struct avec ces deux membres. Si dans chaque maillon tu veux stocker un char ou un int, tu peux créer une struct qui contient une union dans l'un de ses membres avec ces deux types et l'autre membre qui stocke le type (la façon courante de le faire est avec un enum).

                Une autre façon de rendre ta pile générique est de stocker des
                void *
                dans ta pile et laisser le soin aux fonctions appelantes de passer le pointeur void et le transtyper lorsqu'il est récupéré. Tu avais posté un message où tu posais des questions sur les
                void *
                et qui a été supprimé (auquel j'avais répondu... ce qui fait que ma réponse a aussi été supprimée...). Cependant, tu ne pourras pas mélanger les types dans un même programme, à moins de stocker aussi dans une struct l'indication du type, car sinon la fonction appelante ne saura pas comment exploiter le pointeur.
              4. @[Dal]Ah bon vous m’avez répondu,pourtant j’ai rien vu.je pensais que ma réponse a été ignoré d’où ma suppression
              5. @[Dal]Par exemple ,esce qu’on peut écrire
                
                typedef void * Donnee; 
                
                typedef struct Element Element;
                struct Element{
                    Donnee donnee;
                    Element *suivant; 

                Et on met
                 void empiler_donnee(Pile *pile, Donnee donnee){ 
                
                
                
                


                Le void * va pointer sur n’importe quel type de donnée

                Bon après vous avez dit que ça peut causer problème si le type n’est pas indiqué
                Comment retranstyper?

                Le void * ,m’a question est plus une question de curiosité qu’autre chose car en classe on l’a jamais utilisé
            2. Contributeur
              Voilà une illustration d'une struct comprenant une union et un membre permettant de conserver le type de la donnée, en utilisant un enum pour codifier les types.

              #include <stdlib.h>
              
              enum type_donnee { CHAR, INT, DOUBLE, PCHAR, MAX_TYPES };
              union val {
                      char c;
                      int i;
                      double d;
                      char * s;
              };
              
              typedef struct Donnee Donnee;
              struct Donnee {
                      union val val;
                      enum type_donnee type;
              };
              
              typedef struct Element Element;
              struct Element{
                      Donnee donnee;
                      Element *suivant;
              };
              
              typedef struct Pile Pile;
              struct Pile{
                      Element *sommet;
              };
              
              Pile * init_pile(void) {
                      Pile * p = malloc(sizeof(*p));
                      p->sommet = NULL;
                      return p;
              }
              
              void empiler_donnee(Pile *pile, Donnee donnee){
                      if (pile == NULL)
                              exit(EXIT_FAILURE);
                      Element *nouveau = malloc(sizeof(*nouveau));
                      if (nouveau == NULL)
                              exit(EXIT_FAILURE);
              
                      nouveau->donnee = donnee;
                      nouveau->suivant = pile->sommet;
                      pile->sommet = nouveau;
              }
              
              int main(void) {
                      Pile * p = init_pile();
              
                      Donnee donnee;
              
                      donnee.val.c = 'A'; donnee.type = CHAR;
                      empiler_donnee(p, donnee);
              
                      donnee.val.i = 123; donnee.type = INT;
                      empiler_donnee(p, donnee);
              
                      donnee.val.d = 123.45; donnee.type = DOUBLE;
                      empiler_donnee(p, donnee);
              
                      donnee.val.s = "saperlipopette"; donnee.type = PCHAR;
                      empiler_donnee(p, donnee);
              
                      return 0;
              }
              


              Lorsque tu dépiles, tu consultes d'abord le type récupéré, et, en fonction du type, tu accèdes à la valeur récupérée à travers l'union récupérée.

              Dal
              1. Merci j’ai compris c’est plus clair là.

                Je vais essayer de finir mon projet d'informatique et j'espère réussir a le finir sans demander de l'aide.
              2. Je voulais aussi savoir esceque on pouvait se passer du enum type_Donnee car pour moi union val gère déjà bien les type pour ma part
              3. Contributeur
                @Theo_0055Si tu ne gardes pas trace du type, lorsque tu dépiles, tu ne sais pas comment accéder aux données stockées dans l'union pour qu'elle soient interprétées selon le bon type.

                Comme je le disais :

                Lorsque tu dépiles, tu consultes d'abord le type récupéré, et, en fonction du type, tu accèdes à la valeur récupérée à travers l'union récupérée.
              4. Contributeur
                @[Dal]Considère le code suivant :

                        /* je mets à zéro la struct, car je vais faire des trucs funky */
                        Donnee donnee = { 0 };
                        char str[] = "saperlipopette";
                        donnee.val.s = str;
                
                        /* maintenant, disons que je récupère donnee de la Pile sans 
                         * information sur le type, tous ces printf sont du C "valide"
                         * mais un seul restitue correctement l'information stockée */
                        printf("donnee est-elle une chaîne %s\n", donnee.val.s);
                        printf("donnee est-elle un char %c\n", donnee.val.c);
                        printf("donnee est-elle un entier %d\n", donnee.val.i);
                        printf("donnee est-elle un double %f\n", donnee.val.d);
                


                Comment vais-je l'exploiter ?

                Il me faut stocker le type pour garder cette information.

                Si le type que je stocke correspond à l'enum PCHAR et que, par convention, PCHAR désigne un pointeur sur char permettant d'accéder à une chaîne C, alors, je pourrais écrire ceci :

                        Donnee donnee; 
                        char str[] = "saperlipopette";
                        donnee.val.s = str;
                        donnee.type = PCHAR;
                
                        /* maintenant, disons que je récupère donnee de la Pile avec
                         * information sur le type. Je vais pouvoir déterminer comment
                         * adapter mon printf selon le cas */
                        switch (donnee.type) {
                        case PCHAR:
                                printf("donnee est une chaîne %s\n", donnee.val.s);
                        break;
                        case CHAR:
                                printf("donnee est un char %c\n", donnee.val.c);
                        break;
                        case INT:
                                printf("donnee est un entier %d\n", donnee.val.i);
                        break;
                        case DOUBLE:
                                printf("donnee est un double %f\n", donnee.val.d);
                        break;
                        default:
                                printf("Erreur : type non géré\n");
                        }
                
              5. @[Dal]Ok tout est clair désormais dans ma tête

                Je voulais aussi savoir si j’ai une fonction
                 int depiler (Pile * p) 

                Le int avec ce que vous m’avez expliqué on peut le remplacer par
                Donnee depiler (Pile * p)  
            3. Merci je vais essayer de comprendre et je vous refait signe