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 lors d'accès à un tableau


Sujet :

C++

  1. #1
    Membre confirmé Avatar de humitake
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2010
    Messages
    399
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2010
    Messages : 399
    Points : 578
    Points
    578
    Par défaut Erreur de segmentation lors d'accès à un tableau
    Bonjour,

    Je dispose d'une base de données avec des numéros de téléphone, je souhaite récupérer ces numéros depuis mon programme c++. J'ai donc créer une fonction callback que j'utilise lors de l'appel de sqlite3_exec.

    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int SMS::setRecipients(){
        char** recipients;
        recipients = (char**) malloc(128*sizeof(char*));
        for(int i = 0; i < 128; i++)
            recipients[i] = (char*) malloc(16 * sizeof(char));
        recipients[0] = "1";
        bd.setSql("SELECT tel FROM Sms WHERE etat = 1");
        sqlite3_exec(bd.getConnection(), bd.getSql().c_str(), callbackGetRecipients, recipients, NULL);
        std::cout << "requete : ok" << std::endl;
        for(int i = 1; i < atoi(recipients[0]); i++){
            this->recipients += "*";
            this->recipients += recipients[i];
        }
        std::cout << this->recipients << std::endl;
    }

    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int callbackGetRecipients(void *recipients, int nbCol, char** res, char** name){
        char **temp = (char **)recipients;
        int i = atoi(temp[0]);
        strcpy(temp[i], res[0]);
        i++;
        char buffer[16];
        memset(buffer, 0, 16);
        sprintf(buffer, "%d", i);
        strcpy(temp[0], buffer);
        return 0;
    }

    La fonction callback est appelée pour chaque ligne retournée par la base de données. Afin de savoir quel ligne je suis en train de traiter j'incrémente la variable i et je stock son résultat dans la case 0 du tableau "recipients". A chaque passage dans la fonction callback je récupère la valeur contenu dans recipients[0] afin de savoir dans quel case je dois stocker le résultat.

    L'erreur intervient sur la ligne 10 de la fonction callback : "Erreur de segmentation".
    Cependant si je remplace strcpy(temp[0], buffer); par strcpy(temp[1], buffer); (en changeant de case), je n'ai plus cet erreur.
    Je ne peux donc pas avoir accès à la même case que celle que j'ai utilisé pour la récupération de l'index (i).

    Je ne comprend pas à quoi est dû cet erreur, si quelqu'un peut m'aider ...

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Pourquoi, en C++, utilises-tu des pointeurs de pointeurs et des malloc ?

  3. #3
    Membre éprouvé Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Points : 997
    Points
    997
    Par défaut
    Bonjour,
    Je ne vois pas pourquoi tu as cette erreur, mais je vais faire quelques remarques sur ton code.
    (Et, avec un peu de chance, ça résoudra ton problème... )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char** recipients;
    recipients = (char**) malloc(128*sizeof(char*));
    for(int i = 0; i < 128; i++)
        recipients[i] = (char*) malloc(16 * sizeof(char));
    Tu fais du C++, utilise les instructions C++...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char** recipients;
    recipients = new char*[128];
    for(int i = 0; i < 128; i++)
        recipients[i] = new char[16];
    Quoique vu qu'il n'y a que des constantes...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char recipients[128][16];
    Ça t'évitera de faire des delete à la fin de la fonction.


    Aïe !
    Là tu perds l'adresse de la mémoire que tu viens d'allouer...
    (Le problème vient peut-être de là, finalement... )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::strcpy(recipients[0], "1");
    //********//
    recipients[0][0] = '1';
    recipients[0][1] = '\0';

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char **temp = (char **)recipients;
    Ah...
    Encore l'héritage du C...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char **temp = reinterpret_cast<char **>( recipients );

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char buffer[16];
    memset(buffer, 0, 16);
    sprintf(buffer, "%d", i);
    strcpy(temp[0], buffer);
    Hum...
    Quel est donc l'intérêt de passer par une variable auxiliaire...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::sprintf(temp[0], "%d", i);
    Ce n'est pas mieux comme ça ?


    Ceci étant dit, je me pose des questions quant à l'utilisation de char * et de tableaux.
    Héritage du C, ou réel besoin ?
    Parce que d'après ce que tu as posté, on peut très bien faire la même chose avec des std::string et std::vector.
    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
    #include <sstream>
    #include <vector>
     
    int SMS::setRecipients()
    {
        std::vector<std::string> recipients(128, std::string()); // ou recipients(128, "");
        recipients[0] = "1";
        (...)
        sqlite3_exec(bd.getConnection(), bd.getSql().c_str(), callbackGetRecipients, &recipients, NULL);
        (...)
        std::istringstream iss(recipients[0]);
        int max;
        iss >> max;
        for (int i = 0; i < max; i++) {
            (...)
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <sstream>
    #include <vector>
     
    int callbackGetRecipients(void *recipients, int nbCol, char** res, char** name){
        std::vector<std::string>& temp = *reinterpret_cast<std::vector<std::string>*>( recipients );
        std::stringstream ss(temp[0]);
        int i;
        ss >> i;
        temp[i] = res[0];
        ss.str("");
        ss << ++i;
        temp[0] = ss.str();
        return 0;
    }
    Et voilà.
    C'est-y pas mieux comme ça ?
    Si tu as des questions, n'hésite pas.

  4. #4
    Membre éprouvé Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Points : 997
    Points
    997
    Par défaut
    En y réfléchissant, ce n'est peut-être pas la peine de remplir le vector à la création...
    J'avais dû lire ton code un peu trop vite.

    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
    int SMS::setRecipients()
    {
        std::vector<std::string> recipients;
        (...)
        sqlite3_exec(bd.getConnection(), bd.getSql().c_str(), callbackGetRecipients, &recipients, NULL);
        (...)
        for (std::vector<std::string>::iterator it = recipients.begin(); it != recipients.end(); ++it) {
            this->recipients += "*";
            this->recipients += *it;
        }
        (...)
    }
     
     
    int callbackGetRecipients(void *recipients, int nbCol, char** res, char** name){
        std::vector<std::string>& temp = *reinterpret_cast<std::vector<std::string>*>( recipients );
        temp.push_back(res[0]);
        return 0;
    }
    Et hop !
    Même plus besoin de conserver l'index de la ligne à traiter !
    Le vector grossit au fur-et-à-mesure des ajouts, toujours en fin.

    C'est tout de même plus simple, non ?

  5. #5
    Membre confirmé Avatar de humitake
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2010
    Messages
    399
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2010
    Messages : 399
    Points : 578
    Points
    578
    Par défaut
    Houla, effectivement je crois que je vais devoir me repencher sur mes cours de c++

    Désolé de répondre si tardivement, j'étais tellement penché sur le problème que j'en avais complètement oublié ce post
    Au final j'ai découvert qu'il y avait un moyen pour récupérer les résultats sans passer par des fonctions callback, ce qui au final est un petit plus puisque cela m’évitera d'avoir d'innombrable fonction callback Et qui permet en plus de simplifier le code

    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int SMS::setRecipients(){
        sqlite3_stmt *stmt = NULL;
        bd.setSql("SELECT tel FROM Sms WHERE etat = 1");
        int rc = sqlite3_prepare(bd.getConnection(), bd.getSql().c_str(), -1, &stmt, NULL);
        if(rc != SQLITE_OK) exit(-1);
        while(sqlite3_step(stmt) == SQLITE_ROW){
            this->recipients += "*";
            this->recipients += (char *)sqlite3_column_text(stmt, 0);
        }
        sqlite3_finalize(stmt);
        std::cout << this->recipients << std::endl;
    }

    Encore merci

  6. #6
    Membre éprouvé Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Points : 997
    Points
    997
    Par défaut
    C'est vrai que c'est plus simple comme ça.
    N'oublie pas pour autant les opérateurs de transtypage C++ !
    F.A.Q. C++: Comment effectuer une conversion de type explicite (cast) ?

  7. #7
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    ligne 4 : c'est vraiment utile, de passer l'adresse d'un pointeur, plutôt que le pointeur directement ?

    Cela ne serait-il pas intéressant d'utiliser un pointeur intelligent ?

    Ta fonction n'est-elle pas supposée renvoyer un entier ?

    Que sont bd et rc ? Des variables globales ? Ou des variables membres ?

  8. #8
    Membre confirmé Avatar de humitake
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2010
    Messages
    399
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2010
    Messages : 399
    Points : 578
    Points
    578
    Par défaut
    this->recipients += (char *)sqlite3_column_text(stmt, 0); devient donc this->recipients += static_cast<char*>(sqlite3_column_text(stmt, 0));

    Citation Envoyé par oodini
    ligne 4 : c'est vraiment utile, de passer l'adresse d'un pointeur, plutôt que le pointeur directement ?
    J'ai fait un copier coller de la doc, après il est effectivement plus judicieux de placer le pointeur directement vu que de toute façon il n'est utilisé qu'ici.

    Citation Envoyé par oodini
    Cela ne serait-il pas intéressant d'utiliser un pointeur intelligent ?
    Ici je ne peux pas te répondre je ne sais pas ce que c'est
    Je lis la FAQ ( http://cpp.developpez.com/faq/cpp/?page=pointeurs )

    Citation Envoyé par oodini
    Ta fonction n'est-elle pas supposée renvoyer un entier ?
    Oui la fonction renvoie un entier, mais elle écrit le résultat de la base de données dans la variable stmt.

    Citation Envoyé par oodini
    Que sont bd et rc ? Des variables globales ? Ou des variables membres ?
    bd est une instance de ma classe BD qui gère la connection à la base de données et tous se qui y touche.
    rc est une variable qui récupère le résultat (int) renvoyer par la fonction sqlite (cf: ta question précédente) permettant de savoir si elle s'est déroulé correctement.

  9. #9
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par humitake Voir le message
    Ici je ne peux pas te répondre je ne sais pas ce que c'est
    Je lis la FAQ ( http://cpp.developpez.com/faq/cpp/?page=pointeurs )
    Fais-en bon usage. :-)

    Citation Envoyé par humitake Voir le message
    Oui la fonction renvoie un entier, mais elle écrit le résultat de la base de données dans la variable stmt.
    Ben elle ne renvoie rien, tel que c'est là.
    Et ton exit(), ça ne se rencontre guère, en C++. Évite. Pense éventuellement aux exceptions.
    Je me demande par ailleurs quel est l'état de stmt, quand tu appeles cet exit() (cf pointeurs intelligents).

    Citation Envoyé par humitake Voir le message
    bd est une instance de ma classe BD qui gère la connection à la base de données et tous se qui y touche.
    rc est une variable qui récupère le résultat (int) renvoyer par la fonction sqlite (cf: ta question précédente) permettant de savoir si elle s'est déroulé correctement.
    Je ne sais toujours pas si ces variables sont globales, ou membres. Dans le second cas, un préfixe m serait appréciable.

  10. #10
    Membre confirmé Avatar de humitake
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2010
    Messages
    399
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2010
    Messages : 399
    Points : 578
    Points
    578
    Par défaut
    Citation Envoyé par oodini
    Citation Envoyé par humitake
    Oui la fonction renvoie un entier, mais elle écrit le résultat de la base de données dans la variable stmt.
    Ben elle ne renvoie rien, tel que c'est là.
    Et ton exit(), ça ne se rencontre guère, en C++. Évite. Pense éventuellement aux exceptions.
    Je me demande par ailleurs quel est l'état de stmt, quand tu appeles cet exit() (cf pointeurs intelligents).
    Au temps pour moi je croyais que tu parlais de la fonction sqlite3_prepare
    Donc la fonction renvoie bien un int ... return 0; à la fin serra mieux
    Pour le exit c'est encore une fois un copier collé remplacer par un return -1;.

    Citation Envoyé par oodini
    Je ne sais toujours pas si ces variables sont globales, ou membres. Dans le second cas, un préfixe m serait appréciable.
    bd est une variable membre mais rc est ni l'un ni l'autre, il est déclarer dans la fonction :
    int rc = sqlite3_prepare(bd.getConnection(), bd.getSql().c_str(), -1, &stmt, NULL);

  11. #11
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par humitake Voir le message
    rc [...] est déclarer dans la fonction :
    int rc = sqlite3_prepare(bd.getConnection(), bd.getSql().c_str(), -1, &stmt, NULL);
    Ah oui, au temps pour moi.

  12. #12
    Membre éprouvé Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Points : 997
    Points
    997
    Par défaut
    Citation Envoyé par humitake Voir le message
    Citation Envoyé par oodini Voir le message
    ligne 4 : c'est vraiment utile, de passer l'adresse d'un pointeur, plutôt que le pointeur directement ?
    J'ai fait un copier coller de la doc, après il est effectivement plus judicieux de placer le pointeur directement vu que de toute façon il n'est utilisé qu'ici.
    Ce n'est pas utile, c'est indispensable.
    Citation Envoyé par www.sqlite.org
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int sqlite3_prepare(
      sqlite3 *db,            /* Database handle */
      const char *zSql,       /* SQL statement, UTF-8 encoded */
      int nByte,              /* Maximum length of zSql in bytes. */
      sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
      const char **pzTail     /* OUT: Pointer to unused portion of zSql */
    );
    (...)

    *ppStmt is left pointing to a compiled prepared statement that can be executed using sqlite3_step(). If there is an error, *ppStmt is set to NULL. If the input text contains no SQL (if the input is an empty string or a comment) then *ppStmt is set to NULL. The calling procedure is responsible for deleting the compiled SQL statement using sqlite3_finalize() after it has finished with it. ppStmt may not be NULL.

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

Discussions similaires

  1. Erreur de segmentation lors de la compilation
    Par touzack dans le forum Débuter
    Réponses: 2
    Dernier message: 21/07/2010, 13h17
  2. Réponses: 7
    Dernier message: 12/05/2010, 16h33
  3. Erreur de segmentation lors du rafraichissement d'un ListStore
    Par Difool dans le forum GTK+ avec Python
    Réponses: 1
    Dernier message: 23/02/2010, 17h09
  4. Réponses: 1
    Dernier message: 22/03/2009, 20h44
  5. Réponses: 23
    Dernier message: 08/01/2006, 23h59

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