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 :

Chaine et pointeur de char: pourquoi ca marche?


Sujet :

C++

  1. #1
    Membre chevronné
    Homme Profil pro
    Enseignant
    Inscrit en
    Juin 2004
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Juin 2004
    Messages : 539
    Par défaut Chaine et pointeur de char: pourquoi ca marche?
    Bonjour,

    je ne comprends pas pourquoi ce bout de code non seulement passe à la compilation, mais en plus s'exécute comme il faut:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    char* a;
    char* b;
    a="bonjour";
    std::cout << a << endl;
    b="salut a tous";
    a=b;
    std::cout << a;
    Mon compilateur (visual c++ 2008 express) génère-t-il automatiquement du code permettant de gérer dynamiquement la mémoire allouée aux chaines?

    Merci

  2. #2
    Membre Expert
    Avatar de muad'dib
    Homme Profil pro
    Développeur Java
    Inscrit en
    Janvier 2003
    Messages
    1 013
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Janvier 2003
    Messages : 1 013
    Par défaut
    Avec un compilateur C cela fonctionne également. Lors de l'instruction a = "bonjour", le compilateur alloue automatiquement de la mémoire pour cette chaine de caractère. Ceci est appliquable aux chaine de caractères uniquement.

    Cependant cet espace mémoire ne peut être modifié. Je ne sais plus pourquoi, mais il me semble que le compilateur le générant automatiquement, le programmeur n'a pas connaissance de l'adresse de la chaine et il ne peut plus la modifier par la suite. C'est pourquoi il faut déclarer ce type de variable en tant que const: L'écriture correcte serait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const char* a = "bonjour";
    Le code suivant génère une erreur de segmentation dû au fait de la modification de la mémoire contenant la chaine allouée par le compilateur
    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
    #include <stdio.h>
    #include <string.h>
     
    int main() {
            char* a;
     
            a = "bonjour";
     
            printf(a);
            fflush(stdout);
     
            strcpy(a, "salut");
     
            printf(a);
            fflush(stdout);
            return 0;
    }

  3. #3
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 394
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 394
    Par défaut
    La mémoire n'est pas allouée dynamiquement à l'exécution, mais statiquement. La chaîne est stockée directement dans une zone de données en lecture seule, dès le chargement du programme. En fait, dans ce code, a est l'équivalent d'un pointeur vers une variable globale, mais une variable globale qui serait en lecture seule bien qu'elle ne soit pas déclarée const.

    Si tu compiles avec gcc, tu peux activer l'option -Wwrite-strings, et les chaînes deviendront const: Tu auras une erreur de compilation en C++ (ou un warning en C) sur la ligne du a="bonjour"; si a n'est pas un pointeur const.

    Pourquoi les chaînes ne sont-elles pas const dès le début? Je crois que c'est pour la compatibilité avec les plus vieux codes. Je dis peut-être des c*nneries, mais il me semble qu'en un temps que tu ne peux connaitre, le mot-clé const n'existait pas en C.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  4. #4
    Membre chevronné
    Homme Profil pro
    Enseignant
    Inscrit en
    Juin 2004
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Juin 2004
    Messages : 539
    Par défaut
    Merci pour vos réponses.

    Je savais bien qu'on pouvait faire char* a = "bonjour"; mais je ne savais pas que l'on pouvais différer l'affectation de la chaine à a.

    Si je comprends bien, le compilateur va analyse le source, voir le b = "salut a tous", puis le a = b pour savoir quelle est la taille de la mémoire à réserver pour faire pointer a dessus.


    En tout cas, en remplaçant le
    a = "bonjour";
    par
    cin >> a;
    ça plante bien. Ouf!

  5. #5
    Membre émérite

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Par défaut
    Citation Envoyé par jackk Voir le message
    Je savais bien qu'on pouvait faire char* a = "bonjour"; mais je ne savais pas que l'on pouvais différer l'affectation de la chaine à a.
    La variable a n'est pas une chaine, c'est un pointeur vers un caractère.

    Si je comprends bien, le compilateur va analyse le source, voir le b = "salut a tous", puis le a = b pour savoir quelle est la taille de la mémoire à réserver pour faire pointer a dessus.
    Non, aucune chaine n'est alloué dynamiquement. Le code est équivalent à quelque chose comme ça
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    static char const _string1[] = "bonjour";
    static char const _string2[] = "salut";
     
    char* a;
    char* b;
    a=(char*)_string1; // fait pointer a vers le premier caractère de _string1
    std::cout << a << endl;
    b=(char*)_string2; // fait pointeur b vers le premier caractère de _string2
    a=b; // fait pointer a vers le même caractère que b (_string2)
    std::cout << a;

  6. #6
    Membre chevronné
    Homme Profil pro
    Enseignant
    Inscrit en
    Juin 2004
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Juin 2004
    Messages : 539
    Par défaut
    La variable a n'est pas une chaine, c'est un pointeur vers un caractère.
    Je n'ai jamais dit que a était une chaine. Par contre "bonjour" en est bien une.

    Non, aucune chaine n'est alloué dynamiquement. Le code est équivalent à quelque chose comme ça
    dans mon message #4, je ne parle plus de gestion dynamique de la mémoire puisque je parle du compilateur.

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 460
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 460
    Par défaut
    Citation Envoyé par jackk Voir le message
    Je n'ai jamais dit que a était une chaine. Par contre "bonjour" en est bien une. dans mon message #4, je ne parle plus de gestion dynamique de la mémoire puisque je parle du compilateur.
    C'est parce que ce sont plusieurs notions différentes et indépendantes qui sont mises en œuvre ici.

    Tout d'abord, quand tu composes une chaîne de caractères entre guillemets, le C++ suit le modèle du C. Il ne va pas créer un objet évolué tel qu'un std::string ou autre.

    D'autre part, ces caractères ne sont pas alloués dynamiquement à l'exécution du programme. Ils sont réservés en tant que tels à sa compilation, et accessibles dans une zone de mémoire en lecture seule. On pourrait presque dire « dans le code » pour illustrer cela mais ça resterait un abus de langage.

    Ensuite, il s'agit d'une chaîne de caractères consécutifs en mémoire, donc d'un tableau de caractères. Et, comme tous les tableaux, il est repéré par un pointeur sur son premier élément. Une chaîne de caractères en guillets "xxxx" est donc une expression de type const char *. C'est pourquoi tu déclares une variable de type « pointeur sur un const char ».

    Enfin, et c'est la clé du mystère, a et b sont tous les deux des pointeurs (sur des const char). Donc, ils ne contiennent que l'adresse de début de chaque chaîne. Lorsque tu fais a=b, la valeur de a devient égale à celle de b. Donc a et b contiennent toutes les deux l'adresse en mémoire du début de la seconde chaîne. Mais à aucun moment, il n'y destruction ou réallocation du contenu. À noter que, dans ton exemple, l'adresse de début de la première chaîne est perdue. C'est une cause fréquente de fuite de mémoire, mais dans le cas présent, cela n'a aucun impact, puisque la zone référencée n'a pas été allouée au runtime et n'est pas recyclable non plus.

    Si je comprends bien, le compilateur va analyse le source, voir le b = "salut a tous", puis le a = b pour savoir quelle est la taille de la mémoire à réserver pour faire pointer a dessus.
    Non. Le compilateur va examiner la longueur de la chaîne entre guillemets, la rajouter au contenu du segment de données, et la remplacer par l'emplacement où il l'a mise.

    Par la suite, « a = b » est une simple affectation de variables, exactement comme si a et b étaient deux entiers (en fait, c'est même littéralement la même chose puisqu'en pratique, sur les machines 32 bits habituelles, un pointeur a le même format qu'un int).

    C'est pour cela que l'exemple que tu donnes plante : il fait complètement abstraction de l'allocation mémoire : dans le cas de cout, a est un pointeur vers un buffer de caractères à afficher. Il suffit d'aller les lire et tout se passe bien. Dans le cas de cin, a est toujours censé être un pointeur vers un buffer, dans lequel on s'apprête à stocker les caractères reçus. Sauf que ce buffer se trouve toujours en mémoire à lecture seule et qu'on ne peut rien y mettre. Donc évidement, ça va planter.

    Ça planterait aussi si le buffer avait bien été alloué en mémoire inscriptible à l'exécution mais que le nombre de caractères reçus dépassait la taille de ce tampon.

    La seule chose à retenir, donc, est qu'un pointeur est l'emplacement de quelque chose en mémoire et non pas le quelque chose lui-même.

Discussions similaires

  1. Réponses: 2
    Dernier message: 15/03/2014, 20h18
  2. Sizeof d'un pointeur sur char ...
    Par Mike888 dans le forum C
    Réponses: 8
    Dernier message: 03/11/2005, 13h04
  3. [OLE DB] pourquoi ca marche pas ??
    Par aurel89 dans le forum MFC
    Réponses: 3
    Dernier message: 09/09/2005, 17h23
  4. Accept : pourquoi ça marche pas ?
    Par doudblast dans le forum Linux
    Réponses: 16
    Dernier message: 08/03/2005, 11h48
  5. Chaines et pointeurs mais pas "const"
    Par hpfx dans le forum C
    Réponses: 9
    Dernier message: 05/10/2003, 20h23

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