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

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    avril 2011
    Messages
    205
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : avril 2011
    Messages : 205
    Points : 67
    Points
    67

    Par défaut malloc sur char* problème de taille avec strlen

    Bonjour,

    je rencontre le problème suivant :

    je déclare une chaîne de caractères avec un malloc comme ceci :

    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
     
    #define HISC_NameLg 17
     
    struct SMenuGen
    {
       .............
       char *pTmpScName;
       ............
    };
    struct SMenuGen gMenu;
     
    void mafonction(void)
    {
     u32 nLgName = 0;
     
     gMenu.pTmpScName = malloc(HISC_NameLg * sizeof(*gMenu.pTmpScName));
     
    /*Puis je fais un strlen pour avoir la taille*/
     
     nLgName = strlen(gMenu.pTmpScName);
     
    }
    Donc le problème est que si je regarde la taille de ma chaîne avec un printf :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    printf("nLgName = %d\n", nLgName);
    J'obtient la valeur 3. Et si je fait maintenant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    printf("sizeof(*gMenu.pTmpScName) = %d\n", sizeof(*gMenu.pTmpScName));
    La j'ai bien la taille désiré c'est à dire 17.

    Donc je ne comprend pas pourquoi strlen ne me donne pas la bonne taille.

  2. #2
    Expert éminent
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 629
    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 : 2 629
    Points : 6 122
    Points
    6 122

    Par défaut

    Citation Envoyé par hbx360 Voir le message
    Donc je ne comprend pas pourquoi strlen ne me donne pas la bonne taille.
    Parce que tu n'as pas compris le principe même d'une chaîne de caractères en C/ C++, principe qui est utilisé par toutes les fonctions str** de la bibliothèque standard : le caractère '\0' final.

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    avril 2011
    Messages
    205
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : avril 2011
    Messages : 205
    Points : 67
    Points
    67

    Par défaut

    ok mais avant de mettre le '\0' il faut bien connaître la taille du tableau allouer non ?

    Comme ça ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    nLgName = strlen(gMenu.pTmpScName);
     
    u32 i=0;
    for(i = 0; i < nLgName; i++)
    {
     gMenu.pTmpScName[i] = 0;
     if(i == nLgName-1)
       gMenu.pTmpScName[i] = 0;
    }

  4. #4
    Expert éminent
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 629
    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 : 2 629
    Points : 6 122
    Points
    6 122

    Par défaut

    Citation Envoyé par hbx360 Voir le message
    ok mais avant de mettre le '\0' il faut bien connaître la taille du tableau allouer non ?
    Lorsque tu auras un peu d'expérience, en C tu as besoin de 2 tailles
    • La taille d'allocation qui permet de faire des realloc (<- lien cplusplus.com anglais) au besoin et ainsi de limiter les allocations en allouant ta chaîne morceau par morceau.
    • La taille réelle de la chaîne de caractères



    Citation Envoyé par hbx360 Voir le message
    Comme ça ?
    Non c'est de la daube : soit tu mets un '\0' final à la première case soit tu utilises memset (<- lien cplusplus.com anglais)

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    avril 2011
    Messages
    205
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : avril 2011
    Messages : 205
    Points : 67
    Points
    67

    Par défaut

    ok merci pour ton aide.

    Mais en fait sa me dit pas la raison pourquoi y a une différence ?

  6. #6
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    5 121
    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 121
    Points : 16 852
    Points
    16 852

    Par défaut

    Une chaine est une suite de char, désignée par son début, et qui se prolonge jusqu'au premier '\0'.
    Son début est stocké dans un pointeur (char*).

    Mais un pointeur ne désigne jamais qu'une position en mémoire, qui peut être:
    • n'importe quoi (pointeur non initialisée)
    • toujours fausse (dans le cas de l'adresse 0)
    • une autre variable (obtenue avec &bidule)
    • un bloc mémoire alloué dynamiquement (valeur de retour de malloc, calloc ou realloc, entre autre)


    La manière de t'en servir t'appartient, et l'une d'entre elle est la chaine de caractères.
    On peut aussi avoir juste un bloc mémoire qu'on ne veut pas copier (on parle en C de passage par référence).
    Ou encore une séquence (un tableau) dont la taille ne peut pas être connue juste avec le pointeur (il faut une seconde variable pour s'en souvenir).
    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

  7. #7
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    6 638
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 6 638
    Points : 18 757
    Points
    18 757
    Billets dans le blog
    1

    Par défaut

    Bonjour
    Citation Envoyé par hbx360 Voir le message
    Mais en fait sa me dit pas la raison pourquoi y a une différence ?
    Parce que strlen() compte le nombre de caractères d'une chaine en s'appuyant sur le '\0' qui doit la terminer. Mais ce '\0' il faut qu'il soit positionné. Et il ne peut être positionné que
    • manuellement => tu écris directement le '\0' dans un des éléments du tableau (tu en as le droit)
    • par une fonction dédiée à la création de chaines (fgets(), strcpy(), strcat(), etc). Toutes ces fonctions savent (enfin je veux dire que le programmeur qui les a écrite sait) qu'il faut mettre un '\0' donc elles le mettent (enfin c'est le programmeur qui le fait mais le résultat est le même)


    Toi, que fais-tu ? Ben rien de tout ça. Tu demandes juste au système "donne moi de l'espace" donc il te le donne. Il va dans sa mémoire, cherche une zone de libre, la réserve (donc enregistre simplement qu'elle n'est plus libre) et te donne son adresse. Nulle part il n'a positionné quoi que ce soit dans cette zone (déjà ce n'est pas son rôle et d'autre part ce serait un travail inutile puisque la logique veut qu'un développeur qui réserve une zone commence ensuite par la remplir avant de la traiter). Mais toi tu ne l'as pas remplie. Donc la zone contient n'importe quoi (qui peut venir par exemple d'une autre partie du traitement si par exemple plus en amont de ton code elle avait été là aussi réservée, remplie puis libérée => elle garde donc ce qui avait été mis à ce moment là).

    Toutefois il se trouve que par hasard, à la 4° position, la zone contient un caractère '\0'. Ca peut arriver le hasard (après tout, il n'y a que 256 caractères distincts donc sur une mémoire de 4Go les statistiques disent qu'il y aura 4Go/256 = 16 777 216 fois chaque caractère (y compris le '\0'). Donc la fonction strlen() a compté jusqu'à ce '\0' et t'a renvoyé combien de caractères elle avait trouvé (ici 3).
    Ceci dit, ce nombre changera probablement si tu recompiles et relances. Ou si tu rajoutes une instruction en amont (voire aussi en aval) du code. Bref c'est ce qu'on appelle un "comportement indéterminé". Il se produit dans de nombreux cas dont celui où on traite une zone mémoire sans l'avoir remplie auparavant.

    Citation Envoyé par hbx360 Voir le message
    je déclare une chaîne de caractères avec un malloc comme ceci :
    Hé non, c'est là ta principale erreur. malloc() n'a pas pour rôle de déclarer des "chaines de caractères". Déjà le verbe "déclarer" est mal employé car on l'emploie pour indiquer au compilateur l'existence d'une variable. Mais c'est juste du détail qui n'a pour l'instant aucune importance (culture générale quoi).
    Donc (revenons à malloc) son rôle est juste de réserver de l'espace. Ce que sera cet espace ensuite ne regarde que toi. Tu as le droit d'en faire une chaine si tu veux mais pour ça, une seule solution: y mettre par un moyen ou un autre un caractère '\0'.

    Un exemple tout con
    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
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    char toto[100];
    // Ici je déclare l'existence de toto et je la définis comme une zone de 100 caractères (regarde bien la nuance entre "déclarer" et "définir")
    // Tu remarqueras aussi que je n'ai pas dit "chaine". En effet, toto n'est pas une chaine (je n'y ai pas mis de '\0').
    // C'est donc simplement un tableau de caractères
     
    for (i=0; i < 10; i++) toto[i]='X';
    // Ici, la zone "toto" n'est toujours pas une chaine (j'ai beau y avoir mis 10 caractères 'X', je n'y ai toujours pas mis de '\0')
    // Je n'ai donc pas le droit de la faire traiter par aucune fonction de traitement de chaines (strcpy, printf, strlen, etc).
     
    toto[42]='\0';
    // Ici, la zone "toto" est maintenant une chaine puisque j'y ai mis un '\0'
    // Pourquoi je l'ai mis à la position 42 ? Ben parce que j'en avais 1) le droit (j'ai 100 caractères à ma disposition) et 2) l'envie
    // Ceci dit, j'ai rempli les positions de 0 à 9 et la position 42. Je ne sais donc rien des positions de 10 à 41. Mais ça, c'est mon problème.
     
    printf("len=%d\n", strlen(toto));
    // Je ne sais absolument pas ce que j'aurai comme résultat puisque je ne sais rien des cases 10 à 41 (peut-ête y a-t-il un '\0' qui y traine).
    // Peut-être aurai-je 10 ? Peut-être aurai-je 42 ? Peut-être autre chose ?  Mais ça reste ma merde et je ne peux m'en prendre qu'à moi-même.
    // La seule chose dont je sois sûr, c'est que le nombre que j'aurai sera obligatoirement compris entre 10 et 42.
     
    toto[42]='X';
    // Ici la zone "toto" n'est de nouveau plus une chaine. Mais encore une fois, c'est moi seul que ça regarde
    // Tant que je ne demande pas de traitement strXXX(), je suis serein.
     
    toto[10]='\0';
    // Ici la zone "toto" est de nouveau redevenue une chaine mais là, en plus, je la maitrise entièrement du début jusqu'à sa fin naturelle.
    // Et quoi qu'elle contienne après cette position 10, je m'en bats l'oeuil car toutes les fonctions de traitement de chaines s'arrêteront au premier '\0' trouvé.
     
    printf("len=%d\n", strlen(toto));
    // Je sais avec certitude que j'aurai 10
    // Et j'aurais écrit dès le départ strcpy(toto, "XXXXXXXXXX") ça aurait été la même chose (strcpy aurait copié la chaine et rajouté le '\0' à la fin)

    Bref la philosophie du C est "pas de barrière ; le programmeur sait ce qu'il fait. Et s'il sort de la route ben tant pis pour sa tronche".
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

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

Discussions similaires

  1. malloc sur char* (avec le sizeof d'un char[])
    Par fcjunic dans le forum Débuter
    Réponses: 2
    Dernier message: 01/02/2011, 09h32
  2. Problème de taille avec les structures
    Par Folkene dans le forum Débuter
    Réponses: 9
    Dernier message: 20/01/2010, 18h13
  3. Réponses: 3
    Dernier message: 13/09/2008, 20h23
  4. problème de taille avec \includegraphics
    Par thorion dans le forum Tableaux - Graphiques - Images - Flottants
    Réponses: 4
    Dernier message: 13/03/2008, 23h34
  5. Réponses: 21
    Dernier message: 21/11/2005, 10h52

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