IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

La boucle ne respecte pas le premier pas


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2013
    Messages
    120
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Côte d'Ivoire

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2013
    Messages : 120
    Par défaut La boucle ne respecte pas le premier pas
    J'essaie de comprendre le fonctionement des fonctions fgets et des conversions de chaînes en nombre quelconque. Cependant je ne comprends pas
    les tours suivants de la boucle
    do... while()
    On arrive pas à saisir une deuxième note.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    int main(int argc, char const *argv[])
    {
    	int nbre=0;
    	int i= 1 ;
    	char val[3];
    	char c= 'O';
     
        do
    	{
    		printf("NOTE:" );
    		if(fgets(val,3, stdin) !=NULL);
    		 nbre= atoi(val);
     
    		if (nbre>=0 && nbre<=20)
    		{
     
    			printf("MOYENNE: %.2f \n", (float)(nbre/i) ) ;
    		    i++ ;
     
    		}
     
    		printf("Voulez vous continuer? O/N:");
     
    		c=getc(stdin) ;
     
    	}while(c == 'O') ;
     
     
    	return 0;
    }

  2. #2
    Membre Expert

    Homme Profil pro
    Responsable des études
    Inscrit en
    Mars 2009
    Messages
    553
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Responsable des études
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2009
    Messages : 553
    Par défaut
    Copie d'écran ?

  3. #3
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Bonjour,

    - La console attend la saisie de caractères qu'elle mémorise. Dès qu'elle reçoit un retour chariot, l'ensemble des caractères et la fin de ligne sont transmis dans un buffer.
    - La fonction fgets lit dans ce buffer (elle attend tant qu'il n'y a pas assez de caractères ou qu'avant la fin, une fin de ligne ou une fin de fichier est reçue.) Ici le nombre de caractères vaut 2.
    - La fonction getc lit un caractère dans le buffer (elle attend au moins un caractère ou une fin de ligne ou une fin de fichier.)

    Alors, que se passe-t-il ?
    - fgets() attend;
    - l'utilisateur tape "10\n", le buffer contient "10\n";
    - fgets() extrait "10" qui donne le nombre 10;
    - getc() lit le caractère restant dans le buffer '\n';
    - ça n'est pas 'O', c'est fini.

    Autre scénario:
    - fgets() attend;
    - l'utilisateur tape "2\n", le buffer contient "2\n";
    - fgets() extrait "2\n", qui donne le nombre 2;
    - getc() attend;
    - l'utilisateur tape "O\n";
    - getc() lit le 'O', il reste "\n" dans le buffer;
    - on peut continuer;
    - fgets() lit dans le buffer le "\n" qui restait, qui donne un nombre non valide;
    - getc() attend;
    - ...

    Alors attention quand on utilise des fonctions fgets() et getc() successivement, la solution n'est pas simple...

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 473
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 473
    Par défaut
    Et bien sûr, attention à bien taper « O » et pas « o »…

  5. #5
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 830
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 830
    Billets dans le blog
    1
    Par défaut
    Bonjour

    Tu as soulevé un des points les plus difficiles à maitriser: la gestion de ton clavier.

    Déjà tu as dû remarquer que ton souci n'apparaissait qu'avec des notes à 2 chiffres (10, 11, 12, ..., 20). En effet, quand tu saisis "12", tu appuies sur '1', puis '2' puis '<entree>'. Ton clavier stockant tout ce que tu as appuyé, il contient alors ces 3 éléments.
    Ensuite tu demandes à fgets() de récupérer le contenu de ton clavier en lui indiquant que la zone réceptrice fait 3 octets. fgets() va donc en récuperer 2 (elle réserve une place pour y stocker le '\0'). Elle stocke donc dans "val" les éléments '1', '2' et '\0'. Le '\n' reste donc dans le clavier.
    Ensuite, au getc() suivant, le clavier n'étant pas vide, la fonction n'a pas besoin de s'arrêter pour te laisser taper un truc et récupère ce '\n' qui va être stocké dans "c". La variable "c" ne contenant pas 'O', la boucle ne recommence pas.

    Solution: éliminer ce '\n' avant le getc(). Tu pourrais penser à rajouter un "getc()" supplémentaire sous le "fgets()" sauf que ce souci n'apparait pas avec les notes à 1 chiffre. A ce moment là, le clavier étant déjà vide, ton programme bloquerait connement à attendre un truc au clavier.
    Donc la solution est de laisser fgets() gérer une saisie de 3 touches possibles en remplaçant "3" par "4" (rappel à toujours savoir: quel que soit le nombre que tu mets, fgets() retire "1" pour y stocker le '\0'). Comme la fonction s'arrête aussi au <return>, elle pourra gérer les notes à 1 chiffre tout comme les notes à 2 chiffres. Toutefois elle partira dans le décors si l'utilisateur tape "toto" (souci de contrôle de la chose saisie).
    Autre solution pour gérer ce second cas: tu montes la zone de saisie à (par exemple) 1024 octets et utilises ensuite sscanf() pour récupérer tout nombre se trouvant dans la zone saisie. Dans ce cas là, comme la fonction sscanf() retourne le nombre d'items récupérés, tu peux même savoir si la saisie est valide ou pas.

    PS: tu auras un soucis analogie à la saisie "O/N". Si tu réponds "O", tu saisiras 'O' + '\n'. Le getc() récupèrera ce 'O' mais laissera le '\n' dans le clavier qui ira pourrir le fgets() de l'itération suivante. Et si tu rajoutes un "getc()" supplémentaire tu auras des soucis si l'utilisateur ne répond rien à la question O/N. Donc là aussi vaut mieux passer par fgets() et une zone tampon intermédiaire.

    PS2: tu devrais réfléchir au résultat de la division de "nbre/i", ces deux éléments étant des entiers...

    PS3: réfléchis aussi à l'action du point-virgule au bout de ton if...

    PS4: pourquoi 1024 ? Il s'agit d'un nombre consensuel indiquant "buffer". Quand tu veux créer un buffer d'une taille suffisante pour gérer des cas standards, tu peux utiliser la valeur que tu veux (100, 200, 500). Mais utiliser un nombre quelconque induira tout autre utilisateur à se poser des questions (pourquoi 100? pourquoi 200? pourquoi 500?). Alors qu'utiliser une puissance de 2 (256, 512, 1024, 2048) est une indication conventionnelle. Les autres lecteurs/codeurs (tu ne sais jamais ce que peut devenir ton programme et qui peut être amené à le faire évoluer) comprendront immédiatement que tu as mis "1024" parce que justement tu n'en avais rien à cirer de la taille exacte. Donc en effet, on met une taille ayant une signification particulière pour indiquer que cette taille n'en a en réalité aucune.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2013
    Messages
    120
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Côte d'Ivoire

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2013
    Messages : 120
    Par défaut
    Vraiment merci pour l’éclairage sur la fonction fgets et fgetc.

    Citation Envoyé par Sve@r
    Autre solution pour gérer ce second cas: tu montes la zone de saisie à (par exemple) 1024 octets et utilises ensuite sscanf() pour récupérer tout nombre se trouvant dans la zone saisie. Dans ce cas là, comme la fonction sscanf() retourne le nombre d'items récupérés, tu peux même savoir si la saisie est valide ou pas.
    j'avoue que je ne sais comment utiliser la fonction sscanf.
    comme ca?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    	char t[1024]= "Salut";
    	sscanf(t, "%s") ;
    	printf("%s\n",t );
    Mais ne marche pas.
    donnez moi un exemple de comment l'utiliser.

  7. #7
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    exactement comme scanf, mais avec un premier argument supplémentaire, la chaine à lire à la place de stdin.

  8. #8
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 830
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 830
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par free_01_binairy Voir le message
    j'avoue que je ne sais comment utiliser la fonction sscanf.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <stdio.h>
    int saisie_int(char *prompt, char *err) {
    	char t[1024 + 1];
    	int val;
    	while (1) {
    		fputs(prompt, stdout);
    		fgets(t, 1024 + 1, stdin);
     
    		// Je vais demander à sscanf de stocker un entier ("%d") dans "val" en prenant les datas dans "t" et non au clavier. Elle renverra le nb d'entiers correctement récupérés. Suffit donc de regarder si ce nb vaut 1 puisque je veux "un" entier...
    		if (sscanf(t, "%d", &val) == 1) break;
     
    		// Ici je sais que je n'ai pas réussi à récupérer mon entier => forcément il y a un blem dans la saisie...
    		fprintf(stderr, "%s - Recommencez\n", err);
    	}
    	return val;
    }
     
    int main() {
    	int x=saisie_int("Entrez un nombre entier :", "Cette saisie n'est pas un nombre entier");
    	printf("%d\n", x);
    }
    PS: pourquoi "+1" ? Pour indiquer aux autres que tu as bien pensé à garder une place pour le '\0' qui est systématiquement inséré à chaque chaine créée/remplie...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  9. #9
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2013
    Messages
    120
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Côte d'Ivoire

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2013
    Messages : 120
    Par défaut
    Je crois ceci est correct après avoir suivi vos conseils.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    int main(int argc, char const *argv[])
    {
     
    	float note=0;
    	float somme=0 ;
    	int i= 1 ;
    	char val[1024];
    	char R[1024];
     
    	do
    	{
    		printf("NOTE:" );
     
    		if( ( fgets(val,1024, stdin) !=NULL ) )
    		{
    			if ( sscanf(val, "%f", &note) ==1 )
    			{
    				if (note>=0 && note<=20)
    				{
                        somme= somme + note;
    					printf("MOYENNE: %.2f \n", somme/i ) ;
    					i++ ;
     
    				}
    			}
    		}
     
    		printf("Voulez vous continuer? O/N:");
    		fgets(R, 1024, stdin);
    		if (strlen(R)>1) // s'il ne saisis pas un seul caractere, on supposera sa reponse à NON
    		{
    			R[0]= 'N' ;
    		}
     
    	}while(R[0] == 'O') ;
     
     
    	return 0;
    }
    J'attends vos suggestions pour les modifications à apporter.
    Cependant j'ai pas fini avec les difficultés qu'on a pour la saisie securisée des données.

  10. #10
    Membre confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2013
    Messages
    120
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Côte d'Ivoire

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2013
    Messages : 120
    Par défaut
    Citation Envoyé par dalfab
    La fonction fgets lit dans ce buffer
    Mais le buuffer c'est quoi au juste? jusque la je croyais que le buffer c'est le tableau qe je crée pour contenir mes données saisies au clavier.
    A vous entendre parler, il ne s'agirait pas de ça. Puisque mon buffer (enfin selon ma defintion à moi) doit contenir 3 caractères. donc si on saisis un caractère de plus cela devait être absent de mon buffer puisqu'il ne peut en contenir autant. ou est donc stoké le caractère '\n' puisqu'il est absent de mon tableau "val" pour revenir encore pourir mon buffer perso?

    Citation Envoyé par Sve@r
    Déjà tu as dû remarquer que ton souci n'apparaissait qu'avec des notes à 2 chiffres (10, 11, 12, ..., 20). En effet, quand tu saisis "12", tu appuies sur '1', puis '2' puis '<entree>'. Ton clavier stockant tout ce que tu as appuyé, il contient alors ces 3 éléments.
    Ensuite tu demandes à fgets() de récupérer le contenu de ton clavier en lui indiquant que la zone réceptrice fait 3 octets. fgets() va donc en récuperer 2 (elle réserve une place pour y stocker le '\0'). Elle stocke donc dans "val" les éléments '1', '2' et '\0'. Le '\n' reste donc dans le clavier.
    Ensuite, au getc() suivant, le clavier n'étant pas vide, la fonction n'a pas besoin de s'arrêter pour te laisser taper un truc et récupère ce '\n' qui va être stocké dans "c". La variable "c" ne contenant pas 'O', la boucle ne recommence pas.
    Quand mon clavier stocke les donées les touches au clavier, ou les stocke-t-il? n'est pas dans mon tableau "val" ? et le reste qu'il ne peut en contenir, ou le rejette-t-il? cette préoccupation rejoint celle que j'ai soumise `@dalfba`

    La fonction scanf a un comportement similaire, me parait-il, celui de ne pas vider le buffer et de laisser des déchets en son sein.
    Alors ma grande question, commnet faire pour vider le buffer ( selon la definition correcte qu'on peut lui donner.) pour qu'une saisie suivante puisse etre correcte.

    VOICI UN TEXTE QUE J'AI LU QUELQUES PART DANS MES RECHERCHES.

    Bufferisation
    La sortie standard est bufferisée: Les fonctions d’Entrées/Sortie de
    <stdio.h> n’envoient pas directement les éléments sur l’écran.
    UNIX incite à l’enchaı̂nement de commandes ou exécutables via des
    pipes. Mais la communication entre les processus a un coût. Surtout
    si l’on perd son temps à communiquer sans arrêt des messages tout
    petits.
    printf écrit sur le buffer de la sortie standard (de taille 8192 Octets
    par défaut) et une fois le buffer plein (ou un retour à ligne, ou un
    flush, ou ...), on sollicite le processus d’affichage à l’écran.
    Note: La sortie d’erreur n’est pas bufferisé et affiche les caractères
    dès l’ajout dans le flux.
    ils ont ajouté cet exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    int main(void)
    char* nom="Jean";
    	printf("Ou est l'erreur de segmentation?");
    	 nom[30]= 123;
    return 0 ;
    programme correct:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int main(void)
    char* nom="Jean";
          //Un saut de ligne force la vidange du buffer
    	printf("Ou est l'erreur de segmentation\n?");
    	 nom[30]= 123;
    return 0 ;
    La chose meme impliquerait meme la fonction printf
    Je veux un peu d'explication sur cette partie: printf("Ou est l'erreur de segmentation\n?");et printf("Ou est l'erreur de segmentation?");

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 19/11/2015, 11h25
  2. Saisie ne prenant pas le premier élément de la boucle
    Par sk8trasher dans le forum Débuter
    Réponses: 12
    Dernier message: 26/06/2012, 21h32
  3. [Doctrine] Pas d'index, pas de timestamp, pas de valeur par defaut
    Par Snooky68 dans le forum ORM
    Réponses: 1
    Dernier message: 30/06/2011, 10h22
  4. Réponses: 1
    Dernier message: 07/04/2006, 13h35
  5. Réponses: 2
    Dernier message: 14/04/2004, 19h37

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo