Hallo, hab folgendes Programm in C geschrieben (Auschnitt) : void main (void) { double a,b, Ergebnis; char Rechenzeichen; printf("Bitte die erste Variable eingeben\n"); scanf("%lf", &a); printf("Bitte die zweite Variable eingeben\n"); scanf("%lf", &b); printf("Bitte Rechenoperation ausw\x84hlen: ""+,-,*,/""\n"); fflush(stdin); // Tastaturpuffer leeren, da als nächstes sonst ein "Enter" eingelesen würde scanf("%c", &Rechenzeichen); switch(Rechenzeichen) .... Das hier abgebildete Programm funktioniert! Habe beim programmieren zu nächst den "fflush(stdin);" um das "Enter" aus dem Tastaturpuffer zu löschen vergessen. Würde gerne wissen ,warum ich diesen unbedingt an der Stelle brauche, bevor ich einen Wert in eine char Variable einlese, aber er nicht notwendig nach dem zweiten scanf-Befehl ist. Zumindestns zeigt das Programm keine Fehlfunktion an. Bin gespannt ;-) !
> The behavior is undefined if the given stream is of the input type [...] http://en.cppreference.com/w/c/io/fflush Hier eine Lösung (Antwort mit der Bewertung 10): > Quit using scanf. Use fgets and the sscanf http://stackoverflow.com/questions/6277370/replacement-of-fflushstdin Leider steht immernoch in vielen Tutorials und sogar in Büchern, dass fflush zum Leeren des Tastaturbuffers verwendet werden soll...
:
Bearbeitet durch User
be stucki schrieb: > Hier eine Lösung (Antwort mit der Bewertung 10): >> Quit using scanf. Use fgets and the sscanf > http://stackoverflow.com/questions/6277370/replacement-of-fflushstdin Sind "scanf" und "gets" einfach "veraltet"? Also kann ich 1:1 die hier vorgeschlagenen Befehle benutzen ohne mir "Sorgen" machen zu müssen? Hier ist eine Erklärung warum fgets beser ist: http://www.proggen.org/doku.php?id=c:lib:stdio:fgets Unser Lehrer hat bis jetzt nur die Befehle scanf und gets inkl. fflush(stdin) vorgestellt. Soll ich ihn drauf hinweißen das sein Zeug veraltet ist oder gibt es einen Grund warum er die "alten" Befehle benutzt? Danke für die schnelle Antwort!!
gets ist veraltet und auch gefährlich, da die Größe des Eingabepuffers nicht geprüft wird (und auch nicht kann, da sie gar nicht angegeben wird). Darum ist gets (seit C11) auch deprecated. Das Verhalten von fflush() ist für stdin nicht definiert. Unter Linux leert es nicht den Eingabepuffer. Für dein Problem, das überlesen vom '\n', reicht ein Leerzeichen vor dem %c im Formatstring von scanf aus.
1 | scanf(" %c", &Rechenzeichen); |
2 | // ^ da
|
Das Leerzeichen bedeutet: "Überlese alle Whitespace" scanf ist eine sehr mächtige Funktion, die dementsprechend auch Resourcen braucht. Bei fgets brauchst du einen ausreichend großen Speicherbereich für die Eingabe. Dies brauchst du bei scanf nicht unbedingt. fgets speichert allerdings das '\n' mit in der Eingabe ab. Das gibt gerne mal Probleme beim Stringvergleich oder auch bei Dateinamen. Tstyle schrieb: > oder gibt es einen Grund warum er die "alten" Befehle benutzt? Weil er nichts mehr dazu gelernt hat.
Tstyle schrieb: > Sind "scanf" und "gets" einfach "veraltet"? Veraltet ist das falsche Wort. Die beiden Funktionen sind unsicher und können einen Buffer-Overflow erzeugen:
1 | #include <stdio.h> |
2 | |
3 | int main(void){ |
4 | char Str[16]; |
5 | scanf("%s",Str); |
6 | return 0; |
7 | }
|
Wer oder was hindert den Benutzer daran, mehr als 15 Zeichen einzugeben? Genau, niemand! scanf hat keine Ahnung über die Grösse von Str und schreibt munter alle eingegebenen Zeichen in den Speicher. Mit fgets kann sowas nicht passieren:
1 | #include <stdio.h> |
2 | |
3 | int main(void){ |
4 | char Str[16]; |
5 | fgets(Str,sizeof(Str),stdin); |
6 | return 0; |
7 | }
|
Der Benutzer kann zwar immernoch mehr als 15 Zeichen eingeben, aber dein Speicher wird dabei nicht zerschossen, da fgets eine Längenangabe erhalten hat. fgets liest jetzt aber nur die ersten 15 Zeichen ein. Hat der Benutzer mehr Zeichen eingegeben, stehen diese immernoch im Input Buffer und können später noch gelesen werden. Tstyle schrieb: > Soll ich ihn drauf hinweißen das sein Zeug veraltet ist > oder gibt es einen Grund warum er die "alten" Befehle benutzt? Kannst ihm ja einfach sagen, dass du im Internet gelesen hättest, dass scanf unsicher und somit in professioneller Software nicht zu gebrauchen sei und ihn anschliessend fragen, ob dies so richtig sei. Viele Lehrer haben Probleme damit, wenn ihre Fähigkeiten direkt in Frage gestellt werden. Auf die Tatsache, dass fflush(stdin) undefiniertes Verhalten zeigt, solltest du ihn natürlich hinweisen.
:
Bearbeitet durch User
btw, wie habt ihr gelernt, double-Variablen auszugeben? Welchen Formatspecifier nutzt ihr dafür?
Mit
1 | #include <stdio.h> |
2 | |
3 | int main(void){ |
4 | char Str[16]; |
5 | scanf("%15s",Str); |
6 | return 0; |
7 | }
|
bist wieder auf der sicheren Seite. Schwieriger wird es, wenn die Länge des Puffers variabel ist. Aber so einfach kann man fgets und scanf (mit %s) auch nicht vergleichen. Die haben da unterschiedliches Verhalten, was durchaus erwünscht sein kann.
Dirk B. schrieb: > fgets speichert allerdings das '\n' mit in der Eingabe ab. > Das gibt gerne mal Probleme beim Stringvergleich oder auch bei > Dateinamen. Ich sehe das als Vorteil:
1 | char * NewLineChar = strrchr(Str,'\n'); |
2 | if(NewLineChar == NULL){ |
3 | /* Zeile nicht vollständig gelesen */
|
4 | }
|
5 | else{ |
6 | *NewLineChar = '\0'; |
7 | /* Verarbeitung der eingelesenen Daten */
|
8 | }
|
Dirk B. schrieb: > Mit#include <stdio.h> > > int main(void){ > char Str[16]; > scanf("%15s",Str); > return 0; > }bist wieder auf der sicheren Seite. Bis du irgendwann die Grösse des Buffers änderst. Da arbeite ich lieber mit dem sizeof Operator. Dirk B. schrieb: > Aber so einfach kann man fgets und scanf (mit %s) auch nicht > vergleichen. > Die haben da unterschiedliches Verhalten, was durchaus erwünscht sein > kann. Kannst du ein Beispiel machen? Ich habe scanf bis jetzt noch nie vermisst, aber vielleicht kann ich noch was lernen.
Dirk B. schrieb: > #include <stdio.h> > > int main(void){ > char Str[16]; > scanf("%15s",Str); > return 0; > } muss nicht am Ende des Arrays ein "\0" eingefügt werden, damit der Compiler weiss das die Zeichenkette zu Ende ist. Die Funktionen gets, fgets machen das ja selbstständig aber scanf?
Dirk B. schrieb: > btw, wie habt ihr gelernt, double-Variablen auszugeben? > Welchen Formatspecifier nutzt ihr dafür? printf("%lf", dbl);
be stucki schrieb: > Ich sehe das als Vorteil: Ich auch. Ich wollte nur darauf hinweisen, dass man gets nicht so einfach durch fgets ersetzen kann. be stucki schrieb: > Kannst du ein Beispiel machen? Nein. Es gibt dann immer ein Beispiel, wie man es ohne scanf macht.
be stucki schrieb: > Kannst du ein Beispiel machen? Ich habe scanf bis jetzt noch nie > vermisst, aber vielleicht kann ich noch was lernen. Sry, kann dir darauf leider keine Antwort geben. Bis jetzt haben wir diesen Befehl zum Einlesen von Variablen des Typs int, double und char benutzt. Dass man es wie unten gezeigt machen kann, wusste ich bis jetzt noch nicht. Dirk B. schrieb: > #include <stdio.h> > > int main(void){ > char Str[16]; > scanf("%15s",Str); > return 0; > } Dachte immer das man Funktionen wie gets/fgets braucht um eine Zeichenkette einlesen zu können.
Tstyle schrieb: > Dirk B. schrieb: >> btw, wie habt ihr gelernt, double-Variablen auszugeben? >> Welchen Formatspecifier nutzt ihr dafür? > > printf("%lf", dbl); Auch da sieht man es. Der richtige Formatspecifier wäre das %f ohne das l (ell) Das ist eine Besonderheit von Funktionen mit variabler Argumentenliste. Da aber fast alle das mit %lf gemacht haben, wurde es geduldet und ist mittlerweile auch im Standard enthalten. Siehe http://www.cplusplus.com/reference/cstdio/printf/ oder http://en.cppreference.com/w/c/io/fprintf
Tstyle schrieb: > Dachte immer das man Funktionen wie gets/fgets braucht um eine > Zeichenkette einlesen zu können. fgets liest eine ganze Zeile ein (oder bis der Puffer voll ist) scanf mit %s liest bis zum nächste Whitespace (oder bis die angegebene Länge erreicht wurde) Es gibt dann noch %[ Damit kann man auch hübsche Dinge anstellen. Z.B gets simulieren.
1 | int main(void){ |
2 | char Str[16]; |
3 | scanf("%15[^\n] ",Str); |
4 | return 0; |
5 | }
|
Bei der Größenangabe bei scanf muss man aber Bedenken, dass dort die Anzahl der Zeichen angegeben wird. Der Platz für die '\0' muss noch übrig bleiben. Bei fgets wird die Größe des Puffers angegeben. Die '\0' wird berücksichtigt.
Dirk B. schrieb: > Der richtige Formatspecifier wäre das %f ohne das l (ell) In unserem Skriptum steht der Formatspecifier f für den Datentyp float. Laut Definiton von Dirk B. schrieb: > oder http://en.cppreference.com/w/c/io/fprintf steht der f also für Gleitkommazahlen jeglichen Datentyps?
Tstyle schrieb: > Laut Definiton von > > Dirk B. schrieb: >> oder http://en.cppreference.com/w/c/io/fprintf > > steht der f also für Gleitkommazahlen jeglichen Datentyps? Nein. Das steht da auch nicht. Es gibt ja noch long double. Dafür ist der L-Modifier. Also %Lf In der Reference steht beim l-Modifiere double. Allerdings steht das auch bei none
be stucki schrieb: > Leider steht immernoch in vielen Tutorials und sogar in Büchern, dass > fflush zum Leeren des Tastaturbuffers verwendet werden soll... Ja, das sieht man recht oft. Dabei ist das ja überhaupt nicht der Sinn von fflush(). Der Sinn ist, alle Daten, die noch im Puffer liegen, an ihr Ziel auszugeben. Bei Eingabestreams ergibt das keinen Sinn, denn da ist das Ziel ja mein Programm, wohin ich die Daten duch einfaches Lesen bekomme. Es gibt keine Funktion, um den Pufferinhalt eines Streams zu verwerfen.
Rolf Magnus schrieb: > Es gibt keine Funktion, um den Pufferinhalt eines Streams zu verwerfen. Nicht blockierend lesen bis nichts mehr kommt.
Tenga schrieb: > Rolf Magnus schrieb: >> Es gibt keine Funktion, um den Pufferinhalt eines Streams zu verwerfen. > > Nicht blockierend lesen bis nichts mehr kommt. Natürlich kann man lesen und dann selbst das Gelesene verwerfen. Das habe ich nicht bestritten.
Tstyle schrieb: > Dirk B. schrieb: >> Der richtige Formatspecifier wäre das %f ohne das l (ell) > > In unserem Skriptum steht der Formatspecifier f für den Datentyp float. Bei den printf-Varianten ist die Unterscheidung zwischen %f und %lf nicht notwendig, weil ein float-Wert als Argument einer variablen Parameterliste immer nach double konvertiert wird. Bei scanf ist sehr wohl wichtig, dass man zwischen %f und %lf unterscheidet:
1 | float x; |
2 | double y; |
3 | scanf("%f%lf", &x, &y); |
4 | printf("x: %f, y: %f\n", x, y); |
Dirk B. schrieb: > Da aber fast alle das mit %lf gemacht haben, wurde es geduldet und ist > mittlerweile auch im Standard enthalten. Da gabs meines Wissens aber auch einen handfesteren Grund. Die Sache ist die, das eine gleichermassen erlaubte Verwendung von %f unf %lf obwohl eigentlich überflüssig, es einfacher macht, Formatstrings extern zu warten. Damit kann man in einer externen Datei den zu benutzenden Formatspezifier definieren (um ihn zb vom Programm von dort lesen zu lassen), ohne sich um die Unterscheidung zwischen printf und scanf kümmern zu müssen. Selbiges mit Formatspezifiern, die über #define im Code an einer zentralen Stelle gesammelt werden (um zb zwischen %f und %g 'umschalten' zu können). Alles in allem denke ich, das es eine gute Entscheidung war, hier gleich zu ziehen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.