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 " ;
|
|
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:
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é.
|
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 !
|
|
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'.
|
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 ! " ;
strcat (c1, c2);
printf (" %s\n " , c1);
return EXIT_SUCCESS;
}
|
Ce qui donne en sortie sur la console :
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:
|
Il faut que l'espace mémoire soit assez grand pour accueillir les chaînes !
|
|
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.
|
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
|
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
|
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.
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.