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 :

Pointeur sur variable : char * str = "foo";


Sujet :

C

  1. #1
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 149
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 149
    Points : 28 116
    Points
    28 116
    Par défaut Pointeur sur variable : char * str = "foo";
    Bonjour,

    J'ai un soucis avec un bete exemple sur les pointeurs de constante dans un code que je reprends :
    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
    23
    24
    25
    26
    27
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <strings.h>
     
    int main (void)
    {
      char * str = "bonjour";
     
      fprintf (stdout, "declarations OK\n");
     
      str = "toto";
      fprintf (stdout, "non, ca ne peut pas fonctionner 1 \n");
      fprintf (stdout, "%p : %s\n", str, str);
     
      sprintf (str, "court");
      fprintf (stdout, "non, ca ne peut pas fonctionner 2 \n");
      fprintf (stdout, "%p : %s\n", str, str);
     
      sprintf (str, "beaucouptroplong");
      fprintf (stdout, "non, ca ne peut pas fonctionner 3 \n");
      fprintf (stdout, "%p : %s\n", str, str);
     
      return EXIT_SUCCESS;
     
    }
    J'insiste bien sur le fait qu'il n'y a pas de const ici, ni sur le pointeur ni sur la valeur pointee.

    Ma question est la suivante : est-ce que les cas ci-dessus sont corrects, et si non, pourquoi ?
    J'ai bien un indice avec le fait que le sprintf crash sous Linux (les 3 cas passent sous AIX et Solaris), mais je trouve etrange le premier cas, et j'ai du mal a croire que c'est un comportement "normal".

    Et surtout, si je souhaite modifier le contenu de la chaine de caractere, quel est bon comportement en gardant cette declaration ?

    Pour info, j'ai bien lu ceci et je comprends bien le probleme d'overhead de cette construction, mais je ne peux pas tout re-ecrire.
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 620
    Points
    23 620
    Par défaut
    Hello,

    Si tu veux déclarer une chaîne dans ton programme que tu puisses modifier durant l'exécution, il faut écrire :
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
        char str[] = "bonjour";

    …donc un tableau de caractères, dont la taille est définie par la rvalue.

    Si tu écris char * str = "Bonjour";, tu déclares un pointeur, c'est-à-dire une variable qui mesure toujours 4 ou 8 octets de long sur les architectures 32 et 64 bits respectivement et qui contient une adresse mémoire. Il se trouve que tu initialises ce pointeur avec l'adresse d'une chaîne constante, qui elle se retrouve donc en mémoire en lecture seule.

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Le cas 1 est correct : pointeur = un autre pointeur, pas de soucis. Le pointeur est modifiable, tu peux donc lui assigner une nouvelle valeur.
    Les cas 2 et 3 sont incorrects car tu ne peux pas modifier la zone pointée, tu n'as pas réservé d'espace mémoire. Sous Windows, ça plante aussi.

    PS : lien intéressant !

  4. #4
    Membre éclairé
    Profil pro
    Ingénieur sécurité
    Inscrit en
    Février 2007
    Messages
    574
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2007
    Messages : 574
    Points : 751
    Points
    751
    Par défaut
    Citation Envoyé par gangsoleil Voir le message
    Bonjour,
    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
    23
    24
    25
    26
    27
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <strings.h>
     
    int main (void)
    {
      char * str = "bonjour";
     
      fprintf (stdout, "declarations OK\n");
     
      str = "toto";
      fprintf (stdout, "non, ca ne peut pas fonctionner 1 \n");
      fprintf (stdout, "%p : %s\n", str, str);
     
      sprintf (str, "court");
      fprintf (stdout, "non, ca ne peut pas fonctionner 2 \n");
      fprintf (stdout, "%p : %s\n", str, str);
     
      sprintf (str, "beaucouptroplong");
      fprintf (stdout, "non, ca ne peut pas fonctionner 3 \n");
      fprintf (stdout, "%p : %s\n", str, str);
     
      return EXIT_SUCCESS;
     
    }
    "Bonjour" et "toto" sont considere comme constant donc declare dans .rodata de ton elf:

    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
    [dahtah@centos63 ~]$ objdump -s -j .rodata a.out 
    
    a.out:     file format elf64-x86-64
    
    Contents of section .rodata:
     4007d8 01000200 00000000 00000000 00000000  ................
     4007e8 626f6e6a 6f757200 6465636c 61726174  bonjour.declarat
     4007f8 696f6e73 204f4b0a 00746f74 6f000000  ions OK..toto...
     400808 6e6f6e2c 20636120 6e652070 65757420  non, ca ne peut 
     400818 70617320 666f6e63 74696f6e 6e657220  pas fonctionner 
     400828 31200a00 2570203a 2025730a 00636f75  1 ..%p : %s..cou
     400838 72740000 00000000 6e6f6e2c 20636120  rt......non, ca 
     400848 6e652070 65757420 70617320 666f6e63  ne peut pas fonc
     400858 74696f6e 6e657220 32200a00 62656175  tionner 2 ..beau
     400868 636f7570 74726f70 6c6f6e67 00000000  couptroplong....
     400878 6e6f6e2c 20636120 6e652070 65757420  non, ca ne peut 
     400888 70617320 666f6e63 74696f6e 6e657220  pas fonctionner 
     400898 33200a00         
    [dahtah@centos63 ~]$ readelf -S a.out | grep .rodata -A 1
      [15] .rodata           PROGBITS         00000000004007d8  000007d8
           00000000000000c4  0000000000000000   A       0     0     8
    Quant je compile et que je fait tourner ton code, il segfault, mais pas a l'endroit ou je pensais (je pensais qu'il crasherait au second test, ou tu essais d'ecrire dans .rodata):

    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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    [dahtah@centos63 ~]$ gdb a.out 
    GNU gdb (GDB) Red Hat Enterprise Linux (7.2-56.el6)
    Copyright (C) 2010 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "x86_64-redhat-linux-gnu".
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>...
    Reading symbols from /home/cisco/a.out...done.
    (gdb) break main
    Breakpoint 1 at 0x40059d: file /tmp/t.c, line 8.
    (gdb) break fprintf
    Breakpoint 2 at 0x4004a0
    (gdb) r
    Starting program: /home/dahtah/a.out 
    
    Breakpoint 1, main () at /tmp/t.c:8
    8	  char * str = "bonjour";
    Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.107.el6_4.5.x86_64
    (gdb) c
    Continuing.
    declarations OK
    non, ca ne peut pas fonctionner 1 
    
    Breakpoint 2, 0x00000038e2a4f320 in fprintf () from /lib64/libc.so.6
    (gdb) info target
    Symbols from "/home/dahtah/a.out".
    Unix child process:
    	Using the running image of child process 3249.
    	While running this, GDB does not access memory from...
    Local exec file:
    	'/home/dahtah/a.out', file type elf64-x86-64.
    	Entry point: 0x4004b0
    	0x0000000000400200 - 0x000000000040021c is .interp
    	0x000000000040021c - 0x000000000040023c is .note.ABI-tag
    	0x000000000040023c - 0x0000000000400260 is .note.gnu.build-id
    	0x0000000000400260 - 0x0000000000400284 is .gnu.hash
    	0x0000000000400288 - 0x0000000000400330 is .dynsym
    	0x0000000000400330 - 0x0000000000400385 is .dynstr
    	0x0000000000400386 - 0x0000000000400394 is .gnu.version
    	0x0000000000400398 - 0x00000000004003b8 is .gnu.version_r
    	0x00000000004003b8 - 0x00000000004003e8 is .rela.dyn
    	0x00000000004003e8 - 0x0000000000400448 is .rela.plt
    	0x0000000000400448 - 0x0000000000400460 is .init
    	0x0000000000400460 - 0x00000000004004b0 is .plt
    	0x00000000004004b0 - 0x00000000004007c8 is .text
    	0x00000000004007c8 - 0x00000000004007d6 is .fini
    	0x00000000004007d8 - 0x000000000040089c is .rodata
    	0x000000000040089c - 0x00000000004008c0 is .eh_frame_hdr
    	0x00000000004008c0 - 0x0000000000400944 is .eh_frame
    	0x0000000000600948 - 0x0000000000600958 is .ctors
    	0x0000000000600958 - 0x0000000000600968 is .dtors
    	0x0000000000600968 - 0x0000000000600970 is .jcr
    ---Type <return> to continue, or q <return> to quit--- q
    Quit
    (gdb) find 0x00000000004007d8,0x000000000040089c,"bonjour"
    0x4007e8 <__dso_handle+8>
    1 pattern found.
    (gdb) x/s 0x4007e8
    0x4007e8 <__dso_handle+8>:	 "bonjour"
    (gdb) x/4s 0x4007e8
    0x4007e8 <__dso_handle+8>:	 "bonjour"
    0x4007f0 <__dso_handle+16>:	 "declarations OK\n"
    0x400801 <__dso_handle+33>:	 "toto"
    0x400806 <__dso_handle+38>:	 ""
    (gdb) c
    Continuing.
    0x400801 : toto
    
    Program received signal SIGSEGV, Segmentation fault.
    0x00000038e2a89881 in memcpy () from /lib64/libc.so.6
    (gdb) q
    Donc tu assignes bien l'adresse de "toto" a str, le fprintf fonctionne ok, mais il semble y avoir un memcpy vers .rodata dans la libc qui crash (possiblement appele depuis fprintf). Je me suis pas pris la tete, pour regarder plus loin...

    Sous Solaris, AIX, il faudrait verfier les permissions sur la section .data (ou sont stockes les constantes). Si c'est RW, ca expliquerait pourquoi ca marche, mais ca serait pas super niveau securite...

  5. #5
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 149
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 149
    Points : 28 116
    Points
    28 116
    Par défaut
    Merci pour toutes ces precisions.

    En fait, je n'avais pas pense que str = "toto"; assignait a str l'adresse de "toto" -- j'aurai du verifier les adresses.

    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

  6. #6
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 812
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 812
    Points : 7 097
    Points
    7 097
    Par défaut
    Pour prévenir qu'une variable peut être modifié à son insu, on pourrait utiliser le mot clé volatile...

    Si j'ai pas compris désolé.

    Bonne journée.
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Non. Volatile sert justement à indiquer qu'une variable peut-être modifiée par n'importe qui à n'importe quel moment sans qu'il y ait forcément un code à proximité qui fasse cette modification.

  8. #8
    Modérateur
    Avatar de gangsoleil
    Homme Profil pro
    Manager / Cyber Sécurité
    Inscrit en
    Mai 2004
    Messages
    10 149
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Manager / Cyber Sécurité

    Informations forums :
    Inscription : Mai 2004
    Messages : 10 149
    Points : 28 116
    Points
    28 116
    Par défaut
    Mon soucis n'etait pas comment ecrire du code propre, mais comprendre comment fonctionnait le code ci-dessus. Mais c'est bon, j'ai eu tout ce que je voulais, merci a vous.
    "La route est longue, mais le chemin est libre" -- https://framasoft.org/
    Les règles du forum

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    Le standard C contient une incohérence: Les chaînes littérales ne sont pas const, mais y écrire provoque un comportement indéfini.

    Si je me souviens bien, la raison pour laquelle les chaînes littérales ne sont pas const est la compatibilité avec les programmes écrits avant (soit avant qu'il devienne courant de mettre les chaînes en .rodata, soit avant l'invention du mot-clé const lui-même dans le langage).

    Par contre, gcc permet de les rendre const (option -Wwrite-strings). Visual Studio n'offre pas une telle option pour le C, mais les chaînes littérales sont const de type const char[taille] 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.

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

Discussions similaires

  1. Tableau dynamique de pointeurs sur const char*
    Par Le Mérovingien dans le forum Débuter
    Réponses: 6
    Dernier message: 05/06/2008, 14h23
  2. Problème de *pointeur sur des char
    Par Spartan03 dans le forum C++
    Réponses: 2
    Dernier message: 18/09/2005, 14h20

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