IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Iteration sur une liste ==> segmentation fault


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Avril 2014
    Messages
    35
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2014
    Messages : 35
    Points : 32
    Points
    32
    Par défaut Iteration sur une liste ==> segmentation fault
    Bonjour,

    j'ai une erreur sur le code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    for(Object::iterator it =  list.begin(); it !=  list.end(); it++)
                {
                    if ((*it)->getValue()==testValue)
                    {
                    it = list.erase(it);
                    }
                }
    voila apres le list.erase cela fait n'importe quoi et l'iterateur continue apres la fin de la liste. J'ai regarde partout et j'ai pas du tout trouve la solution du probleme :

    - D'abord j'utilisais des std::vector plutot que des std::list puisque j'ai cru comprendre que l'ajout/suppression d'element est plus efficace
    - Puis avec les std::vector la fonction .erase a l'air de "casser" l'iterateur et j'ai cru comprendre que la structure de liste corrigeait cela.
    - j'avais au depart juste list.erase(it) au lieu de it = list.erase(it); car j'ai cru comprendre qu'il faut cette structure pour allouer l'iterateur au prochain element
    - j'ai aussi essayer d'ajouter une ligne it = list.end() a la fin pour sortir de la boucle

    Mais malgres tout cela, j'ai toujours un probleme. La boucle ne s'arrete pas a la fin de ma liste comme elle devrait s'arreter !

    Merci d'avance pour votre aide

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Et si tu utilisais <algorithm>? il existe ce qu'on appelle l'idiome erase-remove:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #include <algorithm>
        liste.erase(std::remove(liste.begin(), liste.end(), testvalue), liste.end());
    (exemple tiré de http://en.cppreference.com/w/cpp/alg...preference.com

    Edit:
    En lisant la page attentivement, il y a liste::remove

    le code devient simplement liste.remove(testvalue);
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Avril 2014
    Messages
    35
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2014
    Messages : 35
    Points : 32
    Points
    32
    Par défaut
    merci pour ta réponse,

    en fait la condition ne porte pas sur la valeur de l'objet de la liste en lui même mais sur un argument de cette classe.

    Comment puis-je adapter std::remove pour que cela marche ? J'ai essaye ainsi en utilisant remove_if :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    list.remove_if(getValue()==testValue);
    mais cela ne semble pas marcher ?

    Sinon en utilisant mon code d'origine avec un iterateur il n'y a pas moyen de le faire marcher par hasard ? Cela m'arrangerait bien de garder cette forme pour mon code dans son ensemble ...

    Merci !

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    La fonction attend soit une valeur, soit une fonction (enfin, un pointeur de fonction ou une lambda)
    regarde bien les liens que j'ai donné.

    Par ailleurs, on doit avoir quelque chose dans la faq.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Attention, le choix de la liste est rarement une bonne idée : le temps de parcours que tu dépenses pour trouver l'élément à supprimer domine le gain réalisé sur la suppression. Voir:

    Find me on github

  6. #6
    Nouveau membre du Club
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Avril 2014
    Messages
    35
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2014
    Messages : 35
    Points : 32
    Points
    32
    Par défaut
    dsl mais j'ai encore un probleme . Je suis le template de l'example pour creer la fonction mais il n'accepte pas le passage de "b" en parametre pour ma fonction (dans l'example il utilise une valeur en dur). Comment faire pour passant des arguments a la fonction test ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
       int b = valueTest;
        list.remove_if([](Object* a){ return (a->getValue() == b); });
    vraiment merci pour ton aide !

    Attention, le choix de la liste est rarement une bonne idée : le temps de parcours que tu dépenses pour trouver l'élément à supprimer domine le gain réalisé sur la suppression. Voir:
    Dans ce cas la, peux-tu peut-etre me dire quel est le probleme avec mon code original et comment je pourrais le regler ?

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 630
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 630
    Points : 10 556
    Points
    10 556
    Par défaut
    Citation Envoyé par StingerBell Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    for(Object::iterator it =  list.begin(); it !=  list.end(); it++)
                {
                    if ((*it)->getValue()==testValue)
                    {
                    it = list.erase(it);
                    }
                }
    Object::iterator


    Pourquoi Object?

  8. #8
    Nouveau membre du Club
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Avril 2014
    Messages
    35
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2014
    Messages : 35
    Points : 32
    Points
    32
    Par défaut
    en faite Object désigne juste une liste d'éléments de ma classe, donc un truc de genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef std::list"Class" = Object

  9. #9
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 630
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 630
    Points : 10 556
    Points
    10 556
    Par défaut
    Ton histoire me semble bizarre, j'ai donc sorti mon gcc favori
    Et il y a une erreur simple: lorsque tu supprimes, ton itérateur est incrémenté 2 fois (1 fois par la méthode erase 1 autre fois par la boucle)

    Voici mon code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    #include<vector>
    #include<iostream>
     
    using namespace std;
     
    class A {
    public:
     
        A(int init_a = 0): a(init_a) {}
     
        int get_a() const { return a; }
     
     
    private:
     
        int a;
    };
     
    #define PRINT \
        cout << endl << endl; \
         \
        for(it = vec.begin(); it != vec.end(); ++it) { \
            cout << it->get_a() << endl; \
        }
     
     
    int main()
    {
        vector<A> vec;
        vec.push_back( A(4) );
        vec.push_back( A(6) );
        vec.push_back( A(4) );
        vec.push_back( A(5) );
        vec.push_back( A(4) );
        vec.push_back( A(4) );
        vec.push_back( A(3) );
        vec.push_back( A(2) );
        vec.push_back( A(4) );
        vec.push_back( A(4) );
        vec.push_back( A(4) );
        vec.push_back( A(1) );
     
        vector<A>::iterator it;
     
        PRINT
     
    //  Remove all 4
    //  Dirty -> voir le post de @jblecanard en dessous :-)
    //  for(it = vec.begin(); it != vec.end(); it++) {
    //      if (it->get_a() == 4) {
    //          it = vec.erase(it);
    //          it--;
    //      }
    //  }
     
        it = vec.begin();
        while(it != vec.end()) {
            if (it->get_a() == 4) {
                it = vec.erase(it);
            } else {
                ++it;
            }
        }
     
        PRINT
     
        return 0;
    }
    Édit: Merci @jblecanard pour cette correction .

    Et effectivement j'ai oublié de préciser qu'en utilisant soit std::vector soit std::list, cela fonctionne

  10. #10
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    C'est exact foetus, cela dit, c'est dangereux de décrémenter l'itérateur pour le réincrémenter juste après. Si le premier élément rencontre la condition, la décrémentation ne passera pas. De même, si tu utilises une forward_list, elle ne passe pas non plus. Rien ne vaut un bon vieux while.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    it = vec.begin();
    while( it != vec.end()) {
      if ((*it).get_a() == 4)
        it = vec.erase(it);
      else
        ++it;
    }
    @StingerBell ce code a été fait sur un vector mais il fonctionne aussi sur une liste.
    Find me on github

  11. #11
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Faire une série d'erase est en O(n²), là où un O(n) est possible avec erase-remove.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  12. #12
    Nouveau membre du Club
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Avril 2014
    Messages
    35
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2014
    Messages : 35
    Points : 32
    Points
    32
    Par défaut
    merci pour vos reponses,


    Faire une série d'erase est en O(n²), là où un O(n) est possible avec erase-remove.
    je comprends pas bien pourquoi un erase serait en n^2 alors qu'un erase remove serait en n. Basiquement ce sont les memes operations qui sont faites non ? Pour un vecteur on retire un element puis on deplace ce qui suit et pour une liste on parcourt la liste jusque la position pour retirer un element et on change les pointeurs non ?

  13. #13
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Voir ceci et ceci. Ce n'est pas valable si tu utilises la fonction membre remove de la liste, puisqu'on ne parle pas du même algo.
    Find me on github

  14. #14
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par StingerBell Voir le message
    je comprends pas bien pourquoi un erase serait en n^2 alors qu'un erase remove serait en n. Basiquement ce sont les memes operations qui sont faites non ? Pour un vecteur on retire un element puis on deplace ce qui suit et pour une liste on parcourt la liste jusque la position pour retirer un element et on change les pointeurs non ?
    Si on enlève un élément unique, la complexité sera en O(n) dans les deux cas. La différence vient du fait que si on enlève plusieurs éléments, avec un erase dans une boucle, on répète plusieurs fois le fait d'enlever un élément (donc O(n²)), alors qu'avec erase/remove, on parcours une seule fois la liste en décalant au fur et à mesure du bon nombre les éléments :
    - avant le premier élément à supprimer, on ne décale pas
    - à partir du premier et jusqu'au second, on décale d'une case vers la gauche les éléments
    - à partir du second et jusqu'au trosiième, on décale de deux cases vers la gauche les éléments
    - ...

    Donc en une seule passe, on a supprimé tous les éléments (d'où O(n))
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  15. #15
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Je ne vois pas trop en quoi ce raisonnement s'applique sur une liste chaînée, puisqu'on ne décale aucun élément. C'est plus pertinent sur un vector, non ?
    Find me on github

  16. #16
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Parce qu'avec la liste chainée, tu parcoure depuis le début systématiquement, vu que tu n'as plus d'itérateur valide.
    exactement comme pour le vecteur.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  17. #17
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par leternel Voir le message
    Parce qu'avec la liste chainée, tu parcoure depuis le début systématiquement, vu que tu n'as plus d'itérateur valide.
    exactement comme pour le vecteur.
    Tu peux garder un itérateur valide tout le long de ton itération.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    // de mémoire
    std::list<int> list;
    for(auto it = list.begin(); it != list.end(); /* rien ici */)
    {
      if(*it == 2)
        it = list.erase(it);
      else
        ++it;
    }
    Il me semble aussi que std::remove ne fonctionne pas pour un std::list (il lui faut un random access container).

    edit : dans tous les cas, pour une liste mieux vaut utiliser la méthode remove ou remove_if

Discussions similaires

  1. Réponses: 2
    Dernier message: 31/08/2010, 18h27
  2. Iteration sur une liste de chaine de caracteres
    Par Harfang dans le forum C++
    Réponses: 2
    Dernier message: 01/04/2009, 20h48
  3. Itération sur une liste d'éléments
    Par anitshka dans le forum Prolog
    Réponses: 3
    Dernier message: 05/07/2006, 22h49
  4. Selectionnet tous ou faire un clear sur une liste
    Par Canou dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 17/11/2004, 10h26
  5. [Débutant][jsp] évènement sur une liste
    Par phoebe dans le forum Servlets/JSP
    Réponses: 5
    Dernier message: 14/05/2004, 10h53

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo