Table des matières

Chaînes de caractères

Après cela, vous devriez comprendre pourquoi Python est à la mode et pourquoi c'est Python que l'on enseigne au lycée et pas C…

En Python, on n'a pas grand chose à faire. On écrit directement une chaîne de caractère dans le programme, par exemple :

python

texte = "blablabla"

et voilà c'est terminé. En C… c'est plus compliqué.

Tableau de char

On a dit qu'un caractère était de type char.

Une chaîne sera un tableau de char.

La chaîne "Chat" serait donc contenu dans un tableau sous cette forme {'C','h','a','t'}

Mais il y a une règle en plus (rien n'est simple !), une chaîne se termine toujours par le caractère spécial '\0'. Donc le chaîne "Chat" sera stockée sous la forme {'C','h','a','t','\0'}, contient donc 5 éléments.

On pourrait déclarer :

char texte[5]; // déclaration du tableau.
texte[0] = 'C';
texte[1] = 'h';
texte[2] = 'a';
texte[3] = 't';
texte[4] = '\0';
printf("%s\n", texte); // affiche Chat
Déclaration raccourcie

On n'est pas obligé de tout écrire ainsi. On peut déclarer de façon plus compacte :

char texte[] = "Chat";
printf("%s\n", texte); // affiche Chat

En effet, C va comprendre que l'on veut créer la chaîne avec les caractères 'C','h','a','t', il ajoute automatiquement \0 à la fin et il déduit donc qu'il faudra un tableau de 5 cases.

Remarquez que

char[] texte = "Chat";

est équivalent et que l'on peut aussi écrire

char* texte = "Chat';
C'est un pointeur

Comme l'indique le dernier type déclaration, texte sera un pointeur. En effet, comme on l'a dit dans les tableaux, la variable obtenue suite à la déclaration d'un tableau est un pointeur pointant sur la première case du tableau.

Ici, texte pointe sur la première case du tableau {'C', 'h', 'a', 't', '\0'} et cette première cas contient un char, texte est donc de type char*, pointeur sur char.

Cas d'un texte dont on ne sait pas d'avance la taille

On vient d'envisager la création d'un texte connu d'avance. On pouvait prévoir la place mémoire à réserver : on savait que pour "Chat" il faudrait en tout 5 cases.

Mais que faire si on ne connaît pas le texte d'avance ? Si on demande un texte au clavier par exemple ? On a dit que l'on devait connaître la taille d'un tableau au moment de la compilation, mais on ne peut pas prévoir ce que l'utilisateur choisira d'écrire…

On prévoit plus grand
char texte[100];
printf("Entrez un mot : ");
scanf("%s", texte);

On prévoit ainsi une taille de 100 caractères en mémoire. Tenant compte du \0', cela nous permet de stocker une chaîne de 99 caractères.

Supposons que l'on écrive "Chien" dans ce tableau, on obtiendra : {'C','h','i','e','n','\0', ...}. Les cases suivantes sont laissées inchangées.

Lors d'un affichage, C n'ira pas plus loin que \0 qui marque la fin de la chaîne.

Dis autrement, si on s'amuse à changer

texte[12] = 'P';

Cela ne servira à rien.

scanf et le texte

Vous avez peut-être remarqué que, contrairement à une utilisation précédente de scanf, nous n'avons écrit ici scanf("%s", texte), sans le &.

Pourquoi cette différence ?

Je rappelle que & indique que l'on prend l'adresse de la variable transmise. Or, comme on l'a dit, texte est un pointeur, c'est une adresse. texte ne contient pas la chaîne de caractères mais l'adresse du début de la chaîne en mémoire. Donc, c'est un peu comme si & était déjà intégré.

Manipulation de chaînes

En Python

code python

chaine = "Tomate"
n = len(chaine) # renvoie la longueur, ici 6
chaine2 = "Poire"
chaine += chaine2 # chaine est modifié en
# ajoutant "Poire" donc contient "TomatePoire"

En C

longueur de chaîne
char* chaine = "Tomate";

On utilise une fonction présente dans <string.h> (il faut donc penser au `#include <string.h>`)

C'est la fonction strlen qui compte le nombre de caractère avant d'atteindre \0. À noter que le retour de la fonction strlen n'est pas de type int, néanmoins C fait la conversion automatiquement de sorte que le code écrit ci-dessous est valable.

int n = strlen(chaine);
concaténation

Pour concaténer chaine et chaine2 et mettre le contenu dans chaine il va falloir placer les caractères de chaine2 à la suite de chaine en plaçant '\0' à la fin. Il faut pour cela que chaine ait été défini en laissant assez de place.

La fonction strcat de <string.h> permet de le faire.

char chaine[20] = "Tomate"; // défini avec de la marge
char* chaine2 = "Poire";
strcat(chaine, chaine2);
comparaison
chaine == chaine2

Comme chaine et chaine2 sont des adresses mémoires, en faisant cela, on compare les adresses, pas les contenus. Si on veut comparer les contenus, il faut parcourir les deux tableaux de caractères (chaînes de caractères) et vérifier s'ils sont les mêmes, en ne tenant compte que de ce qui précède '\0'.

On utilise strcmp(chaine, chaine2).

appartenance

Pour savoir si un caractère c est contenu dans chaine, on utilisera

strchr(chaine, c) // renvoie l'indice de c dans chaine ou NULL si pas trouvé

Bibliothèque ''%%<string.h>%%''

Ce ne sont là que des exemples. Pour en savoir plus il faut consulter le manuel de la bibliothèque.

Encodage

C reconnaît l'utf8 mais la chaîne étant vue comme un tableau de char, il peut se produire des comportements surprenants.

char texte[100] = "Éléphant";

Les caractères É et é vont être encodé par 2 octets, donc 2 char. Cela signifie que ces caractères occuperont 2 cases du tableau.

D'ailleurs ce code génère une erreur :

char c = 'é';

En effet, le codage de 'é' nécessite deux octets et donc un variable de type char ne va pas suffire.

problème avec strlen

La fonction strlen ne tient pas compte de l'encodage. Cette fonction ne compte par le nombre de caractères composant la chaîne mais le nombre d'octets, c'est à dire le nombre de char, or comme on vient de le dire, le caractère 'é' consomme deux cases, deux char.

Pour coder "Éléphant" il faut 10 octets (2 octets pour 'É' et 2 octets pour 'é') si bien que strlen(texte) renvoie 10 au lieu du 8 que l'on aurait espéré en comptant les lettres.

contenu

Autre situation qui peut surprendre : si on énumère le contenu de texte

char texte[100] = "Éléphant";
for (int i=0; i<10; i++){
    printf("%c\n", texte[i]);
}
// retourne à l'écran dans l'ordre � � l � � p h a n t

On voit apparaître le contenu de texte découpé en char et comme on peut s'y attendre, 'É' est découpé en deux char qui sont affichés comme des caractères inconnus.

On peut aussi afficher les char en tant que nombres :

char texte[100] = "Éléphant";
for (int i=0; i<10; i++){
    printf("%02x\n", (unsigned char)texte[i]);
}
// retourne à l'écran dans l'ordre c3 89 6c a9 70 68 61 6e 74

Dans ce cas on convertit le char en unsigned char car on ne veut pas d'un nombre signé et on demande un affichage en hexadécimal "%02x".

Les deux premiers octets c3 89 correspondent bien au codage utf8 de 'É', 6c code 'l' et a9 70 code le 'é', etc.

Heureusement, la fonction printf gère le codage utf8 et donc

printf("%s\n, texte)

affiche le contenu de texte sans problème.