Die Antwort darauf ist, dass fflush(stream)
nur formal für Ausgabeströme definiert ist, so dass fflush(stdout)
in Ordnung ist, aber fflush(stdin)
nicht.
Der Zweck von fflush(stream)
ist es, das Betriebssystem dazu zu bringen, alle Puffer in die darunter liegende Datei zu leeren. Um ein Beispiel für eine legitime Verwendung zu geben: Studenten haben oft Probleme wie „Mein Prompt erscheint nicht!“, wenn sie etwas tun wie:
printf("Enter a number: ");
Sie stellen jedoch fest, dass dies gut funktioniert:
printf("Enter a number:\n");
Natürlich wollen sie keinen Zeilenumbruch nach ihrem Prompt, also haben sie ein kleines Problem.
Der Grund dafür ist, dass die Ausgabe nach stdout
vom Betriebssystem gepuffert wird und das Standardverhalten (oft) darin besteht, die Ausgabe erst dann in das Terminal zu schreiben, wenn ein Zeilenumbruch auftritt. Das Hinzufügen eines fflush(stdout)
nach dem printf()
löst das Problem:
printf("Enter a number: ");fflush(stdout);
In Analogie dazu denken die Leute oft, dass fflush(stdin)
alle unbenutzten Eingaben verwerfen sollte, aber wenn man ein wenig darüber nachdenkt, ergibt das nicht viel Sinn. Was bedeutet es, einen Eingabepuffer zu „spülen“? Wohin wird er „geleert“? Wenn Sie einen Ausgabepuffer leeren, wird die Ausgabe an die zugrundeliegende Datei oder das Terminal gesendet, wo sie letztendlich sowieso landen würde, aber wo würde die Eingabe „letztendlich sowieso landen“? Das kann man nicht wissen! Wie sollte das Verhalten sein, wenn die Eingabedaten aus einer Datei, einer Pipe oder einem Socket stammen? Für Eingabedatenströme ist es überhaupt nicht klar, wie sich fflush()
verhalten soll, aber für Ausgabedatenströme ist es in allen Fällen sehr klar. Daher ist fflush()
nur für Ausgabeströme definiert.
Der Grund, warum die irrtümliche Verwendung von fflush(stdin)
weit verbreitet wurde, ist, dass vor vielen Jahren einige Betriebssysteme ein Schema implementierten, bei dem es so funktionierte, wie viele Leute es erwarteten, nämlich dass unbenutzte Eingaben verworfen wurden. Microsoft DOS ist ein gutes Beispiel dafür. Überraschenderweise implementieren moderne Versionen von Linux auch fflush()
für Eingabeströme.
Das Richtige, um mit „zusätzlichen“ unerwünschten Terminaleingaben umzugehen, ist, sie einfach zu lesen und nichts mit ihnen zu tun. Das ist fast so einfach wie der Aufruf von fflush(stdin)
, funktioniert überall und beruht nicht auf formal undefiniertem Verhalten.
Der C-Standard sagt:
Wenn stream auf einen Ausgabestrom oder einen Aktualisierungsstrom zeigt, bei dem die letzte Operation keine Eingabe war, veranlasst die Funktion fflush, dass alle ungeschriebenen Daten für diesen Strom an die Host-Umgebung geliefert werden, um in die Datei geschrieben zu werden; andernfalls ist das Verhalten undefiniert.
POSIX sagt (verweist auch explizit auf den C-Standard):
Wenn stream auf einen Ausgabestrom oder einen Aktualisierungsstrom zeigt, bei dem die letzte Operation keine Eingabe war, soll fflush() bewirken, dass alle ungeschriebenen Daten für diesen Strom in die Datei geschrieben werden, ….
Aber die Linux-Manpage sagt:
Für Ausgabestreams erzwingt fflush() ein Schreiben aller im Benutzerbereich gepufferten Daten für den angegebenen Ausgabe- oder Aktualisierungsstream über die zugrunde liegende Schreibfunktion des Streams. Bei Eingabedatenströmen verwirft fflush() alle gepufferten Daten, die aus der zugrundeliegenden Datei geholt, aber noch nicht von der Anwendung verbraucht wurden. Der offene Status des Streams bleibt davon unberührt.