c - libreria - Usando fflush(stdin)
limpiar buffer en c++ (4)
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?
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.
De acuerdo con el estándar, fflush
solo puede usarse con buffers de salida, y obviamente stdin
no es uno. Sin embargo, Windows 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.
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 fflush() , 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ónfflush
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 porungetc()
oungetwc()
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 enchufes.
Microsoft define el comportamiento de fflush(stdin)
Windows 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.
notes M.M :
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 comment 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 <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 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 <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 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 compilador 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.