IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Cours C

Date de publication : 31/08/2008

Par Franck Hecht (retour aux articles)
 

Cours général sur le Langage C

XIV. Les chaînes de caractères
XIV-A. Débutant : Initiation
XIV-A-1. Introduction
XIV-A-2. Définition et initialisation d'une chaîne de caractères
XIV-A-3. Les séquences d'échappement
XIV-A-4. Gérer les chaînes de caractères avec nos propres fonctions
XIV-A-4-a. La fonction : str_len
XIV-A-4-b. La fonction : str_cpy
XIV-A-4-c. La fonction : str_chr
XIV-A-5. Résumé
XIV-B. Intermédiaire : Les fonctions standards
XIV-B-1. La fonction : strcat
XIV-B-2. La fonction : strcmp
XIV-B-3. La fonction : strtok
XIV-B-4. Résumé
XIV-C. Avancé : Les chaînes dynamiques
XIV-C-1. Allouer un espace mémoire pour une chaîne de caractères
XIV-C-2. Agrandir un espace mémoire pour une chaîne de caractères
XIV-C-3. La fonction : str_dup
XIV-C-4. Résumé


XIV. Les chaînes de caractères


XIV-A. Débutant : Initiation


XIV-A-1. Introduction

Le type chaîne n'existe pas en langage C ! Les chaînes de caractères sont représentées par un simple tableau de char qui contient alors un caractère par indice et se termine par un caractère de fin de chaîne.

Les chaînes en C s'écrivent entre double quote et les caractères entre simple quote. Voyons comment est représenté schématiquement une chaîne en C (la première ligne représente l'indice de chaque caractère dans le tableau et la seconde ligne les caractères formant la chaîne) :

0 1 2 3 4 5 6 7
'B' 'o' 'n' 'j' 'o' 'u' 'r' '\0'

La chaîne ainsi obtenue représente alors le texte "Bonjour". Vous vous demanderez sans doute ce qu'il advient du caractère '\0' ! Et bien c'est ce caractère sentinel qui représente la fin d'une chaîne de caractères en C, on l'appel le plus souvent caractère de fin de chaîne ou encore caractère nul.

Ce dernier caractère est donc aussi utilisé par les fonctions de la bibliothèque standard de façon transparente (nous en verrons quelques-unes dans le chapitre 14-B) mais, nous verrons auparavant comment se construire nos propres fonctions pour gérer les chaînes !


XIV-A-2. Définition et initialisation d'une chaîne de caractères

Une chaîne de caractères étant représentée par un simple tableau de char, on la déclare et l'initialise de la façon suivante:
char chaine [20] = "Bonjour";
info Le caractère de fin de chaîne ('\0') est ajouté automatiquement par le compilateur lors de la compilation du programme.
Dans ce cas précis, la chaîne "Bonjour" est stockée dans le tableau chaine et le tableau ne peut excéder les 20 caractères y compris le caractère de fin de chaîne ce qui fait donc un total de 19 caractères pour la chaîne elle-même.

On peut également utiliser un pointeur pour définir une chaîne de caractère, le pointeur n'est autre que l'adresse du premier caractère de la chaîne dans notre cas soit, l'expression suivante est tout à fait correcte:
char * chaine = "Bonjour";
Ici nous disposons d'une chaîne non modifiable et l'espace mémoire réservé par le compilateur correspond strictement au nombre de caractères y compris le caractère de fin de chaîne.

Par exemple, il est totalement interdit de faire une action de remplacement de caractère:
chaine [0] = 'b';
Lors de l'exécution, le programme plantera systématiquement ! Il est également possible, dans le cas de chaînes relativement longues, de les mettre sur plusieurs lignes comme par exemple (extrait du poême de Victor Hugo "En hiver la terre pleure") :
char chaine [] = "En hiver la terre pleure ;\n"
                "Le soleil froid, pale et doux,\n"
                "Vient tard, et part de bonne heure,\n"
                "Ennuye du rendez-vous.";
Cette technique peut être très utile lorsque des chaînes sont susceptibles de dépasser la largeur de l'écran dans votre éditeur de texte ou l'environnement de développement que vous utilisez pour taper votre code source !


XIV-A-3. Les séquences d'échappement

Les chaînes de caractères peuvent également contenir des caractères de contrôles aussi nommés séquences d'échappement. Ces caractères sont précédés par un backslash \ comme ceux qu'on a déjà rencontrés plus haut (\0 et \n), voici la liste des caractères d'échappements disponibles:

