OVH Cloud OVH Cloud

printf et scanf

102 réponses
Avatar
Pascal
Bonjour,

J'ai des pb bizarres : dans une fonction j'ai besoin que l'utilisateur
me donne plusieurs infos, je fais donc des printf("question") et des scanf.

Sur les 3 premieres questions, ca marche bien, sur les 2 dernières, le
programme les affiche, mais le scanf ne semble pas marcher. Pourtant le
scanf recup sur les 5 fois un char *. C'est un bug? D'ailleurs si je ne
mets pas de \n sur le printf, parfois c'est encore pire...
voici un ex :
char *nom;
printf("nom\n");
scanf("%s", nom);
etc .. prenom, adress, tel, mail...

Parfois le scanf est comme sauté. Dans une autre fonction, je fais la
meme chose : une question, une reponse. Il m'affiche bien la question,
mais le scanf comme s'il était inactif, le programme ne me laisse pas
répondre, et passe à l'instruction suivante...

10 réponses

1 2 3 4 5
Avatar
Mat Free
J'ai des pb bizarres : dans une fonction j'ai besoin que l'utilisateur me
donne plusieurs infos, je fais donc des printf("question") et des scanf.

Sur les 3 premieres questions, ca marche bien, sur les 2 dernières, le
programme les affiche, mais le scanf ne semble pas marcher. Pourtant le scanf
recup sur les 5 fois un char *. C'est un bug? D'ailleurs si je ne mets pas de
n sur le printf, parfois c'est encore pire...
voici un ex :
char *nom;
printf("nomn");
scanf("%s", nom);
etc .. prenom, adress, tel, mail...

Parfois le scanf est comme sauté. Dans une autre fonction, je fais la meme
chose : une question, une reponse. Il m'affiche bien la question, mais le
scanf comme s'il était inactif, le programme ne me laisse pas répondre, et
passe à l'instruction suivante...


Il faut faire des scanf("%sn") je crois.

--
Mat

Avatar
Emmanuel Delahaye
Pascal wrote on 02/11/04 :
J'ai des pb bizarres : dans une fonction j'ai besoin que l'utilisateur me
donne plusieurs infos, je fais donc des printf("question") et des scanf.


scanf() ? C'est s'exposer à beaucoup de problèmes potentiels...

Sur les 3 premieres questions, ca marche bien, sur les 2 dernières, le
programme les affiche, mais le scanf ne semble pas marcher. Pourtant le scanf
recup sur les 5 fois un char *. C'est un bug? D'ailleurs si je ne mets pas de
n sur le printf, parfois c'est encore pire...


Ce genre de problèmes, par exemple...

voici un ex :
char *nom;


Je le sens mal...

printf("nomn");
scanf("%s", nom);


BOUM! Comportement indéfini... Pas de warning? Revoir les reglags du
compilateur. Il devrait signaler 'utilisation d'une variable non
initialisée... nom doit pointer sur un bloc valide, ou être un tableau
de char.

etc .. prenom, adress, tel, mail...

Parfois le scanf est comme sauté. Dans une autre fonction, je fais la meme
chose : une question, une reponse. Il m'affiche bien la question, mais le
scanf comme s'il était inactif, le programme ne me laisse pas répondre, et
passe à l'instruction suivante...


Laisse tomber scanf(). Pas une fonction de débutant (ni même
d'expérimenté...)

http://mapage.noos.fr/emdel/notes.htm#saisie

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"

Avatar
Anthony Fleury
Pascal wrote:

Bonjour,


Bonsoir,

J'ai des pb bizarres : dans une fonction j'ai besoin que l'utilisateur
me donne plusieurs infos, je fais donc des printf("question") et des
scanf.


ca sent le problème typique des premiers TPs de C :-) [cette phrase n'est en
aucun cas péjorative !]

Sur les 3 premieres questions, ca marche bien, sur les 2 dernières, le
programme les affiche, mais le scanf ne semble pas marcher. Pourtant le
scanf recup sur les 5 fois un char *. C'est un bug? D'ailleurs si je ne
mets pas de n sur le printf, parfois c'est encore pire...
voici un ex :
char *nom;
printf("nomn");
scanf("%s", nom);


OUILLE ! Et où est ce que la place nécessaire pour stocker le nom a été
reservé ? en fait, char* nom déclare une variable de type pointeur sur
char, donc pointeur sur un espace mémoire. Mais cet espace mémoire n'est
pas alloué, c'est l'espace pour stocker le pointeur qui l'est.
Donc plutôt faire :

char nom[NOMBRE_DE_CARACTERE_MAX]; avec NOMBRE_DE_CARACTERE_MAX défini à la
valeur qui va bien, ou bien char* nom = malloc (NOMBRE_DE_CARACTERE_MAX);
avec vérification que nom n'est pas NULL...

Ensuite pour demander le nom, scanf est peut sécurisé tout de même, car tu
peux rentrer 42 caractères alors que seulement 20 sont reservés, et la
pouf...

Donc préferer fgets() :

