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 :

fflush(stdin) et viderBuffer


Sujet :

C

  1. #1
    Membre à l'essai
    Homme Profil pro
    etudiants en programation
    Inscrit en
    Janvier 2017
    Messages
    22
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : Cameroun

    Informations professionnelles :
    Activité : etudiants en programation

    Informations forums :
    Inscription : Janvier 2017
    Messages : 22
    Points : 20
    Points
    20
    Par défaut fflush(stdin) et viderBuffer
    bonjour/ bonsoir s'il vous plait je voudrai savoir la différence en ces deux fonctions:

    et


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void viderBuffer()
    {
        int c = 0;
        while (c != '\n' && c != EOF)
        {
            c = getchar();
        }
    }
    Bizarrement les deux servent à vider le buffer je voulais savoir la plus efficace et pourquoi?
    Merci.

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Bonjour,

    Le comportement de fflush(stdin) est indéfini. Tu ne peux donc PAS affirmer que cela va bien vider ton buffer d'entrée.

    « flush » sert à vider un buffer de sortie, mais doit s'entendre dans le sens de « tirer la chasse ». C'est-à-dire qu'il ne détruit pas le contenu du buffer mais, au contraire, provoque son envoi immédiat vers son destinataire (généralement l'écran dans le cas de stdout), là ou justement, on met par défaut les données en attente le temps d'en avoir suffisamment ou de laisser une condition particulière se produire (retour à la ligne, timeout, fin du processus, etc.).

    Dans le cas de stdin, le destinataire, c'est justement le processus qui est à l'origine de l'appel : « fflush(stdin) » signifierait donc « envoie-moi immédiatement tout ce qui m'est destiné », et ce contenu a toujours été à disposition du processus. Le comportement le plus cohérent consiste donc, dans ce cas, à ne rien faire du tout, mais comme la norme C refuse (à juste titre) de définir tout comportement réglementaire à tenir, certains systèmes profitent de ce libre champ pour effacer le buffer quand même et présenter cela comme une fonctionnalité disponible, ce qui fait prendre de mauvaises habitudes au programmeur.

    C'est intéressant parce que cela montre que tu ne peux pas non plus détruire le contenu d'un buffer de sortie de cette façon : fflush(stdout) enverra bien le contenu vers son destinataire, mais tu ne pourra pas l'annuler.

    L'ennui, à ce stade, est que provoquer l'abandon des données déjà reçues ne relève pas du langage C standard : c'est de la programmation système (dépendante de ton OS), voire matérielle. Certes, la bibliothèque C standard tient des buffers pour ses propres opérations, mais rien ne te permet de savoir a priori qu'une frappe clavier, par exemple, se trouve déjà dans le buffer de stdin si le processus a du retard.


    Voir aussi cette discussion :
    https://www.developpez.net/forums/d1...nction-fflush/

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 54
    Points : 32
    Points
    32
    Par défaut
    Salut,

    personnellement, j'utilise une macro..
    C'est beaucoup plus facile a utiliser ou tu veux dans ton code sans l'alourdir..

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define CLEAR_STDIN { int c; while((c = getchar()) != '\n' && c != EOF); }

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    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 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Quel gain par rapport à une fonction?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void clear_stdin() { for(int c = 0; c!='\n' && c!=EOF; c = getchar()); }
    Une macro est un excellent moyen de faire provoquer des problèmes.
    En l'occurence, ta macro est un identifieur, pas une macro appelée, et elle n'aime pas avoir un point virgule qui la suit.
    Du coup, tu as un nom sans parenthèses, ni point virgules, qui n'est pas une valeur, et fait quelque chose.

    Ca casse l'esthétique du code, et donc sa lisibilité.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Dunkhan Voir le message
    personnellement, j'utilise une macro..
    Bonjour

    Personnellement je contrôle toujours mon stdin donc si je provoque une saisie c'est que je sais qu'il est vide donc je n'ai pas besoin de le vider (c'est d'ailleurs parce qu'on n'a théoriquement jamais besoin de le vider que fflush(stdin) reste indéterminé). Si stdin a des trucs dedans c'est déjà d'une part parce que quelqu'un (ou quelque chose) les y a mis et donc d'autre part c'est pour que ces trucs soient traités.
    Et donc vider stdin c'est aussi ne plus pouvoir utiliser ton programme dans cette configuration: commande_externe | ton_programme. Hé oui, stdin ne signifie pas forcément "clavier" (ou du moins pas tout le temps)...

    Citation Envoyé par Dunkhan Voir le message
    C'est beaucoup plus facile a utiliser ou tu veux dans ton code sans l'alourdir...
    Idem²
    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 à l'essai
    Homme Profil pro
    etudiants en programation
    Inscrit en
    Janvier 2017
    Messages
    22
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : Cameroun

    Informations professionnelles :
    Activité : etudiants en programation

    Informations forums :
    Inscription : Janvier 2017
    Messages : 22
    Points : 20
    Points
    20
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Bonjour,

    Le comportement de fflush(stdin) est indéfini. Tu ne peux donc PAS affirmer que cela va bien vider ton buffer d'entrée.

    « flush » sert à vider un buffer de sortie, mais doit s'entendre dans le sens de « tirer la chasse ». C'est-à-dire qu'il ne détruit pas le contenu du buffer mais, au contraire, provoque son envoi immédiat vers son destinataire (généralement l'écran dans le cas de stdout), là ou justement, on met par défaut les données en attente le temps d'en avoir suffisamment ou de laisser une condition particulière se produire (retour à la ligne, timeout, fin du processus, etc.).
    soit plus explicite quand tu dis <<indéfini>> au départ merci

  7. #7
    Membre à l'essai
    Homme Profil pro
    etudiants en programation
    Inscrit en
    Janvier 2017
    Messages
    22
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : Cameroun

    Informations professionnelles :
    Activité : etudiants en programation

    Informations forums :
    Inscription : Janvier 2017
    Messages : 22
    Points : 20
    Points
    20
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Bonjour

    Personnellement je contrôle toujours mon stdin donc si je provoque une saisie c'est que je sais qu'il est vide donc je n'ai pas besoin de le vider (c'est d'ailleurs parce qu'on n'a théoriquement jamais besoin de le vider que fflush(stdin) reste indéterminé). Si stdin a des trucs dedans c'est déjà d'une part parce que quelqu'un (ou quelque chose) les y a mis et donc d'autre part c'est pour que ces trucs soient traités.
    Et donc vider stdin c'est aussi ne plus pouvoir utiliser ton programme dans cette configuration: commande_externe | ton_programme. Hé oui, stdin ne signifie pas forcément "clavier" (ou du moins pas tout le temps)...


    Idem²
    hummm excuse moi mais je ne te comprends pas ce que je voudrais savoir pourquoi dois-je utiliser cette fonction au lieu de fflush(stdin) ou porquoi dois-je utiliser fflush(stdin) au lieu de cette fonction
    merci

  8. #8
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par a.brice Voir le message
    soit plus explicite quand tu dis <<indéfini>> au départ merci
    Prends-le exactement dans sa forme littérale la plus pure: un comportement indéfini (perso je dis plutôt "indéterminé") est un comportement qu'on ne connait pas donc qu'on ne peut pas définir.

    Citation Envoyé par a.brice Voir le message
    ce que je voudrais savoir pourquoi dois-je utiliser cette fonction au lieu de fflush(stdin)
    Parce que l'instruction fflush(stdin) est une instruction illicite et donc le C ne te garantit absolument pas le comportement de cette instruction. Ca peut vider stdin, ne pas le vider mais ne rien faire d'autre, inverser ton écran, rebooter ton PC, reformater ton disque dur, etc.
    Ca peut marcher, ne pas marcher, marcher les jours pairs et ne pas marcher les jours impairs, etc etc etc. C'est exactement ce que ça signifie chaque fois que tu verras en C le terme "comportement indéterminé".

    Il faut savoir que le C est totalement permissif. Pour aller le plus vite possible il ne vérifie absolument pas si ce que tu écris est autorisé. Si ça l'est tout va bien, si ça ne l'est pas tu tombes alors dans le "comportement indéterminé". C'est donc à toi de savoir en permanence ce que tu écris et pourquoi tu l'écris.

    Alors que le code de cette fonction, lui, est parfaitement licite et fait exactement ce qui est écrit.

    Citation Envoyé par a.brice Voir le message
    ou pourquoi dois-je utiliser fflush(stdin) au lieu de cette fonction
    Ce que je dis, moi, c'est que tu ne dois utiliser ni l'un ni l'autre mais au contraire toujours maîtriser ton stdin pour ne jamais avoir besoin de le vider ni même imaginer avoir besoin de le vider. Si tu maitrises en permanence les données qui y entrent et que tu traites ces données dans leur intégralité ben en effet il n'y a aucune raison d'avoir besoin de le vider.
    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 à l'essai
    Homme Profil pro
    etudiants en programation
    Inscrit en
    Janvier 2017
    Messages
    22
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : Cameroun

    Informations professionnelles :
    Activité : etudiants en programation

    Informations forums :
    Inscription : Janvier 2017
    Messages : 22
    Points : 20
    Points
    20
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Prends-le exactement dans sa forme littérale la plus pure: un comportement indéfini (perso je dis plutôt "indéterminé") est un comportement qu'on ne connait pas donc qu'on ne peut pas définir.


    Parce que l'instruction fflush(stdin) est une instruction illicite et donc le C ne te garantit absolument pas le comportement de cette instruction. Ca peut vider stdin, ne pas le vider mais ne rien faire d'autre, inverser ton écran, rebooter ton PC, reformater ton disque dur, etc.
    Ca peut marcher, ne pas marcher, marcher les jours pairs et ne pas marcher les jours impairs, etc etc etc. C'est exactement ce que ça signifie chaque fois que tu verras en C le terme "comportement indéterminé".

    Il faut savoir que le C est totalement permissif. Pour aller le plus vite possible il ne vérifie absolument pas si ce que tu écris est autorisé. Si ça l'est tout va bien, si ça ne l'est pas tu tombes alors dans le "comportement indéterminé". C'est donc à toi de savoir en permanence ce que tu écris et pourquoi tu l'écris.

    Alors que le code de cette fonction, lui, est parfaitement licite et fait exactement ce qui est écrit.


    Ce que je dis, moi, c'est que tu ne dois utiliser ni l'un ni l'autre mais au contraire toujours maîtriser ton stdin pour ne jamais avoir besoin de le vider ni même imaginer avoir besoin de le vider. Si tu maitrises en permanence les données qui y entrent et que tu traites ces données dans leur intégralité ben en effet il n'y a aucune raison d'avoir besoin de le vider.
    merci là je suis satisfait je demandai ça en faite parceque j'ai un code ou je manipule les caractères et quand j'utilise le scanf() disons par exemple

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char tab[30]="";
    printf("entrer votre nom");
    scanf("%s",tab);
    printf("votre nom est enregistré merci");
    à l’exécution ça donne :

    entrer votre nom<<entrer>>
    blabla <<entrer>>
    <<entrer>>
    b(ou n'importe quel caractère)<<entrer>>
    votre nom est enregistré merci
    même quand j'utilise le fflush(stdin) j'ai le même résultat alors que en utilisant cette fonction voici le résultat

    entrer votre nom<<entrer>>
    blabla<<entrer>>
    votre nom est enregistré merci

    C'est pour cela que je voulais vider le buffer.

  10. #10
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par a.brice Voir le message
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    char tab[30]="";
    printf("entrer votre nom");
    scanf("%s",tab);
    Inutile de remplir "tab" puisque tu le re-remplis de nouveau 2 lignes plus bas.

    Citation Envoyé par a.brice Voir le message
    même quand j'utilise le fflush(stdin) j'ai le même résultat alors que en utilisant cette fonction
    La preuve que ni fflush(stdin) ni cette fonction ne sont une solution à ton problème.

    Citation Envoyé par a.brice Voir le message
    et quand j'utilise le scanf()
    Ben tu l'utilises pas.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <stdio.h>
    #include <string.h>
     
    int main(void) {
    	char nom[20 + 1];
    	char *c;
     
    	fputs("Entrez votre nom :", stdout);
    	fgets(nom, 20 + 1, stdin);
    	if (c=strchr(nom, '\n')) *c='\0';		// Suppression du '\n'
    	printf("Votre nom [%s] est bien enregistré\n", nom);
    	return 0;
    }

    scanf() est une fonction qui attend des entrées "formatées". Or ce que tape un utilisateur est tout sauf formaté (ne serait-ce que parce qu'il tape <return> pour valider). Et c'est encore pire si on veut faire saisir des nombres. Là, le <return> reste carrément dans stdin et va pourrir la saisie suivante.

    Donc moi ce que je fais saisir c'est du texte en vrac via fgets(). Ainsi je suis certain que stdin est toujours clean.

    Et ensuite, seulement ensuite, je traite le texte saisi...

    Code c : 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
    #include <stdio.h>
    #include <string.h>
     
    int saisie()
    {
    	char zone[100];
    	char *c;
    	int nb;
    	while (1) {
    		fputs("Entrez un nombre :", stdout);
    		fgets(zone, 100, stdin);
    		if (sscanf(zone, "%d", &nb) == 1)		// sscanf renvoie le nombre d'info correctement récupérées - Moi j'en attends une seule.
    			break;
    		if (c=strchr(zone, '\n')) *c='\0';
    		printf("Saisie incorrecte [%s] n'est pas un nombre correct\n", zone);
    	}
    	return nb;
    }
     
    int main(void) {
    	int nb=saisie();
    	printf("Votre nombre [%d] est bien enregistré\n", nb);
    	return 0;
    }

    Je sais bien que scanf() est le plus facile à enseigner pour débuter mais la facilité n'est pas forcément ce qu'il y a toujours de mieux. "Plus rapide, plus facile, plus séduisant est le côté obscur"...
    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]

  11. #11
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Bonjour,

    Citation Envoyé par a.brice Voir le message
    soit plus explicite quand tu dis <<indéfini>> au départ merci
    Cela signifie que la norme C elle-même indique que le comportement de fflush() sur un flux d'entrée est officiellement indéfini, c'est-à-dire qu'elle refuse de statuer sur ce point :

    Citation Envoyé par n1256
    2 If stream points to an output stream or an update stream in which the most recent
    operation was not input
    , the fflush function causes any unwritten data for that stream
    to be delivered to the host environment to be written to the file; otherwise, the behavior is
    undefined
    .

    Donc, déjà, sur ce point en particulier : on sait à présent que fflush(stdin) n'a jamais existé en soi, et que c'est directement la norme qui te dit si tu le fais quand même, personne ne pourra te garantir le résultat.

    Outre ce cas de figure, il est très fréquent de rencontrer des « comportements indéfinis » (undefined behavour) dans la norme C. Cela ne veut pas dire que la norme est incomplète, ni que ses rédacteurs se sont mis dans une impasse. C'est en fait une attitude à avoir et que l'on retrouve fréquemment, entre autre, en électronique numérique : chaque contrainte a des effets de bord sur l'implémentation et il est parfois compliqué de toutes les honorer à la fois. Donc il vaut mieux laisser « en l'air » tout ce qui sort de l'objectif ciblé plutôt que d'imposer arbitrairement des valeurs ou comportements par défaut qui seront compliqués à mettre en œuvre et qui ne serviront à rien.

    Par exemple : le plus petit type natif de donnée en langage C est le char, dont le sizeof() vaut toujours 1 par définition. La plupart du temps, 1 char = 1 octet, ce qui est généralement le format choisi par le reste de l'industrie, même en dehors du langage C. Pourtant, le langage C le définit, lui, comme un entier binaire suffisamment long pour pouvoir représenter les 91 caractères réglementaires réclamés par la même norme, soit en théorie, un minimum de 7 bits. Ceci permet de faire passer des char à travers une transmission série à parité (Ex : 7 bits, parité paire, un bit de stop, comme sur le Minitel) mais aussi d'implémenter cela facilement sur les architectures qui ne se basent pas sur l'octet : certains micro-contrôleurs, par exemple, utilisent des mots de 12 bits pour les instructions programme et 8 bits pour la RAM intégrée. Dans cet exemple, il est tout-à-fait légal de coder un char sur 12 bits de façon à ce que 1 mot binaire corresponde à 1 caractère. Si la norme avait spécifié « 1 char = 1 octet » parce que c'est ce qui aurait semblé le plus naturel sur le coup, il aurait été beaucoup plus compliqué d'implémenter un compilateur.


    Par ailleurs, en ce qui concerne fflush(stdin) en particulier, il faut bien se rendre compte que tout part d'une mauvaise perception (fréquente) de ce à quoi sert fflush() en soi : si cela vide bien un buffer, c'est APRÈS en avoir traité le contenu et s'être assuré de son intégrité. C'est tout le contraire d'un abandon.


    Enfin, pour être exact, on s'aperçoit que le fflush() des man pages de Linux présente un comportement similaire MAIS :

    Citation Envoyé par man fflush
    CONFORMING TO
    C89, C99, POSIX.1-2001, POSIX.1-2008.

    POSIX.1-2001 did not specify the behavior for flushing of input streams, but the behavior is specified in POSIX.1-2008.

    Ça veut dire que ce n'est pas dans C89, ce n'est pas dans C99, ce n'est MÊME PAS dans POSIX-2001, mais que POSIX-2008 a fini par statuer tellement les gens faisaient l'erreur souvent. Et quand on cherche ladite déclaration, on tombe sur ça : http://pubs.opengroup.org/onlinepubs/9699919799/

    Citation Envoyé par POSIX.1-2008
    The Open Group Base Specifications Issue 7
    IEEE Std 1003.1-2008, 2016 Edition
    Copyright © 2001-2016 The IEEE and The Open Group

    The functionality described on this reference page is aligned with the ISO C standard. Any conflict between the requirements described here and the ISO C standard is unintentional. This volume of POSIX.1-2008 defers to the ISO C standard.

    C'est en tête de page et en surbrillance : POSIX-2008, au moins sur ce point, s'aligne sur le C ISO. En 2008, c'était C99, vu au dessus. Aujourd'hui, c'est C11. Et quand on cherche le dernier draft de C11, on lit la même chose :

    Citation Envoyé par n1570
    2 If stream points to an output stream or an update stream in which the most recent
    operation was not input, the fflush function causes any unwritten data for that stream
    to be delivered to the host environment to be written to the file; otherwise, the behavior is
    undefined.

    Donc rien de nouveau sous le soleil dans la dernière version de la norme C. Comme ce comportement est « indéfini », POSIX-2008 propose quand même ceci :

    For a stream open for reading with an underlying file description, if the file is not already at EOF, and the file is one capable of seeking, the file offset of the underlying open file description shall be set to the file position of the stream, and any characters pushed back onto the stream by ungetc() or ungetwc() that have not subsequently been read from the stream shall be discarded (without further changing the file offset).

    Ça veut dire que — si c'est possible — un fflush(stdin) doit adopter le même comportement qu'un fflush(stdout), c'est-à-dire vider le buffer en retournant les caractères vers leur expéditeur plutôt qu'en les envoyant vers leur destinataires, mais en aucun cas les détruire. Cela signifie aussi que vu de ton programme, cela n'aurait aucun effet visible : les caractères seraient d'abord renvoyés au périphérique en amont, puis rappelés à la ligne d'après, ce qui remplirait à nouveau le buffer. Et tu récupérerais alors les mêmes caractères, alors même que tu as voulu t'en débarrasser.

    Si l'on veut utiliser cette possibilité, il faut aussi garantir que le programme est bien écrit en contexte POSIX-2008, avec les bonnes features macros en tête de programme, et avec une ligne de commentaire au dessus de l'appel pour justifier son utilisation.



    En bref : aucune raison d'utiliser fflush(stdin), ni sur la forme, ni sur le fond.

  12. #12
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 54
    Points : 32
    Points
    32
    Par défaut
    Citation Envoyé par Sve@r Voir le message

    Ben tu l'utilises pas.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <stdio.h>
    #include <string.h>
     
    int main(void) {
    	char nom[20 + 1];
    	char *c;
     
    	fputs("Entrez votre nom :", stdout);
    	fgets(nom, 20 + 1, stdin);
    	if (c=strchr(nom, '\n')) *c='\0';		// Suppression du '\n'
    	printf("Votre nom [%s] est bien enregistré\n", nom);
    	return 0;
    }

    Code c : 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
    #include <stdio.h>
    #include <string.h>
     
    int saisie()
    {
    	char zone[100];
    	char *c;
    	int nb;
    	while (1) {
    		fputs("Entrez un nombre :", stdout);
    		fgets(zone, 100, stdin);
    		if (sscanf(zone, "%d", &nb) == 1)		// sscanf renvoie le nombre d'info correctement récupérées - Moi j'en attends une seule.
    			break;
    		if (c=strchr(zone, '\n')) *c='\0';
    		printf("Saisie incorrecte [%s] n'est pas un nombre correct\n", zone);
    	}
    	return nb;
    }
     
    int main(void) {
    	int nb=saisie();
    	printf("Votre nombre [%d] est bien enregistré\n", nb);
    	return 0;
    }
    Bonjour,

    Dans ces deux fonctions, je ne comprend pq on déréférence le edit: je crois que j'ai compris(??).. on ne veut pas modifier la valeur de c proprement dis, qui ici ne sert que de variable temporaire pour stocker et comparer, mais modifier ce qui est contenu a l'adresse ou l'on a trouver ce que l'on cherche et ou c pointe du coup, ici "\n" (??)



    Citation Envoyé par ternel Voir le message
    Quel gain par rapport à une fonction?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void clear_stdin() { for(int c = 0; c!='\n' && c!=EOF; c = getchar()); }
    Une macro est un excellent moyen de faire provoquer des problèmes.
    En l'occurence, ta macro est un identifieur, pas une macro appelée, et elle n'aime pas avoir un point virgule qui la suit.
    Du coup, tu as un nom sans parenthèses, ni point virgules, qui n'est pas une valeur, et fait quelque chose.

    Ca casse l'esthétique du code, et donc sa lisibilité.
    Quel est la différence entre une macro appelée et un identifieur??

  13. #13
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Dunkhan Voir le message
    Dans ces deux fonctions, je ne comprend pq on déréférence le edit: je crois que j'ai compris(??).. on ne veut pas modifier la valeur de c proprement dis, qui ici ne sert que de variable temporaire pour stocker et comparer, mais modifier ce qui est contenu a l'adresse ou l'on a trouver ce que l'on cherche et ou c pointe du coup, ici "\n" (??)
    Exactement. J'ai une chaine nommée "zone" (que je ne veux bien évidemment pas perdre) mais je veux tronquer cette chaine au '\n' qui s'y trouve (correspondant au <return> tapé par l'utilisateur et donc récupéré par le fgets()).
    La meilleure façon de tronquer une chaine à un endroit est de mettre un '\0' à cet endroit (parce que toute fonction traitant la chaine s'arrête au premier '\0' rencontré). Me suffit donc de trouver l'endroit où il y a ce '\n'. Suffit donc d'une boucle.
    Mais n'ayant pas envie de réinventer la roue, j'utilise la fonction strchr() qui a pour but de chercher un caractère dans une chaine (c'est donc elle qui fait la boucle en question) et de renvoyer l'adresse de la position contenant ce caratère.
    Et l'adresse d'un caractère se stocke dans un "char étoile". C'est bien un déréférencement mais je déréférence juste la position [x] de la chaine "zone" contenant le '\n'.

    Citation Envoyé par Dunkhan Voir le message
    Quel est la différence entre une macro appelée et un identifieur??
    Le principal problème d'une macro est que son appel produit en fait une transformation (l'appel est remplacé par le code de la macro directement recopié dans le source). Outre le fait que cela peut alourdir le source à compiler, cela génère 3 dangers potetiels

    Le premier: #define CARRE(x) x * x. Que se passe-t-il si on écrit int i=5; j=CARRE(i+1) ??? Cela se traduira par j=i+1*i+1 soit 11 au lieu de 36.
    La solution: encadrer les paramètres de la macro par des parenthèses. Si on écrit #define CARRE(x) (x) * (x) alors l'instrution int i=5; j=CARRE(i+1) donnera j=(i+1)*(i+1) ce qui fait alors bien 36 comme attendu. Ce souci n'apparait pas avec une fonction où le paramètre qu'on lui envoie est d'abord calculé avant d'être envoyé.

    Le second: #define SOMME(x, y) (x) + (y). Ici on a bien protégé les paramètres mais que se passe-t-il si on écrit int i=2*SOMME(2, 3) ??? Cela se traduira par i=2*(2) + (3) soit 7 au lieu de 10.
    La solution: encadrer la totalité de la macro par des parenthèses. Si on écrit #define SOMME(x, y) ((x) + (y)) alors l'instrution int i=2*SOMME(2, 3) donnera i=2*((2)+(3)) ce qui fait alors bien 10 comme attendu. Ce souci n'apparait pas avec une fonction où on attend de recevoir son retour avant d'y appliquer un calcul éventuel.
    Et c'est aussi pour une raison d'esthétique que généralement on met (enfin que moi je mets) aussi des parenthèses même aux macros ne contenant que des constantes
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #define SOMME(x, y)     ((x) + (y))
    #define SIZE            (100)   // Parenthèses inutiles mais code tellement plus élégant...

    Le troisième: #define CARRE(x) ((x) * (x)). Ici tout a été écrit comme il faut. Mais que se passe-t-il pour "i" si on écrit int i=5; j=CARRE(i++) ???
    C'est le plus gros souci qu'on a avec les macros et il n'y a aucune solution pour l'éviter. La seule chose qu'on ait à notre disposition, c'est que généralement(*) les macros sont écrites en majuscules et donc on peut les reconnaitre et ainsi éviter de les appeler en leur passant un élément qui contient un opérateur de ce style.
    Et bien évidemment ce souci n'existe pas avec une fonction car le paramètre qu'on lui envoie n'est évalué qu'une seule fois.

    (*) Généralement parce que pas tout le temps. Par exemple les fonctions isalpha(), isdigit(), isspace(), etc... malgré leur orthographes sont des macros...

    Inversement le souci qu'on a avec une fonction c'est le temps nécessaire à son appel et la récupération de son retour. C'est suite à ça qu'on a vu apparaitre les fonctions "inline" en C++ (et aussi maintenant en C) => pouvoir utiliser les avantages des fonctions concernant l'évaluation de leurs paramètres et de leurs retours combinés à celui des macros concernant la vitesse d'exécution d'un code directement recopié plutôt qu'appelé...
    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]

  14. #14
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 470
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 470
    Points : 6 107
    Points
    6 107
    Par défaut
    A propos du problème de CARRE(i++), avec GCC, on peut tricher et écrire :
    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
    #include <stdio.h>
     
    #ifndef __GNUC__
    #error Ce code utilise des extensions du langage C et ne peut compiler que sous GCC.
    #endif
     
    // Extensions du langage C utilisées :
    // https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs
    // https://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof
    // Remarque : Avec l'option -Wpedantic, utiliser CARRE affichera un avertissement.
    #define CARRE(x) \
           ({typeof(x) _x = (x); _x*_x;})
     
    int main()
    {
    	int test = 3;
    	printf("%d\n", CARRE(test++)); // affiche 9
    	printf("%d\n", CARRE(++test)); // affiche 25
    	return 0;
    }
    Mais ce n'est pas du C standard et il reste un problème : les collisions de macro.
    On peut redéfinir une macro sans que cela n'entraîne une erreur de compilation. Au mieux, cela affichera un avertissement.
    Par contre, si on redéfinit une fonction, cela entraînera une erreur de compilation.

    Citation Envoyé par Sve@r Voir le message
    Par exemple les fonctions isalpha(), isdigit(), isspace(), etc... malgré leur orthographes sont des macros...
    Ça dépend de l'implémentation.
    Chez moi, avec GCC 6.3.0 via MinGW, isalpha() est déclarée comme une fonction.

  15. #15
    Membre à l'essai
    Homme Profil pro
    etudiants en programation
    Inscrit en
    Janvier 2017
    Messages
    22
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : Cameroun

    Informations professionnelles :
    Activité : etudiants en programation

    Informations forums :
    Inscription : Janvier 2017
    Messages : 22
    Points : 20
    Points
    20
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Inutile de remplir "tab" puisque tu le re-remplis de nouveau 2 lignes plus bas.


    La preuve que ni fflush(stdin) ni cette fonction ne sont une solution à ton problème.


    Ben tu l'utilises pas.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include <stdio.h>
    #include <string.h>
     
    int main(void) {
    	char nom[20 + 1];
    	char *c;
     
    	fputs("Entrez votre nom :", stdout);
    	fgets(nom, 20 + 1, stdin);
    	if (c=strchr(nom, '\n')) *c='\0';		// Suppression du '\n'
    	printf("Votre nom [%s] est bien enregistré\n", nom);
    	return 0;
    }

    scanf() est une fonction qui attend des entrées "formatées". Or ce que tape un utilisateur est tout sauf formaté (ne serait-ce que parce qu'il tape <return> pour valider). Et c'est encore pire si on veut faire saisir des nombres. Là, le <return> reste carrément dans stdin et va pourrir la saisie suivante.

    Donc moi ce que je fais saisir c'est du texte en vrac via fgets(). Ainsi je suis certain que stdin est toujours clean.

    Et ensuite, seulement ensuite, je traite le texte saisi...

    Code c : 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
    #include <stdio.h>
    #include <string.h>
     
    int saisie()
    {
    	char zone[100];
    	char *c;
    	int nb;
    	while (1) {
    		fputs("Entrez un nombre :", stdout);
    		fgets(zone, 100, stdin);
    		if (sscanf(zone, "%d", &nb) == 1)		// sscanf renvoie le nombre d'info correctement récupérées - Moi j'en attends une seule.
    			break;
    		if (c=strchr(zone, '\n')) *c='\0';
    		printf("Saisie incorrecte [%s] n'est pas un nombre correct\n", zone);
    	}
    	return nb;
    }
     
    int main(void) {
    	int nb=saisie();
    	printf("Votre nombre [%d] est bien enregistré\n", nb);
    	return 0;
    }

    Je sais bien que scanf() est le plus facile à enseigner pour débuter mais la facilité n'est pas forcément ce qu'il y a toujours de mieux. "Plus rapide, plus facile, plus séduisant est le côté obscur"...

    j'ai une question quand tu dis le <<ré-remplir à la ligne 2 >>là j'ai un et non un scanf()
    ma deuxième question est que quand tu utilise fgets() et du donne le nombre maximal de caractères à récupérer je pense c'est egalement possible avec un scanf()
    quel est l'impact precis de mettre [20+1] svp
    merci

  16. #16
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par a.brice Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    char tab[30]="";
    printf("entrer votre nom");
    scanf("%s",tab);
    printf("votre nom est enregistré merci");
    j'ai une question quand tu dis le <<ré-remplir à la ligne 2 >>là j'ai un et non un scanf()
    J'ai pas dit "à la ligne 2" mais "2 lignes en dessous" (sous-entendu "en dessous de la ligne qui initialise "tab"). Donc en fait à la ligne3 où le scanf() remplis "tab". Donc tu remplis deux fois "tab" sans l'avoir traité entre temps. Le premier remplissage est alors inutile

    Citation Envoyé par a.brice Voir le message
    ma deuxième question est que quand tu utilise fgets() et du donne le nombre maximal de caractères à récupérer je pense c'est egalement possible avec un scanf()
    Oui c'est possible => scanf("%20s", tab) mais déjà ce n'est pas le rôle prévu à l'origine pour scanf() que de faire faire de la saisie à l'utilisateur. D'autre part, (selon mon avis) plus une fonction de saisie est simple moins on a de risques de la faire foirer et de laisser des trucs dans stdin

    Citation Envoyé par a.brice Voir le message
    quel est l'impact precis de mettre [20+1] svp
    Tu montres aux autres lecteurs que tu as bien pensé à l'espace pour le '\0'. C'est dommage concernant fgets() tu es obligé de le répéter parce que cette fonction y pense aussi (elle s'arrête un caractère avant le nombre demandé) donc ça fait un peu con mais c'est un faible prix à payer. Par ailleurs en réalité on écrit rarement "20 + 1". On écrit plutôt
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #define SZ_NOM         (20)
    char nom[SZ_NOM + 1];
    fgets(nom, SZ_NOM + 1, stdin);
    Donc on a bien conscience que le nom aura vraiment 20 caractères possibles et non 19 ; et ça permet aussi de pouvoir faire facilement évoluer la taille (une seule modif à faire)...
    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]

  17. #17
    Membre à l'essai
    Homme Profil pro
    etudiants en programation
    Inscrit en
    Janvier 2017
    Messages
    22
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : Cameroun

    Informations professionnelles :
    Activité : etudiants en programation

    Informations forums :
    Inscription : Janvier 2017
    Messages : 22
    Points : 20
    Points
    20
    Par défaut
    merci bro

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 25/02/2009, 18h23
  2. [Débutant] Différence entre fonction et procédure
    Par secondechance dans le forum Langage
    Réponses: 9
    Dernier message: 21/08/2008, 21h11
  3. Réponses: 0
    Dernier message: 14/03/2008, 00h16
  4. API Windows différence entre fonctions simple EX et A
    Par Astraya dans le forum Windows
    Réponses: 3
    Dernier message: 11/02/2008, 09h39
  5. différence entre fonction() et fonction(void)
    Par ram-0000 dans le forum C++
    Réponses: 8
    Dernier message: 07/11/2007, 17h31

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