Programmation • IoT

Chap 8 – Les chaînes de caractères

Document réservé

Vous consultez actuellement la présentation publique de ce chapitre.

Les documents PDF complets, comprenant les développements théoriques, les exemples détaillés et les exercices, sont disponibles sur demande.

Pour obtenir un accès, contactez-moi via la page Contact en indiquant les domaines qui vous intéressent (C++, ESP-IDF, électronique, etc.).

8.2 Pourquoi les chaînes de caractères ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 8.3 Une première chaîne de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 8.6 Parcourir une chaîne de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 8.7 Copier une chaîne de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 8.8 Comparer deux chaînes de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 8.9 Concaténer deux chaînes de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Chapitre 8 – Les chaînes de caractères Chapitre 8 – Les chaînes de caractères Dans ce chapitre, nous utilisons les caractères classiques (char), suffisants pour comprendre le fonctionnement des chaînes de caractères. Les systèmes modernes utilisent souvent des encodages plus évolués (comme UTF-8) afin de représenter les caractères de toutes les langues. Ces encodages ne modifient pas les principes étudiés ici et seront abordés plus tard si nécessaire. Pourquoi les chaînes de caractères ? Jusqu’à présent, nous avons appris à mémoriser différents types de données. Par exemple, une variable de type int permet de stocker un nombre entier, tandis qu’une variable de type char permet de mémoriser un seul caractère. Imaginons maintenant que nous souhaitions enregistrer le prénom d’un étudiant. Nous pourrions écrire : char prenom = ’M’; Cette variable contient bien une lettre, mais seulement la première du prénom. Comment représenter le mot complet Une première idée pourrait être d’utiliser plusieurs variables : Cette solution fonctionne, mais elle présente rapidement de nombreux inconvénients. • Chaque lettre doit être mémorisée dans une variable différente. • Le nombre de variables dépend de la longueur du mot. • Il devient difficile d’afficher, de copier ou de comparer un texte. • Cette méthode est pratiquement inutilisable pour représenter une phrase complète. Nous avons besoin d’une structure plus adaptée au stockage d’un texte. Or, nous connaissons déjà un outil capable de regrouper plusieurs valeurs de même type sous un seul nom : le Une chaîne de caractères n’est rien d’autre qu’un tableau de caractères. Nous allons découvrir dans ce chapitre comment ces tableaux sont organisés en mémoire et comment les manipuler efficacement. À votre avis, comment le langage C++ sait-il où se termine le mot « Michel » lorsqu’il est stocké en mémoire ? Nous répondrons à cette question dans la section suivante. Une première chaîne de caractères Comme une chaîne de caractères est un tableau de char, sa déclaration ressemble beaucoup à celle d’un tableau Le programme suivant crée une chaîne contenant le prénom « Michel » : char prenom[] = « Michel »; À première vue, cette écriture paraît très simple. Pourtant, plusieurs questions se posent immédiatement. • Combien de cases mémoire ont été réservées ?
Accueil
Chapitre 8 – Les chaînes de caractères • Chaque lettre occupe-t-elle une case ? • Pourquoi les guillemets (« ) sont-ils utilisés alors qu’un caractère s’écrit entre apostrophes (’) ? • Comment l’ordinateur sait-il où se termine le mot ? Pour répondre à ces questions, nous allons examiner ce qui est réellement stocké en mémoire. Combien de cases mémoire pensez-vous que le tableau prenom contient ? char prenom[] = « Michel »; • Impossible de le savoir Ne cherchez pas la réponse dans la documentation. Essayez simplement de réfléchir avant de poursuivre votre Regardons la mémoire Lorsqu’un programme est exécuté, les variables sont stockées dans la mémoire de l’ordinateur. Considérons la déclaration suivante : char prenom[] = « Michel »; À première vue, on pourrait penser que la variable prenom contient simplement le mot « Michel ». En réalité, ce qui est stocké en mémoire est beaucoup plus précis. En exécutant les instructions suivantes char prenom[] = « Michel »; std::cout << sizeof(prenom) << std::endl; std::cout << strlen(prenom) << std::endl; • On constate que sizeof(prenom) et strlen(prenom) ne renvoient pas la même valeur. Nous pouvons représenter le contenu de la mémoire de la manière suivante : Les six premières cases ne posent aucun problème. Elles contiennent les six lettres du prénom. Mais une question reste sans réponse : Que contient la dernière case ? Cette dernière case n’est pas vide. Elle contient un caractère très particulier appelé caractère nul, noté :
Accueil
Chapitre 8 – Les chaînes de caractères La mémoire contient donc en réalité : Le caractère ’\0’ marque la fin de la chaîne de caractères. Il ne s’agit pas de la valeur ’0’ (le chiffre zéro), mais d’un caractère spécial dont le code ASCII vaut zéro. Toutes les fonctions manipulant des chaînes de caractères recherchent ce caractère pour savoir où le texte se termine. Sans lui, l’ordinateur serait incapable de déterminer où s’arrête le mot. Comment fait strlen() pour trouver 6 ? strlen() est une fonction prédéfinie qui fonctionne comme la fonction longueur() du petit programme suivant : int longueur(const char chaine[]) while (chaine[i] != ’\0’) // fonction equivalente a strlen() int main(int argc, const char ∗ argv[]) { char prenom[] = « Michel »; std::cout << sizeof(prenom) << std::endl; std::cout << longueur(prenom) << std::endl; Le caractère ’\0’ sert de sentinelle. Lorsqu’un programme le rencontre, il sait que le texte est terminé et qu’il ne doit pas continuer sa lecture. • Une chaîne de caractères en C++ n’est pas un nouveau type de données. C’est simplement un tableau de char dont le dernier élément est toujours le caractère spécial ’\0’. • C’est ce caractère qui indique la fin de la chaîne. char prenom[20] = « Michel »; Que valent les grandeurs std::cout << sizeof(prenom) << std::endl; std::cout << longueur(prenom) << std::endl;
Accueil
Chapitre 8 – Les chaînes de caractères Les chaînes littérales Depuis le début de ce chapitre, nous utilisons des écritures comme : char prenom[] = « Michel »; char ville[] = « Geneve »; Les mots « Michel », « Genève » et « Suisse » sont appelés des chaînes littérales. Une chaîne littérale est une suite de caractères écrite directement dans le programme entre guillemets (« ). Lors de la compilation, le compilateur crée automatiquement le tableau de caractères correspondant et ajoute le caractère spécial ’\0’ à la fin de la chaîne. Ainsi, les deux déclarations suivantes sont équivalentes : char prenom[] = « Michel »; ’M’,’i’,’c’,’h’,’e’,’l’,’\0’ La première écriture est simplement beaucoup plus pratique. • Les guillemets ( » « ) permettent d’écrire directement une chaîne de caractères. • Le compilateur ajoute automatiquement le caractère ’\0’ à la fin de la chaîne. ’M’,’i’,’c’,’h’,’e’,’l’ définit un tableau de caractères, mais pas une chaîne de caractères. Un tableau de char n’est pas nécessairement une chaîne de caractères. Pour qu’un tableau de caractères soit une véritable chaîne de caractères, son dernier élément doit être le caractère spécial ’\0’. La fonction strlen(prenom) provoque un comportement indéfini du programme dans ce cas. Lien avec les systèmes embarqués Cette convention est utilisée dans de très nombreuses bibliothèques C/C++, notamment celles destinées aux systèmes embarqués comme les ESP32. Parcourir une chaîne de caractères Comme une chaîne de caractères est un tableau de char, nous pouvons accéder à chacun de ses éléments à l’aide Le programme suivant affiche les caractères de la chaîne un par un.
Accueil
Chapitre 8 – Les chaînes de caractères char prenom[] = « Michel »; for(int i = 0; prenom[i] != ’\0’; i++) std::cout << prenom[i] << std::endl; Contrairement au chapitre précédent, la boucle ne s’arrête plus lorsque l’indice atteint la taille du tableau. Elle s’arrête lorsqu’elle rencontre le caractère spécial ’\0’. Fig. 1 – Schéma du fonctionnement de la Si l’on déclare la fonction void afficher(const char chaine[]) la fonction ne connaît pas la taille du tableau reçu. Pour savoir où se termine le texte, elle doit parcourir les caractères jusqu’à rencontrer la sentinelle de fin de chaîne ’\0’. Parcours d’une chaîne avec un pointeur Reprenons la déclaration de la chaine de caractères suivante : char prenom[] = « Michel »; Nous allons parcourir cette chaine avec un pointeur.
Accueil
Chapitre 8 – Les chaînes de caractères char prenom[] = »Michel »; char∗ p = &prenom[0]; while(∗p != ’\0’) std::cout << std::endl; La sortie de ce programme est On voit que le pointeur reçoit l’adresse du premier caractère. Ensuite le pointeur est incrémenté jusqu’à ce que la valeur du caractère lu soit le caractère ’\0’. Cette première écriture est parfaitement correcte et correspond directement à ce que nous avons appris sur les char∗ p = &prenom[0]; Cependant, dans la plupart des expressions, le nom d’un tableau est automatiquement converti en l’adresse de son premier élément. On écrit donc généralement : Les deux écritures sont équivalentes. En remplaçant la déclaration du pointeur par l’écriture habituelle, le reste du programme demeure inchangé. std::endl ne produit aucun affichage à lui seul. Il doit être envoyé à un flux de sortie, comme std::cout, à l’aide de l’opérateur <<.
Accueil
Chapitre 8 – Les chaînes de caractères Essayez d’expliquer la sortie de ce programme. char prenom[] = »Michel »; char∗ p = &prenom[0]; while(∗p != ’\0’) << static_cast<const void∗>(p)
Accueil
Chapitre 8 – Les chaînes de caractères Programme qui retourne VRAI si un mot est un palindrome. bool pallindrome(char pal[]) char∗ debut = &pal[0]; = &pal[strlen(pal)−1]; if(∗debut != ∗fin) char mot[] = »radar »; if (pallindrome(mot)) std::cout << « VRAI\n »; std::cout << « FAUX\n »; Copier une chaîne de caractères Après avoir appris à parcourir une chaîne de caractères, nous pouvons maintenant réaliser une première opération utile : copier son contenu dans une autre chaîne. Le principe est très simple. Nous parcourons la chaîne source caractère par caractère et nous recopions chaque caractère dans une seconde chaîne. Lorsque nous rencontrons le caractère ’\0’, nous savons que la copie est terminée. Il est indispensable de placer ce même caractère à la fin de la chaîne destination afin que la nouvelle chaîne soit elle aussi correctement terminée. void copier(char destination[], char source[]) while(source[i] != ’\0’) destination[i] = source[i]; destination[i] = ’\0’;
Accueil
Chapitre 8 – Les chaînes de caractères char source[] = « Bonjour »; char destination[20] = «  »; copier(destination, source); std::cout << destination << std::endl; La boucle copie un à un tous les caractères de la chaîne source. Lorsque le caractère ’\0’ est rencontré, la boucle s’arrête. Il est alors indispensable de placer ce même caractère à la fin de la chaîne destination, faute de quoi celle-ci ne serait plus une chaîne de caractères valide. • Une fonction de copie ne doit pas seulement recopier les caractères visibles. Elle doit également ajouter le caractère ’\0’ qui marque la fin de la chaîne. Comparer deux chaînes de caractères Il est souvent nécessaire de savoir si deux chaînes de caractères contiennent exactement le même texte. Pour cela, nous comparons leurs caractères un à un. Dès que deux caractères sont différents, nous pouvons conclure que les deux chaînes ne sont pas identiques. Si, au contraire, nous atteignons simultanément le caractère ’\0’ des deux chaînes, alors elles sont identiques. bool comparer(char chaine1[], char chaine2[]) while(chaine1[i] != ’\0’ && chaine2[i] != ’\0’) if(chaine1[i] != chaine2[i]) return chaine1[i] == chaine2[i]; char mot1[] = « Michel »; char mot2[] = « Michel »; if(comparer(mot1, mot2)) std::cout << « Les deux chaines sont identiques. » << std::endl; std::cout << « Les deux chaines sont differentes. » << std::endl;
Accueil
Chapitre 8 – Les chaînes de caractères Les deux chaines sont identiques. La boucle compare les deux chaînes caractère par caractère. Si une différence est rencontrée, la fonction retourne immédiatement la valeur false. Lorsque la boucle se termine, deux situations sont possibles : • les deux chaînes sont arrivées simultanément au caractère ’\0’ : elles sont identiques ; • une seule chaîne est arrivée au caractère ’\0’ : l’une est plus courte que l’autre. return chaine1[i] == chaine2[i]; retourne donc true uniquement si les deux chaînes se terminent exactement au même endroit. • Deux chaînes de caractères sont identiques si elles contiennent les mêmes caractères dans le même ordre et si leur premier caractère ’\0’ apparaît au même endroit. • Deux tableaux de caractères peuvent donc être différents tout en contenant la même chaîne de caractères, car tout ce qui se trouve après le premier ’\0’ ne fait plus partie de la chaîne. Concaténer deux chaînes de caractères Concaténer deux chaînes consiste à ajouter la seconde à la suite de la première. Pour cela, nous devons tout d’abord rechercher la fin de la première chaîne, c’est-à-dire son premier caractère ’\0’. Nous recopions ensuite les caractères de la seconde chaîne à partir de cet emplacement avant d’ajouter un nouveau caractère ’\0’ à la fin. void concatener(char destination[], char source[]) while(destination[i] != ’\0’) while(source[j] != ’\0’) destination[i] = source[j]; destination[i] = ’\0’; char texte[30] = « Bonjour »;
Accueil
Chapitre 8 – Les chaînes de caractères char ajout[] =  » Michel »; concatener(texte, ajout); std::cout << texte << std::endl; La première boucle recherche le premier caractère ’\0’ de la chaîne destination. La seconde boucle recopie ensuite les caractères de la chaîne source à partir de cet emplacement. Comme pour la fonction copier(), il est indispensable d’ajouter un nouveau caractère ’\0’ à la fin de la chaîne • Concaténer deux chaînes consiste simplement à copier la seconde chaîne à partir de la fin de la première. • La chaîne destination doit être suffisamment grande pour contenir le résultat de la concaténation. Dans le cas contraire, le programme écrira au-delà des limites du tableau, ce qui conduit à un comportement indéfini. Dans les exemples précédents, nous avons supposé que la chaîne de destination était suffisamment grande. Nous verrons par la suite comment garantir qu’elle dispose toujours de la mémoire nécessaire. Les chaînes de caractères constituent un cas particulier de tableau de char. Leur principale caractéristique est la présence d’un premier caractère ’\0’ qui marque la fin de la chaîne. Dans ce chapitre, nous avons appris à parcourir une chaîne de caractères, à la copier, à la comparer et à la concaténer. Ces opérations, bien que simples, sont à la base de très nombreux programmes. Dans les chapitres suivants, nous verrons comment rendre ces fonctions plus générales et plus sûres. Nous découvrirons notamment comment transmettre efficacement des chaînes à des fonctions et comment lever la limitation imposée par la taille fixe des tableaux de caractères.
Accueil
cpp-Chap8- Chapitre 8 – Les chaînes de caractères
Accueil
Termes à ajouter au glossaire
Contenu

Inscription

×
Cancel