¿Por qué printf no se vacía después de la llamada a menos que haya una nueva línea en la cadena de formato?

Por que printf no vaciar después de la llamada a menos que haya una nueva línea en la cadena de formato? ¿Es este un comportamiento POSIX? ¿Cómo podría haberlo hecho? printf enjuagar inmediatamente cada vez?

preguntado el 11 de noviembre de 09 a las 13:11

¿investigaste si esto pasa con algún archivo o solo con terminales? Eso sonaría como una función de terminal inteligente para no generar una línea incompleta desde un programa en segundo plano, aunque espero que no se aplique a La programa de primer plano. -

Bajo Cygwin bash estoy viendo este mismo mal comportamiento incluso si una nueva línea is en la cadena de formato. Este problema es nuevo en Windows 7; el mismo código fuente funcionó bien en Windows XP. MS cmd.exe se vacía como se esperaba. La solución setvbuf(stdout, (char*)NULL, _IONBF, 0) soluciona el problema, pero seguramente no debería haber sido necesario. Estoy usando MSVC ++ 2008 Express. ~~~ -

Para aclarar el título de la pregunta: printf(..) no enrojece en sí mismo, es el almacenamiento en búfer de stdout que puede vaciar al ver una nueva línea (si está almacenada en búfer de línea). Reaccionaría de la misma manera a putchar('\n');, asi que printf(..) no es especial en este sentido. Esto contrasta con cout << endl;, la documentación de la cual menciona de manera destacada el rubor. La documentación de printf no menciona rubor en absoluto. -

escribir (/ vaciar) es potencialmente una operación costosa, probablemente se almacena en búfer por razones de rendimiento. -

@EvgeniSergeev: ¿Existe un consenso de que la pregunta ha diagnosticado incorrectamente el problema y que el lavado ocurre cuando hay una nueva línea en el salida? (poner uno en la cadena de formato es una forma, pero no la única, de obtener uno en la salida). -

10 Respuestas

La stdout stream está almacenado en búfer de línea de forma predeterminada, por lo que solo mostrará lo que hay en el búfer después de que alcance una nueva línea (o cuando se le indique). Tiene algunas opciones para imprimir inmediatamente:

Imprimir en stderren lugar de usar fprintf (stderr is sin búfer por defecto):

fprintf(stderr, "I will be printed immediately");

Descargue la salida estándar siempre que lo necesite fflush:

printf("Buffered, will be flushed");
fflush(stdout); // Will now print everything in the stdout buffer

Editar: Del comentario de Andy Ross a continuación, también puede deshabilitar el almacenamiento en búfer en stdout usando setbuf:

setbuf(stdout, NULL);

o su versión segura setvbuf como se explica aquí

setvbuf(stdout, NULL, _IONBF, 0); 

Respondido el 10 de junio de 20 a las 11:06

O, para deshabilitar el almacenamiento en búfer por completo: setbuf(stdout, NULL); - Andy Ross

Además, solo quería mencionar que aparentemente en UNIX una nueva línea normalmente solo vaciará el búfer si stdout es una terminal. Si la salida se redirige a un archivo, una nueva línea no se vaciará. - tiempo

Siento que debería agregar: acabo de probar esta teoría, y estoy descubriendo que usando setlinebuf() en una transmisión que no está dirigida a una terminal is enjuague al final de cada línea. - Doddy

"Tal como se abrió inicialmente, el flujo de error estándar no está completamente almacenado en búfer; los flujos de entrada y salida estándar están completamente almacenados en búfer si y solo si se puede determinar que el flujo no se refiere a un dispositivo interactivo"; consulte esta pregunta: stackoverflow.com/questions/5229096/… - Seppo Enarvi

@RuddZwolinski Si esta va a ser una buena respuesta canónica de "¿por qué no está imprimiendo?", Parece importante mencionar la distinción entre terminal / archivo según "¿Printf siempre vacía el búfer al encontrar una nueva línea?" directamente en esta respuesta altamente votada, frente a las personas que necesitan leer los comentarios ... - HostileFork dice que no confíes en SE

No, no es un comportamiento POSIX, es un comportamiento ISO (bueno, is Comportamiento POSIX pero solo en la medida en que se ajusten a ISO).