Caractère Désignation
\0 Caractère nul pour les fins de chaînes
\a Signal sonore
\b Retour en arrière
\f Saut de page
\n Saut de ligne
\r Retour chariot
\t Tabulation horizontale
\v Tabulation verticale
\' Caarctère '
\" Caractère "
\? Caractère ?
\\ Caractère \
Pour montrer quelques exemples d'utilisation, en sachant que la simple quote est utilisée pour initialiser un simple char et que la double quote sert pour encadrer une chaîne de caractères, on ne peut bien sûr pas les utiliser à l'intérieur des chaînes directement, il faut passer par leur équivalent en caractère d'échappement, par exemple:
char chaine [] = "\"En \'hiver\' la terre pleure\"";
Donnerait sur la console la sortie suivante:
"En 'hiver' la terre pleure"

XIV-A-4. Gérer les chaînes de caractères avec nos propres fonctions

Dans ce chapitre nous allons voir comment faire quelques actions sur les chaînes par nos propres moyens. La bibliothèque standard du C propose bien entendu un grand jeu de fonction mais il est tout de même nécessaire de bien prendre un main le principe des chaînes en langage C.


XIV-A-4-a. La fonction : str_len

Voyons dans un premier temps comment déterminer la longueure d'une chaîne. Nous appellerons cette fonction str_len et fonctionne de façon identique à la fonction standard strlen, voici son code (nous étudierons le code par la suite) :
size_t str_len (const char * s)
{
   size_t size = 0;

   while (*s++)
   {
      size++;
   }

   return size;
}
Notre fonction str_len prend en argument, un pointeur sur une chaîne de caractères (l'adresse du premier caractère) et renvoie une valeur de type size_t. On aurait bien pû prendre le type int par exemple, mais le type size_t à justement été ajouté dans le langage pour les données de longueure, autant l'utiliser !

Le type size_t correspond en générale à un entier non signé mais cela n'est pas garantit par la norme, ca dépend surtout du système d'exploitation que vous utilisez.

Dans la fonction, nous commençons par déclarer une variable de type size_t nommé size dont nous renvoyons le résultat à la fin de la fonction.

L'expression de la boucle teste le caractère et détermine si elle arrive au caractère de fin de chaîne ou non puis nous incrémentons l'adresse du pointeur pour passer au caractère suivant.

Dans le corps de la boucle, ou incrémente la variable size à chaque caractère non nul rencontré, la boucle s'arrête dès qu'elle rencontre le caractère '\0'.

Notre fonction s'utilise ainsi :
#include <stdlib.h>
#include <stdio.h>


int main (void)
{
   size_t size = 0;
   char chaine [20] = "Bonjour !";

   size = str_len (chaine);
   printf ("Longueure de la chaine : %d\n", size);

   return EXIT_SUCCESS;
}
Ce qui nous donne la sortie suivante sur la console :
Longueure de la chaine : 9

XIV-A-4-b. La fonction : str_cpy

Notre seconde fonction presonnelle que nous nommerons str_cpy va nous permettre de copier une chaîne de caractère d'un tableau à un autre, tout comme la fonction standard strcpy, voici une implémentation possible :
void str_cpy (const char * src, char * dest)
{
   size_t i = 0;
   size_t len = str_len (src) + 1;

   for (i = 0; i < len; i++)
   {
      dest[i] = src[i];
   }
}
Décortiquons notre fonction... Dans un premier temps, elle prend en argument un pointeur sur la chaîne source et un autre sur un espace pour la chaîne de destination. Le premier argument est déclaré const car il ne doit pas être modifié, cela nous protège d'éventuelles mauvaises manipulation !

Dans la fonction, on déclare deux variables de type size_t nommées i qui servira de variable d'incrémentation et len qui contiendra la longueure de la chaîne source. On initialise la variable len directement par l'appel de notre fonction str_len puis nous ajoutons 1 au résultat en retour pour nous garantir que le caractère de fin de chaîne sera également copié.

warning Une chaîne ne se terminant pas par un caractère de fin de chaîne peut provoquer des bugs lors de l'exécution du programme, voir même l'arrêt complet du programme !
Dans notre boucle for, nous copions la chaîne caractère par caractère en nous déplaçant dans la chaîne grâce à la variable i qui est incrémentée à chaque tour de boucle !

Voyons commen s'utilise notre fonction :
#include <stdlib.h>
#include <stdio.h>


int main (void)
{
   char c1 [20] = "Bonjour !";
   char c2 [20];

   str_cpy (c1, c2);

   printf ("Chaine 1 : %s\n", c1);
   printf ("Chaine 2 : %s\n", c2);

   return EXIT_SUCCESS;
}
Ce qui à l'exécution nous donne la sortie suivante :
Chaine 1 : Bonjour !
Chaine 2 : Bonjour !
warning Attention... L'espace où sera stockée la chaîne de destination doit être assez grand pour pouvoir accueillir la chaîne complète y compris son caractère de fin, que ce soit avec nos propres fonctions ou les fonctions de la bibliothèque standard, les règles sont les mêmes !

XIV-A-4-c. La fonction : str_chr

Tout comme la fonction standard strchr, notre fonction str_chr permet de récupérer un pointeur sur la première occurrence d'un caractère dans la chaîne passée en argument. Voyons une implémentation possible:
char * str_chr (const char * s, int c)
{
   char * tmp = NULL;

   while (*s++)
   {
      if (*s == c)
      {
         tmp = s;
         break;
      }
   }

   return tmp;
}
La fonction prend en premier argument, un pointeur sur une chaîne de caractères. Le second argument correspond à un entier et non pas à un char pour la simple et bonne raison qu'un char n'est en fait représenté que par un simple entier mais nous pouvons tout de même transmettre en argument un caractère entre simple quote !

Dans notre fonction, nous commençons par déclarer un pointer sur char qu'on initialise à NULL. En effet, lorsque la fonction ne trouve pas de caractère correspondant au second argument, elle retourne NULL, ce qui permet alors de déterminer si la fonction a réussie ou échouée.

La condition de notre boucle est la même que dans notre première fonction, on parcours la chaîne tant que le caractère rencontré n'est pas un caractère nul caractère de fin de chaîne) et on incrémente l'adresse du pointeur après test du caractère pour passer au caractère suivant.

Dans le corps de notre boucle, on test si le caractère courant pointé par s est équivalent au caractère passé en argument, ici une simple comparaison d'entier suffit mais sinon, dans le cas d'une comparaison de chaîne il faut utiliser des fonctions appropriées à cette tâche, ce que nous verrons au chapitre XIV-B !

Si le caractère correspond à celui recherché, on attribut l'adresse du pointeur s au pointeur tmp, pointeur qui est renvoyé par la fonction puis nous mettons un terme prématurément à la boucle par l'instruction break.

Une possible utilisation de notre fonction serait par exemple le remplacement de toutes les occurrences d'un caractère par un autre, voici comment nous pourrions faire:
int main (void)
{
   char chaine [20] = "Bonjour !";
   char * p = chaine;

   printf ("Chaine avant traitement : %s\n", chaine);

   while (p != NULL)
   {
      p = str_chr (p, 'o');

      if (p != NULL)
      {
         *p = 'O';
      }
   }

   printf ("Chaine apres traitement : %s\n", chaine);

   return EXIT_SUCCESS;
}
Comme dans les exemples précédents, nous commençons par déclarer et initialiser une chaîne de caractère, nous prendrons le même texte "Bonjour !", puis on déclare un pointeur sur char qu'on initialise en passant notre chaîne chaine. Le pointeur p pointe alors sur le premier caractère de notre chaîne.

Dans notre boucle, nous recherchons toutes les occurrences de la lettre 'o' tant que la fonction str_chr ne renvoie pas NULL. Si le pointeur p n'est pas NULL, nous remplaçons alors le caractère courant par la lettre majuscule 'O'.

idea Vous pouvez remarquer dans l'apprel de la fonction str_chr, que dans le premier argument nous ne passons pas directement la chaîne chaine mais le pointeur p. En effet, ce pointeur pointe sur la dernière occurrence du caractère recherché, passer le pointeur en argument comme ici, nous permet de commencer la recherche à partir de ce dernier caractère trouvé, cela nous évite de recommencer la recherche du début de la chaîne, c'est une optimisation non négligeable lorsqu'il s'agit de faire un remplacement comme ici mais sur des chaînes beaucoup plus longues !
Nous obtenons alors la sortie suivant sur la console:
Chaine avant traitement : Bonjour !
Chaine apres traitement : BOnjOur !

XIV-A-5. Résumé

Dans ce chapitre nous avons les fondements de base des chaînes de caractères en langage C. Nous avons vus que les chaînes ne sont rien de plus qu'unes suite de caractères stockée en mémoire (dans le tas ou la pile, suivant si la chaîne est créée dynamiquement, ce que nous verrons à la dernière section de ce chapitre, ou si c'est une chaîne définie lors de la compilation). Nous avons également vu comment se créer facilement des fonctions personnelles pour gérer les chaînes, ceci peut être très souvent nécessaire mais la bibliothèque standard fournie déjà un bel ensemble de fonctions qui sont à utiliser en premier lieu !

