Usando fflush (entrada estándar)

11 minutos de lectura

avatar de usuario
Nombre de usuario incorrecto

Así que una búsqueda rápida en Google para 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 informática le enseñó a la clase a hacerlo.

Que malo es usar fflush(stdin)? ¿Realmente debería abstenerme de usarlo, aunque mi profesor lo esté usando y parezca funcionar perfectamente?

  • codificaciónhorror.com/blog/2007/03/…

    – Blue Raja – Danny Pflughoeft

    5 de junio de 2010 a las 4:51

  • Ambas cosas ventanas y linux definir el comportamiento de fflush() en un flujo de entrada, e incluso definirlo de la misma manera (milagro de milagros). Los estándares POSIX, C y C++ para fflush() no definen el comportamiento, pero ninguno de ellos impide que un sistema lo defina. Si está codificando para lograr la máxima portabilidad, evite fflush(stdin); si está programando para plataformas que definen el comportamiento, utilícelo, pero tenga en cuenta que no es portátil.

    –Jonathan Leffler

    22 de diciembre de 2013 a las 22:13

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

    –MM

    28 de septiembre de 2014 a las 2:41

  • También depende exactamente de lo que esperas. fflush(stdin) que hacer.

    –Keith Thompson

    13 de diciembre de 2015 a las 3:54

  • @JonathanLeffler El documento de Windows diceIf the stream was opened in read mode, or if the stream has no buffer, the call to fflush has no effect, and any buffer is retainedy el documento de Linux dice For input streams, fflush() discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application. Esa no es exactamente la misma manera, Windows retiene el búfer y Linux descarta el búfer.

    – ssbssa

    17 de agosto de 2020 a las 10:26


avatar de usuario
Eli Bendersky

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(ARCHIVO *ostream);

ostream 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.

Así que no es una cuestión de “qué tan malo” es esto. fflush(stdin) simplemente no es portátil, por lo que no debe usarlo si desea que su código sea portátil entre compiladores.

  • @BlueRaja: hay defensa por un error de novato aquí, pero no hay defensa para un maestro ¡propagando conocimientos erróneos! Cualquier referencia de fflush deja en claro que está destinado a flujos de salida justo en el primer párrafo, ¡no tiene que memorizar el estándar C para eso!

    – Eli Bendersky

    5 de junio de 2010 a las 5:04

  • @Eli: Nadie puede saberlo todo. El procesador nunca sabrá su error hasta que alguien le diga… Usé fflush(stdin) durante años hasta que descubrí que es UB (por accidente)

    – Blue Raja – Danny Pflughoeft

    5 de junio de 2010 a las 5:06


  • Err, ¿no debería uno normalmente consultar la documentación de una función antes de usarla? ¿Especialmente un profesor?

    – Alex Budovski

    5 de junio de 2010 a las 5:28

  • Otro punto de defensa sería la siguiente parte de la página de manual (varias versiones de glibc en Linux): “Para flujos de entrada, fflush() descarta cualquier dato almacenado en el búfer que se haya obtenido del archivo subyacente, pero que la aplicación no haya consumido. El estado abierto de la transmisión no se ve afectado.” Aunque es UB, algunas implementaciones parecen ofrecer garantías sin mencionar su estado con respecto al estándar.

    –Daniel Fischer

    29/10/2012 a las 20:23

  • Hay otro aspecto que rara vez veo mencionado: fflush(stdin) es mucho peor que solo el comportamiento definido por la implementación. Incluso si funcionara como la mayoría de la gente pretende, sería terrible. Imagínese si stdin no es alguien que escribe input tontamente, sino que proviene de otro programa o redirección de shell: leería el comienzo del archivo y luego simplemente borraría el resto. Es realmente tonto pensar que stdin siempre es algo tan lento como un operador humano.

    –Rafael Lerm

    17 de enero de 2015 a las 23:14

  • Aunque supongo que te olvidaste de esta respuesta stackoverflow.com/a/58884121/918959

    – Antti Haapala — Слава Україні

    28 sep 2020 a las 21:20

  • @AnttiHaapala Gracias por el consejo, pero no, no lo olvidé; ambas respuestas están vinculadas en mis notas sobre este tema. Hay aún más buenas respuestas canónicas en stackoverflow.com/questions/34219549.

    – Steve cumbre

    29 de septiembre de 2020 a las 10:23

  • Lo que quiero decir es que están en la misma pregunta: D

    – Antti Haapala — Слава Україні

    29 de septiembre de 2020 a las 11:58

  • @AnttiHaapala Sí, lo entiendo. Cuando publiqué la segunda, SO me preguntó: “Ya tienes una respuesta a esta pregunta, ¿estás seguro de que quieres responderla de nuevo?”, y respondí: “Sí”. Para estas preguntas eternas, siempre estoy tratando de encontrar formas diferentes/mejores/alternativas de responderlas. (Otro ejemplo es stackoverflow.com/questions/949433).

    – Steve cumbre

    29 de septiembre de 2020 a las 12:24

