Utilisation de fflush (stdin)

Ainsi, une recherche rapide sur Google pour fflush(stdin) pour effacer le tampon d'entrée révèle de nombreux sites web mettant en garde contre son utilisation. Et pourtant, c'est exactement comme ça que mon professeur de CS a appris à la classe à le faire.

Quelle est la mauvaise utilisation de fflush(stdin)? Devrais-je vraiment m'abstenir de l'utiliser, même si mon professeur l'utilise et qu'il semble fonctionner parfaitement?

58
demandé sur Lundin 2010-06-05 08:50:23

4 réponses

Simple: c'est un comportement indéfini, puisque fflush est destiné à être appelé sur un flux de sortie. Ceci est un extrait de la norme C:

Int fflush (fichier * 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ée, le la fonction fflush provoque tout non écrit données pour ce flux à livrer à l'environnement hôte à écrire pour le fichier; sinon, le comportement être indéterminé.

Donc ce n'est pas une question de "à quel point" c'est. fflush(stdin) est tout simplement mauvais, et vous ne faut pas l'utiliser, jamais.

59
répondu Eli Bendersky 2010-06-05 04:53:39

Convertir les commentaires en réponse - et les étendre puisque le problème réapparaît périodiquement.

Standard C et POSIX laissent {[8] } comme comportement indéfini

Les normesPOSIX , C et c++ pour fflush() indiquent explicitement que le comportement est indéfini, mais aucun d'entre eux 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 vers 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 provoque l'écriture dans le fichier de toutes les données non écrites pour ce flux à livrer à l'environnement hôte; sinon, le comportement est indéfini.

POSIX se reporte principalement à la norme C mais il marque ce texte comme une extension C.

[CX] pour un flux ouvert à la lecture, si le fichier n'est pas déjà à EOF, et que le fichier est capable de en cherchant, le décalage de fichier de la description de fichier ouverte sous-jacente doit être réglé sur la position de fichier du flux, et tous les caractères repoussés sur le flux par ungetc() ou ungetwc() qui n'ont pas été lus par la suite du flux doivent être supprimés (sans modifier davantage le décalage de fichier).

Notez que les bornes ne sont pas capables de chercher; ni les tuyaux ni les prises.

Microsoft définit le comportement de fflush(stdin)

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

Si le flux est ouvert pour l'entrée, fflush efface le contenu du tampon.

M. M notes:

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 commentaire notes 'Microsoft et le runtime Visual Studio' - si vous utilisez un bibliothèque d'exécution Non Microsoft C, le comportement que vous voyez dépend de cette bibliothèque.

La documentation et la pratique Linux semblent se contredire

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

Pour les flux d'entrée, fflush() supprime toutes 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 restez un peu perplexe et surpris par la documentation Linux disant que fflush(stdin) fonctionnera. Malgré cette suggestion, il ne fonctionne généralement pas sur 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-au moins lorsque le flux d'entrée est un périphérique non consultable 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 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 taper une nouvelle ligne de texte pour obtenir des informations pour le second getchar() à lire.

Compte tenu de ce que dit le standard POSIX, une meilleure démonstration est peut-être nécessaire, et la documentation Linux devrait être clarifiée.

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 consultable. Sur Ubuntu, la première ligne semble comme:

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 Mac OS X /etc/passwd. Les lignes non-commentaires sont conformes à la mise en page normale, de sorte que l'entrée root est:

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) (ne suivant donc pas POSIX sur ce problème). Le comportement Linux correspond au comportement POSIX documenté, mais la spécification POSIX est beaucoup plus prudente dans ce qu'elle dit-elle spécifie un fichier capable de chercher, mais les terminaux, bien sûr, ne supportent pas la recherche. Il est également beaucoup moins utile que la spécification Microsoft.

Résumé

Microsoft documente le comportement de fflush(stdin). Apparemment, il fonctionne comme documenté sur la plate-forme Windows, en utilisant le compilateur Windows natif et les bibliothèques de support C runtime.

Malgré la documentation au 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 beaucoup plus soigneusement formulée. Selon la norme, le comportement de fflush(stdin) n'est pas défini. POSIX ajoute le qualificatif 'sauf si le fichier d'entrée est consultable', ce qui n'est pas le cas d'un terminal. 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 de Microsoft peut l'utiliser et cela fonctionnera, mais méfiez-vous des problèmes de portabilité.

POSIX permet d'ignorer l'entrée de terminal non lue d'un descripteur de fichier

La manière standard POSIX d'ignorer les informations non lues d'un descripteur de fichier terminal (par opposition à un flux de fichiers comme 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.

22
répondu Jonathan Leffler 2016-03-21 00:09:16

Selon la norme, fflush ne peut être utilisé qu'avec des tampons de sortie, et évidemment stdin n'en est pas un. Cependant, certains compilateurs fournissent l'utilisation de fflush(stdin) comme extension. Dans ce cas, vous pouvez l'utiliser, mais cela affectera la portabilité, de sorte que vous ne pourrez plus utiliser de compilateur conforme aux normes sur terre et attendre les mêmes résultats.

18
répondu tiftik 2010-06-05 05:10:38

Citation de POSIX:

Pour un flux ouvert à la lecture, si le fichier n'est pas déjà à EOF, et le fichier est un capable de rechercher, le décalage de fichier de la description de fichier ouvert sous-jacente 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 par la suite à partir du flux doit être dis- breveté (sans changer le fichier offset).

Notez que terminal n'est pas capable de chercher.

0
répondu Ryan Chen 2018-05-18 03:08:36