Un bon exercice reste tout de même de se créer des fonctions personnelles !


XIV-B. Intermédiaire : Les fonctions standards

La liste des fonctions standard est assez importante, c'est pour cette raison qu'elles ne seront pas toutes passées en revue dans ce cours mais en voici la liste:

Dans ce chapitre nous verrons les fonctions strcat qui permet la concaténation de chaînes, strcmp qui permet de comparer deux chaînes entre elles et un gros point sera fait sur la fonction strtok qui permet d'éclater une chaîne de caractères.


XIV-B-1. La fonction : strcat

La fonction strcat dont voici le prototype :
char *strcat (char *dest, const char *src);
permet de concaténer deux chaînes de caractères autrement dit, de fusionner deux chaînes pour n'en former qu'une seule. Cette fonction prend comme argument (dans l'ordre) un pointeur (dest) sur la chaîne de destination qui doit être assez grandepour contenir les deux chaînes avec le caractère de fin et un autre pointeur (src) contenant la chaîne à copier sur dest. Elle renvoie un pointeur sur la chaîne finale dest.

Voyons un exemple d'utilisation :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main (void)
{
   char c1 [50] = "Bonjour";
   char * c2 = ", le monde !";

   /* Copie de la chaine c2 a la suite de la chaine c1. */
   strcat (c1, c2);
   printf ("%s\n", c1);

   return EXIT_SUCCESS;
}
Ce qui donne en sortie sur la console :
Bonjour, le monde !
On peut remarquer à la vue du résultat, que la fonction strcat ajoute automatiquement le caractère de fin de chaîne. En réalité, elle écrase le caractère de fin de la chaîne c1 puis copie ajoute un nouveau zéro final (caractère de fin de chaîne) à la fin de la copie de la chaîne c2.

Dans l'exemple ci-dessus, on peut voir qu'il n'est pas obligatoire de récupérer le retour de la fonction. En effet, récupérer le pointeur sur la chaîne finale n'a d'intérêt que par exemple, si vous voulez imbriquer des appels de fonctions comme dans l'exemple suivant (il en va de même pour les autres fonctions de la bibliothèque standard retournant un pointeur sur la chaîne de destination) :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main (void)
{
   char c1 [50] = "Bonjour";
   char c2 [50] = ", le ";
   char * c3 = "monde !";

   strcat (c1, strcat (c2, c3));
   printf ("%s\n", c1);

   return EXIT_SUCCESS;
}
Je ne vous préconise cependant pas ce type de programmation qui rend le code plus difficile à lire et même à comprendre ! La fonction strcat à une soeur nommée strncat. La fonction strncat :
char *strncat (char *dest, const char *src, size_t n);
fonctionne de la même manière que strcat à ceci près qu'elle ne copie que les n (le troisième argument) premiers caractères de la chaîne src au lieu de la chaîne complète !


XIV-B-2. La fonction : strcmp

La fonction strcmp permet de comparer deux chaînes de caractères. Voici son prototype :
int strcmp (const char *s1, const char *s2);
Cette fonction compare la chaîne s1 à la chaîne s2 et renvoie une des valeurs suivante :

  • Entier négatif si la chaîne s1 est inférieur à la chaîne s2
  • 0 si la chaîne s1 est égale à la chaîne s2
  • Entier positif si la chaîne s1 est supérieur à la chaîne s2
Voyons un peu comment cela fonctionne :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main (void)
{
   char * s1 = "Bonjour !";
   char * s2 = "bonjour !";
   char * s3 = "Bonjour !";
   char * s4 = "BonJour..";

   printf ("s1 vs s2 : %d\n", strcmp (s1, s2));
   printf ("s1 vs s3 : %d\n", strcmp (s1, s3));
   printf ("s1 vs s4 : %d\n", strcmp (s1, s4));

   return EXIT_SUCCESS;
}
Ce qui nous donne comme résultat :
s1 vs s2 : -1
s1 vs s3 : 0
s1 vs s4 : 1
Interpétons ensemble ces résultats. La comparaison des chaînes se fait par rapport à la valeur ASCII des caractères, dans la première comparaison, le test se termine dès le premier caractère car les lettres B et b sont différentes, la valeur de B étant 66 et celle de b étant 98, la chaîne s1 est de ce fait inférieure à la chaîne s2 d'où le résultat -1.

Les chaînes s1 et s3 étant parfaitement identique, il n'y a pas de surprise pour le résultat obtenu.

Dans le dernier test, s4 contient un J masjuscule donc étant donné que J vaut 74 et que j vaut 106, la chaîne s1 est donc supérieure à la chaîne s4 !


XIV-B-3. La fonction : strtok

La fonction strtok permet d'éclater une chaîne de caractères, on peut très bien récupérer une phrase mot par mot ou sauter de ligne à chaque virgule. Voici son prototype:
char *strtok (char *s, const char *delim);
Son premier argument est un pointeur sur une chaîne de caractères. Cette chaîne ne doit pas être en lecture seule donc, une chaîne définie à la compilation comme ceci:
char * chaine = "Ma chaine de caracteres !";
ne peut être utilisée car la fonction strtok modifie la chaîne en ajoutant avant chaque retour (si des caractères délimiteurs ont été rencontrés) un caractère de fin de chaîne !

Le second argument est la liste des caractères délimiteur servants à éclater la chaîne. Elle renvoie pour finir, un pointeur sur le début de la sous-chaîne trouvée.

Voyons un exemple simple:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main (void)
{
   char * p = NULL;
   char * ret = NULL;
   char chaine [] = "En hiver la terre pleure ;\n"
                    "Le soleil froid, pale et doux,\n"
                    "Vient tard, et part de bonne heure,\n"
                    "Ennuye du rendez-vous.";


   p = chaine;
   while ((ret = strtok (p, " .,;\n")) != NULL)
   {
      printf ("%s\n", ret);
      p = NULL;
   }


   return EXIT_SUCCESS;
}
Nous commençons ici (en passant sur les initialisations préalables) par faire pointer p sur la chaîne chaine. Ceci pour une bonne raison, le premier paramètre de la fonction strtok doit être la chaîne de caractère à traiter mais lors des appels suivants, pour continuer à traiter et avancer dans la même chaîne, le paramètre doit ensuite être mis à la valeur NULL.

Nous testons donc le retour de la fonction strtok directement dans l'expression de la boucle while, vous pouvez remarquer dans le second argument de la fonction comment passer plusieurs caractères délimiteurs. Dans notre exemple, nous voulons récupérer uniquement les mots sans les espaces.

Dans la boucle on peut remarquer que nous passons le pointeur p à la valeur NULL après le premier appel à strtok. Nous avons donc la sortie suivante sur la console:
En
hiver
la
terre
pleure
Le
soleil
froid
pale
et
doux
Vient
tard
et
part
de
bonne
heure
Ennuye
du
rendez-vous
Le pointeur retourné par strtok pointe directement sur l'espace mémoire occupé par la chaîne de caractères, si vous désirez stocker ces fragments de chaînes, il vous faut les placer dans un espace mémoire différent, un tableau de tableau de char (aussi appelé plus communément: "tableau de chaînes") par exemple:
char tableau [25][25];
warning Il faut que l'espace mémoire soit assez grand pour accueillir les chaînes !
info Dans l'exemple ci-dessus, nous avons au final un tableau pouvant contenir 25 chaînes de 25 caractères maximum, y compris le zéro de fin !
On peut aussi passer par des chaînes dynamiques par exemple, copier une chaîne ou une sous-chaîne dans un espace mémoire alloué au préalable, c'est ce sujet qui sera traité au chapitre XIV-C !


XIV-B-4. Résumé

Dans ce chapitre nous avons vu quelques fonctions de la bibliothèque standard permettant de gérer les chaînes de caractères, celles-ci dont certaines autres sont sans doute parmis les plus utilisées. Elles permettent des actions basiques sur les chaînes, vous vous devez donc de les connaîtres un minimum, un jour viendra peut-être où vous devrez sans doute les utiliser pour construire votre propre bibliothèque avec des fonctions plus avancées, ce qui est un exercice que je conseil.


XIV-C. Avancé : Les chaînes dynamiques

Dans ce chapitre nous allons étudier les chaînes dynamique autrement dit, comment allouer un espace mémoire pour contenir une chaîne, comment agrandir cet espace pour pouvoir agrandir la chaîne. Nous verrons également en guise d'exercice, comment construire une fonction permettant de dupliquer une chaîne comme la fonction strdup (non standard, disponible sur systèmes Unixoïdes uniquement).


XIV-C-1. Allouer un espace mémoire pour une chaîne de caractères

Allouer un espace mémoire pour les chaînes se fait de la même façon que pour toute autre donnée à ceci près qu'un char ne vaut par définition que 1 octet, on peut donc se passer de quelques formalités. Il nous est en effet pas nécessaire de ce fait de multiplier la taille d'un char par le nombre d'octets qu'ils nous faut comme on le ferais pour un type entier ou autre type comme par exemple:
tableau = malloc (10 * sizeof (int));
Inutile donc de compliquer le code par des fioritures sans intérêts ! Voyons un petit exemple:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main (void)
{
   char * texte = NULL;
   char * ligne1 = "En hiver la terre pleurre ;";
   char * ligne2 = "Le soleil froid, pale et doux,";


   texte = malloc (strlen (ligne1) + 1);

   if (texte != NULL)
   {
      strcpy (texte, ligne1);
      printf ("%s\n", texte);

      free (texte);
   }


   return EXIT_SUCCESS;
}
Nous avons en sortie:
En hiver la terre pleurre ;
On commence par déclarer les pointeurs dont nous avons besoin, le pointeur ligne2 sera utiliser au chapitre XIV-C-2 et nous passons le pointeur texte à la valeur NULL.

warning Pensez à toujours initialiser vos variables y compris les pointeurs à des valeurs par défaut, cela vous permettra d'éviter des problèmes. Pour les pointeurs, mieux vaut les initialiser à la valeur NULL si vous ne les initialisez pas tout de suite à une valeur définie !
Vient ensuite l'allocation proprement dite.
texte = malloc (strlen (ligne1) + 1);
Pour la taille, nous utilisons la fonction strlen pour déterminer la longueure de la chaîne ligne1 que nous voulons copier dans texte. N'oublier jamais d'ajouter 1 au résultat pour avoir de la place pour le caractère de fin de chaîne sinon vous risquez fortement d'avoir un comportement indéfinit lors de l'affichage ou du traitement de la chaîne ! Cette façon de procéder permet d'avoir la taille exacte pour notre espace mémoire ce qui évite donc d'allouer des espaces trop grands que nous n'utilisons pas complètement, nous pouvons toujours par la suite agrandir cet espace pour agrandir notre chaîne de caractères, ce que nous verrons au chapitre XIV-C-2 !

Nous testons ensuite si l'allocation à bien été effectuée
if (texte != NULL)
warning Prennez toujours le soin de tester le retour des fonctions d'allocation, si une allocation devait échouer pour une raison X, vous évitez donc un problème lors de l'exécution du programme !
et si c'est le cas, nous copions la chaîne puis nous l'affichons. Pour finir, on libère la chaîne avant la fin du programme
free (texte);
warning Pensez à toujours libérer les espaces mémoire que vous allouez !

XIV-C-2. Agrandir un espace mémoire pour une chaîne de caractères

Dans les chaînes dynamiques, il arrive très fréquement de devoir faire des concaténation car les chaînes grandissent. Par exemple dans le cas d'un éditeur de texte, il peut arriver d'avoir à agrandir le buffer qui stocke le texte que vous voyez à l'écran tout en gardant bien entendu, le texte de départ puis ajouter les nouveaux caractères entrés par l'utilisateur.

Voyons un exemple simple d'agrandissement d'une chaîne en partant de notre précédent code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main (void)
{
   char * texte = NULL;
   char * p = NULL;
   char * ligne1 = "En hiver la terre pleurre ;";
   char * ligne2 = " Le soleil froid, pale et doux,";


   texte = malloc (strlen (ligne1) + 1);

   if (texte != NULL)
   {
      strcpy (texte, ligne1);
      printf ("%s\n", texte);


      p = realloc (texte, strlen (texte) + strlen (ligne2) + 1);

      if (p != NULL)
      {
         texte = p;
         strcat (texte, ligne2);
         printf ("%s\n", texte);
      }


      free (texte);
   }


   return EXIT_SUCCESS;
}
Le code ajouté:
p = realloc (texte, strlen (texte) + strlen (ligne2) + 1);

if (p != NULL)
{
   texte = p;
   strcat (texte, ligne2);
   printf ("%s\n", texte);
}
permet ici d'agrandir la zone mémoire pour pouvoir concaténer la chaîne texte avec la seconde chaîne ligne2. Pour ce faire, utilisons la fonction realloc, pour le résultat de retour de la fonction, nous avons également ajouté un pointeur nommé p. En effet, il faut utiliser un pointeur intermédiaire pour le retour de la fonction car elle retourne la valeur NULL en cas d'échec, si nous avions utilisé directement le pointeur texte et que la fonction échoue, nous perdons la liaison avec notre chaîne ce qui nous empêche alors d'en libérer l'espace lorsque nous n'en avons plus besoin !

Pour agrandir l'espace de la chaîne, nous calculons simplement la longueure de notre chaîne de base, résultat auquel on ajoute la longueure de la chaîne à concaténer et on ajoute également 1 pour le zéro final.

Si l'allocation résussie, on met à jour notre pointeur texte en attribuant l'adresse du pointeur p qui pointe vers la nouvelle zone mémoire. Nous concaténons ensutie simplement les deux chaînes et nous l'affichons, le résultat sur la console de ce petit programme est le suivant:
En hiver la terre pleurre ;
En hiver la terre pleurre ; Le soleil froid, pale et doux,

XIV-C-3. La fonction : str_dup

Malgré la grande quantité de fonctions de la bibliothèque standard, il peut arriver que vous ayez besoin de fonctions supplémentaire, certaines sont fournies dans des bibliothèques étendues mais ne faisans pas parties des normes ANSI/ISO par exemple, sous les systèmes Unixoïdes (Linux, Unix), il existe d'autres normes comme POSIX, BSD, etc...

Nous allons voir comment construire une fonction de duplication de chaîne comme la fonction strdup qui est fournies dans les bibliothèques qui font parties de l'extension GNU.

Voyons d'abord le code de notre fonction:
char * str_dup (const char * s)
{
   char * dup = NULL;


   if (s != NULL)
   {
      size_t size = strlen (s) + 1;
      dup = malloc (size);

      if (dup != NULL)
      {
         memcpy (dup, s, size);
      }
   }


   return dup;
}
Nous testons en premier lieu si l'argument passé en argument, donc une chaîne, est valide. Si c'est le cas, on commence par déterminer la taille de la chaîne avec sont zéro de fin, pour pouvoir réserver par la suite (l'expression suivante) un espace mémoire pour la copie de la chaîne. si l'allocation s'effectue sans problème, on copie la chaîne par le biais de la fonction memcpy. La fonction memcpy permet la copie d'une zone mémoire peu importe son type, tout en donnant la possibilité de choisir le nombre d'octets à copier. Ici nous copions la chaîne complète y compris son zéro de fin.

On termine par renvoyer le pointeur sur la copie de la chaîne. Il va de soi qu'il faut libérer la chaîne une fois que vous en avez plus besoin. Voici un exemple d'utilisation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


char * str_dup (const char * s)
{
   char * dup = NULL;


   if (s != NULL)
   {
      size_t size = strlen (s) + 1;
      dup = malloc (size);

      if (dup != NULL)
      {
         memcpy (dup, s, size);
      }
   }


   return dup;
}


int main (void)
{
   char * copy = NULL;
   char * chaine = "En hiver la terre pleurre ;";


   copy = str_dup (chaine);

   if (copy != NULL)
   {
      printf ("%s\n", copy);
      free (copy);
   }


   return EXIT_SUCCESS;
}
La sortie est sans surprise:
En hiver la terre pleurre ;

XIV-C-4. Résumé

Nous voici au terme du chapitre sur les chaînes de caractères. Dans ce dernier volet vous avez appris à créer des chaînes de façon dynamique soit, à allouer un espace mémoire suffisant pour y copier une chaîne et comment agrandir cet espace mémoire pour une extension de la chaîne de caractères. Pour terminer, nous avons vu comment construire une fonction de duplication de chaîne comme celle fournie avec la bibliothèque de l'extension GNU.



Valid XHTML 1.1!Valid CSS!

Copyright © 2008 Franck Hecht. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.