• Aucun résultat trouvé

Lire ou écrire des chaînes

Dans le document Conservatoire National des Arts et Métiers (Page 118-125)

adresses croissantes

6. Les chaînes de caractères

6.2 Lire ou écrire des chaînes

Les prototypes des fonctions permettant de lire ou bien d’écrire des chaînes se trouvent dans le fichier d’entête stdio.h. Si vous souhaitez les utiliser, la ligne suivante doit être inclue au début de votre fichier source :

#include <stdio.h>

Les fonctions printf et scanf opèrent à partir des entrées-sorties standard stdin (le clavier par défaut) et stdout (l’écran par défaut). Le programme suivant :

#include <stdio.h>

main() {

char s1[10];

scanf("%s", s1);

}

va lire une suite de caractères au clavier pour les ranger dans le tableau s1 en commençant à partir de s1[0] et en ajoutant automatiquement un caractère nul en fin de chaîne. Le code de format %s fonctionne comme les codes de formats numériques (et non comme le code de format %c), c’est-à-dire qu’il commence par sauter les délimiteurs éventuels (espace ou fin de ligne) et qu’il s’interrompt lorsqu’il rencontre un de ces délimiteurs. scanf ne peut donc pas lire de chaîne commençant par un espace ou contenant un espace.

Il est tout à fait possible que scanf provoque un débordement de tableau. Il suffit pour cela de taper au clavier plus de 9 caractères. Les caractères excédentaires seront placés après la fin du tableau et risque donc d’écraser d’autres données. Il faut donc réserver généreusement l’emplacement nécessaire au stockage de la chaîne (char s1[255];).

Notez bien que scanf ne marche qu’avec un tableau (ou un pointeur initialisé par malloc) et pas avec un pointeur non-initialisé (il faut que l’espace mémoire soit réservé pour accueillir la chaîne).

#include <stdio.h>

main() {

char *s1;

scanf("%s", s1); 0 débordement }

ATTENTION : la chaîne littérale est constante et ne peut pas être modifiée :

#include <stdio.h>

main() {

char *s1, *s2 = "toto", s3[128] = "titi";

s1 = "tata";

s1[0] = '1'; 0 chaine constante s2[0] = '1'; 0 chaine constante s3[0] = '1'; ♥ chaine modifiable }

Pour être sûr que le buffer du clavier est vide de tout caractère avant de faire un scanf, la fonction fflush peut être utilisée :

#include <stdio.h>

main() {

char s1[100], s2[100];

scanf("%s", s1);

fflush(stdin); /* vide le buffer clavier */

scanf("%s", s2);

}

Avec le programme précédent, si vous tapez : un deux ↵

trois ↵

Vous obtiendrez un dans s1 et trois dans s2. Sans fflush, vous auriez obtenu un dans s1 et deux dans s2 (sans attente sur le deuxième scanf).

La fonction printf accepte elle aussi le code de format %s et l’utilise comme les autres codes de format.

#include <stdio.h>

main() {

char s1[100];

scanf("%s", s1);

printf("s1 = %s", s1);

}

Le résultat obtenu est : essai ↵

s1 = essai

Exercice 6.1 : quels résultats fournira le programme suivant ?

Exercice 6.2 : expliquez le fonctionnement du programme suivant. Quels résultats fournira-t-il ?

#include <stdio.h>

#include <stdlib.h>

#define NB_CHAR 100 main()

{

char *s1;

s1 = malloc(NB_CHAR * sizeof(char));

scanf("%s", s1);

printf("%s\n", s1);

}

Exercice 6.3 : expliquez le fonctionnement du programme suivant. Quels résultats fournira-t-il ?

#include <stdio.h>

main() {

char *s1 = "bonjour";

int i;

printf("\n");

i = 0;

while(s1[i])

putchar(s1[i++]);

}

Exercice 6.4 : modifiez le programme de l’exercice 6.3 en n’utilisant que les pointeurs (sans utiliser le formalisme « tableaux »). Deux versions sont possibles.

Il est possible d’utiliser une variable chaîne à la place de la chaîne littérale du printf comme dans cet exemple :

#include <stdio.h>

main() {

char s1[100] = "essai";

char s2[100] = "s1 = %s";

printf(s2, s1);

}

Le résultat obtenu est : s1 = essai

