web-dev-qa-db-fra.com

Utiliser fflush (stdin)

Une recherche rapide de fflush(stdin) sur Google pour effacer le tampon d'entrée révèle donc de nombreux sites Web qui déconseillent de l'utiliser. Et pourtant, c’est exactement ainsi que mon professeur d’enseignement supérieur a appris à la classe à le faire.

Quel est le degré d'utilisation de fflush(stdin)? Devrais-je vraiment m'abstenir de l'utiliser, même si mon professeur l'utilise et que cela semble fonctionner parfaitement?

61
wrongusername

Simple: il s’agit d’un comportement indéfini, car fflush doit être appelé sur un flux de sortie. Ceci est un extrait de la norme C:

int fflush (FILE * ostream);

ostream pointe vers un flux de sortie ou un flux de mise à jour dans lequel le plus opération récente n'a pas été entré, le La fonction fflush provoque toute écriture non écrite données pour ce flux à livrer à l'environnement hôte à écrire au dossier; sinon, le comportement est indéfini.

Donc, ce n'est pas une question de "comment mauvais" c'est. fflush(stdin) est manifestement faux , et vous ne devez pas l’utiliser, jamais .

63
Eli Bendersky

Convertir les commentaires en réponses - et les prolonger car le numéro réapparaît périodiquement.

Standard C et POSIX laissent fflush(stdin) comme comportement indéfini

Les normes POSIX , C et C++ pour fflush() indiquent explicitement que le comportement n'est pas défini, mais aucune n'empêche un système de le définir.

ISO/IEC 9899: 2011 - la norme C11 - dit:

§7.21.5.2 La fonction fflush

¶2 Si stream pointe sur un flux de sortie ou un flux de mise à jour dans lequel l'opération la plus récente n'a pas été entrée, la fonction fflush fait en sorte que toutes les données non écrites de ce flux soient transmises à l'environnement hôte pour être écrites dans le fichier; sinon, le comportement n'est pas défini.

POSIX reporte principalement le standard C mais il marque ce texte comme une extension C.

[CX] Pour un flux ouvert en lecture, si le fichier n’est pas déjà à EOF et que le fichier est capable de le rechercher, le décalage de fichier de la description du fichier ouvert sous-jacent doit être défini sur la position du fichier dans le flux, et Les caractères renvoyés par ungetc() ou ungetwc() dans le flux et qui n'ont pas été lus par la suite à partir du flux doivent être supprimés (sans modifier davantage le décalage de fichier).

Notez que les terminaux ne sont pas capables de chercher; ni les tuyaux ni les sockets.

Microsoft définit le comportement de fflush(stdin)

Microsoft et le runtime Visual Studio définissent le comportement de fflush() sur un flux d'entrée.

Si le flux est ouvert en entrée, fflush efface le contenu du tampon.

M.Mnotes :

Cygwin est un exemple de plate-forme assez commune sur laquelle fflush(stdin) n'efface pas l'entrée.

C’est pourquoi cette version de réponse de mon comment notes indique «Microsoft et le runtime Visual Studio» - si vous utilisez une bibliothèque d’exécution C autre que Microsoft, le comportement que vous observez dépend de cette bibliothèque.

La documentation et la pratique de Linux semblent se contredire

Étonnamment, Linux documente nominalement le comportement de fflush(stdin) également, et le définit même de la même manière (miracle de miracles).

Pour les flux d'entrée, fflush() ignore les données mises en mémoire tampon qui ont été extraites du fichier sous-jacent, mais qui n'ont pas été consommées par l'application.

Je reste un peu perplexe et surpris de constater que la documentation de Linux indique que fflush(stdin) fonctionnera . Malgré cette suggestion, cela ne fonctionne généralement pas sous Linux. Je viens de vérifier la documentation sur Ubuntu 14.04 LTS; il dit ce qui est cité ci-dessus, mais empiriquement, cela ne fonctionne pas - du moins lorsque le flux d'entrée est un périphérique non recherchable tel qu'un terminal.

demo-fflush.c

#include <stdio.h>

int main(void)
{
    int c;
    if ((c = getchar()) != EOF)
    {
        printf("Got %c; enter some new data\n", c);
        fflush(stdin);
    }
    if ((c = getchar()) != EOF)
        printf("Got %c\n", c);

    return 0;
}

Exemple de sortie

$ ./demo-fflush
Alliteration
Got A; enter some new data
Got l
$

Cette sortie a été obtenue à la fois sur Ubuntu 14.04 LTS et Mac OS X 10.11.2. À ma connaissance, cela contredit ce que dit le manuel Linux. Si l'opération fflush(stdin) fonctionnait, je devrais saisir une nouvelle ligne de texte pour obtenir des informations pour la deuxième getchar() à lire.

Compte tenu de ce que dit le standard POSIX, il faudrait peut-être une meilleure démonstration et clarifier la documentation Linux.

