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 : 42
    Localisation : France

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

    Informations forums :
    Inscription : Juin 2004
    Messages : 5 840
    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
    Avatar de Emmanuel Delahaye
    Profil pro
    Retrait�
    Inscrit en
    D�cembre 2003
    Messages
    14 512
    D�tails du profil
    Informations personnelles :
    �ge : 69
    Localisation : France, Paris (�le de France)

    Informations professionnelles :
    Activit� : Retrait�

    Informations forums :
    Inscription : D�cembre 2003
    Messages : 14 512
    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)"

  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 : 42
    Localisation : France

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

    Informations forums :
    Inscription : Juin 2004
    Messages : 5 840
    Par d�faut
    Corrig� selon tes conseils.


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

    Informations professionnelles :
    Activit� : Retrait�

    Informations forums :
    Inscription : D�cembre 2003
    Messages : 14 512
    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);

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

    Informations professionnelles :
    Activit� : Chercheur

    Informations forums :
    Inscription : Avril 2007
    Messages : 985
    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...

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

    Informations professionnelles :
    Activit� : Retrait�

    Informations forums :
    Inscription : D�cembre 2003
    Messages : 14 512
    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� ...

  7. #7
    Nouveau 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
    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
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 515
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyr�n�es)

    Informations professionnelles :
    Activit� : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 515
    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 tr�s actif
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    551
    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 : 551
    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.

  10. #10
    Nouveau 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
    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
    Ing�nieur d�veloppement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 498