Hmm...fat-lobyte hat geschrieben: Falls ihr irgendwelche Fehler findet (inhaltlich oder rechtschreibfehler)
Guter Text, soweit.Frage #2: Wie und wieso soll ich die Standardausgabe löschen?
Antwort: Betrachtet folgende Programmme:
Code: Alles auswählen
#include <stdio.h> int main() { int x, y; printf("Bitte geben einen Summanden ein: "); scanf("%d", &x); printf("Bitte geben noch einen Summanden ein: "); scanf("%d", &y); printf("%d + %d = %d\n", x, y, x+y); printf("Bitte druecken sie [color=#FF0000]E[/color]nter um zu beenden: "); getchar(); return 0; }
Diese zwei Programme, die genau das gleiche tun enthalten einen typischen Anfängerfehler.Code: Alles auswählen
#include <iostream> int main() { int x, y; std::cout<<"Bitte geben einen Summanden ein: "; std::cin>>x; std::cout<<"Bitte geben noch einen Summanden ein: "; std::cin>>y; std::cout<<x<<" + "<<y<<" = "<<x+y<<'\n'; std::cout<<"Bitte druecken sie [color=#FF0000]E[/color]nter um zu beenden: "; std::cin.get(); return 0; [color=#FF0000]Leerzeile[/color] }
Der Gedanke hinter den Programmen ist folgender: "Ich lese eine Zahl ein, dann die Nächste, und dann warte ich bis der User Enter drückt".
Dieses Programm laufen zu lassen verursacht aber zwei Überraschungen:
wenn man zwei zahlen eingibt, dann wartet das Programm nicht bis man Enter drückt, sondern beendet sich sofort.
Wenn man bei der ersten Aufforderung einen Buchstaben statt einer Zahl eingibt,dann wird die zweite Zahl gar nicht mehr abgefragt.
Der Wert der beiden Zahlen ist "Seltsam", und manchmal ist nicht mal das Ergebnis der einfachen Addition richtig.
Bei den meisten Anfängern herrscht nun verwirrung, die Welt der Logik scheint zusammengebrochen zu sein.
Ich kann euch versichern: dem ist nicht so. Der Schuldige ist der Eingabepufferüberflüssiges Return
(auch "Anfängerschreck" genannt).
Sehn wir uns das ganze im detail noch mal an.
scanf(), das übrigens wie auch std::cin zur Gattung "Böse Überraschungen" gehört, interessiert sich nämlich für die Zeichenfolgen, die zu den Formatierungsargumenten passen.
Und zwar nur dafür. Wenn ihr die tasten "1" und "2" drückt, wenn ihr zwölf eintippen wollt, wandern diese Zeichen erstmal in den Eingabepuffer. Dort bleiben sie so lange, bis jemand "Enter" drückt. Das kommt daher, dass die Standardeingabe Zeilengepuffert ist. Das ist übrigens auch der grund wieso ihr "Enter" drücken müsst,überflüssiges Return
damit es weitergeht.
Nun, wenn ihr dann enter drückt kommt erst mal ein Newline*zeichen '\n' in den Eingabepuffer rein.
Zu diesem Zeitpunkt sieht der Eingabepuffer so aus: "12\n".
scanf() und cin interessieren sich nur für die '1' und die '2', denn das ist für sie schon eine Zahl. Diese Zeilen lesen sie, und entfernen sie aus dem eingabepuffer. Das wars dann.
Das newline bleibt aber im Eingabepuffer!
Nun gehts weiter. Der nächste aufruf von scanf() und std::cin ignoriert alle whitespace Zeichen (leerzeichen, tabs, newlines...), also eben auch das newline das im speicher ist. Ihr gebt eine zahl ein, diese wird eingelesen, und das nächste newline bleibt auch im puffer.
Zum schluss kommt der aufruf nach getchar()/cin.get(). getchar liest nur ein zeichen bis zum nächsten newline ein, cin.get() liest sowieso nur ein Zeichen ein.
Also, ein newline ist bereits im Puffer. Das wird von getchar()/cin.get() gelesen, und beide sind glücklich und zufrieden, auch ganz ohne den Benutzer.
Das ist der Grund für das erste verhalten.
--------------Abstand, neues Thema, eben war "Zum Schluss"
Versucht mal beim ersten Prompt ein (oder mehrere) buchstaben einzugeben.
Sehen wir uns an was da passiert.
scanf()/cin>> interessieren sich wie gesagt nur für die Zeichen, die dem Format entsprechen. Nun werden sie aber vor Zeichen gestellt, die nicht zum Format passen! Die beiden wollen ein int! Bekommen haben sie aber etwas anderes.
Beleidigt kehren die beiden zurück, ohne ihre Arbeit verrichtet zu haben. x ist jetzt gleich wie vorher (in einem Wort: 'unverändert'). Da wir vorher x aber nicht initialisiert haben ist x undefiniert! Das bedeutet es kann alles sein. Die zeichen, die der user eingegeben hat bleiben im Puffer.
Beim zweiten Aufruf versucht scanf()/cin>> wieder die Zeichen zu lesen, schafft es wieder nicht, und das ergebnis ist das gleiche wie Vorher.
Und wieso kanns jetzt sein, dass die Addition nicht stimmt? Wir haben gesagt dass der Inhalt der beiden Variablen undefiniert ist. Das bedeutet, es kann auch sein dass die Variablen beide einen sehr hohen (bzw. sehr niedrigen) Wert haben. Wenn man die beiden dann addiert kann es sein, dass die Variable in die das Ergebnis reingeschrieben wird die Variable nicht fassen kann (Variablen haben eine begrenzte größe und ein begrenztes Fassungsvermögen).
Das nennt man "Overflow" (bzw. "Underflow"), und ist eigentlich eine andere Geschichte.Zwei uninitialisierte Variablen bedeutet eine Addition von zwei Zufallszahlen - unanhängig von der Größe der Variablen.
Tja, für getchar()/cin.get() gilt das gleiche wie schon oben. Es bedient sich einfach aus der reichhaltigen Auswahl im Eingabepuffer.
Jetzt wisst ihr, wieso scanf() und std::cin Artefakte sind, die von Bösen mächten erschaffen wurden.
Aber wie löst man das Problem? Verwendet einfach cin>> und scanf() nicht! Von der standardeingabe zu lesen wird anscheinend in Übungsprogrammen für Anfänger gerne gemacht. Die korrekte behandlung von Fehlern ist aber ganz und gar nicht einfach und erfordert einiges an Erfahrung.
Erstens einmal müssen wir Fehler erkennen lernen.
Dazu sehen wir uns scanf() in einer referenz an, zum beispiel hier. Beim Rückgabewert steht folgendes:Für uns bedeutet das, wir müssen nur überprüfen, ob der Rückgabewert auch tatsächlich der Anzahl der gewünschten Objekte entspricht:http://www.cplusplus.com/reference hat geschrieben:On success, the function returns the number of items succesfully read. This count can match the expected number of readings or fewer, even zero, if a matching failure happens.
In the case of an input failure before any data could be successfully read, EOF is returned.Damit stellen wir schon mal sicher, dass der Wert eingelesen wurde. Wir können auch so lange fragen, bis der user das eingibt, was wir wollten. Da bei einem Fehler die zeichen aber im Puffer bleiben würden, würde das zu einer Endlosschleife führen.Code: Alles auswählen
... int num_read; ... printf("Bitte geben einen Summanden ein: "); num_read = scanf("%d", &x); if (num_read != 1) { printf("Sie mussten eine Zahl eingeben!\n"); return 1; }
Der Code sieht so aus:clear_stdin() ist eine kleine Selbstgeschriebene Funktion. Sie sieht so aus:Code: Alles auswählen
... int num_read; ... do { clear_stdin(); printf("Bitte geben einen Summanden ein: "); num_read = scanf("%d", &x); } while(num_read != 1);
Diese funktion liest alle Zeichen bis zum nächsten newline (oder EOF) und verwirft sie wieder. Diese funktion sollte vor jedem scanf() aufgerufen werden.Code: Alles auswählen
void clear_stdin() { int ch; /* Wichtig! Muss int, und nicht char sein. */ while(c = getchar()) != '\n' && c != EOF) /* Nichts tun */; }
Sehen wir uns die Referenz von cin an: http://www.cplusplus.com/reference/iostream/istream/ Nach ein einigem lesen stellen wir fest, dass man den status der Standardeingabe über die Methode good() abfragen kann.
Denn Eingabepuffer kann man mit folgendem Code löschen:Dieser code bedeutet: lies und ignoriere alle Zeichen bis '\n' vorkommt, aber höchstens 9999. Somit kann man alle überflüssigen Zeichen im Puffer entfernen. 9999 ist eine von mir zur vereinfachnung gewählte Zahl.Code: Alles auswählen
cin.ignore(9999, '\n')
Was ist wenn mehr als 9999 Zeichen drinnen sind? Ganz korrekt wäre folgendes:So, die abgesicherte Version sieht so aus:Code: Alles auswählen
#include <limits> std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Wie ihr seht ist aus dem vermeintlich einfachen Programm eine nachschlageübung geworden. Ich hoffe dieser kleine exkurs hat euch genug frustriert, um ab jetzt cin und scanf zu meiden (aber nicht genug, um C/C++ zu meiden).Code: Alles auswählen
do { std::cin.clear(); std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); std::cout<<"Bitte geben einen Summanden ein: "; cin<<x; } while(!std::cin.good());
Rechtschreibmäßig funktioniert deine Shift-Taste zu selten. Ich habe ihn jetzt syntaktisch korrigiert, damit du die Sachen siehst. Am Anfang habe ich noch die Buchstaben korrigert, ab einem Punkt nur noch eingefärbt. Bei "das gleiche" bin ich mir nicht sicher. Ich würde sagen, achte in Zukunft auf die Großschreibung und hau die Fragen einfach ins Wiki und gib einen Vermerk an. Wer Fehler findet, korrigiert sie und die Diskussion zum Inhalt finden wir dann wieder hier.
Ich würde Vorschlagen auf der FAQ Seite nur Links auf CFAQ:KurzeFrage zu packen. Bei 20 Fragen haben wir sonst Romane auf einer FAQ Seite.
PS: Auch ein wertvoller Beitrag für mich. Erstens habe ich mich nie so mit cin / scanf auseinandergesetzt, weil einfach nur böse, zweitens weiß ich jetzt endlich wie man URLs im Text versteckt.... url="bla" hat nämlich nicht funktioniert...