Ninguna de las respuestas existentes señala un aspecto clave del problema.

si te encuentras falto para “borrar el búfer de entrada”, probablemente esté escribiendo un programa interactivo de línea de comandos, y sería más exacto decir que lo que quiere es descartar caracteres del actual línea de entrada que aún no ha leído.

esto no es lo que fflush(stdin) hace. Las bibliotecas de C que admiten el uso fflush en un flujo de entrada, documéntelo como haciendo ninguna cosa, o como descartar datos almacenados en búfer que se han leído del archivo subyacente pero que no se han pasado a la aplicación. Eso puede ser fácilmente más o menos entrada que el resto de la línea actual. Probablemente funciona por accidente en muchos casos, porque el controlador de terminal (en su modo predeterminado) proporciona entrada a un programa interactivo de línea de comandos, una línea a la vez. Sin embargo, en el momento en que intente alimentar su programa desde un archivo real en el disco (quizás para pruebas automatizadas), el núcleo y la biblioteca C cambiarán a datos de almacenamiento en búfer en “bloques” grandes (a menudo de 4 a 8 kB) sin relación con los límites de línea, y se preguntará por qué su programa está procesando la primera línea del archivo y luego omitiendo varias docenas de líneas y recogiendo en el medio de alguna línea aparentemente aleatoria debajo. O, si decide probar su programa en un nivel muy largo línea escrita a mano, entonces el controlador de la terminal no podrá darle al programa la línea completa a la vez y fflush(stdin) no se lo saltará todo.

Entonces, ¿qué deberías hacer en su lugar? El enfoque que prefiero es, si está procesando la entrada una línea a la vez, entonces leer una línea completa de una vez. La biblioteca C tiene funciones específicas para esto: fgets (en C90, totalmente portátil, pero todavía te hace procesar líneas muy largas en trozos) y getline (Específico de POSIX, pero administrará un malloced buffer para que pueda procesar líneas largas de una sola vez, sin importar cuánto tiempo duren). Por lo general, hay una traducción directa del código que procesa “la línea actual” directamente desde la entrada estándar al código que procesa una cadena que contiene “la línea actual”.

  • pero como Eli Bendersky El fflush mencionado está destinado a ser utilizado en el flujo de salida. así que si usamos en stdin causas UB .

    – Abhishek Mané

    15 de junio de 2021 a las 5:37

  • Las implementaciones de @AbhishekMane C pueden definir el comportamiento que el estándar no define, y los programas C son libres de confiar en tales definiciones; simplemente los hace menos que perfectamente portátiles. El punto de esta respuesta es que incluso si está de acuerdo con confiar en las extensiones de implementaciónutilizando fflush(stdin) sigue siendo un error, porque las extensiones de implementación comunes para fflush(stdin) no hagas lo que realmente quieres.

    – zwol

    15 de junio de 2021 a las 13:08

  • Ahora lo tengo. Gracias

    – Abhishek Mané

    15/06/2021 a las 14:40