La salida estándar está almacenada en búfer de línea si se puede detectar para hacer referencia a un dispositivo interactivo; de lo contrario, está completamente almacenada en búfer. Entonces hay situaciones en las que printf no se descarga, incluso si obtiene una nueva línea para enviar, como:

myprog >myfile.txt

Esto tiene sentido para la eficiencia ya que, si está interactuando con un usuario, probablemente este quiera ver cada línea. Si está enviando la salida a un archivo, lo más probable es que no haya un usuario en el otro extremo (aunque no es imposible, podrían estar siguiendo el archivo). Ahora tu podría Argumentan que el usuario quiere ver todos los personajes, pero hay dos problemas con eso.

La primera es que no es muy eficiente. La segunda es que el mandato ANSI C original era codificar principalmente existente comportamiento, en lugar de inventar Un nuevo comportamiento, y esas decisiones de diseño se tomaron mucho antes de que ANSI comenzara el proceso. Incluso ISO hoy en día actúa con mucho cuidado al cambiar las reglas existentes en los estándares.

En cuanto a cómo lidiar con eso, si fflush (stdout) después de cada llamada de salida que desee ver de inmediato, eso resolverá el problema.

Alternativamente, puede utilizar setvbuf antes de operar en stdout, para configurarlo como sin búfer y no tendrá que preocuparse por agregar todos esos fflush líneas a su código:

setvbuf (stdout, NULL, _IONBF, BUFSIZ);

Solo tenga en cuenta que puede afectar bastante el rendimiento si se encuentran las enviando la salida a un archivo. También tenga en cuenta que el soporte para esto está definido por la implementación, no garantizado por el estándar.

Sección ISO C99 7.19.3/3 es el bit relevante:

Cuando una corriente es sin búfer, los personajes deben aparecer desde el origen o en el destino lo antes posible. De lo contrario, los caracteres pueden acumularse y transmitirse hacia o desde el entorno del host como un bloque.

Cuando una corriente es totalmente amortiguado, los caracteres están destinados a ser transmitidos hacia o desde el entorno del host como un bloque cuando se llena un búfer.

Cuando una corriente es línea en búfer, los caracteres están destinados a ser transmitidos hacia o desde el entorno del host como un bloque cuando se encuentra un carácter de nueva línea.

Además, los caracteres están destinados a transmitirse como un bloque al entorno del host cuando se llena un búfer, cuando se solicita la entrada en un flujo sin búfer o cuando se solicita la entrada en un flujo con búfer de línea que requiere la transmisión de caracteres desde el entorno del host. .

El soporte para estas características está definido por la implementación y puede verse afectado a través de la setbuf y setvbuf funciones.

Respondido el 20 de junio de 20 a las 10:06

Me encontré con un escenario en el que incluso hay un '\ n', printf () no se vacía. Se superó agregando un fflush (stdout), como mencionaste aquí. Pero me pregunto la razón por la que '\ n' no pudo vaciar el búfer en printf (). - qiang xu

@QiangXu, la salida estándar tiene búfer de línea solo en el caso en que se pueda determinar definitivamente que se refiere a un dispositivo interactivo. Entonces, por ejemplo, si redirige la salida con myprog >/tmp/tmpfile, que está completamente almacenado en búfer en lugar de en línea. Desde la memoria, la determinación de si su salida estándar es interactiva se deja a la implementación. - paxdiablo

además, en Windows, llamar a setvbuf (...., _IOLBF) no funcionará ya que _IOLBF es lo mismo que _IOFBF allí: msdn.microsoft.com/en-us/library/86cebhfs.aspx - Piotr Lopusiewicz

Probablemente sea así debido a la eficiencia y porque si tiene varios programas escribiendo en un solo TTY, de esta manera no obtendrá caracteres en una línea entrelazados. Entonces, si los programas A y B están generando, generalmente obtendrá:

program A output
program B output
program B output
program A output
program B output

Esto apesta, pero es mejor que

proprogrgraam m AB  ououtputputt
prproogrgram amB A  ououtputtput
program B output

Tenga en cuenta que ni siquiera está garantizado que se descargue en una nueva línea, por lo que debe descargar explícitamente si le importa.

respondido 11 nov., 09:17

Para descargar inmediatamente la llamada fflush(stdout) or fflush(NULL) (NULL significa enjuagar todo).

