Warum ist printf
nach dem Aufruf nicht spülen, es sei denn, ein Zeilenumbruch ist in der Formatzeichenfolge enthalten? Ist dies ein POSIX-Verhalten? Wie könnte ich printf
jedes Mal sofort spülen?
Antworten
Zu viele Anzeigen?En stdout
stream ist standardmäßig zeilengepuffert, zeigt also nur den Inhalt des Puffers an, nachdem er einen Zeilenumbruch erreicht hat (oder wenn er dazu aufgefordert wird). Sie haben ein paar Optionen, um sofort zu drucken:
-
Drucken auf
stderr
stattdessen mitfprintf
(stderr
es standardmäßig ungepuffert ):fprintf(stderr, "I will be printed immediately");
-
Spülen
stdout
wann immer Sie es brauchen, indem Siefflush
:printf("Buffered, will be flushed"); fflush(stdout); // Will now print everything in the stdout buffer
-
Deaktivieren Sie die Pufferung auf stdout durch die Verwendung von
setbuf
:setbuf(stdout, NULL);
-
Oder verwenden Sie die flexiblere
setvbuf
:setvbuf(stdout, NULL, _IONBF, 0);
Nein, es ist kein POSIX-Verhalten, es ist ein ISO-Verhalten (nun, es es POSIX-Verhalten, aber nur insoweit, als sie ISO-konform sind).
Die Standardausgabe ist zeilengepuffert, wenn erkannt werden kann, dass sie sich auf ein interaktives Gerät bezieht, ansonsten ist sie vollständig gepuffert. Es gibt also Situationen, in denen printf
wird nicht gespült, selbst wenn es einen Zeilenumbruch zum Senden bekommt, wie z.B.:
myprog >myfile.txt
Dies ist aus Gründen der Effizienz sinnvoll, denn wenn Sie mit einem Benutzer interagieren, möchte dieser wahrscheinlich jede Zeile sehen. Wenn Sie die Ausgabe in eine Datei senden, ist es sehr wahrscheinlich, dass am anderen Ende kein Benutzer sitzt (obwohl es nicht unmöglich ist, er könnte die Datei verfolgen). Jetzt können Sie podría argumentieren, dass der Benutzer jedes Zeichen sehen möchte, aber es gibt zwei Probleme damit.
Der erste ist, dass sie nicht sehr effizient ist. Der zweite ist, dass der ursprüngliche Auftrag von ANSI C darin bestand, in erster Linie die bestehende Verhalten, anstatt es zu erfinden neu und diese Designentscheidungen wurden getroffen, lange bevor ANSI mit dem Prozess begann. Selbst die ISO geht heutzutage sehr vorsichtig vor, wenn sie bestehende Regeln in den Normen ändert.
Wie kann man damit umgehen, wenn man fflush (stdout)
nach jedem Ausgabeaufruf, den Sie sofort sehen wollen, wird das Problem gelöst.
Alternativ können Sie auch setvbuf
vor dem Einsatz am stdout
auf ungepuffert einstellen, und Sie müssen sich nicht mehr um das Hinzufügen all dieser fflush
Zeilen in Ihren Code einfügen:
setvbuf (stdout, NULL, _IONBF, BUFSIZ);
Bedenken Sie jedoch, dass dies die Leistung erheblich beeinträchtigen kann, wenn Sie sont Senden der Ausgabe in eine Datei. Denken Sie auch daran, dass die Unterstützung hierfür implementierungsabhängig ist und nicht durch den Standard garantiert wird.
ISO C99 Abschnitt 7.19.3/3
ist das relevante Bit:
Wenn ein Stream ungepuffert Die Zeichen sollen so schnell wie möglich von der Quelle oder am Zielort erscheinen. Andernfalls können Zeichen akkumuliert und als Block an die oder von der Host-Umgebung übertragen werden.
Wenn ein Stream vollständig gepuffert Wenn ein Puffer gefüllt ist, sollen die Zeichen als Block zur oder von der Host-Umgebung übertragen werden.
Wenn ein Stream zeilengepuffert Wenn ein Zeilenumbruch auftritt, sollen die Zeichen als Block an die oder von der Host-Umgebung übertragen werden.
Darüber hinaus sollen Zeichen als Block an die Host-Umgebung übertragen werden, wenn ein Puffer gefüllt ist, wenn eine Eingabe auf einem ungepufferten Stream angefordert wird oder wenn eine Eingabe auf einem zeilengepufferten Stream angefordert wird, der die Übertragung von Zeichen aus der Host-Umgebung erfordert.
Die Unterstützung für diese Merkmale ist implementierungsabhängig und kann über die
setbuf
ysetvbuf
Funktionen.
Wahrscheinlich ist das so, weil es effizienter ist und weil Sie, wenn Sie mehrere Programme auf einen einzigen TTY schreiben, auf diese Weise keine verschachtelten Zeichen in einer Zeile erhalten. Wenn also Programm A und B ausgeben, erhalten Sie normalerweise:
program A output
program B output
program B output
program A output
program B output
Das stinkt, aber es ist besser als
proprogrgraam m AB ououtputputt
prproogrgram amB A ououtputtput
program B output
Beachten Sie, dass es nicht einmal garantiert ist, dass bei einem Zeilenumbruch geflusht wird, so dass Sie explizit flushen sollten, wenn Sie Wert auf Flushing legen.
- See previous answers
- Weitere Antworten anzeigen
2 Stimmen
Haben Sie untersucht, ob dies mit jeder Datei oder nur mit Terminals passiert? das klingt nach einem cleveren Terminal-Feature, keine unvollendeten Zeilen aus einem Hintergrundprogramm auszugeben, obwohl ich vermute, dass es nicht für die Programm im Vordergrund.
8 Stimmen
Unter Cygwin bash sehe ich das gleiche Fehlverhalten, auch wenn ein Zeilenumbruch es in der Formatzeichenfolge. Dieses Problem ist neu in Windows 7; der gleiche Quellcode funktionierte unter Windows XP problemlos. MS cmd.exe wird wie erwartet gespült. Die Lösung
setvbuf(stdout, (char*)NULL, _IONBF, 0)
umgeht das Problem, wäre aber sicher nicht nötig gewesen. Ich verwende MSVC++ 2008 Express. ~~~19 Stimmen
Um den Titel der Frage zu verdeutlichen:
printf(..)
führt keine Spülung durch selbst, sondern die Zwischenspeicherung vonstdout
die beim Erkennen eines Zeilenumbruchs gelöscht werden kann (wenn sie zeilengepuffert ist). Es würde auf die gleiche Weise reagieren aufputchar('\n');
Así queprintf(..)
ist in dieser Hinsicht keine Besonderheit. Dies steht im Gegensatz zucout << endl;
die deren Dokumentation wird die Spülung an prominenter Stelle erwähnt. Die Dokumentation von printf wird das Spülen überhaupt nicht erwähnt.2 Stimmen
Schreiben (/Flushing) eine potenziell teure Operation ist, wird sie wahrscheinlich aus Leistungsgründen gepuffert.
0 Stimmen
@EvgeniSergeev: Gibt es einen Konsens darüber, dass die Frage das Problem falsch diagnostiziert hat, und dass das Flushen passiert, wenn ein Zeilenumbruch in der Ausgabe ? (eine in den Formatstring zu setzen ist eine Möglichkeit, aber nicht die einzige, um eine in der Ausgabe zu erhalten).