c:faq - Vorschläge

Diskussionen zu Tutorials, Änderungs- und Erweiterungswünsche
Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Forenregeln - temp

Beitrag von Xin » Sa Jul 12, 2008 6:03 pm

fat-lobyte hat geschrieben: Falls ihr irgendwelche Fehler findet (inhaltlich oder rechtschreibfehler)
Hmm... ;-)
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;
}

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]
}
Diese zwei Programme, die genau das gleiche tun enthalten einen typischen Anfängerfehler.
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:
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.
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:

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;
}
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.
Der Code 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);
clear_stdin() ist eine kleine Selbstgeschriebene Funktion. Sie sieht so aus:

Code: Alles auswählen

void clear_stdin()
{
    int ch; /* Wichtig! Muss int, und nicht char sein. */    
    while(c = getchar()) != '\n' && c != EOF)
        /* Nichts tun */;
}
Diese funktion liest alle Zeichen bis zum nächsten newline (oder EOF) und verwirft sie wieder. Diese funktion sollte vor jedem scanf() aufgerufen werden.


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:

Code: Alles auswählen

cin.ignore(9999, '\n')
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.
Was ist wenn mehr als 9999 Zeichen drinnen sind? Ganz korrekt wäre folgendes:

Code: Alles auswählen

#include <limits>

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
So, die abgesicherte Version sieht so aus:

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());
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).
Guter Text, soweit.

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...
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: Vorschläge für die FAQ

Beitrag von fat-lobyte » Sa Jul 12, 2008 8:29 pm

Xin hat geschrieben: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.
Done.
Falls wer Fehler findet, darf er sie behalten. ;)
Ne, natürlich nicht, bitte sofort ausbessern, wenn ihr was seht. Dafür sind wikis ja da.
Xin hat geschrieben: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...
Hm, nachdem ich das geschrieben hab, finde ich cin und scanf() echt zum kotzen (noch mehr als sonst). Bin nämlich in jeden Anfängerfehler reingelaufen, den ich da selbst beschrieben hab :roll:
Haters gonna hate, potatoes gonna potate.

Metamorph
Beiträge: 123
Registriert: Sa Jul 05, 2008 12:27 pm

Re: Vorschläge für die FAQ

Beitrag von Metamorph » So Jul 13, 2008 11:45 am

Wäre es nicht besser, den zweiten Artikel ins Wiki zu setzen?
Thema sichere Eingabe oder so ähnlich.
So wären die Eingabeprobleme von Anfang an weg und niemand würde sich zu sehr an scanf() oder cin.get() gewöhnen.
fat-lobyte hat geschrieben:Wie und wieso soll ich die Standardausgabe löschen?
Ich glaube kaum, dass sich ein Anfänger solch eine Frage stellen würde.

Ansonsten ein interessanter Beitrag.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: Vorschläge für die FAQ

Beitrag von fat-lobyte » So Jul 13, 2008 11:52 am

Metamorph hat geschrieben:Wäre es nicht besser, den zweiten Artikel ins Wiki zu setzen?
Erstens ist es schon im Wiki.
Und zweitens, du meinst wahrscheinlich ihn ins C Tutorial zu setzen. Ich finde genau das sollte es nicht sein, denn jeder sollte scanf() und cin meiden, und nicht im Tutorial erst auf die Idee gebracht werden. Ich habe außerdem das Dumpfe gefühl, dass das wirklich eine "Häufig gestellte Frage" wird.
Metamorph hat geschrieben:Ich glaube kaum, dass sich ein Anfänger solch eine Frage stellen würde.
Das vielleicht nicht, aber viele werden sich wundern, wieso denn auf einmal ihr Programm nicht richtig funktioniert. Wir können dann darauf verweisen.
Falls du einen besseren Vorschlag für einen Titel hast, gerne, poste es hier rein.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: c:faq - Vorschläge

Beitrag von fat-lobyte » Fr Jul 18, 2008 4:05 pm

Und hallo nochmals.
Hier der Vorschlag für die dritte Frage.
Hier gehts da drum, die beste Referenz für die Standardbibliothek zu finden. Ich habe ein paar Websites vorgestellt, die ich kenne und die ich gut finde. Falls jemand weitere (gute) kennt, möge er hier den Vorschlag posten.
Hilfe bräuchte ich noch bei der Formatierung, die sieht nämlich echt besch* aus. Ich werde später noch mal durchgehen und nach Fehlern suchen, aber wenn jemand welche finded kann man sie ja gleich ausbessern.
Also dann, hier ist die Frage:

http://tutorial.proggen.org/doku.php?id ... referenzen
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: c:faq - Vorschläge

Beitrag von Kerli » Do Jul 31, 2008 10:48 am

Ich bin jetzt auch auf eine mögliche Frage gestoßen. Es geht darum, weshalb bei der Konvertierung von Gleitkommezahlen in Ganzzahlen immer abgerundet wird und wie man richtig runden kann.
Was meint ihr gehört das auch in die FAQ?

edit: Ich habs jetzt gleich einmal eingebaut.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: c:faq - Vorschläge

Beitrag von Kerli » Do Jul 31, 2008 12:09 pm

In der FAQ werden es schon langsam immer mehr Fragen, aber es hat so ziemlich jede ein anderes Layout. Ich finde wir sollten das etwas vereinheitlichen. Was haltet ihr von einem Layout ähnlich wie bei der 4. Frage?
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: c:faq - Vorschläge

Beitrag von fat-lobyte » Do Jul 31, 2008 12:28 pm

Sieht gut aus. Ich wollte das Layout für die andern Fragen auch ändern, aber ich wusste nicht in was. Könntest du das übernehmen?
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: c:faq - Vorschläge

Beitrag von Kerli » Do Jul 31, 2008 12:50 pm

fat-lobyte hat geschrieben:Sieht gut aus. Ich wollte das Layout für die andern Fragen auch ändern, aber ich wusste nicht in was. Könntest du das übernehmen?
Schon erledigt :D
Ich hab auch gleich überall einen Backlink zur FAQ-Übersicht eingefügt. Ich hab mir gedacht, dass könnte zur Navigation ganz nützlich sein.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: c:faq - Vorschläge

Beitrag von Dirty Oerti » Do Jul 31, 2008 1:20 pm

Kerli hat geschrieben:
fat-lobyte hat geschrieben:Sieht gut aus. Ich wollte das Layout für die andern Fragen auch ändern, aber ich wusste nicht in was. Könntest du das übernehmen?
Schon erledigt :D
Ich hab auch gleich überall einen Backlink zur FAQ-Übersicht eingefügt. Ich hab mir gedacht, dass könnte zur Navigation ganz nützlich sein.
Ja, das ist super. In Hinsicht auf Navigation fehlt in unserem Wiki sowieso noch etwas.
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Antworten