Eine schnelle Google-Suche zum fflush(stdin)
Löschen des Eingabepuffers zeigt also zahlreiche Websites an, die davor warnen, ihn zu verwenden. Und doch hat mein CS-Professor der Klasse genau das beigebracht.
Wie schlecht ist die Verwendung fflush(stdin)
? Sollte ich wirklich darauf verzichten, obwohl mein Professor es benutzt und es einwandfrei zu funktionieren scheint?
fflush()
eines Eingabestreams und definieren es sogar auf die gleiche Weise (Wunder der Wunder). Die POSIX-, C- und C ++ - Standards fürfflush()
definieren das Verhalten nicht, aber keiner von ihnen hindert ein System daran, es zu definieren. Wenn Sie Codierung für maximale Portabilität, zu vermeidenfflush(stdin)
; Wenn Sie für Plattformen codieren, die das Verhalten definieren, verwenden Sie es. Beachten Sie jedoch, dass es nicht portierbar ist.fflush(stdin);
die Eingabe nicht gelöscht wird.fflush(stdin)
.If 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 retained
, und das Linux-Dokument sagt,For input streams, fflush() discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application.
dass dies nicht genau der gleiche Weg ist. Windows behält den Puffer bei und Linux verwirft den Puffer.Antworten:
Einfach: Dies ist ein undefiniertes Verhalten, da
fflush
es für einen Ausgabestream aufgerufen werden soll. Dies ist ein Auszug aus dem C-Standard:Es geht also nicht darum, "wie schlimm" das ist.
fflush(stdin)
ist eindeutig falsch , und Sie dürfen es niemals benutzen .quelle
fflush
macht deutlich, dass er für Ausgabestreams direkt im ersten Absatz gedacht ist. Sie müssen sich dafür nicht den C-Standard merken!fflush(stdin)
jahrelang benutzt, bis ichfflush()
alle gepufferten Daten verworfen, die aus der zugrunde liegenden Datei abgerufen, aber nicht von der Anwendung verwendet wurden. Das Öffnen Der Status des Streams bleibt davon unberührt. " Obwohl es sich um UB handelt, scheinen einige Implementierungen Garantien zu geben, ohne den Status in Bezug auf den Standard zu erwähnen.fflush(stdin)
ist viel schlimmer als nur ein implementierungsdefiniertes Verhalten. Selbst wenn es so funktionieren würde, wie es die meisten Menschen beabsichtigen, wäre es schrecklich. Stellen Sie sich vor, stdin ist nicht jemand, der dumm Eingaben eingibt, sondern von einem anderen Programm oder einer anderen Shell-Umleitung stammt: Es würde den Anfang der Datei lesen und dann einfach den Rest löschen. Es ist wirklich dumm zu denken, dass stdin als menschlicher Bediener immer etwas so Langsames ist.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 VerhaltenDie 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:
POSIX weicht größtenteils vom C-Standard ab, markiert diesen Text jedoch als C-Erweiterung.
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.MM- Hinweise :
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).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 zweitegetchar()
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, daherroot
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.quelle
fflush
Kann laut Standard nur mit Ausgabepuffern verwendet werden und ist offensichtlichstdin
keiner. Doch einige bieten Standard - C - Bibliotheken die Verwendungfflush(stdin)
als Erweiterung. In diesem Fall können Sie es verwenden, es beeinträchtigt jedoch die Portabilität, sodass Sie keine standardkonforme Standard-C-Bibliothek mehr auf der Erde verwenden können und dieselben Ergebnisse erwarten.quelle
Ich glaube, Sie sollten niemals anrufen
fflush(stdin)
, aus dem einfachen Grund, dass Sie es niemals für notwendig halten sollten, Eingaben zu löschen. Realistisch gesehen gibt es nur einen Grund, von dem Sie vielleicht denken, dass Sie es müssen, und zwar: um schlechte Eingaben zu überwinden,scanf
die anhaften.Zum Beispiel könnten Sie ein Programm , das in einer Schleife Lesen ganzen Zahlen mit sitzt
scanf("%d", &n)
, und Sie haben entdeckt , dass das erste Mal der Benutzer ein nicht-stellige Zeichen wie'x'
, das Programm in eine Endlosschleife geht .In dieser Situation haben Sie meines Erachtens grundsätzlich drei Möglichkeiten:
fflush(stdin)
, dann durch Aufrufengetchar
, um Zeichen zu lesen, bis\n
, wie oft empfohlen).scanf
zum Lesen der Eingabe .Wenn Sie ein Anfänger sind,
scanf
scheint dies der einfachste Weg zu sein, Eingaben zu lesen. Daher ist Wahl 3 beängstigend und schwierig. Aber # 2 scheint eine echte Ausrede zu sein, denn jeder weiß, dass benutzerunfreundliche Computerprogramme ein Problem sind, also wäre es schön, es besser zu machen. Allzu viele anfängliche Programmierer werden in eine Ecke gemalt und haben das Gefühl, keine andere Wahl zu haben, als # 1 zu tun. Sie müssen mehr oder weniger Eingaben mit verwendenscanf
, was bedeutet, dass sie bei schlechten Eingaben hängen bleiben, was bedeutet, dass sie einen Weg finden müssen, um die schlechten Eingaben zu löschen, was bedeutet, dass sie sehr versucht sind, sie zu verwendenfflush(stdin)
.Ich möchte alle beginnenden Programmierer ermutigen, andere Kompromisse einzugehen:
Während der frühesten Stadien Ihrer C - Programmierung Karriere, bevor Sie bequem etwas anders als mit
scanf
, nur keine Sorge über schlechten Eingang . Ja wirklich. Fahren Sie fort und verwenden Sie Cop-out Nr. 2 oben. Stellen Sie sich das so vor: Sie sind Anfänger, es gibt viele Dinge, die Sie noch nicht wissen, und eines der Dinge, die Sie noch nicht wissen, ist: Gehen Sie mit unerwarteten Eingaben elegant um.Erfahren Sie so bald wie möglich, wie Sie Eingaben mit anderen Funktionen als ausführen
scanf
. An diesem Punkt können Sie anfangen, mit schlechten Eingaben elegant umzugehen, und Ihnen stehen viel mehr, viel bessere Techniken zur Verfügung, bei denen Sie nicht versuchen müssen, die schlechten Eingaben überhaupt zu löschen.Mit anderen Worten, Anfänger, die noch nicht weiterkommen,
scanf
sollten Cop-out Nr. 2 verwenden können. Wenn sie bereit sind, sollten sie von dort zu Technik Nr. 3 übergehen, und niemand sollte Technik Nr. 1 verwenden, um dies zu versuchen Flush-Eingang überhaupt (und schon gar nicht mitfflush(stdin)
.quelle
fflush(stdin)
, dann durch Aufrufengetchar
, um Zeichen zu lesen, bis\n
, wie oft empfohlen). " - Ein Aufruf vongetchar()
Doesn lesen Charakter't s , bis er findet\n
. Wenn mehrere Zeichen vorhanden sind, wird bei einem Aufruf vongetchar()
nur das zuletzt eingegebene Zeichen abgerufen, nicht alle bis und auch ohne die neue Zeile. Darübergetchar()
hinaus kann auch eine Newline verbraucht werden.Das Spülen
fflush(stdin)
von Eingaben ähnelt dem Wünscheln von Wasser mit einem Stab in der Form des Buchstabens "S".Und wenn man den Leuten hilft, Eingaben auf eine "bessere" Weise zu spülen, ist das so, als würde man zu einem S-Stick-Browser eilen und sagen: "Nein, nein, du machst es falsch, du musst einen Y-förmigen Stick verwenden!".
Mit anderen Worten, das eigentliche Problem ist nicht, dass
fflush(stdin)
es nicht funktioniert. Das Anrufenfflush(stdin)
ist ein Symptom für ein zugrunde liegendes Problem. Warum müssen Sie Eingaben überhaupt "spülen"? Das ist dein Problem.Und normalerweise besteht das zugrunde liegende Problem darin, dass Sie
scanf
in einem der vielen verwirrenden Modi unerwartet Zeilenumbrüche oder andere Leerzeichen in der Eingabe belassen. Die beste langfristige Antwort ist daher zu lernen, wie man Eingaben mit besseren Techniken als machtscanf
.quelle
Zitat aus POSIX :
Beachten Sie, dass das Terminal nicht suchen kann.
quelle