précédent  index  suivant

8. Chaînes de caractères


8.1 Comment comparer deux chaînes ?

Pour comparer deux chaînes entre elles, il faut utiliser la fonction strcmp(), et non l'opérateur ==. Celui-ci comparera les pointeurs entre eux, ce qui n'est probablement pas ce qui est voulu !

	const char * sz = "non";
	if(strcmp(sz, "oui") == 0) {
		/* sz et "oui" sont egaux */
	}
		

Il existe aussi la fonction strncmp() qui permet de contrôler la longueur de comparaison.

8.2 Comment recopier une chaîne dans une autre ?

Il faut utiliser la fonction strcpy(), et non l'opérateur d'affectation =. Il faut s'assurer que l'on dispose d'espace suffisant dans la chaîne cible avant d'utiliser strcpy(), qui ne fait aucun contrôle de débordement.

Pour une copie plus sécurisée, on préférera la fonction strncpy().

8.3 Comment lire une chaîne au clavier ?

Il y a de nombreuses fonctions qui le font. Le plus sûr est d'utiliser fgets().

	char tab[20];
	fgets(tab, sizeof tab, stdin);
		

Voici un exemple complet d'utilisation propre de fgets() pour la lecture d'une ligne, avec un contrôle d'erreurs. Cette fonction est fournie par Emmanuel Delahaye.

	#include <stdio.h>
	#include <string.h>

	int get_line(char *buf, size_t size) {
	   int ret; /* 0=Ok 1=Err 2=Incomplet
	             * On peut aussi definir des constantes. (Macros, enum...)
	             */

	   if (fgets(buf, size, stdin) != NULL) {
		  char *p = strchr(buf, '\n'); /* search ... */
		  if (p != NULL) {
			 *p = 0; /* ... and kill */
			 ret = 0;
		  } else {
			 ret = 2;
		  }
	   } else {
		  ret = 1;
	   }
	   return ret;
	}
		

Il peut être intéressant dans certains cas de faire un traitement particulier dans le cas 2 (lecture incomplète), comme vider le buffer du flux ou redimentionner la zone de réception (voir la question 14.5).

La fonction gets() est à proscrire, car il n'y a aucun contrôle de débordement, ce qui peut engendrer de nombreux bugs (stack overflow) (cf. 8.7).

8.4 Comment obtenir la valeur numérique d'un char (et vice-versa) ?

En C, un char est un petit entier. Il n'y a donc aucune conversion à faire. Quand on a un char, on a aussi sa valeur, et vice-versa.

8.5 Que vaut sizeof(char) ?

Un char vaut et vaudra toujours 1 indépendamment de l'implémentation. En effet, les tailles d'allocation en C se calculent en char (size_t). Or, un char a une taille de 1 char. Donc

	sizeof(char) == (size_t) 1
		

Pour autant, un char ne fait pas forcément 8 bits (un octet) (voir aussi 16.2).

8.6 Pourquoi sizeof('a') ne vaut pas 1 ?

En C, les caractères constants sont des int, et non des char. Ainsi,

	sizeof('a') == sizeof(int)
		

ce qui peut valoir 2 ou 4 sur votre machine, ou autre chose.

8.7 Pourquoi ne doit-on jamais utiliser gets() ?

Serge Paccalin donne l'exemple suivant :

	#include <stdio.h>

	int main(void)
	{
	   char chaine[16];

	   printf("Tapez votre nom :\n");
	   gets(chaine);
	   printf("Vous vous appelez %s.\n",chaine);
	   return 0;
	}
		

Quand on tape une chaîne de plus de 15 caractères, rien ne va plus : gets() accepte sans broncher la chaîne mais lorsqu'il est question de la stocker dans

	char chaine[16]
		

on peut obtenir un magnifique Segmentation fault (core dumped). Dans certains cas, avec certains compilateurs sur certaines machines et certains OS, il est possible que ça passe. Mais attention ! C'est un leurre, et le changement de cible démontrera la malfaçon.

Cette fonction gets(), trop dangereuse, a été supprimée de la nouvelle norme C11.

8.8 Pourquoi ne doit-on presque jamais utiliser scanf() ?

scanf() est une fonction de la bibliothèque standard, qui est souvent la première que l'on apprend pour lire des données au clavier. Cette fonction n'est pas plus dangereuse qu'une autre, à condition de bien savoir l'utiliser, ce qui n'est pas donné à tout le monde.

Par exemple, regardez le programme suivant donné par Serge Paccalin :

	#include <stdio.h>

	int main(void)
	{
	   int val = 0;

	   while (val != 1) {
		  printf("Tapez un nombre"
				 "(1 pour arreter le programme) :\n");
		  scanf(" %d",&val);
		  printf("Vous avez saisi %d.\n",val);
	   }
	   return 0;
	}
		

Et il explique : « Quand le programme demande un nombre, taper "toto" suivi de la touche Entrée. Le programme part en boucle parce que tous les scanf() successifs butent sur "toto" qui reste indéfiniment dans stdin. »

Dans la plupart des cas, l'utilisation de la fonction fgets() sera plus simple et moins risquée.

scanf() est une fonction qui peut être utilisée dans certaines conditions, et à condition de bien savoir ce que l'on fait. L'utilisation de scanf() pour la lecture de nombres (entiers ou flottants) est l'une des plus acceptable. Par contre, la lecture d'une chaîne avec scanf() sans contrôle de format est aussi dangereux que gets().

	scanf("%s", astring) ;
		

est donc a proscrire.

scanf() n'est préférable à fgets() que dans le cas ou l'on veut lire mot par mot et non ligne par ligne, et conserver le reste dans le buffer du flux d'entrée. Sinon, lire un mot avec fgets() ne pose pas de problème et est même plus simple.

	scanf("%4s", astring) ;
		

Cette construction, par exemple, ne pose pas les problèmes cités plus haut (si le contrôle d'erreurs est fait), et est parfois utile.

Voir aussi les questions 8.7 et 8.3


précédent  index  suivant