Les chaines de caractères
Pour manipuler les chaines de caractères, Python possède un type str
. Un caractère unique est un cas particulier d’un objet de type str
de longueur 1
.
A contrario le langage C dispose du type primitif char
pour manipuler les caractères uniques, et la chaîne de caractères est traitée comme un tableau de n
char
terminé par \0
.
On retrouve là les deux caractéristiques des deux langages :
Python est un langage haut niveau et manipule la chaine de caractère ;
C est un langage bas niveau et manipule le caractère.
Déclaration
La chaine de caractères étant un tableau de caractères, la déclaration peut donc se faire de façon similaire à ce qu’on a vu pour les tableaux :
char s[14] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', ' ', '!', '\0'};
On lui préfèrera cependant une façon plus compacte, où la taille sera déduite de la partie RHS de la déclaration (comme déjà vu dans le chapitre sur les tableaux) et la chaine définie par une suite de caractères délimitée par des guillemets "
:
char s[] = "Hello World !";
Pour cette deuxième façon d’initialiser la chaine de caractères, le caractère terminal \0
(nécessaire pour identifier la fin) est inséré automatiquement à la fin.
Corollaire : une chaine de n
caractères utilisera n+1
octets en mémoire.
A expérimenter
On considère le programme suivant :
1// str.c
2
3#include <stdio.h>
4
5int main()
6{
7 char s[] = "Hello World !";
8
9 for (int i=0 ; s[i]!='\0' ; i++)
10 printf("i = %2d, s[%2d] = %c, &s[%2d] = %p\n", i, i, s[i], i, &s[i]);
11
12return 0;
13}
Répondez aux questions suivantes :
avant de l’exécuter, uniquement en l’examinant, que fait-il ?
prêter une attention particulière à l’écriture de la condition de continuation (ligne 9). Quelle propriété des chaines de caractères met elle en oeuvre ?
après exécution du programme, et observation des adresses mémoire, peut on retrouver la taille allouée à une variable de type
char
?pourquoi le caractère terminal
\0
n’est il pas affiché ?
Affichage
L’expérimentation précédente met en oeuvre un affichage caractère par caractère, utile pour la compréhension mais sans intérêt pour l’affichage dans le terminal. La fonction printf()
dispose de l’espace réservé %s
pour l’affichage des chaines :
Le code
printf("%s\n", s);
produit dans le terminal:
Hello World !
Manipulation avec des pointeurs
Puisqu’une chaine de caractères est un tableau, on peut également la manipuler avec un pointeur. En particulier, si s
est déclarée comme une chaine de caractères, les trois instructions ci dessous sont équivalentes et affichent l’adresse mémoire du premier caractère de la chaîne.
printf("%p\n", s);
printf("%p\n", &s);
printf("%p\n", &s[0]);
On peut également se servir de l’arithmétique des pointeurs pour l’affichage caractère par caractère :
char *p = s;
while(*p != '\0') {
printf("%c", *p);
p++;
}
Note
On pourrait aussi penser à utiliser un pointeur pour déclarer la chaine :
char* p = "Hello World !";
Mais cette façon de faire devra toutefois être évitée car dans ce cas la modification n’est pas une opération sûre.
Il est préférable de déclarer la chaine de façon classique, puis un pointeur pour la parcourir :
char s[] = "Hello World !";
char *p = s;
while(*p != '\0') {
printf("%c", *p);
p++;
}
Tableau de chaines
Une chaine de caractères étant déjà un tableau de caractères, un tableau de chaines de caractères sera donc implémenté sous la forme d’un tableau multidimensionnel :
1// arrayofstrings.c
2
3#include <stdio.h>
4
5const int SIZE = 5;
6
7int main()
8{
9
10 char *simpsons[] = {
11 "Homer",
12 "Marge",
13 "Bart",
14 "Lisa",
15 "Maggie"};
16
17 char *p;
18
19 for (int i = 0; i < SIZE; i++)
20 {
21 p = simpsons[i];
22
23 while (*p != '\0')
24 {
25 printf("%c", *p);
26 p++;
27 }
28
29 printf("\n");
30 }
31}
Le code ci dessus présente deux particularités :
l’utilisation d’une constante (ligne 5). Dans tout le code
SIZE
sera remplacée par le compilateur par la valeur5
;- le double parcours du tableau à deux dimensions :
on accède aux chaines de caractères par indice (ligne 19) ;
chaque chaine est parcourue par pointeur (ligne 23).
L’exécution donne le résultat attendu
$ gcc -Wall -Wextra arrayofstrings.c -o arrayofstrings
$ ./arrayofstrings
Homer
Marge
Bart
Lisa
Maggie
$
Lecture du clavier
Jusqu’à présent les données utilisées étaient stockées « en dur » dans le code source. Ca nous a permis de mettre en évidence les principes élémentaires du langage C mais il y aurait évidemment une plus grande souplesse à pouvoir lire quelques informations directement au clavier, durant l’exécution du programme.
La fonction fgets()
permet ceci :
1#include <stdio.h>
2#include <string.h>
3
4int main(){
5 char s[100];
6
7 printf("Entrez une chaine de caractères : %s\n", s);
8 fgets(s, 100, stdin);
9 printf("La chaine de caractères récupérée dans s : %s\n", s);
10 printf("composée de %ld caractères lus au clavier\n", strlen(s));
11}
A la ligne 8, la fonction fgets()
prend en paramètres :
la chaine dans laquelle les caractères saisis au clavier seront stockés (elle est déclarée ligne 5) ;
le nombre maximal de caractères à lire ;
et le périphérique utilisé.
stdin
représente le clavier mais on pourrait tout aussi bien lire les données dans un fichier.
Lorsqu’on exécute ce programme:
$ gcc -std=c99 -Wall -Wextra read-keyboard.c -o read-keyboard
$ ./read-keyboard
Entrez une chaine de caractères :
hello
La chaine de caractères récupérée dans s : hello
composée de 6 caractères lus au clavier
$
Pour cette exécution, l’utilisateur a rentré au clavier la chaine hello
et validé la saisie avec Enter.
Observer l’affichage dans le terminal et notamment :
le saut de ligne. D’où provient il ?
le nombre de caractères stockés dans la chaine
s
. Est il en accord avec la chaine entrée au clavier ? D’où provient l’écart ?
Exercice
Ecrire une boucle permettant d’afficher chaque caractère de la chaine sur une ligne séparée en ajoutant l’information de position. Le résultat devrait être similaire à
$ ./read-keyboard
Entrez une chaine de caractères :
hello
La chaine de caractères récupérée dans s : hello
composée de 6 caractères lus au clavier
Caractère 0 : h
Caractère 1 : e
Caractère 2 : l
Caractère 3 : l
Caractère 4 : o
Caractère 5 :
$
Conversion numérique
Il est courant que les données entrées au clavier soient numériques, sous forme d’entiers ou de nombres rééls. La fonction fgets()
stocke la saisie au clavier dans une chaine de caractères. Il faut donc une opération de conversion de type. Celle ci est réalisée en C avec les fonctions suivantes, dont le prototype est défini dans <stdlib.h>
:
atoi()
pour la conversion vers un entier ;atod()
pour la conversion vers un double.
Exercice
Ajouter une fonction de conversion au programme précédent, ainsi qu’une manipulation numérique de la chaine convertie. Le résultat devrait être similaire à
$ ./read-keyboard
Entrez une chaine de caractères :
123456
après conversion : 123456
et multiplication par 2 : 246912
Passage des arguments en ligne de commande
Lire le clavier c’est bien, mais passer directement les arguments sur la ligne de commande, c’est mieux !
Jusqu’à présent, l’appel de la fonction main()
se faisait sans aucun argument. Et la ligne de commande déclenchant l’exécution du programme ne comportait qu’un seul élément.
Par exemple:
$ ./read-keyboard
Mais le langage C permet de récupérer les caractères saisis sur la même ligne, pour les utiliser comme données d’entrée. Pour cela, il faut utiliser les deux paramètres optionnels (dont on s’est passé jusqu’à présent) pour appeler la fonction main()
:
int main( int argc, char *argv[] )
Ici :
argc
représente le nombre total d’arguments sur la ligne de commande, y compris le nom du programme exécutable ;et
*argv[]
un pointeur vers le premier élément d’un tableau de strings qui contient les arguments.
Un exemple d’utilisation :
// cmdline.c
#include <stdio.h>
int main(int argc, char *argv[])
{
for (int i=0; i < argc ; i++)
{
printf("argument %d : %s\n", i, argv[i]);
}
return 0;
}
Lorsqu’on exécute ce programme:
$ ./cmdline james bond 007
argument 0 : ./cmdline
argument 1 : james
argument 2 : bond
argument 3 : 007