char
(et vice-versa) ?sizeof(char)
?sizeof('a')
ne vaut pas 1 ?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.
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().
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).
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.
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).
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.
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.
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