Creo que nunca debes llamar fflush(stdin), y por la sencilla razón de que, en primer lugar, nunca debería encontrar necesario intentar vaciar la entrada. Siendo realistas, solo hay una razón por la que podría pensar que tuvo que vaciar la entrada, y es: para superar una entrada incorrecta que scanf está atascado.

Por ejemplo, puede tener un programa que se encuentra en un bucle leyendo números enteros usando scanf("%d", &n). Muy pronto descubrirá que la primera vez que el usuario escribe un carácter que no es un dígito como 'x'el programa entra en un bucle infinito.

Ante esta situación, creo que básicamente tienes tres opciones:

  1. Vacíe la entrada de alguna manera (si no usando fflush(stdin)luego llamando getchar en un bucle para leer caracteres hasta \ncomo suele recomendarse).
  2. Dígale al usuario que no escriba caracteres que no sean dígitos cuando se esperan dígitos.
  3. Usa algo que no sea scanf para leer la entrada.

Ahora, si eres un principiante, scanf parece como la forma más fácil de leer la entrada, por lo que la opción n. ° 3 parece aterradora y difícil. Pero el n.° 2 parece una verdadera evasión, porque todos saben que los programas de computadora que no son fáciles de usar son un problema, por lo que sería bueno hacerlo mejor. Así que demasiados programadores principiantes quedan arrinconados, sintiendo que no tienen más remedio que hacer el #1. Más o menos tienen que hacer la entrada usando scanflo que significa que se atascará en una entrada incorrecta, lo que significa que tienen que encontrar una manera de eliminar la entrada incorrecta, lo que significa que están muy tentados a usar fflush(stdin).

Me gustaría alentar a todos los programadores principiantes de C a que hagan un conjunto diferente de compensaciones:

  1. Durante las primeras etapas de su carrera como programador en C, antes de que se sienta cómodo usando algo que no sea scanfsólo no te preocupes por la mala entrada. En realidad. Continúe y use la salida n.° 2 anterior. Piénselo de esta manera: es un principiante, hay muchas cosas que aún no sabe cómo hacer, y una de las cosas que aún no sabe cómo hacer es: lidiar con gracia con entradas inesperadas.

  2. Tan pronto como pueda, aprenda cómo ingresar usando funciones que no sean scanf. En ese punto, puede comenzar a lidiar con gracia con la entrada incorrecta, y tendrá muchas más técnicas disponibles para usted, mucho mejores, que no requerirán tratar de “deshacerse de la entrada incorrecta” en absoluto.

O, en otras palabras, principiantes que todavía están atascados usando scanf deben sentirse libres de usar la evasión n.° 2, y cuando estén listos, deben pasar de ahí a la técnica n.° 3, y nadie debería usar la técnica n. fflush(stdin).

  • pero como Eli Bendersky El fflush mencionado está destinado a ser utilizado en el flujo de salida. así que si usamos en stdin causas UB .

    – Abhishek Mané

    15 de junio de 2021 a las 5:37

  • Las implementaciones de @AbhishekMane C pueden definir el comportamiento que el estándar no define, y los programas C son libres de confiar en tales definiciones; simplemente los hace menos que perfectamente portátiles. El punto de esta respuesta es que incluso si está de acuerdo con confiar en las extensiones de implementaciónutilizando fflush(stdin) sigue siendo un error, porque las extensiones de implementación comunes para fflush(stdin) no hagas lo que realmente quieres.

    – zwol

    15 de junio de 2021 a las 13:08

  • Ahora lo tengo. Gracias

    – Abhishek Mané

    15/06/2021 a las 14:40

avatar de usuario
Comunidad

Cita de POSIX:

Para un flujo abierto para lectura, si el archivo aún no está en EOF, y el archivo es capaz de buscar, el desplazamiento del archivo de la descripción del archivo abierto subyacente se establecerá en la posición del archivo del flujo, y cualquier carácter retrocederá en el flujo mediante ungetc() o ungetwc() que no se hayan leído posteriormente del flujo se descartarán (sin cambiar más el desplazamiento del archivo).

Tenga en cuenta que el terminal no es capaz de buscar.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad