Konvertieren von Kommentaren in eine Antwort - und Erweitern dieser Kommentare, da das Problem regelmäßig erneut auftritt.
Standard C und POSIX bleiben fflush(stdin)
als undefiniertes Verhalten
Die POSIX- , C- und C ++ - Standards für geben fflush()
explizit an, dass das Verhalten undefiniert ist, aber keiner von ihnen hindert ein System daran, es zu definieren.
ISO / IEC 9899: 2011 - die C11-Norm - sagt:
§7.21.5.2 Die fflush-Funktion
¶2 Wenn stream
auf einen Ausgabestream oder einen Aktualisierungsdatenstrom verwiesen wird, in den der letzte Vorgang nicht eingegeben wurde, fflush
bewirkt die Funktion, dass ungeschriebene Daten für diesen Datenstrom an die Hostumgebung gesendet und in die Datei geschrieben werden. Andernfalls ist das Verhalten undefiniert.
POSIX weicht größtenteils vom C-Standard ab, markiert diesen Text jedoch als C-Erweiterung.
[CX] Wenn für einen zum Lesen geöffneten Stream die Datei noch nicht bei EOF ist und die Datei gesucht werden kann, wird der Dateiversatz der zugrunde liegenden Beschreibung der geöffneten Datei auf die Dateiposition des Streams und auf einen beliebigen Wert gesetzt Zeichen, die von dem Stream zurückgeschoben wurden ungetc()
oder ungetwc()
die später nicht aus dem Stream gelesen wurden, werden verworfen (ohne den Datei-Offset weiter zu ändern).
Beachten Sie, dass Terminals nicht suchen können. Weder sind Rohre oder Muffen.
Microsoft definiert das Verhalten von fflush(stdin)
Microsoft und die Visual Studio-Laufzeit definieren das Verhalten fflush()
eines Eingabestreams.
Wenn der Stream für Eingaben geöffnet ist, wird fflush
der Inhalt des Puffers gelöscht.
MM- Hinweise :
Cygwin ist ein Beispiel für eine ziemlich verbreitete Plattform, auf der fflush(stdin)
die Eingabe nicht gelöscht wird.
Aus diesem Grund vermerkt diese Antwortversion meines Kommentars "Microsoft und die Visual Studio-Laufzeit". Wenn Sie eine Nicht-Microsoft C-Laufzeitbibliothek verwenden, hängt das angezeigte Verhalten von dieser Bibliothek ab.
Linux-Dokumentation und -Praxis scheinen sich zu widersprechen
Überraschenderweise dokumentiert Linux nominell auch das Verhalten von fflush(stdin)
und definiert es sogar auf die gleiche Weise (Wunder der Wunder).
Bei Eingabestreams werden fflush()
alle gepufferten Daten verworfen, die aus der zugrunde liegenden Datei abgerufen, aber nicht von der Anwendung verwendet wurden.
Ich bin immer noch etwas verwirrt und überrascht über die Linux-Dokumentation, die besagt, dass dies fflush(stdin)
funktionieren wird. Trotz dieses Vorschlags funktioniert es normalerweise nicht unter Linux. Ich habe gerade die Dokumentation zu Ubuntu 14.04 LTS überprüft. es sagt, was oben zitiert wurde, aber empirisch funktioniert es nicht - zumindest wenn der Eingabestream ein nicht durchsuchbares Gerät wie ein Terminal ist.
demo-fflush.c
#include <stdio.h>
int main(void)
{
int c;
if ((c = getchar()) != EOF)
{
printf("Got %c; enter some new data\n", c);
fflush(stdin);
}
if ((c = getchar()) != EOF)
printf("Got %c\n", c);
return 0;
}
Beispielausgabe
$ ./demo-fflush
Alliteration
Got A; enter some new data
Got l
$
Diese Ausgabe wurde sowohl unter Ubuntu 14.04 LTS als auch unter Mac OS X 10.11.2 erhalten. Nach meinem Verständnis widerspricht es dem, was das Linux-Handbuch sagt. Wenn die fflush(stdin)
Operation funktionieren würde, müsste ich eine neue Textzeile eingeben, um Informationen für die zweite getchar()
zu lesen.
In Anbetracht dessen, was der POSIX-Standard sagt, ist möglicherweise eine bessere Demonstration erforderlich, und die Linux-Dokumentation sollte präzisiert werden.
demo-fflush2.c
#include <stdio.h>
int main(void)
{
int c;
if ((c = getchar()) != EOF)
{
printf("Got %c\n", c);
ungetc('B', stdin);
ungetc('Z', stdin);
if ((c = getchar()) == EOF)
{
fprintf(stderr, "Huh?!\n");
return 1;
}
printf("Got %c after ungetc()\n", c);
fflush(stdin);
}
if ((c = getchar()) != EOF)
printf("Got %c\n", c);
return 0;
}
Beispielausgabe
Beachten Sie, dass dies /etc/passwd
eine durchsuchbare Datei ist. Unter Ubuntu sieht die erste Zeile folgendermaßen aus:
root:x:0:0:root:/root:/bin/bash
Unter Mac OS X sehen die ersten 4 Zeilen folgendermaßen aus:
##
# User Database
#
# Note that this file is consulted directly only when the system is running
Mit anderen Worten, oben in der Mac OS X- /etc/passwd
Datei befindet sich ein Kommentar . Die nicht kommentierten Zeilen entsprechen dem normalen Layout, daher root
lautet der Eintrag:
root:*:0:0:System Administrator:/var/root:/bin/sh
Ubuntu 14.04 LTS:
$ ./demo-fflush2 < /etc/passwd
Got r
Got Z after ungetc()
Got o
$ ./demo-fflush2
Allotrope
Got A
Got Z after ungetc()
Got B
$
Mac OS X 10.11.2:
$ ./demo-fflush2 < /etc/passwd
Got #
Got Z after ungetc()
Got B
$
Das Mac OS X-Verhalten ignoriert das (oder scheint es zumindest zu ignorieren) fflush(stdin)
(daher folgt POSIX in diesem Problem nicht). Das Linux-Verhalten entspricht dem dokumentierten POSIX-Verhalten, aber die POSIX-Spezifikation ist in ihren Aussagen weitaus vorsichtiger - sie gibt eine Datei an, die suchen kann, aber Terminals unterstützen natürlich nicht das Suchen. Es ist auch viel weniger nützlich als die Microsoft-Spezifikation.
Zusammenfassung
Microsoft dokumentiert das Verhalten von fflush(stdin)
. Anscheinend funktioniert es wie auf der Windows-Plattform dokumentiert, wobei der native Windows-Compiler und die C-Laufzeitunterstützungsbibliotheken verwendet werden.
Trotz gegenteiliger Dokumentation funktioniert es unter Linux nicht, wenn die Standardeingabe ein Terminal ist, aber es scheint der POSIX-Spezifikation zu folgen, die weitaus sorgfältiger formuliert ist. Nach dem C-Standard ist das Verhalten von fflush(stdin)
undefiniert. POSIX fügt das Qualifikationsmerkmal "es sei denn, die Eingabedatei ist durchsuchbar" hinzu, was ein Terminal nicht ist. Das Verhalten ist nicht das gleiche wie bei Microsoft.
Folglich wird portabler Code nicht verwendet fflush(stdin)
. Code, der an die Microsoft-Plattform gebunden ist, wird möglicherweise verwendet und funktioniert. Beachten Sie jedoch die Portabilitätsprobleme.
POSIX-Methode zum Verwerfen ungelesener Terminaleingaben aus einem Dateideskriptor
Die POSIX Standardweg ungelesene Daten von einem Terminal Dateideskriptor zu verwerfen (wie dergleichen zu einem Dateistrom entgegengesetzt stdin
) wird bei illustriert Wie kann ich flush ungelesene Daten von einer TTY - Eingabewarteschlange auf einem Unix - System . Dies funktioniert jedoch unterhalb der Standard-E / A-Bibliotheksebene.