La respuesta a esto es que fflush(stream)
sólo se define formalmente para flujos de salida, por lo que fflush(stdout)
está bien, pero fflush(stdin)
no lo es.
El propósito de fflush(stream)
es hacer que el sistema operativo vacíe cualquier búfer al archivo subyacente. Para un ejemplo de uso legítimo, los estudiantes a menudo tienen problemas como «¡mi prompt no aparece!» si hacen algo como:
printf("Enter a number: ");
Sin embargo, encuentran que esto funciona bien:
printf("Enter a number:\n");
Por supuesto, no quieren una nueva línea después de su prompt, así que tienen un poco de problema.
La razón de esto es que la salida a stdout
es almacenada en el buffer por el sistema operativo y el comportamiento por defecto es (a menudo) sólo para escribir realmente la salida a la terminal cuando se encuentra una nueva línea. Añadir un fflush(stdout)
después del printf()
resuelve el problema:
printf("Enter a number: ");fflush(stdout);
Ahora, trabajando por analogía, la gente suele pensar que fflush(stdin)
debería descartar cualquier entrada no utilizada, pero si lo piensas un poco eso no tiene mucho sentido. ¿Qué significa «tirar» un búfer de entrada? ¿A dónde se «vacía»? Si se vacía un búfer de salida, la salida se envía al archivo subyacente o a la terminal, donde acabaría de todos modos, pero ¿dónde acabaría la entrada «de todos modos»? No hay forma de saberlo. ¿Cuál debería ser el comportamiento si los datos del flujo de entrada provienen de un archivo, una tubería o un socket? No está nada claro para los flujos de entrada cuál debe ser el comportamiento de fflush()
, pero está muy claro para los flujos de salida en todos los casos. Por lo tanto, fflush()
sólo se define para los flujos de salida.
La razón por la que el uso erróneo de fflush(stdin)
se convirtió en algo común es que, hace muchos años, unos pocos sistemas operativos implementaron un esquema en el que funcionaba como mucha gente esperaba, descartando la entrada no utilizada. Microsoft DOS es un buen ejemplo. Sorprendentemente, las versiones modernas de Linux también implementan fflush()
para los flujos de entrada.
Lo que hay que hacer con la entrada de terminal «extra» no deseada es simplemente leerla y no hacer nada con ella. Esto es casi tan fácil como llamar a fflush(stdin)
, funciona en todas partes y no depende de un comportamiento formalmente indefinido.
El estándar de C dice:
Si stream apunta a un stream de salida o a un stream de actualización en el que la operación más reciente no fue de entrada, la función fflush hace que cualquier dato no escrito para ese stream sea entregado al entorno del host para ser escrito en el archivo; de lo contrario, el comportamiento es indefinido.
POSIX dice (también difiere explícitamente del estándar C):
Si stream apunta a un stream de salida o a un stream de actualización en el que la operación más reciente no fue de entrada, fflush() hará que cualquier dato no escrito para ese stream se escriba en el fichero, ….
Pero la página de manual de Linux dice:
Para flujos de salida, fflush() fuerza una escritura de todos los datos almacenados en el buffer del espacio de usuario para el flujo de salida o de actualización dado a través de la función de escritura subyacente del flujo. Para flujos de entrada, fflush() descarta cualquier dato almacenado en el buffer que haya sido obtenido del archivo subyacente, pero que no haya sido consumido por la aplicación. El estado abierto del flujo no se ve afectado.