Usando fflush (stdin)

De modo que una búsqueda rápida en Google de fflush(stdin) para borrar el búfer de entrada revela numerosos sitios web que advierten 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, aunque mi profesor lo esté usando y parece funcionar sin problemas?

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

int fflush (FILE * ostream);

ostream apunta a una secuencia de salida o una secuencia de actualización en la que no se ingresó la operación más reciente, la función fflush hace que los datos no escritos para esa transmisión se entreguen al entorno de host para escribirse en el archivo; de lo contrario, el comportamiento no está definido.

Entonces no es una cuestión de “qué tan malo” es esto. fflush(stdin) es claramente incorrecto , y no debes usarlo nunca .

Convirtiendo comentarios en una respuesta y ampliándolos, ya que el problema reaparece periódicamente.

Standard C y POSIX dejan fflush(stdin) como comportamiento indefinido

Los estándares POSIX , C y C ++ para fflush() indican explícitamente que el comportamiento no está definido, pero ninguno 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 la ruta apunta a una secuencia de salida o una secuencia de actualización en la que no se fflush la operación más reciente, la función fflush hace que todos los datos no escritos para la transmisión de esa secuencia al entorno de host se escriban en el archivo; de lo contrario, el comportamiento no está definido.

POSIX generalmente difiere al estándar C, pero marca este texto como una extensión C.

[CX] Para una secuencia abierta para lectura, si el archivo aún no está en EOF y el archivo es capaz de buscar, el desplazamiento de archivo de la descripción de archivo abierto subyacente se establecerá en la posición de archivo de la secuencia, y cualquier los caracteres ungetc() presionar en la secuencia por ungetc() o ungetwc() que no se hayan leído posteriormente de la secuencia se descartarán (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 los sockets.

Microsoft define el comportamiento de fflush(stdin)

Microsoft y el tiempo de ejecución de Visual Studio definen la definición del comportamiento de fflush() en una secuencia de entrada.

Si la transmisión está abierta para la entrada, fflush borra el contenido del buffer.

Notas de MM :

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 cual esta versión de respuesta de mi comentario señala “Microsoft y el tiempo de ejecución de Visual Studio”: si usa una biblioteca de ejecución de C no de Microsoft, el comportamiento que ve depende de esa biblioteca.

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

Sorprendentemente, Linux documenta nominalmente 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 información almacenada en el búfer que se haya extraído del archivo subyacente, pero no ha sido consumida por la aplicación.

Me quedo un poco desconcertado y sorprendido con la documentación de Linux que dice que fflush(stdin) funcionará. A pesar de esa sugerencia, por lo general no funciona en Linux. Acabo de consultar la documentación en Ubuntu 14.04 LTS; dice lo que se cita arriba, pero empíricamente, no funciona, al menos cuando el flujo de entrada es un dispositivo no buscable, como un terminal.

demo-fflush.c

 #include  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 fflush(stdin) funcionó, tendría que escribir una nueva línea de texto para obtener información para el segundo getchar() para leer.

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

demo-fflush2.c

 #include  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 así:

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

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

 ## # 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 comentario 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 son compatibles con la búsqueda. También es mucho menos útil que la especificación de Microsoft.

Resumen

Microsoft documenta el comportamiento de fflush(stdin) . Aparentemente, funciona según lo documentado en la plataforma de Windows, usando el comstackdor nativo de Windows y las bibliotecas de soporte de C runtime.

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

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

Modo POSIX para descartar la entrada del terminal no leído desde un descriptor de archivo

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

De acuerdo con el estándar, fflush solo puede usarse con buffers de salida, y obviamente stdin no es uno. Sin embargo, algunos comstackdores 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 comstackdor compatible con los estándares en la tierra y esperar los mismos resultados.

Cita de POSIX :

Para una secuencia abierta para lectura, si el archivo aún no está en EOF, y el archivo es capaz de buscar, el desplazamiento de archivo de la descripción de archivo abierto subyacente se establecerá en la posición de archivo de la secuencia, y cualquier carácter retrocedido en 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 el terminal no es capaz de buscar.