demo-fflush2.c

#include <stdio.h>

int main(void)
{
    int c;
    if ((c = getchar()) != EOF)
    {
        printf("Got %c\n", c);
        ungetc('B', stdin);
        ungetc('Z', stdin);
        if ((c = getchar()) == EOF)
        {
            fprintf(stderr, "Huh?!\n");
            return 1;
        }
        printf("Got %c after ungetc()\n", c);
        fflush(stdin);
    }
    if ((c = getchar()) != EOF)
        printf("Got %c\n", c);

    return 0;
}

Exemple de sortie

Notez que /etc/passwd est un fichier à rechercher. Sur Ubuntu, la première ligne ressemble à ceci:

root:x:0:0:root:/root:/bin/bash

Sur Mac OS X, les 4 premières lignes ressemblent à:

##
# User Database
# 
# Note that this file is consulted directly only when the system is running

En d'autres termes, il y a des commentaires en haut du fichier /etc/passwd de Mac OS X. Les lignes sans commentaire sont conformes à la présentation normale, l'entrée root est donc:

root:*:0:0:System Administrator:/var/root:/bin/sh

Ubuntu 14.04 LTS:

$ ./demo-fflush2 < /etc/passwd
Got r
Got Z after ungetc()
Got o
$ ./demo-fflush2
Allotrope
Got A
Got Z after ungetc()
Got B
$

Mac OS X 10.11.2:

$ ./demo-fflush2 < /etc/passwd
Got #
Got Z after ungetc()
Got B
$

Le comportement de Mac OS X ignore (ou du moins semble ignorer) le fflush(stdin) (donc ne suit pas POSIX sur ce problème). Le comportement Linux correspond au comportement POSIX documenté, mais la spécification POSIX est beaucoup plus prudente: elle spécifie un fichier capable de rechercher, mais les terminaux, bien sûr, ne prennent pas en charge la recherche. Il est également beaucoup moins utile que la spécification Microsoft.

Résumé

Microsoft documente le comportement de fflush(stdin). Apparemment, cela fonctionne comme décrit sur la plate-forme Windows, en utilisant le compilateur Windows natif et les bibliothèques de support d'exécution C.

Malgré la documentation contraire, cela ne fonctionne pas sous Linux lorsque l’entrée standard est un terminal, mais il semble suivre la spécification POSIX qui est formulée avec beaucoup plus de soin. Selon le standard C, le comportement de fflush(stdin) n'est pas défini. POSIX ajoute le qualificatif "sauf si le fichier d'entrée est cherchable", ce qu'un terminal n'est pas. Le comportement n'est pas le même que celui de Microsoft.

Par conséquent, le code portable n'utilise pas fflush(stdin). Le code lié à la plate-forme Microsoft peut l’utiliser et cela fonctionnera, mais méfiez-vous des problèmes de portabilité.

Méthode POSIX pour supprimer les entrées non lues du terminal à partir d'un descripteur de fichier

La méthode standard POSIX pour supprimer les informations non lues d'un descripteur de fichier terminal (par opposition à un flux de fichiers tel que stdin) est illustrée à Comment vider les données non lues d'une file d'attente d'entrée tty sur un système Unix . Cependant, cela fonctionne en dessous du niveau standard de la bibliothèque d'E/S.

29
Jonathan Leffler

Selon la norme, fflush ne peut être utilisé qu'avec des tampons de sortie, et il est évident que stdin n'en fait pas partie. Cependant, certains compilateurs fournissent l'utilisation de fflush (stdin) en tant qu'extension. Dans ce cas, vous pouvez l'utiliser, mais la portabilité en sera affectée. Par conséquent, vous ne pourrez plus utiliser un compilateur conforme aux normes sur la Terre et vous obtiendrez les mêmes résultats.

18
tiftik

Citation de POSIX :

Pour un flux ouvert en lecture, si le fichier n'est pas déjà à EOF et que le fichier en est un capable de rechercher, le décalage de fichier de la description du fichier ouvert sous-jacent doit être défini à la position du fichier du flux, et tous les caractères repoussés sur le flux par ungetc () ou ungetwc () qui n'ont pas été lus ultérieurement à partir du flux doivent être dis - cardé (sans modifier davantage le décalage de fichier).

Notez que le terminal n'est pas capable de chercher.

1
Ryan Chen

Lors de la prise d'une chaîne d'entrée avec des espaces, le tampon n'est pas effacé pour l'entrée suivante et considère l'entrée précédente pour la même chose. Pour résoudre ce problème, fflush (stdin) est utilisé pour effacer le flux/la mémoire tampon.

Conformément à la norme C, il s'agit d'un comportement indéfini. Cependant, certains compilateurs tels que Microsoft Visual Studio le permettent.

0
Shubham Kumar