fgets(nom, NOMBRE_DE_CARACTERE_MAX, stdin);

vérifier le retour de fgets() et enlever le n que fgets laisse et tout ira
bien...

etc .. prenom, adress, tel, mail...


Pareil pour les autres alors...

Parfois le scanf est comme sauté. Dans une autre fonction, je fais la
meme chose : une question, une reponse. Il m'affiche bien la question,
mais le scanf comme s'il était inactif, le programme ne me laisse pas
répondre, et passe à l'instruction suivante...


ca c'est sûrement parce que scanf a un comportement « merdique ». En effet,
si tu fais ceci :

int nombre, char caractere;

printf("Entrez le nombren");
scanf("%d", &nombre);
printf("Entrez le caractèren");
scanf("%c", &caractere);

tu vas avoir la surprise que le caractère est sauté (de même pour une chaine
dans ton cas).

Pourquoi ? et bien parce que scanf a laissé le caractère 'n' qui est tapé
derrière le nombre pour valider la première saisie, et quand il regarde
l'entrée standard il va retrouver ce caractère et le prendre pour la saisie
d'après. Conclusion il va « ignorer » (du point de vue de l'utilisateur) la
saisie d'après :-)

Une solution pour éviter ceci (si tu persistes et/ou est obligé d'utiliser
scanf()) est de vider le buffer d'entrée après toute saisie avec scanf() :

while(fgetc(stdin) != 'n');

Qui va boucler tant qu'il n'a pas rencontrer de 'n' dans le buffer
d'entrée. Deux conséquences à cela :
- si une saisie a été faite, et qu'il reste des caractères dont un caractère
'n' dans stdin, cette boucle prendra tous les caractères jusqu'à ce fameux
caractère 'n' et donc logiquement après les autres saisies ne seront pas «
ignorées ».
- Si aucune saisie n'a été faite, ca attendra que l'utilisateur appuie une
fois sur Entrée, ca ne continuera pas le programme tant que cette action
n'aura pas été faite. En gros, cette boucle pourrait suivre tout scanf().
Ca en plus de vérifier le retour de scanf() (qui évite les boucles infinies
en TP...) est un bon début pour utiliser correctement scanf.

Anthony
--
Alan Turing thought about criteria to settle the question of whether
machines can think, a question of which we now know that it is about as
relevant as the question of whether submarines can swim.
-- Dijkstra

Avatar
Richard Delorme
Bonjour,

J'ai des pb bizarres : dans une fonction j'ai besoin que l'utilisateur
me donne plusieurs infos, je fais donc des printf("question") et des scanf.

Sur les 3 premieres questions, ca marche bien, sur les 2 dernières, le
programme les affiche, mais le scanf ne semble pas marcher. Pourtant le
scanf recup sur les 5 fois un char *. C'est un bug? D'ailleurs si je ne
mets pas de n sur le printf, parfois c'est encore pire...
voici un ex :
char *nom;
printf("nomn");
scanf("%s", nom);


Attention, nom ne pointe nul part...

etc .. prenom, adress, tel, mail...

Parfois le scanf est comme sauté. Dans une autre fonction, je fais la
meme chose : une question, une reponse. Il m'affiche bien la question,
mais le scanf comme s'il était inactif, le programme ne me laisse pas
répondre, et passe à l'instruction suivante...


scanf("%s", nom) ne lit qu'un mot, pas une ligne complête. Donc si l'on
saisie une adresse en plusieurs mots, le premier scanf lit le premier
mot, le second scanf le second mot, etc. d'une seule saisie. D'où se
comportement bizarre.
Saisir une ligne avec scanf est toutefois possible. P.ex. :

char adresse[81];
int n_lu;
printf("adresse >"); fflush(stdout);
n_lu = scanf("%80[^n]%*[^n]", adresse);
getchar();
if (n_lu != 1) {
/* message d'erreur */
}

La signification de tout ceci :
80[^n] lit tous les caractères jusqu'au (et à l'exclusion de)
fin-de-ligne ('n'), et à concurrence de 80 caractères, taille maximale
d'une chaine stockable dans adresse, tel qu'il est défini ici.
Le second %*[^n] sert à lire le reste ligne (si elle est trop longue),
tout en ne stockant pas le résultat.
Enfin, le getchar() qui suit lit le caractère 'n' qu'on avait pas
encore lu.
Enfin, on vérifie avec n_lu qu'une adresse a bien été entrée.

Avatar
Hamiral
fgets(nom, NOMBRE_DE_CARACTERE_MAX, stdin);


Dans ce cas-là il faudra déclarer nom comme suit :
char nom[NOMBRE_DE_CARACTERES_MAX + 1];
ou
char* nom = malloc( NOMBRE_DE_CARACTERES_MAX + 1 );

Sinon fgets va faire boum quand elle va ajouter le '' en fin de chaîne ...

--
Florent Curé
florent (point) cure (arobase) free (point) fr

Avatar
Pascal
Anthony Fleury wrote:

OUILLE ! Et où est ce que la place nécessaire pour stocker le nom a été


