Usando fflush (stdin)


Así que una búsqueda rápida en Google para fflush(stdin) para borrar el búfer de entrada revela numerosos sitios web advirtiendo contra su uso. Y sin embargo, así es exactamente como mi profesor de CS enseñó a la clase a hacerlo.

¿Qué tan malo es usar fflush(stdin)? ¿Debería realmente abstenerme de usarlo, a pesar de que mi profesor lo está usando y parece funcionar sin problemas?

Author: Lundin, 2010-06-05

4 answers

Simple: este es un comportamiento indefinido, ya que fflush está destinado a ser llamado en un flujo de salida. Este es un extracto del estándar C:

Int fflush (FILE * ostream);

O'Stream apunta a un flujo de salida o un flujo de actualización en el que la mayoría operación reciente no fue de entrada, el fflush función causa cualquier no escrito datos para que se entreguen los flujos al entorno host a escribir al archivo; de lo contrario, el comportamiento ser indefinido.

Así que no es una cuestión de "qué tan malo" es esto. fflush(stdin)es claramente incorrecto, y usted no debe usarlo, nunca.

 59
Author: Eli Bendersky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-05 04:53:39

Convertir comentarios en una respuesta - y extenderlos ya que el problema reaparece periódicamente.

Estándar C y POSIX dejan fflush(stdin) como comportamiento indefinido

Los estándares POSIX, C y C++ para fflush() explícitamente establecen que el comportamiento es indefinido, pero ninguno de ellos impide que un sistema lo defina.

ISO/IEC 9899: 2011 - el estándar C11-dice:

§7.21.5.2 La función fflush

¶2 Si stream apunta a un flujo de salida o un flujo de actualización en el que no se ingresó la operación más reciente, la función fflush hace que cualquier dato no escrito para ese flujo que se entregue al entorno host se escriba en el archivo; de lo contrario, el comportamiento no está definido.

POSIX se remite principalmente al estándar C, pero marca este texto como una extensión C.

[CX] Para una secuencia abierta para lectura, si el archivo no está ya en EOF, y el archivo es capaz de buscando, el desplazamiento del archivo de la descripción del archivo abierto subyacente se establecerá en la posición del archivo de la secuencia, y cualquier carácter devuelto a la secuencia por ungetc() o ungetwc() que no se hayan leído posteriormente de la secuencia se descartará (sin cambiar más el desplazamiento del archivo).

Tenga en cuenta que los terminales no son capaces de buscar; tampoco lo son las tuberías o tomas de corriente.

Microsoft define el comportamiento de fflush(stdin)

Microsoft y el Visual Studio runtime define el comportamiento de fflush() en un flujo de entrada.

Si el flujo está abierto para la entrada, fflush borra el contenido del búfer.

M. M notas:

Cygwin es un ejemplo de una plataforma bastante común en la que fflush(stdin) no borra la entrada.

Esta es la razón por la que esta versión de respuesta de mi comentario notas 'Microsoft y el tiempo de ejecución de Visual Studio' - si utiliza un no es una biblioteca de tiempo de ejecución de Microsoft C, el comportamiento que ve depende de esa biblioteca.

La documentación y la práctica de Linux parecen contradecirse entre sí

Sorprendentemente, Linux nominalmente documenta el comportamiento de fflush(stdin) también, e incluso lo define de la misma manera (milagro de milagros).

Para flujos de entrada, fflush() descarta cualquier dato almacenado en búfer que se haya obtenido del archivo subyacente, pero que no haya sido consumido por la aplicación.

I permanezca un poco perplejo y sorprendido por la documentación de Linux que dice que fflush(stdin) funcionará. A pesar de esta sugerencia, normalmente no funciona en Linux. Acabo de revisar la documentación de Ubuntu 14.04 LTS; dice lo citado anteriormente, pero empíricamente, no funciona - al menos cuando el flujo de entrada es un dispositivo no buscable, como 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;
}

Ejemplo de salida

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

Esta salida se obtuvo tanto en Ubuntu 14.04 LTS como en Mac OS X 10.11.2. A mi entender, contradice lo que dice el manual de Linux. Si la operación fflush(stdin) funcionó, tendría que escribir una nueva línea de texto para obtener información para el segundo getchar() a leer.

Dado lo que dice el estándar POSIX, tal vez se necesite una mejor demostración, y la documentación de Linux debería aclararse.

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;
}

Ejemplo de salida

Tenga en cuenta que /etc/passwd es un archivo buscable. En Ubuntu, la primera línea se ve como:

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

En Mac OS X, las primeras 4 líneas se ven como:

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

En otras palabras, hay comentarios en la parte superior del archivo Mac OS X /etc/passwd. Las líneas sin comentarios se ajustan al diseño normal, por lo que la entrada root es:

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
$

El comportamiento de Mac OS X ignora (o al menos parece ignorar) el fflush(stdin) (por lo tanto, no sigue a POSIX en este tema). El comportamiento de Linux corresponde al comportamiento POSIX documentado, pero la especificación POSIX es mucho más cuidadosa en lo que dice: especifica un archivo capaz de buscar, pero los terminales, por supuesto, no admiten la búsqueda. También es mucho menos útil que la especificación de Microsoft.

Resumen

Microsoft documenta el comportamiento de fflush(stdin). Al parecer, funciona como está documentado en la plataforma Windows, utilizando el compilador nativo de Windows y las bibliotecas de soporte de tiempo de ejecución de C.

A pesar de la documentación a la al contrario, no funciona en Linux cuando la entrada estándar es un terminal, pero parece seguir la especificación POSIX que está mucho más cuidadosamente redactada. Según el estándar C, el comportamiento de fflush(stdin) es indefinido. POSIX agrega el calificador 'a menos que el archivo de entrada sea buscable', que un terminal no lo es. El comportamiento no es el mismo que el de Microsoft.

En consecuencia, el código portable no utiliza fflush(stdin). El código que está vinculado a la plataforma de Microsoft puede usarlo y funcionará, pero cuidado con los problemas de portabilidad.

POSIX forma de descartar la entrada de terminal no leída de un descriptor de archivo

La forma estándar POSIX de descartar la información no leída de un descriptor de fichero de terminal (a diferencia de un flujo de ficheros como stdin) se ilustra en Cómo puedo vaciar los datos no leídos de una cola de entrada tty en un sistema Unix. Sin embargo, eso está operando por debajo del nivel estándar de la biblioteca de E/S.

 22
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-21 00:09:16

De acuerdo con el estándar, fflush solo se puede usar con búferes de salida, y obviamente stdin no es uno. Sin embargo, algunos compiladores proporcionan el uso de fflush(stdin) como una extensión. En ese caso, puede usarlo, pero afectará la portabilidad, por lo que ya no podrá usar ningún compilador compatible con los estándares en la tierra y esperar los mismos resultados.

 18
Author: tiftik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-05 05:10:38

Cita de POSIX:

Para una secuencia abierta para lectura, si el archivo no está ya en EOF, y el archivo es uno capaz de buscar, el desplazamiento de archivo de la descripción del archivo abierto subyacente se establecerá a la posición del archivo de la secuencia, y cualquier carácter empujado de nuevo en la secuencia por ungetc () o ungetwc () que no hayan sido leídos posteriormente de la secuencia serán dis- cardado (sin cambiar más el archivo compensar).

Tenga en cuenta que terminal no es capaz de buscar.

 0
Author: Ryan Chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-18 03:08:36