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 :

Les chaines de caractères en C [Tutoriel]


Sujet :

C

  1. #1
    Rédacteur

    Avatar de gege2061
    Femme Profil pro
    Administrateur de base de données
    Inscrit en
    Juin 2004
    Messages
    5 840
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Juin 2004
    Messages : 5 840
    Points : 11 625
    Points
    11 625
    Par défaut Les chaines de caractères en C
    http://nicolasj.developpez.com/articles/libc/string/

    Après un rappel sur la structure des chaines de caractères en C et un aperçu des fonctions de la bibliothèque standard servant à manipuler ces chaines, je vous propose de créer notre propre bibliothèque de manipulation des chaines de caractères.
    Vous pouvez laisser un commentaire sur cet article à la suite.

  2. #2
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par gege2061 Voir le message
    Les chaînes de caractères sont en fait stockées dans un tableau de caractères
    Il n'y a pas de 'caractères'. C'est un tableau d'entiers de type char.

    "Les chaînes de caractères sont en fait stockées dans un tableau de char"

    de code ASCII
    Il n'y a pas de 'code ASCII ' en C. une chaine est terminée par un caractère valant 0 (ou '\0' en octal ou '\x0' en hexadécimal, si on aime la complication).

    Le fait qu'il ait la même valeur que le NUL du charset ASCII est un détail d'implémentation qui ne nous intéresse pas dans une théorie générale sur le langage C.

    \0 n'existe pas en C. C'est '\0'.

    toute modification de la chaîne s3 se conclura par une erreur de segmentation
    Non. Ça se traduira par un comportement indéterminé.

    Ca fait déjà beaucoup d'imprécisions... J'ai pas trop envie de continuer...

    Je propose :

    "<...> dont la fin est marquée par un caractère nul, de valeur 0 et représenté par le caractère '\0' ou '\x0' ou la valeur 0 directement.

    Nota : le chiffre 0 (zéro) est le caractère '0'. Sa valeur dépend du charset utilisé (en ASCII, 48 décimal)"
    Pas de Wi-Fi à la maison : CPL

  3. #3
    Rédacteur

    Avatar de gege2061
    Femme Profil pro
    Administrateur de base de données
    Inscrit en
    Juin 2004
    Messages
    5 840
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Juin 2004
    Messages : 5 840
    Points : 11 625
    Points
    11 625
    Par défaut
    Corrigé selon tes conseils.


  4. #4
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par gege2061 Voir le message
    Corrigé selon tes conseils.

    OK.
    II-N. strtok
    Tu parles de 'ct', mais le paramètre est 't'...

    III-A. Modifier la case d'une chaîne de caractère
    ces fonctions retournant un espace alloué, j'aurais mis un suffixe _dyn ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *str_tolower_dyn (char const *s);
    char *str_toupper_dyn (char const *s)
    ;

    ou un rappel à strdup() ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *str_dup_tolower (char const *s);
    char *str_dup_toupper (char const *s);
    avec un rappel à 'free()'...

    j'aurais aussi prévu le cas plus simple de la chaine modifiable :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *str_tolower (char *s);
    char *str_toupper (char *s);
    Pas de Wi-Fi à la maison : CPL

  5. #5
    Membre éprouvé
    Avatar de ol9245
    Homme Profil pro
    Chercheur
    Inscrit en
    Avril 2007
    Messages
    985
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Chercheur

    Informations forums :
    Inscription : Avril 2007
    Messages : 985
    Points : 1 158
    Points
    1 158
    Billets dans le blog
    1
    Par défaut des mallocs à l'intérieur d'une fonction : est-ce bien raisonnable ?
    Bonjour,

    Je passais par ici par hasard. Je vois bien que l'exemple date un peu...
    Une remarque sur les exemples de code donnés, comme par exemple celui-ci :
    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
    char *str_tolower (const char *ct)
    {
       char *s = NULL;
    
       if (ct != NULL)
       {
          int i;
          s = malloc (sizeof (*s) * (strlen (ct) + 1));
          if (s != NULL)
          {
             for (i = 0; ct[i]; i++)
             {
                s[i] = tolower (ct[i]);
             }
             s[i] = '\0';
          }
       }
       return s;
    }
    Je crois que c'est une mauvaise idée de montrer à des débutants des exemples de fonctions structurées comme celle-ci. Une fonction est responsable de l'utilisation qu'elle fait de la mémoire. Donc si une fonction alloue de la mémooire avec un malloc, elle doit la libérer avec un free abant de sortir.

    En conséquence, une fonction comme celle-ci ne peut pas renvoyer son résultat sous forme d'une chaine de caractère qu'elle a allouée sur le tas. C'est une mauvaise pratique. La responsabilité d'allouer de la mémoire revient à l'appellant de la fonction, pas à la fonction elle-même.

    La solution la plus simple est de faire la modification sur la chaine en place. Sil l'appelant veut garder l'original, libre à lui de faire une copie avant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void to_upper(char *c)
    {
    	for (; *c; ++c)
    		if (*c >= 'a' && *c <= 'z')
    			*c += 'A' - 'a';
    }
    Une autre solution, pas très satisfaisante, est de prendre deux arguments, l'un en entrée, l'autre en sortie. Un peu moche.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void to_upper(const char *in, char *out)
    {
    	for (; *in; ++in, ++out)
    		*out = *in + (*in >= 'a' && *in <= 'z' ? 'A' - 'a' : 0);
    }
    Voilà. C'est juste mon grain de sel...
    "La vraie grandeur se mesure par la liberté que vous donnez aux autres, et non par votre capacité à les contraindre de faire ce que vous voulez." Larry Wall, concepteur de Perl.

  6. #6
    Expert éminent sénior
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Points : 20 985
    Points
    20 985
    Par défaut
    Citation Envoyé par ol9245 Voir le message
    Bonjour,

    Je passais par ici par hasard. Je vois bien que l'exemple date un peu...
    Une remarque sur les exemples de code donnés, comme par exemple celui-ci :
    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
    char *str_tolower (const char *ct)
    {
       char *s = NULL;
    
       if (ct != NULL)
       {
          int i;
          s = malloc (sizeof (*s) * (strlen (ct) + 1));
          if (s != NULL)
          {
             for (i = 0; ct[i]; i++)
             {
                s[i] = tolower (ct[i]);
             }
             s[i] = '\0';
          }
       }
       return s;
    }
    Je crois que c'est une mauvaise idée de montrer à des débutants des exemples de fonctions structurées comme celle-ci. Une fonction est responsable de l'utilisation qu'elle fait de la mémoire.
    Comme malloc() ? Ou fopen () ?

    J'ai bien insisté que le fait que la fonction allouait un bloc et qu'il fallait le libérer après usage. C'est la seule façon de faire des copies de chaines réentrantes, ce qui permet le multi-tâche ...

    Donc si une fonction alloue de la mémoire avec un malloc, elle doit la libérer avec un free avant de sortir.
    Dans ce cas, une variable locale devrait suffire ...

    En conséquence, une fonction comme celle-ci ne peut pas renvoyer son résultat sous forme d'une chaine de caractère qu'elle a allouée sur le tas. C'est une mauvaise pratique. La responsabilité d'allouer de la mémoire revient à l’appelant de la fonction, pas à la fonction elle-même.
    Voilà une position bien dogmatique. Il faudra en parler à celui qui a écrit fopen() ...

    Dans la pratique évidemment qu'on passe son temps à allouer ici et à libérer là. Certes ça demande quelques efforts de programmation et des outils de vérification (j'ai même fini par en écrire un, voir ma bibliothèque CLIB), mais c'est le seul moyen d'être efficace et de ne pas passer son temps à recopier des données qui peuvent parfois être longues ...

    Même si tu utilises un système plus symétrique qui ressemble à de la programmation objet (ADT), il y a quand même un fonction de création de l'objet et une fonction de destruction de celui-ci. Le fait que l'on contrôle manuellement la destruction permet de créer des objet dynamiques persistants (le temps de leur utilisation et dans n'importe quel tâche).

    Sur ce, moi je suis musicien, maintenant, alors ces problèmes là, je m'en tape un peu, là ...
    Pas de Wi-Fi à la maison : CPL

  7. #7
    Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2017
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 2
    Points : 2
    Points
    2
    Par défaut
    Dans ce cas, une variable locale devrait suffire ...
    Bonjour, à ma connaissance, contrairement aux langages disposant d'un système de "garbage collector", la mémoire allouée par malloc a besoin d'être libérée avant la perte de sa référence, en l'occurrence, la variable locale contenant l'adresse de cette mémoire... sinon c'est la fuite (en avant) de mémoire assurée.
    En C: 1 malloc => 1 free

  8. #8
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 471
    Points : 6 109
    Points
    6 109
    Par défaut
    den75,
    Quand ol9245 a énoncé la règle générale :
    si une fonction alloue de la mémooire avec un malloc, elle doit la libérer avec un free abant de sortir
    et que Emmanuel Delahaye avait répondu :
    Dans ce cas, une variable locale devrait suffire
    il ne proposait pas d'appeler malloc sans appeler free, mais d'allouer une variable automatique, qui sera donc automatiquement désallouée quand on sortira de la portée de cette variable.

    Cependant, quand la taille à allouer est inconnue à la compilation, il vaut souvent mieux allouer avec malloc que d'allouer un VLA (Variable Length Array) ou d'appeler une fonction non standard comme alloca.

    A part ça, de mon côté, je ne suis pas toujours contre les fonctions qui appellent malloc et demandent à l'utilisateur de la fonction d'appeler eux-même free. Mais, pour éviter les fuites mémoires, il faut utiliser une convention. Par exemple, j'aime bien le suffixe "_dyn" proposé par Emmanuel Delahaye.

    Par contre, pour une fonction qui convertit une chaîne en minuscules, je n'aurais pas fait d'allocation dans le tas. J'aurais fait comme ça :
    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
    #include <ctype.h>
    #include <stdio.h>
     
    void MyProject_str_to_tolower(char* restrict dest, const char* restrict src)
    {
    	for(; *src != '\0'; ++src, ++dest)
    		*dest = tolower(*src);
    	*dest = '\0';
    }
     
    int main()
    {
    	const char src[] = "Hello world!";
    	char dest[sizeof(src)]; // L'utilisateur est libre d'allouer la chaîne résultante dans la pile.
    	MyProject_str_to_tolower(dest, src);
    	printf("%s", dest);
    	return 0;
    }
    C'est proche de ce la solution que ol9245 avait qualifiée de moche, mais je ne trouve pas ça moche.

    Edit 2017-01-25-17h27 : Conversion en majuscules remplacée par une conversion en minuscules pour mieux coller aux précédents messages du fil.

  9. #9
    Membre expérimenté
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    543
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 543
    Points : 1 745
    Points
    1 745
    Par défaut
    Bonsoir, La discussion date cependant je peut-être apporté quelque précision sans trop lancé de débats.
    Citation Envoyé par Emmanuel Delahaye Voir le message

    Non. Ça se traduira par un comportement indéterminé.
    Je pense qu'il n'y a pas de comportement indéterminé en soi, mais il y a bien un segment défaut justifier .
    Je m’explique. Ici s3 const char *s3 = "Développez"; est un pointeur constant sur une chaîne constante. Lors de la phase de compilation toute modification sur s3 sera interdite et la compilation échouera, car l’erreur a été décelé, mais dans le cas contraire "char *s3 = "Développez" le compilateur placera la chaîne constante dans un segment de données en lecture seule et toute tentative de modification à travers ce pointeur aboutira systématiquement à une erreur de segmentation donc pour une variable pointeur sur une chaîne constante on aura systématiquement une erreur de segmentation et non un comportement indéterminé.

    Cas compilation avec const
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #include <stdio.h>
    #include <stdlib.h>
     
    int main( void ){
     
        const char *ptr = "Bonjour";
        ptr[2] = 'Z';                     
        return EXIT_SUCCESS;
    }
    Code Gcc : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    root@mv:~# gcc *.c -Wall -Werror -g -o Deb 
    source.c: In function 'main':
    source.c:7:9: error: assignment of read-only location '*(ptr + 2u)'
      ptr[2] = 'Z';

    Cas compilation sans const
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <stdio.h>
    #include <stdlib.h>
    
    int main( void ){
        
        char *ptr = "Bonjour";
        ptr[2] = 'Z';                     <<< Thread 1: address access protected (fault address: 0x400566)
        return EXIT_SUCCESS;
    }

    Code Gcc : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    root@mv:~# gcc *.c -Wall -Werror -g -o Deb 
    root@mv:~# ./Deb 
    Segmentation fault


    à bientôt.
    Celui qui peut, agit. Celui qui ne peut pas, enseigne.
    Il y a deux sortes de savants: les spécialistes, qui connaissent tout sur rien,
    et les philosophes, qui ne connaissent rien sur tout.
    George Bernard Shaw

  10. #10
    Candidat au Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2017
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2017
    Messages : 2
    Points : 2
    Points
    2
    Par défaut
    il ne proposait pas d'appeler malloc sans appeler free, mais d'allouer une variable automatique, qui sera donc automatiquement désallouée quand on sortira de la portée de cette variable.
    Oui, bien sûr. J'avais mal lu et le code de départ et les commentaires.
    Maintenant concernant le choix entre:

    Méthode 1:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void Appelant()
    {
    char src[]="Hello";
    char dst[LENGTH];
     
    StrManip(src,dst);
    ...
    }
    ou

    Méthode 2:
    void Appelant()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    {
    char src[]="Hello";
    char *dst=StrManip(src);
    ...
    free(dst);
    ...
    }
    Je pense que si LENGTH est prédictible voire simplement bornée alors il faut choisir la Méthode 1
    Mais dans le cas où la taille mémoire demandée pour le résultat est "trop variable" alors la Méthode 2 peut faire sens.

  11. #11
    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
    Citation Envoyé par sambia39 Voir le message
    Je pense qu'il n'y a pas de comportement indéterminé en soi, mais il y a bien un segment défaut justifier .
    [...]
    mais dans le cas contraire "char *s3 = "Développez" le compilateur placera la chaîne constante dans un segment de données en lecture seule et toute tentative de modification à travers ce pointeur aboutira systématiquement à une erreur de segmentation donc pour une variable pointeur sur une chaîne constante on aura systématiquement une erreur de segmentation et non un comportement indéterminé.
    C'est bien un comportement indéterminé https://www.securecoding.cert.org/co...+qualification

    Il n'y a pas obligatoirement d'erreur de segmentation. Par exemple, j'ai constaté que sur certains MCU, si tu essayes d'écrire à une adresse qui pointe vers de la mémoire flash, ça n'a juste pas d'effet.

  12. #12
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Citation Envoyé par sambia39 Voir le message
    mais dans le cas contraire "char *s3 = "Développez" le compilateur placera la chaîne constante dans un segment de données en lecture seule
    Je pense que ceci n'est pas vrai. Je ne vois nul part dans la norme du langage qu'une chaîne définie par char* doit se trouver en mémoire en lecture seule. Je sais que c'est souvent ce qui se passe et pour résoudre le souci, on fait souvent char s3[] = "Développez" si on veut modifier la chaîne mais je ne suis pas sûr que la norme le force vu que le C est utilisé sur des architectures qui n'ont pas de section lecture seule.

    Mais j'ai un doute donc je pose la question Si j'ai raison, alors il vaut mieux dire "souvent" que toujours.

  13. #13
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    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 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Concrêtement, ce n'est pas char* qui peut y être placée, ca c'est une variable sans importance pour la question.
    C'est la chaine litérale. Et encore, la norme ne l'impose pas. Je crois qu'elle n'impose même pas l'existence d'une mémoire dédiée à ce type d'objet.
    Elle pourrait tout aussi bien être dans le tas, ou en bas de la pile.
    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

  14. #14
    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
    La norme précise qu'il s'agit d'un UB si on tente de modifier les chaînes littérales. Ce qui est requis est qu'une telle chaîne soit stockée dans un tableau statique de la bonne longueur (pas plus, pas moins) → norme C11 6.4.5 String literals.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    char *ptr="hello world"; // ptr pointe sur le premier caractère du littéral
    *ptr='H'; // UB !
     
    char arr[]="hello world"; // on copie le littéral dans le tableau
    arr[0]='H'; // OK
    Dans le code précédent, la norme n'oblige en rien à avoir le littéral "hello world" en read only, ni même à n'avoir qu'une version de ce littéral ni même plusieurs.
    Le fait est qu'une optimisation courante est d'avoir des chaînes littérales en read only (plus contraignant qu'un const) simplement pour pouvoir gagner de la place et fusionner plusieurs chaînes dans un même tableau. gcc proposait un option il y a quelques années pour «rendre» ces chaînes écrivables (?) mais cette option a été enlevée. La tendance est de forcer une plus grande const correctness→-Wwrite-strings (pour autant que ce soit utile et cohérent en C). Du coup on voit souvent :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    const char *ptr="hello world";
    // à la place de
    char *ptr="hello world";

  15. #15
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    picodev

  16. #16
    Membre expérimenté
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    543
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 543
    Points : 1 745
    Points
    1 745
    Par défaut
    Bonsoir,

    Citation Envoyé par Bktero Voir le message
    C'est bien un comportement indéterminé https://www.securecoding.cert.org/co...+qualification.....
    Citation Envoyé par fearyourself Voir le message
    Je pense que ceci n'est pas vrai. Je ne vois nul part dans la norme du langage qu'une chaîne définie par char* doit se trouver en mémoire en lecture seule. Je sais que c'est souvent ce qui se passe et pour résoudre le souci, on fait souvent char s3[] = "Développez" si on veut modifier la chaîne mais je ne suis pas sûr que la norme le force vu que le C est utilisé sur des architectures qui n'ont pas de section lecture seule.

    Mais j'ai un doute donc je pose la question Si j'ai raison, alors il vaut mieux dire "souvent" que toujours.
    J’ai écrit que cela n’est pas un comportement indéterminé à cause de ce qui se trouve sous le capot.
    Alors comment se présentent les choses : dans un premier temps on va décompiler l’exécutable afin de déterminer où est-ce que notre variable pointeur sur chaîne constante se trouve-t-il, mais également savoir si le segment mémoire ou se trouve cette variable peut-être lue et modifiée

    Code objdump -Sr source.o && readelf -l Deb : 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
    root@mv:~# objdump -Sr source.o && readelf -l Deb
    
    source.o:     file format elf64-x86-64
    
    
    Disassembly of section .text:
    
    0000000000000000 <main>:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main( void ){
       0:	55                   	push   %rbp
       1:	48 89 e5             	mov    %rsp,%rbp
    
    	char *ptr = "DEBUG_TEST";
       4:	48 c7 45 f8 00 00 00 	movq   $0x0,-0x8(%rbp)
       b:	00 
    			8: R_X86_64_32S	.rodata
    	*(ptr) = 'A';
       c:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
      10:	c6 00 41             	movb   $0x41,(%rax)
    	return EXIT_SUCCESS;
      13:	b8 00 00 00 00       	mov    $0x0,%eax
    }
      18:	5d                   	pop    %rbp
      19:	c3                   	retq   
    
    Elf file type is EXEC (Executable file)
    Entry point 0x4003c0
    There are 8 program headers, starting at offset 64
    
    Program Headers:
      Type           Offset             VirtAddr           PhysAddr
                     FileSiz            MemSiz              Flags  Align
      PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                     0x00000000000001c0 0x00000000000001c0  R E    8
      INTERP         0x0000000000000200 0x0000000000400200 0x0000000000400200
                     0x000000000000001c 0x000000000000001c  R      1
          [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
      LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                     0x000000000000068c 0x000000000000068c  R E    200000
      LOAD           0x0000000000000690 0x0000000000600690 0x0000000000600690
                     0x0000000000000228 0x0000000000000230  RW     200000
      DYNAMIC        0x00000000000006a8 0x00000000006006a8 0x00000000006006a8
                     0x00000000000001d0 0x00000000000001d0  RW     8
      NOTE           0x000000000000021c 0x000000000040021c 0x000000000040021c
                     0x0000000000000044 0x0000000000000044  R      4
      GNU_EH_FRAME   0x0000000000000560 0x0000000000400560 0x0000000000400560
                     0x0000000000000034 0x0000000000000034  R      4
      GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                     0x0000000000000000 0x0000000000000000  RW     10
    
     Section to Segment mapping:
      Segment Sections...
       00     
       01     .interp 
       02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 
       03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 
       04     .dynamic 
       05     .note.ABI-tag .note.gnu.build-id 
       06     .eh_frame_hdr 
       07     
    root@mv:~#

    Observerez ci-dessus que la chaîne constante a été placer dans (mon cas) dans .RODATA. ( Read-Only Data) en clair donné constant en lecture seule. Qui est une zone distinguée du segment de données "si je peux dire ainsi" On distingue également que cette zone permet la lecture et l’exécution, mais pas l’écriture donc essayer de modifier l’élément qui se trouve dans cette zone débouche systématiquement sur une erreur de segmentation (sous UNIX tout comme GNU/LINUX et Mac Os); ce qui justifie le segment défaut et que nous n’avons pas affaire à un comportement indéterminé, mais bien une erreur de segmentation par ce que l’on a voulu écrire dans une zone qui ne nous est pas autorisée.

    Pour ce qui est de UB @picodev a bien expliquer là choses
    à bientôt@
    Celui qui peut, agit. Celui qui ne peut pas, enseigne.
    Il y a deux sortes de savants: les spécialistes, qui connaissent tout sur rien,
    et les philosophes, qui ne connaissent rien sur tout.
    George Bernard Shaw

  17. #17
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Histoire qu'on soit clair: je discutais juste du fait que la phrase disait : le compilateur place les char *s = "blah" dans une zone lecture seule. Et je voulais juste faire noter que cela n'est pas toujours le cas.

    Dans 99% des systèmes, ce que tu as dit est vrai

Discussions similaires

  1. question sur les chaine de caractères
    Par amy0o0 dans le forum C
    Réponses: 11
    Dernier message: 14/10/2006, 14h14
  2. [CR 10] Traitement sur les chaines de caractères
    Par sylviefrfr dans le forum SAP Crystal Reports
    Réponses: 1
    Dernier message: 11/09/2006, 09h16
  3. question sur les chaines de caractères
    Par pierrOPSG dans le forum C
    Réponses: 5
    Dernier message: 13/04/2006, 18h55
  4. les chaines de caractères
    Par mrtatou dans le forum C
    Réponses: 4
    Dernier message: 25/01/2006, 14h18
  5. xsl : test sur les chaine de caractère
    Par yos dans le forum XSL/XSLT/XPATH
    Réponses: 1
    Dernier message: 13/07/2005, 15h43

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