en fait j'ai réecris rapidement mon code, mais je me sers bien d'un char
nom [TAILLE]; (je fais même un memset)

Donc préferer fgets() :

fgets(nom, NOMBRE_DE_CARACTERE_MAX, stdin);

vérifier le retour de fgets() et enlever le n que fgets laisse et tout ira
bien...


Ba c'est super pratique ça...

Pourquoi ? et bien parce que scanf a laissé le caractère 'n' qui est tapé
derrière le nombre pour valider la première saisie, et quand il regarde
l'entrée standard il va retrouver ce caractère et le prendre pour la saisie
d'après. Conclusion il va « ignorer » (du point de vue de l'utilisateur) la
saisie d'après :-)



J'ai enfin l'explication de ce "bug". Je trouve ça aberrant que la
fonction scanf soit si mal foutu. Faudrait changer le man, et foutre
"grosse fonction qui fait n'imp".

En tout cas merci Anthony pour tes explications détaillése.

--
Pascal

Avatar
Emmanuel Delahaye
Hamiral wrote on 02/11/04 :
fgets(nom, NOMBRE_DE_CARACTERE_MAX, stdin);


Dans ce cas-là il faudra déclarer nom comme suit :
char nom[NOMBRE_DE_CARACTERES_MAX + 1];
ou
char* nom = malloc( NOMBRE_DE_CARACTERES_MAX + 1 );

Sinon fgets va faire boum quand elle va ajouter le '' en fin de chaîne ...


Non. Relire la doc de fgets().

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"C is a sharp tool"


Avatar
Anthony Fleury
Hamiral wrote:

fgets(nom, NOMBRE_DE_CARACTERE_MAX, stdin);


Dans ce cas-là il faudra déclarer nom comme suit :
char nom[NOMBRE_DE_CARACTERES_MAX + 1];
ou
char* nom = malloc( NOMBRE_DE_CARACTERES_MAX + 1 );

Sinon fgets va faire boum quand elle va ajouter le '' en fin de chaîne
...


Pas d'après le man de fgets(), ni sûrement d'après la norme :-) [je l'ai pas
sous la main]

GETS(3) Linux Programmer's Manual GETS(3)

NAME
fgetc, fgets, getc, getchar, gets, ungetc - input of char­
acters and strings

SYNOPSIS
#include <stdio.h>

int fgetc(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
int getc(FILE *stream);
int getchar(void);
char *gets(char *s);
int ungetc(int c, FILE *stream);

DESCRIPTION
fgets() reads in at most one less than size characters
from stream and stores them into the buffer pointed to by
s. Reading stops after an EOF or a newline. If a newline
is read, it is stored into the buffer. A '' is stored
after the last character in the buffer.

Anthony
--
Alan Turing thought about criteria to settle the question of whether
machines can think, a question of which we now know that it is about as
relevant as the question of whether submarines can swim.
-- Dijkstra


Avatar
Nicolas Cavigneaux

fgets(nom, NOMBRE_DE_CARACTERE_MAX, stdin);


Dans ce cas-là il faudra déclarer nom comme suit :
char nom[NOMBRE_DE_CARACTERES_MAX + 1];
ou
char* nom = malloc( NOMBRE_DE_CARACTERES_MAX + 1 );

Sinon fgets va faire boum quand elle va ajouter le '' en fin de chaîne ...


Vu dans le man 3 fgets:

char * fgets (char * s, int size, FILE * stream);

fgets() lit au plus size - 1 caractères depuis stream et les place dans
le buffer pointé par s. La lecture s'arrête après EOF ou un retour-
chariot. Si un retour-chariot (newline) est lu, il est placé dans le
buffer. Un caractère nul '' est placé à la fin de la ligne.

donc il me semble bien que ce soit Anthony qui ait raison.

Bonne soirée.
--
Nicolas Cavigneaux | GPG KeyID : F0954C41
| http://bounga.ath.cx


Avatar
Hamiral
Hamiral wrote:



fgets(nom, NOMBRE_DE_CARACTERE_MAX, stdin);


Dans ce cas-là il faudra déclarer nom comme suit :
char nom[NOMBRE_DE_CARACTERES_MAX + 1];
ou
char* nom = malloc( NOMBRE_DE_CARACTERES_MAX + 1 );

Sinon fgets va faire boum quand elle va ajouter le '' en fin de chaîne
...



Pas d'après le man de fgets(), ni sûrement d'après la norme :-) [je l'ai pas
sous la main]

fgets() reads in at most one less than size characters


Mais c'est pas possible ça, la deuxième fois que je poste un ânerie plus
grosse que moi sur ce groupe. Pourant j'avais lu la manpage juste
avant de poster, pour être sûr. Mais j'ai dû lire un peu vite, j'ai
sauté le "one less" ...
Mea culpa, je posterai plus avant de lire 7 fois la manpage correspondante.

Encore désolé.


--
Florent Curé
florent (point) cure (arobase) free (point) fr



1 2 3 4 5