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 :

Erreur de segmentation Skiplist


Sujet :

C++

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Décembre 2011
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 18
    Points : 16
    Points
    16
    Par défaut Erreur de segmentation Skiplist
    Bonsoir,
    Pour mon cours d'algorithmique il m'a été demandé d'implémenter une skiplist, j'ai un soucis dans la méthode de ma classe SkipList qui réalise l'insertion des données.

    Voici mon code pour la classe SkipList :

    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
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    #include <cstdlib>
    #include <time.h>
     
    #include "skiplist.hpp"
     
    using namespace std;
     
    SkipList::SkipList()
        :level(0),size(0),proba(.5)
    {
        head = new Node(0, 32);
     
        srand (time(NULL));
    }
     
    SkipList::~SkipList()
    {
        Node *prec = NULL;
     
        for(Node *n = head; n != NULL; n = n->getNextAtLevel(0))
        {
            delete prec;
            prec = n;
        }
     
        delete prec;
    }
     
    bool SkipList::contains(const int &key) const
    {
        Node *current = head;
     
        for(int i = level; i >= 0; --i)
            if(current->getNextAtLevel(i))
            {
                while(current->getNextAtLevel(i)->getKey() < key)
                {
                    current = current->getNextAtLevel(i);
     
                    if(!current->getNextAtLevel(i))
                        break;
                }
     
                if(current->getNextAtLevel(i))
                    if(current->getNextAtLevel(i)->getKey() == key)
                        return true;
            }
     
        return false;
    }
     
    void SkipList::add(const int &key)
    {	
        unsigned nodeSize = 1;
     
        unsigned val = 1;
     
        while(val)
        {
            val = rand()%2;
            if(val)
                ++nodeSize;
        }
     
        if(nodeSize > level)
            level = nodeSize - 1;
     
        Node *newNode = new Node(key, nodeSize);
        Node *current = head;
     
        for(int i = level; i >= 0; --i)
        {
            if(!current->getNextAtLevel(i))
            {
                if(i < nodeSize)
                    current->setNextAtLevel(i, newNode);
     
                continue;
            }
     
            while(1)
            {
                if(current->getNextAtLevel(i))
                {
                    if(current->getNextAtLevel(i)->getKey() < key)
                        current = current->getNextAtLevel(i);
                    else
                        break;
                }
                else
                    break;
            }
     
            if(current->getNextAtLevel(i))
                if(current->getNextAtLevel(i)->getKey() == key)
                {
                    delete newNode;
                    return;
                }
     
            if(current->getNextAtLevel(i))
                if(current->getNextAtLevel(i)->getKey() > key)
                {
                    newNode->setNextAtLevel(i, current->getNextAtLevel(i));
                    current->setNextAtLevel(i, newNode);
                }
     
            ++size;
        }
    }
    Et celui de ma classe Node :
    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
    #include "node.hpp"
     
    Node::Node(const int &key, const unsigned &size)
    	:key(key), size(size)
    {
    	next = new Node *[size];
     
    	for(unsigned i = 0; i < size; ++i)
    		next[i] = NULL;
    }
     
    Node::~Node()
    {
    	delete []next;
    }
     
    unsigned Node::getSize() const
    {
    	return size;
    }
     
    int Node::getKey() const
    {
    	return key;
    }
     
    Node* Node::getNextAtLevel(const unsigned &level) const
    {
    	return next[level];
    }
     
    void Node::setNextAtLevel(const unsigned &level, Node *node)
    {
    	next[level] = node;
    }
    Le problème semble se situer à la ligne 85 de SkipList.cpp, en effet l'appel de la fonction getNextAtLevel() renvoi 0x21 sous linux et 0xabababab sous windows. J'ai eu beau retourné le problème dans tous les sens je ne vois pas où se situe mon erreur.

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Avant d'aller plus loin, as-tu conscience de ce que fait le bout de code (qui apparait dans SkipList entre la ligne 81 et la ligne 92), un peu épuré ci-dessous
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    while(1)
    {
        /* ...*/
    }
    Si, déjà, tu trouves la bonne réponse, il y a de grandes chances que tu trouves la solution à ton problème

    Au fait, tu devrais peut être réfléchir un tout petit peux aux deux premières lignes de ma signature, car j'ai vraiment l'impression que ton algorithme est... bancal à la base

    Il faut dire que je suis plus ou moins allergique aux mots cle continue de manière générale et au mot clé break quand il ne se trouve pas dans un switch->case

    Ah, tant qu'à faire, quelle méthode de représentation d'algorithme utilises tu
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre éprouvé Avatar de gnto
    Homme Profil pro
    Ingénieur système logiciel
    Inscrit en
    Janvier 2006
    Messages
    923
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur système logiciel
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2006
    Messages : 923
    Points : 1 210
    Points
    1 210
    Par défaut
    Bonjour,

    Complétement d'accord avec koala01, pourquoi casser les séquences ? Les break dans les boucles me donne l'impression qu'il n'y a pas de pilote a bord, on code a vu. Idem sur les return en pleine méthode, c'est "pour moi", moins lisible.

    Dans le destructeur, je pense que tu delete un pointeur nul. Et je pense qu'une boucle conditionnel type while serait plus appropriée. Une boucle for est une boucle dont on maitrise le nombre d'iterations avant running.

  4. #4
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 043
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 043
    Points : 2 234
    Points
    2 234
    Par défaut
    Bonjour,
    En plus de plusoyer tout mes collègues ici présent. Ton crash vient du fait que tu réalises un "out of bound" sur ton tableau next dans la classe Node.
    En effet, tu ne vérifies jamais que level est bien inférieur à size. Donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Node* Node::getNextAtLevel(const unsigned &level) const
    {
    	return next[level];
    }
    retourne n'importe quoi car ici level est plus grand que size. Si ton level > size retourne null_ptr.
    Homer J. Simpson


  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
    Salut !

    Je suis en grande partie d'accord avec les précédentes interventions. Je ne suis pas contre continue et break comme Koala, ils sont utiles et ont du sens quand ils sont bien utilisés, mais là tu en abuses car il y a plusieurs endroits où tu pourrais t'en passer et écrire des boucles plus claires.

    Tu utilises aussi beaucoup et pas très bien les one-liners (boucles conditionnelles sans accolades). Exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    if(current->getNextAtLevel(i))
      if(current->getNextAtLevel(i)->getKey() == key)
      {
        delete newNode;
        return;
      }
    Qui s'écrit

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if(current->getNextAtLevel(i) && current->getNextAtLevel(i)->getKey() == key)
    {
      delete newNode;
      return;
    }
    Dans le destructeur, je pense que tu delete un pointeur nul.
    Alors juste pour la culture, delete sur un pointeur nul est parfaitement valide. Attention cependant à ne pas confondre un pointeur nul et un pointeur qui a été delete. Je suppose que tu débutes alors ne prend pas mal nos petits "reproches", ce sont des bonnes habitudes à prendre pour ne pas t'arracher les cheveux par la suite . Tu pourras corriger tes soucis en réorganisant ton code et en étant attentif au point soulevé par Astraya.
    Find me on github

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Décembre 2011
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 18
    Points : 16
    Points
    16
    Par défaut
    Bonjour,
    Merci à vous tous pour vos réponses.

    Je suis conscient que le code que je vous ait envoyé n'est pas très "propre", javais commencé par faire des tests du type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if(current->getNextAtLevel(i) && && current->getNextAtLevel(i)->getKey() == key)
    Idem dans mon while, seulement lorsque je voyais qu'appeler la méthode getKey() renvoyais une erreur, j'ai pensé que d'appeler cet méthode à partir d'un pointeur nul pouvais causer mon bug. C'est pourquoi j'ai commencé par faire le test de nullité et ensuite le test sur la valeur.

    Je voulais surtout remercier Astraya qui ayant mit le doigt sur mon erreur m'a fait comprendre que mon algorithme était faux. En effet, il est normalement pas normal d'envoyer une valeur supérieure à la taille du nœud dans getNextAtLevel. L'erreur se situe lors de l'insertion du nouveau nœud, en effet je ne teste pas si l'indice du niveau courant est inférieur à la taille du nœud à insérer avant de l'insérer. J'ai donc corrigé cela en faisant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    if(current->getNextAtLevel(i) && current->getNextAtLevel(i)->getKey() > key && i < nodeSize)
    {
    	newNode->setNextAtLevel(i, current->getNextAtLevel(i));
    	current->setNextAtLevel(i, newNode);
    }

  7. #7
    Membre éprouvé Avatar de gnto
    Homme Profil pro
    Ingénieur système logiciel
    Inscrit en
    Janvier 2006
    Messages
    923
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Ingénieur système logiciel
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2006
    Messages : 923
    Points : 1 210
    Points
    1 210
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    Alors juste pour la culture, delete sur un pointeur nul est parfaitement valide. Attention cependant à ne pas confondre un pointeur nul et un pointeur qui a été delete. Je suppose que tu débutes alors ne prend pas mal nos petits "reproches", ce sont des bonnes habitudes à prendre pour ne pas t'arracher les cheveux par la suite . Tu pourras corriger tes soucis en réorganisant ton code et en étant attentif au point soulevé par Astraya.
    Je crois pas avoir dit que ce soit invalide. Faire un while(true) ou while(1) n'est pas invalide, c'est juste maladroit.

    On peut avoir un débat sur optimisation de ligne de code, d'instruction Vs lisibilité, maitrise du code et algorithmie.

  8. #8
    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
    Citation Envoyé par gnto Voir le message
    Je crois pas avoir dit que ce soit invalide. Faire un while(true) ou while(1) n'est pas invalide, c'est juste maladroit.
    En effet, mais faire un delete d'un pointeur nul n'a rien de maladroit, au contraire, c'est très pratique dans les destructeurs car on n'a pas besoin de tester le pointeur, ça le rend le code plus lisible.
    Find me on github

  9. #9
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par gnto Voir le message
    Bonjour,

    Complétement d'accord avec koala01, pourquoi casser les séquences ? Les break dans les boucles me donne l'impression qu'il n'y a pas de pilote a bord, on code a vu. Idem sur les return en pleine méthode, c'est "pour moi", moins lisible.
    En fait, je ne suis très certainement pas un fanatique du SESE (Single Entry Single Exist), bien au contraire : s'il faut faire un return à l'intérieur d'une boucle parce que cela ne sert strictement à rien de continuer, je n'ai strictement aucune objection à le faire.

    C'est vraiment au break et au continue dans les boucles que je suis allergique

    Simplement parce que les boucles sont plus ou moins interchangeables et qu'il est tout à fait possible de choisir un autre type de boucle qui pourrait prendre une condition de sortie plus précise que la boucle pour, qui est itérative
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Erreurs de segmentation !
    Par anti-conformiste dans le forum Applications et environnements graphiques
    Réponses: 16
    Dernier message: 18/10/2005, 11h11
  2. Erreur de segmentation
    Par Trunks dans le forum C
    Réponses: 3
    Dernier message: 06/10/2005, 18h28
  3. Erreur de segmentation (Inconnue)
    Par Dark-Meteor dans le forum C
    Réponses: 5
    Dernier message: 08/09/2005, 13h42
  4. [Dev-C++] Erreur de segmentation...
    Par sas dans le forum Dev-C++
    Réponses: 11
    Dernier message: 26/03/2005, 14h25
  5. erreur de segmentation
    Par transistor49 dans le forum C++
    Réponses: 10
    Dernier message: 15/03/2005, 11h18

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