Attention, il faut noter que printf ne peut pas connaître la longueur de la chaîne. Il ne connaît que le pointeur de début de chaîne s1 (qui est égal à &s1[0]) et affiche tous les caractères dans la chaîne jusqu’à ce qu’il rencontre un caractère nul (\0). Si vous supprimez le caractère nul à la fin de s1, printf affichera tout ce qu’il trouve en mémoire jusqu’à ce qu’il tombe sur un \0.

Exercice 6.5 : faites un tableau représentant les 8 premiers emplacements en mémoire de s1 et montrer son évolution lors de l’exécution du programme suivant.

#include <stdio.h>

main() {

char s1[100] = "coucou";

s1[6] = 1;

printf("s1 = %s", s1);

}

Exercice 6.6 : modifiez le programme suivant pour qu’il n’imprime que les 4 derniers caractères de s1.

#include <stdio.h>

main() {

char *s1 = "bonjour";

printf("%s\n", s1);

}

Les fonctions sprintf et sscanf opèrent à partir d’une chaîne de caractères. La fonction sscanf réalise la même chose que scanf mais à partir d’une chaîne et pas à partir du clavier (entrée standard = stdin). Exemple :

#include <stdio.h>

main() {

char s1[100] = "1 2";

int x, y;

sscanf(s1, "%d%d", &x, &y);

printf("x = %d, y = %d", x, y);

}

Le résultat obtenu est : x = 1, y = 2

La fonction sprintf se comporte de la même manière que printf sauf qu’au lieu d’écrire sur l’écran (sortie standard = stdout), elle écrit dans une chaîne de caractères. Exemple :

#include <stdio.h>

main() {

char s1[100] = "1 2";

int x = 1, y = 2;

sprintf(s1, "x = %d, y = %d\n", x, y);

printf(s1);

}

Le résultat obtenu est : x = 1, y = 2

Exercice 6.7 : écrivez un programme qui lit deux nombres entiers fournis obligatoirement sur une même ligne. Le programme ne devra pas « planter » en cas de réponse incorrecte (avec des caractères invalides par exemple) comme le ferait scanf("%d%d", …); mais simplement afficher un message et demander une autre saisie. Le comportement du programme devra être le même si le nombre de valeurs correctement saisies est insuffisant.

Par contre, il devra ignorer les valeurs excédentaires. On utilisera gets et sscanf.

Exercice 6.8 : complétez les ???? dans le programme suivant afin d’obtenir le comportement suivant :

1) l’utilisateur tape un nom de fichier sans extension (nom_de_base),

2) le programme crée 10 fichiers appelés nom_de_base.1, nom_de_base.2, ..., nom_de_base.10.

#include <stdio.h>

main() {

char nom_de_base[100];

char nom_de_fichier[100];

FILE *fp; /* pointeur sur fichier */

int i;

scanf(????);

for (i = 0; i < 10; i++) { sprintf(????);

/* création du fichier */

fp = fopen(nom_de_fichier, "w");

fclose(fp); /* fermeture du fichier */

} }

Les fonctions gets et puts opèrent à partir des entrées-sorties standards stdin (le clavier par défaut) et stdout (l’écran par défaut). Elles manipulent exclusivement des chaînes de caractères. Dans le programme suivant :

#include <stdio.h>

main() {

char s1[100];

gets(s1);

puts(s1);

}

Le résultat obtenu est :

^^un^^deux^^trois ↵

^^un^^deux^^trois

L’instruction gets(s1) va lire une suite de caractères et la ranger dans s1, terminée par un caractère nul. Il faut noter qu’à la différence de scanf :

• aucun délimiteur n’est sauté avant la lecture,

• les espaces sont lus comme les autres caractères,

• la lecture ne s’arrête qu’à la rencontre d’un caractère fin de ligne qui n’est pas copié dans la chaine. Aucun contrôle du nombre de caractères lus n’étant réalisé, le débordement de s1 est tout à fait possible si la chaine saisie est trop longue. C’est une faille de sécurité importante du langage C.

La fonction puts(s1) affiche les caractères trouvés à partir de &s1[0] jusqu’à atteindre le caractère nul, puis réalise un changement de ligne (c’est là la seule différence notable avec printf).

Exercice 6.9 : écrivez un programme ayant le comportement suivant en utilisant les fonctions gets, puts, scanf et printf.

Où habitez-vous ? paris ↵

Donnez votre nom et prénom : dupont jean ↵ Bonjour M. dupont jean qui habitez paris

Dans le document Conservatoire National des Arts et Métiers (Page 118-125)

Documents relatifs