FAQ:Stdio

[ FAQ in de.comp.lang.c ]


Diese FAQ bezieht sich in ihrer Gänze auf den inzwischen nicht mehr aktuellen ISO-C Standard 9899:1990, vielfach auch als C90 bezeichnet. Der seit Dezember 1999 existierende neue ISO 9899:1999 Standard (oder auch C99) wird nicht berücksichtigt.


[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


Frage 11.1: Was ist an diesem Code falsch:

	char c;
	while((c = getchar()) != EOF)...

Antwort: Die Variable zur Aufnahme des Rückgabewertes von getchar muß vom Typ int sein. getchar kann alle möglichen char- Werte, aber auch EOF liefern. Wird der Rückgabewert in einem char gespeichert, kann ein normales Zeichen als EOF mißverstanden werden, oder das EOF wird als char interpretiert und deshalb nicht als solches erkannt (insbesondere, wenn der Typ char vorzeichenlos ist).

Referenz: CT&P Abschn. 5.1 S. 70.


Frage 11.2: Wie kann ich ein '%' Zeichen in einem printf Format String drucken? Ich habe \% versucht, aber es hat nicht funktioniert.

Antwort: Einfach das Prozent Zeichen verdoppeln: %% .

Referenzen: K&R I Abschn. 7.3 S. 147; K&R II Abschn. 7.2 S. 154 ANSI Abschn. 4.9.6.1 .


Frage 11.3: Warum funktioniert der Code scanf("%d", i); nicht?

Antwort: scanf braucht Zeiger auf die Variablen, denen es die Werte zuweisen soll; es muß also scanf("%d", &i); heissen.


Frage 11.4: Warum funktioniert der Code:

	double d;
	scanf("%f", &d);

nicht?

Antwort: scanf benutzt %lf für Werte vom Typ double und %f für float. (Man beachte den Unterschied zu printf, das %f sowohl für double als auch für float benutzt, da Argumente in variablen Argumentlisten in C den Default Promotions unterworfen werden).


Frage 11.5: Warum funktioniert der Code

	while(!feof(infp)) {
		fgets(buf, MAXLINE, infp);
		fputs(buf, outfp);
	}

nicht?

Antwort: Die Ein-/Ausgabe von C ist anders als die von PASCAL. EOF wird nur angezeigt nachdem eine Eingabefunktion versucht hat zu lesen und dabei das Dateiende erreicht wurde. Üblicherweise sollte man den Rückgabewert der Eingabefunktion prüfen (in diesem Fall fgets), dann braucht man feof() meist nicht zu benutzen.


Frage 11.6: Warum sagt jeder, dass man gets() nicht benutzen soll?

Antwort: Bei gets() kann die Größe des Eingabepuffers nicht übergeben werden. Somit kann ein Überlaufen dieses Puffers nicht verhindert werden. Siehe Frage 3.1, dort ist ein Code-Fragments gezeigt, das die Verwendung von fgets() anstelle von gets() illustriert.


Frage 11.7: Warum enthält errno den Wert ENOTTY nach dem Aufruf von printf?

Antwort: Viele Implementationen des stdio Pakets passen ihr Verhalten geringfügig an, wenn stdout ein Terminal ist. Um die Unterscheidung durchzuführen, führen diese Implementationen eine Operation aus, die fehlschlägt (mit ENOTTY) wenn stdout kein Terminal ist. Auch wenn die Ausgabeoperation ansonsten erfolgreich durchgeführt wurde, enthält errno noch den Wert ENOTTY.

Referenz: CT&P Abschn. 5.4 p. 73.


Frage 11.8: Mein Programm erwartet eine Eingabe, wobei zwischenzeitliche Ausgaben nicht immer auf dem Bildschirm erscheinen, insbesondere wenn die Ausgabe durch eine Pipe zu einem anderen Programm geleitet wird.

Antwort: Es ist das Beste, ein explizites fflush(stdout) zu benutzen, wenn die Ausgabe definitiv sichtbar sein soll. Verschiedene Mechanismen versuchen das fflush zur "rechten Zeit" auszuführen, aber sie werden meist nur aktiv, wenn stdout ein Terminal ist. (Siehe Frage 11.7.)


Frage 11.9: Wenn ich mit scanf von einer Tastatur einlese, dann scheint es auf die Eingabe einer zusätzlichen Zeile zu warten.

Antwort: scanf wurde für die Eingabe eines freien Formates entwickelt, das man selten braucht, wenn man von einer Tastatur einliest. Insbesondere ein "\n" in einem Formatstring bedeutet _nicht_, dass auf ein Newline gewartet werden soll, sondern daß Zeichen gelesen und verworfen werden sollen, bis ein whitespace Zeichen kommt.

Ein damit zusammenhängendes Problem ist, dass unerwartete nichtnumerische Eingaben scanf dazu veranlassen können zu "Klemmen". Wegen dieser Probleme ist es üblicherweise besser, fgets zum Lesen einer ganzen Zeile und anschließend sscanf oder andere Stringfunktionen zum Zerlegen des Zeilenpuffers zu verwenden. Wenn man sscanf benutzt, sollte man nicht vergessen, den Rückgabewert zu überprüfen um sicherzugehen, dass die erwartete Anzahl von Elementen gefunden wurde.


Frage 11.10: Ich versuche eine Datei direkt zu aktualisieren, indem ich sie mit fopen Mode "r+" öffne, dann einen bestimmten String lese und abschließend den modifizierten String zurückschreibe. Leider scheint das nicht zu funktionieren.

Antwort: Man muß vor dem Schreiben unbedingt fseek aufrufen, um erstens an den Anfang des Strings zurückzukehren, der überschrieben werden soll und zweitens weil ein Aufruf von fseek oder fflush immer zwischen Lese- und Schreibvorgang erforderlich ist, wenn man die Lese/ Schreib "+" Modi benutzt. Man sollte außerdem bedenken, dass man nur Zeichen mit der gleichen Anzahl von Ersatzzeichen überschreiben kann (siehe auch Frage 17.4).

Referenz: ANSI Abschn. 4.9.5.3 S. 131.


Frage 11.11: Wie kann ich jeweils ein einzelnes Zeichen einlesen, ohne auf die Return-Taste zu warten?

Antwort: Siehe Frage 16.1.


Frage 11.12: Wie kann ich bereitstehende Zeichen löschen, so dass die vom Benutzer bereits eingegebenen Zeichen nicht bei der nächsten Eingabeaufforderung eingelesen werden? Kann man dazu fflush(stdin) verwenden? Antwort: Die Anweisung "fflush(stdin);" kann dafür nicht verwendet werden. Die Funktionsweise von fflush ist nur für Ausgaben festgelegt worden. Deshalb darf der an fflush übergebene Zeiger nur auf einen Ein-/Ausgabestrom, dessen letzte Aktion keine Eingabe war, oder einen Ausgabestrom verweisen(7.19.5.2p2 C99)[*]. Die Funktion fflush dient dazu, gepufferte Ausgabedaten eines Stromes vorzeitig an die Laufzeitumgebung zu übergeben. Normalerweise geschieht dies automatisch, wenn der Puffer voll ist und weitere Daten geschrieben werden sollen, oder wenn fclose aufgerufen wird. Das Verhalten von fflush auf einen Ein-/Ausgabestrom, dessen letzte Aktion keine Ausgabe war, oder einen Eingabestrom, ist undefiniert. Auf Eingabedaten ist fflush somit nicht anwendbar! Ein Pendant zu fflush im Sinne des Verwerfens ungelesener Eingaben gibt es nicht. Eine solche Funktion wäre schwer plattformübergreifend zu realisieren, weil sich ungelesene Zeichen nicht nur in den Puffern des laufenden Programms, sondern auch in denen der Laufzeitumgebung befinden können. Eine Lösung ist das Einlesen sämtlicher Eingaben. Das Interpretieren der Eingabe erfolgt zu einem späteren Zeitpunkt, und Fehleingaben könnten dann ignoriert werden. Dazu bieten sich die fgets- und sscanf-Funktionen an. [*] An fflush kann auch ein Nullzeiger übergeben werden. Ein solcher Aufruf entspricht einem Aufruf von fflush für alle aktuell geöffneten Ein-/Ausgabeströme, deren letzte Aktion eine Ausgabe war, bzw. alle geöffneten Ausgabeströme (7.19.5.2p3 C99).


Frage 11.13: Wie kann ich stdin oder stdout von einem Programm aus in eine Datei umleiten?

Antwort: Unter Verwendung von freopen.


Frage 11.14: Ich habe einmal freopen benutzt. Wie kann ich das originale stdin oder stdout zurückbekommen?

Antwort: Wenn man hin und herschalten muß, dann ist die Benutzung von freopen nicht sinnvoll. Besser ist die Verwendung einer eigenen Variable für das Ein- bzw. Ausgabefile. Dieser Variable kann man je nach Bedarf einen Zeiger auf den einen oder anderen Stream zuweisen, dabei bleiben die orginalen Variablen für stdin und stdout unverändert.


Frage 11.15: Wie kann ich den Namen einer Datei ermitteln, der zu einem bestimmten Dateihandle gehört?

Antwort: Dieses Problem ist grundsätzlich unlösbar. Unter UNIX z.B. wäre theoretisch ein Absuchen der gesamten Festplatte erforderlich (was u.U. spezielle Zugriffsrechte erfordert) und dieser Versuch würde fehlschlagen, wenn das Dateihandle eine Pipe oder eine gelöschte Datei referenziert. Bei einer Datei mit mehreren Links ist außerdem die Antwort irreführend. Es ist das Beste, sich die Namen der Dateien selbst zu merken, wenn man sie öffnet (evtl. mit einer Wrapper-Funktion um fopen).

[ Inhalt ][ Index ][ ZurÜck ][ Weiter ]


[ FAQ Logo ]   © 1997-2004 Jochen Schoof (joscho@bigfoot.de)
Diese Version wurde am 14. März 2004 erzeugt. Sie wird zukünftig nicht weiter gepflegt.