Partage
  • Partager sur Facebook
  • Partager sur Twitter

Erreur de segmentation

    9 janvier 2021 à 16:11:57

    Bonjour à tous, je ne comprends pas pourquoi il y a une erreur de segmentation dans ce code :

    #ifndef SET_INTL_H
    #define SET_INTL_H
    
    struct Cell{
      int val;
      Cell* suiv; 
    };
    
    class setInt{
      int nbrElements;
      Cell* debut;
      Cell* P;
    
     public:
      setInt();
      ~setInt();
      setInt(setInt &);
    
      void ajoutElement(int);
      bool appartientA(int);
      int cardinal();
      void init();
      int prochain();
      bool existe();
    
      
      void afficher();
      
    
    };
    #endif
      
    #include <iostream>
    #include "setIntL.h"
    using namespace std;
    
    setInt::setInt():nbrElements(0), debut(NULL){
    }
    
    setInt::~setInt(){
      Cell* suppr;
      while(debut != NULL){
        suppr = debut;
        debut = debut->suiv;
        delete [] suppr;
      }
    }
    
    setInt::setInt(setInt &e){
      nbrElements = e.nbrElements;
      Cell* adsource = e.debut;
      Cell* adbut;
      debut = NULL;
      while(adsource){
        adbut=new Cell;
        adbut->val=adsource->val;
        adbut->suiv=debut;
        debut=adbut;
        adsource=adsource->suiv;
      }
    }
    void setInt::ajoutElement(int n){
      if(!(appartientA(n))){
    
          Cell* nouv = new Cell;
          nouv->val=n;
          nouv->suiv=debut;
          debut = nouv;
          nbrElements++;
        }
        else
          cout<<"Ce nombre appartient déjà à l'ensemble !"<<endl;
    }
    bool setInt::appartientA(int n){
      while((debut->suiv)!=NULL){
        if ((debut->val)==n){
          return true;
        }
        else
          debut = debut->suiv;
      }
      return false;
    }
    
    int setInt::cardinal(){
      return nbrElements;
    }
    
    void setInt::afficher(){
      Cell* P;
      P = debut;
      while((P)!=NULL){
        cout<<"-->|"<<P->val<<"|";
        P = P->suiv;
      }
      cout<<endl;
    }
    void setInt::init(){
      P = debut;
    }
    
    int setInt::prochain(){
      if (P){
        int k = P->val;
        P = P->suiv;
        return k;
      }
      else
        return 0;
    }
    bool setInt::existe(){
      return(P!=NULL);
    }
    
    #include <iostream>
    #include "setIntL.h"
    using namespace std;
    
    
    int main(){
      
      setInt ens;
      ens.ajoutElement(8);
      ens.ajoutElement(10);
      cout<<"Cardinal ens : "<<ens.cardinal()<<endl;
      ens.afficher();
      
      setInt ens2 = ens;
      ens2.afficher();
      ens2.ajoutElement(9);
      cout<<"Cardinal ens2 : "<<ens2.cardinal()<<endl;
      ens2.afficher();
      
    
      cout<<"------------------------------------------------"<<endl;
      
      cout<<"Saisissez 10 nombres :";
      setInt ens3;
      int n = 0;
      for(int i = 0; i<10; i++){
        cin>>n;
        ens3.ajoutElement(n);
      }
      cout<<endl;
      cout<<"Il y a "<<ens3.cardinal()<<" éléments différents"<<endl;
      cout<<"Ce sont : ";
      while(ens3.existe()){
        cout<<ens3.prochain()<<", ";
      }
      cout<<endl;
      
      
        
      
    
      return 0;
    }
    





    -
    Edité par EjeXjdk 9 janvier 2021 à 16:13:32

    • Partager sur Facebook
    • Partager sur Twitter
      9 janvier 2021 à 16:46:31

      Eje Xjdk,

      Quand tu as ce type d'erreur, utilise un débugueur, il y a sûrement une indirection sur un pointeur nul.

      (Sauf si c'est un exercice, et que tu n'as pas le choix, ... sinon, il y a déjà l'équivalent que ta liste chainée dans la bibliothèque standard : vector)

      Cordialement.

      • Partager sur Facebook
      • Partager sur Twitter
        9 janvier 2021 à 17:15:01

        J'ai rarement vu autant de new dans un code C++. J'ai bien l'impression que tu fais du C++ d'un autre temps et c'est une des raisons d'un programme si buggé. Trop de gestion de mémoire manuelle, trop d'erreurs.

        Utilise des smart pointers (si vraiment nécessaire, ici à priori non) et des tableaux de la bibliothèque standard. Cela résoudra pas mal de problèmes dont un basique de double delete car tu n'as pas désactivé tes constructeurs de copie.

        • Partager sur Facebook
        • Partager sur Twitter

        git is great because Linus did it, mercurial is better because he didn't.

          9 janvier 2021 à 17:19:01

          Effectivement c'est dans le cadre d'un exo. Comment j'utilise le debuggeur ?

          markand a écrit:

          J'ai rarement vu autant de new dans un code C++. J'ai bien l'impression que tu fais du C++ d'un autre temps et c'est une des raisons d'un programme si buggé. Trop de gestion de mémoire manuelle, trop d'erreurs.

          Utilise des smart pointers (si vraiment nécessaire, ici à priori non) et des tableaux de la bibliothèque standard. Cela résoudra pas mal de problèmes dont un basique de double delete car tu n'as pas désactivé tes constructeurs de copie.

          C'est dans le cadre d'un exo, et j'essaye d'utiliser ce qui est permis. Mais je vais essayer de réduire le nombre de new de mon programme.

          -
          Edité par EjeXjdk 9 janvier 2021 à 17:21:10

          • Partager sur Facebook
          • Partager sur Twitter
            9 janvier 2021 à 17:25:22

            Dans ce cas je t'invite tout de même à développer propre et moderne et à le fournir au professeur de cette manière. De ce fait, il se daignera enfin à se mettre à jour et comprendra que personne en C++ code de cette façon en 2021.

            Même dans le cadre d'un exercice cela reste stupide, on ne code pas de cette manière « en vrai »

            Pour ta question concernant le debugger, un minimum de recherche t'en dira plus. C'est un sujet trop vaste. Si tu utilises un IDE (XCode, Visual Studio, Qt Creator), le debugage est amplement facilité par les vues graphiques pour analyser la pile d'appel, variables, etc.

            • Partager sur Facebook
            • Partager sur Twitter

            git is great because Linus did it, mercurial is better because he didn't.

              9 janvier 2021 à 17:38:13

              C'est un exercice que j'ai trouvé dans un bouquin (qui je crois n'est pas trop vieux), mais qu'est ce que tu appelles "développer propre" ? Les gens n'utilisent plus d'allocation dynamique en C++ ?
              • Partager sur Facebook
              • Partager sur Twitter
                9 janvier 2021 à 17:44:08

                EjeXjdk a écrit:

                C'est un exercice que j'ai trouvé dans un bouquin (qui je crois n'est pas trop vieux), mais qu'est ce que tu appelles "développer propre" ? Les gens n'utilisent plus d'allocation dynamique en C++ ?


                Si, en utilisant du C++ moderne (C++11, C++14, C++17 et C++20) sur lequel s'appuie les techniques de base du C++ sûr avec RAII notamment.

                Exemple de code totalement riche en problèmes :

                List *list = new List[120];
                
                if (readList(list))
                    return NULL; // un premier leak
                
                doSomeStuff();
                
                // oups, un deuxième leak si doSomeStuff lève une exception
                
                return list;

                En utilisant (même pas forcément du moderne, mais à minima, la bibliothèque standard) on s'affranchit de ces problèmes.

                std::vector<Item> items;
                
                items.emplace_back("foo");
                
                doSomeStuff();
                
                // no problem, le destructeur de std::vector nettoie tout.

                Pour résumer : En C++ moderne et sûr tu n'es pas censé avoir un quelconque new/delete à moins d'avoir une réelle raison de le faire.

                -
                Edité par markand 9 janvier 2021 à 17:46:21

                • Partager sur Facebook
                • Partager sur Twitter

                git is great because Linus did it, mercurial is better because he didn't.

                  9 janvier 2021 à 18:53:28

                  EjeXjdk a écrit:

                  C'est un exercice que j'ai trouvé dans un bouquin (qui je crois n'est pas trop vieux), mais qu'est ce que tu appelles "développer propre" ? Les gens n'utilisent plus d'allocation dynamique en C++ ?

                  Quel livre ?

                  Le problème est que ton code est au final plus du "C with class" que du C++. En gros, c'est juste du C avec les fonctions dans la class, mais fondamentalement, c'est penser le code comme du C. Et donc du C++ incorrect.

                  // C
                  foo(monObjet, arg1, arg2);
                  
                  // "C with class"
                  monObjet.foo(arg1, arg2);

                  Globalement, cela veut dire que tu pourras oublier ce que tu apprends en C++, ca ne sera pas du tout accepté dans un vrai projet C++. Le problème est que apprend du code qui est incorrect, mais :

                  - sans apprendre pourquoi il pose problème

                  - sans apprendre comment écrire du code correct

                  Même dans un projet C moderne, c'est pas sur que ton code soit accepté.

                  Quelques remarques :

                  - NULL -> nullptr

                  - initialise tes variables lors de la déclaration

                  struct Cell{
                    int val = 0;
                    Cell* suiv = nullptr;
                  };
                   

                  - ne sépare pas déclaration et initialisation

                  Cell* P;
                  P = debut;
                  
                  // devient
                  
                  Cell* P = debut;

                  - utilises delete avec new et delete[] avec new[]

                  - vire la fonction init. C'est le role du constructeur d'initialiser la classe.

                  - il se passe quoi si "debut" est null dans ce cas ?

                  while((debut->suiv)!=NULL){

                  Je n'ai pas regardé le code en détail.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    9 janvier 2021 à 19:15:23

                    Le livre c'est "Exercices en Langage C++, 4ème édition, Claude Delannoy".

                    J'ai changé le code car ça ne marchait justement pas dans ca cas là, quand "debut" était nul. Mais par contre, un truc que je ne comprends pas avec les listes simplement chaînées, c'est pourquoi pour parcourir une liste L par exemple, on doit initialiser un pointeur (dans l'exemple P) sur sa première chaîne, et mettre une boucle du type :

                    while(P != nullptr){
                    P = P->succ;
                    }

                    Pourquoi ne peut on pas directement faire ceci, en utilisant directement la liste que l'on souhaite parcourir :

                    while(L != nullptr){
                    L = L->succ;
                    }

                    On économiserait une variable en mémoire non ? Car que ce soit dans les bouquins ou dans mes cours, à chaque parcours de lsite chaîné, on initialise un pointeur qui va parcourir.

                    Et aussi, quelles différences il y a entre NULL et nullptr ?



                    • Partager sur Facebook
                    • Partager sur Twitter
                      9 janvier 2021 à 20:14:37

                      EjeXjdk a écrit:

                      Le livre c'est "Exercices en Langage C++, 4ème édition, Claude Delannoy".

                      Ahhhhhhhh okkkkkkkkkk. Bon, voila, il ne faut pas chercher plus loin.

                      C'est pas qu'on veut dire du mal de certains cours ou livre, mais force est de constater que quand on voit du code qui est assez mauvais, c'est souvent les mêmes sources qu'on voit.

                      Donc encore et toujours, on est obligé de déconseiller les cours C++ de Delannoy. C'est une catastrophe.

                      Et pour "(qui je crois n'est pas trop vieux)", malheureusement, il faut savoir que les auteurs et éditeurs font souvent des nouvelles éditions, juste pour mieux vendre un livre, mais que le contenu n'est pas mis à jour. Désolé pour toi si tu l'as acheté.

                      EjeXjdk a écrit:

                      Et aussi, quelles différences il y a entre NULL et nullptr ?

                      La reponse courte (que beaucoup de débutants n'aiment pas, mais, si on prend du recul, est la meilleure réponse à leur donner) : parce que c'est comme ça. Ca fait partie des bonnes pratiques, il FAUT les suivre, même si on ne le comprend pas.

                      Une réponse un peu plus détaillée : c'est une question de typage fort, qui est un moyen d'améliorer la qualité du code (= minimiser le risque de bugs) en faisant plus de vérification a la compilation.

                      int i = NULL;    // pas d'erreur de compilation
                      int j = nullptr; // erreur de compilation

                      EjeXjdk a écrit:

                      Mais par contre, un truc que je ne comprends pas avec les listes simplement chaînées, c'est pourquoi pour parcourir une liste L par exemple, on doit initialiser un pointeur (dans l'exemple P) sur sa première chaîne, et mettre une boucle du type :

                      while(P != nullptr){
                      P = P->succ;
                      }

                      Pourquoi ne peut on pas directement faire ceci, en utilisant directement la liste que l'on souhaite parcourir :

                      while(L != nullptr){
                      L = L->succ;
                      }

                      On économiserait une variable en mémoire non ? Car que ce soit dans les bouquins ou dans mes cours, à chaque parcours de lsite chaîné, on initialise un pointeur qui va parcourir.L

                      Cela dépend si tu veux modifier L ou pas. Si tu dois garder L "intact", alors tu es obligé de copier le pointeur avant de l'utiliser.

                      D'ailleurs, pour éviter de modifier L par erreur, il faut normalement ajouter "const" sur les fonctions qui ne doivent pas modifier L :

                        void ajoutElement(int);
                        bool appartientA(int) const;
                        int cardinal() const;
                        void init();
                        int prochain() const;
                        bool existe() const;
                       
                         
                        void afficher() const;

                      Idem pour le constructeur de copie, "e" ne doit pas etre modifié :

                      setInt::setInt(const setInt &e){

                      Il manque aussi l'opérateur d'affectation par copie "operator=(const setInt&)". Et pleins de petites choses qui ne vont pas, mais c'est du detail pour le moment.

                      • Partager sur Facebook
                      • Partager sur Twitter
                        9 janvier 2021 à 20:40:48

                        Ah j'ai acheté le livre sur le cours et le livre pour les exos :/, t'aurais un, ou des bons livres à me conseiller sur le C++, et pour d'autres langages pour le jour où je m'y met ?

                        J'avoue que le "parce que c'est comme ça" m'a pas fait plaisir ahah, mais je pensais que le langage C++ était faiblement comme le langage C.

                        D'accord et effectivement j'ai oublié le mot-clé const.

                        -
                        Edité par EjeXjdk 14 janvier 2021 à 10:48:32

                        • Partager sur Facebook
                        • Partager sur Twitter

                        Erreur de segmentation

                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                        • Editeur
                        • Markdown