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 :

Comportement différent d'une "librairie"


Sujet :

C

  1. #1
    Inactif   Avatar de Deallyra
    Profil pro
    Étudiant
    Inscrit en
    Février 2007
    Messages
    1 997
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2007
    Messages : 1 997
    Points : 1 769
    Points
    1 769
    Par défaut Comportement différent d'une "librairie"
    Bonsoir,

    J'ai fait une mini librairie qui me permet de générer "pseudo-aléatoirement" une suite de caractère formant un mot.

    Le problème étant que si je teste la librairie directement, cela fonctionne/
    Si je le teste depuis une autre application, mes résultats ne sont pas tout à fait corrects...
    sasa@sasaki:/media/Lapinator/Complexite/_Sources/rand$ ./rand 5 10

    Words :
    mfiof
    keuqoygqgf

    sasa@sasaki:/media/Lapinator/Complexite/_Sources/A1$ ./A1 5 10

    First word : jbvas
    Second word : lykkvqpcsb�{
    Comme vous pouvez le voir, ma chaîne de caractère pour le second mot (mais pour le premier également en certains cas) est altérée.
    Au lieu d'avoir un mot de 10 caractères comme demandé au lancement du programme via le second paramètre, j'en ai 10 plus... quelque chose, symbolisé présentement par le " �{ ".

    Sauriez vous comment pallier à ce problème ?

    Merci beaucoup

    L'archive peut être récupérée ici : http://deallyra.developpez.free.fr/_...rces@01-24.zip

    /A1/ correspond au dossier où mon executable ne m'affiche pas les "bons" résultats.
    /Library/ contient les fichiers qui me permettent la génération aléatoire
    /rand/ possède l'executable qui m'affiche les bons résultats. C'était mon programme de test

  2. #2
    Expert éminent
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Points : 8 389
    Points
    8 389
    Par défaut
    En langage C les chaînes de caractères se terminent par un caractère spécial noté '\0' marquant la fin dune chaîne. Par exemple la chaîne "ab" comporte 3 caractères : 'a', 'b' et '\0'. Partout où tu déclares un tableau pour stocker une chaîne donc prévois toujours la place pour ce caractère '\0'. Et on passant, j'ai vu lenght quelque part, tu voulais sûrement écrire length. Voic un exemple mais tu dois appliquer la modif partout où tu utilise des tableaux de caractères :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    char* randomWord(int length){
      char word[length + 1];
      char letter;
      int integer;
      int i;
      for (i = 0; i < length; i++){
        integer = integerGeneration(26);
        letter = getLetterByPosition(integer);
        word[i] = letter;
      }
      // ajouter le '\0' sinon ce n'est pas une chaine :
      word[i] = '\0'; // tu peux remplacer word[i] par word[length - 1] puisqu'ici i = length - 1
      return strdup(word);
    }
    Sans le '\0', les fonctions traitant des chaînes de caractères telles que printf ne sauront pas à quel moment s'arrêter et vont alors à un certain moment afficher n'importe quoi jusqu'à ce qu'un '\0' soit rencontré (avec de la veine, comme dans ton premier programme, un '\0' suit l'emplacement mémoire où tu as stocké ta chaîne et le bug est alors caché).

    A part ça, l'adresse retournée par strdup est l'adresse d'une mémoire allouée dynamiquement. En C ce que cela signifie c'est que tu dois tôt ou tard libérer cette mémoire en appelant free(). En faisant ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void generateWord(char* word, int wordLength){
        randInizialize();
        strcpy(word, randomWord(wordLength));
    }
    Tu perds l'adresse que la fonction randomWord avait retourné donc tu n'auras plus aucun moyen de libérer cette mémoire est c'est (très) mal. Correction donc :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void generateWord(char* word, int wordLength){
        randInizialize();
        char* pointer = randomWord(wordLength);
        strcpy(word, pointer);
        // on a pu copie les donnees, on n'a donc plus besoin de pointer. Liberons dans ce cas la memoire qu'elle pointe.
        free(pointer);
    }
    Mais c'est inutilement complexe. Ta méthode dans le premier programme est de loin meilleure, mais n'oublie pas les free() ...

  3. #3
    Inactif   Avatar de Deallyra
    Profil pro
    Étudiant
    Inscrit en
    Février 2007
    Messages
    1 997
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2007
    Messages : 1 997
    Points : 1 769
    Points
    1 769
    Par défaut
    Bonjour et merci de prendre du temps pour ces manques de connaissance que j'ai en C ^^

    J'ai fait les modifications requises sur mon code et désormais cela fonctionne correctement, en apparence mais je l'espère aussi, au niveau de la mémoire ^^

    Cependant, j'avoue ne pas trop voir comment m'en sortir pour libérer la mémoire du strdup.

    Il me sert car lorsque je retournais directement "word", le premier mot était effacé au profit du second puisqu'ils avaient (je le pense) la même adresse mémoire.

    Si je fais directement un
    return strdup(word);
    Je n'ai plus aucun contrôle sur ce mot.

    Il s'agit d'une allocation dynamique que je ne nomme pas. Comment appelez un free dessus ?
    Je pense que ma façon de faire est mauvaise sur cet aspect... Cependant, si jamais je mets le strdup(word) dans une variable, je ne vois pas comment mieux faire...

    Sauf si je me débrouille à renvoyer un pointeur sur cette variable pour ensuite faire le free une fois l'appel à la fonction terminée...
    Mais j'ai l'impression de bidouiller plus qu'autre chose :/

  4. #4
    Expert éminent
    Avatar de Melem
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Janvier 2006
    Messages
    3 656
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Janvier 2006
    Messages : 3 656
    Points : 8 389
    Points
    8 389
    Par défaut
    J'ai fait les modifications requises sur mon code et désormais cela fonctionne correctement, en apparence mais je l'espère aussi, au niveau de la mémoire ^^
    J'espère aussi, mais je peux le confirmer ou l'infirmer si tu uploades la nouvelle version .

    Si je fais directement un
    return strdup(word);
    Je n'ai plus aucun contrôle sur ce mot.

    Il s'agit d'une allocation dynamique que je ne nomme pas. Comment appelez un free dessus ?
    Ce ne sont pas les variables qui importent mais les adresses. strdup retourne une adresse sur laquelle il faudra un jour faire un free. Tu peux faire "voyager" cette adresse de variable en variable, du moment que tu ne la perds pas de vue pour que tu puisses appeler free le moment venu. Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    char * f1(void) {
        return f2();
    }
     
    char * f2(void) {
       return strdup("ab");
    }
     
    ...
     
    char * p = f1();
    q = p;
    free(q);
    L'adresse retournée par strdup est retournée par f2(), puis par f1(), puis stockée dans p et ensuite dans q, donc jusque là, on n'a jamais perdu de vue l'adresse. Un free(q) permet de libérer la mémoire allouée par l'appel à strdup. Un appel à strdup => Un et un seul appel à free. On aurait aussi pu remplacé free(q) par free(p), puisque p et q contiennent la même adresse. Mais free(q) ensuite free(p) par exemple c'est un bug, parce que c'est comme si on appelait free(q) deux fois ...

    Ce qui n'allait pas dans ta fonction generateWord, c'est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void generateWord(char* word, int wordLength){
        randInizialize();
        strcpy(word, randomWord(wordLength)); // cette ligne
    }
    Tu passes l'adresse directement à strcpy et voilà, on la perd définitivement ! C'est pourquoi avant strcpy il fallait stocker l'adresse dans une variable puis appeler free après le strcpy.

    Ceci étant, je trouve que c'est plus simple de fusionner randomWord et generateWord, sans strdup. Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void randgenWord(char* word, int length){
      char letter;
      int integer;
      int i;
      for (i = 0; i < length; i++){
        integer = integerGeneration(26);
        letter = getLetterByPosition(integer);
        word[i] = letter;
      }
      // ajouter le '\0' sinon ce n'est pas une chaine :
      word[i] = '\0'; // tu peux remplacer word[i] par word[length - 1] puisqu'ici i = length - 1
    }
    Utilisation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char word10[10 + 1]; // Et non char* word10 !
    randgenWord(word10, 10);
    C'est plus simple non ? Tu peux mettre char* au lieu de void comme type de retour de randgenWord et ajouter donc un return word; Comme ça tu pourras écrire des trucs comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char word10[10 + 1];
    printf("%s\n", randgenWord(word10, 10));

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 30/01/2012, 15h48
  2. Réponses: 6
    Dernier message: 16/08/2010, 17h42

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