Respondido el 25 de Septiembre de 15 a las 02:09

Ten en cuenta fflush(NULL); suele ser una muy mala idea. Matará el rendimiento si tiene muchos archivos abiertos, especialmente en un entorno de subprocesos múltiples donde luchará con todo por bloqueos. - R .. GitHub DEJA DE AYUDAR A ICE

Nota: las bibliotecas en tiempo de ejecución de Microsoft no admiten el almacenamiento en búfer de línea, por lo que printf("will print immediately to terminal"):

https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf

Respondido 26 Abr '20, 03:04

Peor que printf ir inmediatamente a la terminal en el caso "normal" es el hecho de que printf y fprintf obtener un búfer más grueso incluso en los casos en que su salida se utiliza de inmediato. A menos que MS haya arreglado cosas, eso hace imposible que un programa capture stderr y stdout de otro e identifique en qué secuencia se enviaron las cosas a cada uno. - Super gato

no, no imprime eso inmediatamente en la terminal a menos que no se haya configurado un almacenamiento en búfer. De forma predeterminada, se utiliza el almacenamiento en búfer completo: phuclv

stdout se almacena en búfer, por lo que solo se generará después de que se imprima una nueva línea.

Para obtener resultados inmediatos, haga lo siguiente:

  1. Imprimir en stderr.
  2. Hacer stdout sin búfer.

respondido 11 nov., 09:16

Or fflush(stdout). - rastajedi

"por lo que solo se imprimirá después de que se imprima una nueva línea". No solo este, sino al menos otros 4 casos. búfer lleno, escribir en stderr (esta respuesta se menciona más adelante), fflush(stdout), fflush(NULL). - chux - Reincorporar a Monica

de forma predeterminada, stdout está almacenado en búfer de línea, stderr no está almacenado en búfer y el archivo está completamente almacenado en búfer.

Respondido 29 Jul 10, 03:07

En su lugar, puede fprintf a stderr, que no tiene búfer. O puede vaciar la salida estándar cuando lo desee. O puede configurar stdout como sin búfer.

respondido 11 nov., 09:16

Utiliza setbuf(stdout, NULL); para deshabilitar el almacenamiento en búfer.

contestado el 31 de mayo de 15 a las 04:05

Generalmente hay 2 niveles de almacenamiento en búfer:

1. Caché del búfer del núcleo (hace que la lectura / escritura sea más rápida)

2. Almacenamiento en búfer en la biblioteca de E / S (reduce el número de llamadas al sistema)

Tomemos un ejemplo de fprintf and write().

Cuando usted llama fprintf(), no se escribe directamente en el archivo. Primero va al búfer stdio en la memoria del programa. Desde allí, se escribe en la memoria caché del búfer del kernel mediante la llamada al sistema de escritura. Entonces, una forma de omitir el búfer de E / S es directamente usando write (). Otras formas son usando setbuff(stream,NULL). Esto establece el modo de almacenamiento en búfer en ningún búfer y los datos se escriben directamente en el búfer del núcleo. Para forzar que los datos se transfieran al búfer del kernel, podemos usar "\ n", que en el caso del modo de búfer predeterminado de 'búfer de línea', vaciará el búfer de E / S. O podemos usar fflush(FILE *stream).

Ahora estamos en el búfer del kernel. Kernel (/ OS) quiere minimizar el tiempo de acceso al disco y, por lo tanto, lee / escribe solo bloques de disco. Entonces cuando un read() se emite, que es una llamada al sistema y se puede invocar directamente o mediante fscanf(), el kernel lee el bloque de disco del disco y lo almacena en un búfer. Después, los datos se copian desde aquí al espacio de usuario.

Del mismo modo que fprintf() los datos recibidos del búfer de E / S se escriben en el disco mediante el kernel. Esto hace que read () write () sea más rápido.

Ahora para forzar al kernel a iniciar un write(), después de lo cual la transferencia de datos es controlada por controladores de hardware, también hay algunas formas. Nosotros podemos usar O_SYNC o indicadores similares durante las llamadas de escritura. O podríamos usar otras funciones como fsync(),fdatasync(),sync() para hacer que el kernel inicie las escrituras tan pronto como los datos estén disponibles en el búfer del kernel.

Respondido 21 ago 19, 17:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.