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 :

Différentes déclarations de Chaines de Caractères


Sujet :

C

  1. #1
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2016
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Août 2016
    Messages : 11
    Points : 12
    Points
    12
    Par défaut Différentes déclarations de Chaines de Caractères
    Bonjour à tous,

    J'apprends le C et je me demandais quelques questions sur la déclaration des chaînes de caractères. Je me demandais quelles étaient les différences principales entre ces deux déclarations :
    1) char *chaine1= "hello!";2) char chaine2[] = "hello!";De ce que j'ai compris, la déclaration 1) stocke un pointeur vers une chaîne de caractères donc la chaîne de caractères elle-même n'est pas "située" dans la variable mais à une certaine adresse. En revanche, la déclaration 2) stocke la chaîne de caractère avec la mémoire minimale nécessaire.
    J'aurais donc plusieurs questions :
    - si je fais un printf("%p", chaine1); j'obtiens une adresse (normal c'est un pointeur), ce qui est cohérent car si je fais un sizeof(chaine1), j'obtiens 4 octets. Par contre, si je fais un printf("%s", chaine1) j'obtiens une chaîne de caractères... donc chaine1 est un pointeur ou une chaîne de caractères ? Pour moi c'est soit une adresse, soit un tableau de caractères, mais pas les deux !
    - même question pour chaine2, j'obtiens également une adresse mais pourtant chaine2 est une variable qui prend la place nécessaire pour stocker "hello!" et non celle d'un pointeur.
    - si je modifie chaine1 par une commande de type strcpy, le programme plante, j'imagine que cela vient du fait que je souhaite modifier une adresse et non une chaîne de caractères, mais je ne comprends pas bien.
    - si je modifie chaine2 par une chaîne plus grande, que se passe-t-il au niveau mémoire ? Est-ce que la commande strcpy s'assure qu'il y a de la place et change l'adresse le cas échéant ? ou n'y a-t-il aucune sécurité et dans ce cas on peut écraser des données mises en mémoire ?

    Merci de vos réponse,
    Andrew

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 630
    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 : 4 630
    Points : 10 556
    Points
    10 556
    Par défaut
    En fait, ce n'est pas compliqué (*)

    Tu vas créer 2 variables locales (sur la pile):
    • un pointeur
    • un tableau à taille fixe que tu laisses le compilateur fixer/ trouver la taille (**)


    Mais, le pointeur va pointer vers un "string literal" qui est contant.


    D'ailleurs avec un compilateur C++, tu devrais avoir un warning pour te forcer à coder "const char *chaine1= "hello!";"


    ** -> c'est la même chose que char test[] = {'H', 'e', 'l', 'l', 'o', '!'};


    Citation Envoyé par jamesandrew Voir le message
    Pour moi c'est soit une adresse, soit un tableau de caractères, mais pas les deux !
    Ce n'est pas la même chose sur le fond
    • Un pointeur peut pointer vers un type incomplet, on peut faire des pointeurs de pointeurs de pointeurs.
    • Un tableau a une taille en octets, il y a l'arithmétique des pointeurs qui changent et on utilise la syntaxe [] (mais la syntaxe pointeur fonctionne *(tab + X))


    Mais, un tableau c'est aussi un pointeur, qui pointe sur le premier élément


    Édit: On peut lire sur cette page string literal
    In C, string literals are of type char[], and can be assigned directly to a (non-const) char*. C++03 allowed it as well (but deprecated it, as literals are const in C++). C++11 no longer allows such assignments without a cast.

    A string literal is not necessarily a C string: if a string literal has embedded null characters, it represents an array which contains more than one string.


    *: J'espère ne pas me tromper même après vérifications

  3. #3
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2016
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Août 2016
    Messages : 11
    Points : 12
    Points
    12
    Par défaut
    Merci foetus pour ta réponse. Je n'ai pas tout à fait compris quand tu dis qu'un tableau est aussi un pointeur. En fait, voilà ce que j'ai cru comprendre :

    char *chaine1= "hello!" définit un pointeur chaine1 de taille 4 octets (un entier) qui contient l'adresse du premier caractère de "hello!", chaine1[0] correspond à l'évaluation de la variable pointée par chaine1 soit *(chaine1), chaine1[1] correspond à l'évaluation de la variable pointée par chaine1 + 1 soit *(chaine1 + 1) etc.

    alors que

    char chaine2[] = "hello!" définit un tableau de caractères de taille 7 octets (6 caractères + le caractère nul) qui se trouve être aussi un pointeur qui contient l'adresse du premier caractère de "hello!". Est-ce que chaine2[0] correspondrait dans ce cas à la valeur du premier élément du tableau (égal à l'évaluation de la variable pointée par chaine2) ?

    Pour être plus clair, ce que je veux dire, c'est que les opérations qui se cachent derrière l'évaluation chaine1[0] ne sont pas les mêmes que pour chaine2[0] bien que le résultat soit le même. Dans un cas, on évalue la variable pointée par chaine1 alors que dans l'autre on évalue la valeur du premier élément du tableau chaine2. Ai-je juste ?

    Pour revenir sur la taille en octets de chaine2, s'il s'agit d'un tableau et d'un pointeur, pourquoi se taille n'est-elle pas 11 octets (4 pour le pointeur + 7 pour les caractères) ?

  4. #4
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 453
    Points : 43 106
    Points
    43 106
    Par défaut
    Pour revenir sur la taille en octets de chaine2, s'il s'agit d'un tableau et d'un pointeur, pourquoi se taille n'est-elle pas 11 octets (4 pour le pointeur + 7 pour les caractères) ?
    car ton pointeur contient une adresse 32 bits->4 octets. Un pointeur est une variable qui contient l'adfresse d'une variable
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 630
    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 : 4 630
    Points : 10 556
    Points
    10 556
    Par défaut
    Citation Envoyé par jamesandrew Voir le message
    C'est que les opérations qui se cachent derrière l'évaluation chaine1[0] ne sont pas les mêmes que pour chaine2[0]
    Si ce sont les même opérations , parce qu'on travaille avec le même type que ce soit avec un pointeur ou avec un tableau.

    La seule différence, c'est le fait que le "string literal" soit constant, mais qu'on peut modifier les cases du tableau (pas sa taille, elle est fixe)


    Citation Envoyé par jamesandrew Voir le message
    Pour revenir sur la taille en octets de chaine2, s'il s'agit d'un tableau et d'un pointeur, pourquoi se taille n'est-elle pas 11 octets (4 pour le pointeur + 7 pour les caractères) ?
    Non, tu travailles en 32 bits: c'est 4 octets pour un pointeur.

    Sinon teste ceci printf("%d %d\n", (sizeof(chaine1) / sizeof(chaine1[0])), (sizeof(chaine2) / sizeof(chaine2[0]));.
    Mais cela ne fonctionne pas partout

  6. #6
    Membre émérite
    Homme Profil pro
    sans emploi
    Inscrit en
    Janvier 2014
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2014
    Messages : 539
    Points : 2 601
    Points
    2 601
    Par défaut
    Citation Envoyé par foetus Voir le message
    [...]
    Mais, un tableau c'est aussi un pointeur, qui pointe sur le premier élément
    [...]
    Non, un tableau n'est pas un pointeur. La confusion vient qu'un tableau est toujours convertit en pointeur sur son premier élément sauf dans 3 cas (utilisation avec & unaire,sizeof et alignof).

    Citation Envoyé par Norme C11 - 6.3.2.1.3
    execpt when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
    norme C11

    Un pointeur est une variable qui contient une adresse, l'objet pointé est interprété suivant le type du pointeur. Un tableau est une suite contigüe d'objets dont le type est donné par le type du tableau. On ne peut assigner un tableau.

    C'est aussi la raison pour laquelle il y a une différence entre
    • une initialisation :
      Ici on déclare une variable de type tableau de char et on la définit avec une longueur égale à celle de la chaîne "a string" et on initialise son contenu avec le contenu de "a string".
    • une assignation
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      char str[12];
      str="a string";
      On déclare une variable de type tableau et on en définit la longueur égale à 12. Aucune initialisation est faite, et l'assignation va échouer car elle est invalide.

    À noter que la chaîne "a string" peut ou non exister en mémoire de manière indépendante (suivant les options de compilation, les autres chaînes littérales, …). Si elle réside en mémoire alors elle sera dans une section marquée read-only. Cela vient du fait que les compilateurs optimisent ces littérales en les «compressant». Les littérales ont un type const char *. Il existe aussi des options de compilation permettant de ne pas les mettre en read-only (-fwritable-strings avec gcc) ou au contraire d'émettre un warning quand on tente de le faire (-Wwrite-strings pour gcc).

  7. #7
    Membre émérite
    Homme Profil pro
    sans emploi
    Inscrit en
    Janvier 2014
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2014
    Messages : 539
    Points : 2 601
    Points
    2 601
    Par défaut
    Citation Envoyé par jamesandrew Voir le message
    [...]
    Pour être plus clair, ce que je veux dire, c'est que les opérations qui se cachent derrière l'évaluation chaine1[0] ne sont pas les mêmes que pour chaine2[0] bien que le résultat soit le même. Dans un cas, on évalue la variable pointée par chaine1 alors que dans l'autre on évalue la valeur du premier élément du tableau chaine2. Ai-je juste ?
    [...]
    Une expression telle que A[B] n'est que du sucre syntaxique, elle sera toujours «transformée» en *(A+B), un des deux doit être de type pointer et l'autre de type entier. Cela explique pourquoi une expression comme int n=5;char c=n["0123456789"]; est valide.

    Dans le cas d'un pointeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char *p;
    ...
    p[i]='a';
    ...
    On a le comportement suivant :
    p[i]='a' est transformé en *(p+i)='a' qui en français donne on part de l'adresse indiquée par p, on se dépace de i*(la taille du type pointé par p) byte pour obtenir une nouvelle adresse à la quelle on place la valeur 'a'.

    Dans le cas d'un tableau :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char p[42];
    ...
    p[i]='a';
    ...
    On a le comportement suivant :
    p[i]='a' est transformé en *(p+i)='a' puis en *( «pointeur sur le premier élément de p» + i) qui en français donne on part de l'adresse indiquée par le premier élément de p, on se dépace de i*(la taille du type pointé par p) byte pour obtenir une nouvelle adresse à la quelle on place la valeur 'a'.

    En gros un tableau se comporte souvent (au 3 execptions près) comme un pointeur mais n'est pas un pointeur.

  8. #8
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2016
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Août 2016
    Messages : 11
    Points : 12
    Points
    12
    Par défaut
    Merci à tous pour vos réponses, vous êtes hyper cools sur ce forum. Grâce à vous, j'ai maintenant compris les différences entre pointeur et tableau, et pourquoi je faisais cette confusion.

    Par rapport à ce que tu as dit picodev, quand tu parles de mauvaise assignation :
    char str[12];
    str="a string";

    Si je fais strcpy(str, "a string"), cela fonctionne, tu saurais pourquoi ?
    J'en profite pour réitérer une de mes questions initiales, si par exemple on déclare une variable de type tableau de 12 caractères et qu'ensuite on fait un strcpy pour assigner une chaîne plus longue que 12 caractères, que se passe-t-il au niveau mémoire ? Est-ce que la commande strcpy s'assure qu'il y a de la place et change l'adresse le cas échéant ? ou n'y a-t-il aucune sécurité et dans ce cas on peut écraser des données mises en mémoire ?

  9. #9
    Membre émérite
    Homme Profil pro
    sans emploi
    Inscrit en
    Janvier 2014
    Messages
    539
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : sans emploi
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2014
    Messages : 539
    Points : 2 601
    Points
    2 601
    Par défaut
    Citation Envoyé par jamesandrew Voir le message
    Par rapport à ce que tu as dit picodev, quand tu parles de mauvaise assignation :
    char str[12];
    str="a string";
    Ce n'est pas une mauvaise assignation, c'est simplement interdit par la norme : c'est non valide.
    Citation Envoyé par jamesandrew Voir le message
    Si je fais strcpy(str, "a string"), cela fonctionne, tu saurais pourquoi ?
    Toujours avec un char str[12];, cela fontionne simplement parce que la fonction reçoit comme argument un pointeur sur le premier élément du tableau str et non un tableau, de plus il ne s'agit pas d'une assignation (il n'y a pas de signe =).

    Citation Envoyé par jamesandrew Voir le message
    J'en profite pour réitérer une de mes questions initiales, si par exemple on déclare une variable de type tableau de 12 caractères et qu'ensuite on fait un strcpy pour assigner une chaîne plus longue que 12 caractères, que se passe-t-il au niveau mémoire ?
    Il peut se passer n'importe quoi, il s'agit d'un comportement indéfini → tu tapes dans de la mémoire qui n'appartient plus à l'objet que tu passes en paramètres. Comportement indéfini signifie entre autre (au choix) il ne se passe rien de particulier et ton programme semble fonctionner correctement (jusqu'au jour où il tombe en panne), tu tapes dans la mémoire à côté de l'objet qui peut être de la mémoire qui appartient à une autre variable du coup tu modifies la valeur d'une autre variable, tu tapes dans de la mémoire qui ne t'appartient pas du tout et ça provoque un gros crash.

    Citation Envoyé par jamesandrew Voir le message
    Est-ce que la commande strcpy s'assure qu'il y a de la place et change l'adresse le cas échéant ? ou n'y a-t-il aucune sécurité et dans ce cas on peut écraser des données mises en mémoire ?
    Non, la philosophie du C est telle qu'on part du principe que «l'utilisateur est adulte et responsable, il sait ce qu'il fait et on le laisse se tirer dans le pied s'il l'a décidé».
    Il y a la fonction strncpy qui est un peu plus sécurisée si tu veux.
    Il existe aussi des outils qui détecte ce genre de comportement illicite (valgrind) et également des options de compilations (gcc et clang proposent des address sanitizers de plus en plus perfectionnés).

  10. #10
    Membre à l'essai
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2016
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : Août 2016
    Messages : 11
    Points : 12
    Points
    12
    Par défaut
    Merci picodev t'es vraiment trop fort. Grâce à la précision de tes réponses, tout est clair maintenant

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 22/07/2008, 17h16
  2. Réponses: 6
    Dernier message: 27/04/2008, 10h53
  3. Comparer la chaine de caractère de deux cellules différentes
    Par idir.17 dans le forum Macros et VBA Excel
    Réponses: 11
    Dernier message: 21/03/2007, 14h48
  4. Réponses: 4
    Dernier message: 12/03/2006, 20h47
  5. Réponses: 4
    Dernier message: 14/12/2005, 17h25

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