Anfängerfrage: Dauerschleife bei Buchstabeneingabe?



  • Hi,

    bin ganz neu hier. Müssen für die Uni jetzt c++ lernen und hab das Programm der ersten Übung eigentlich laufen, aber bei der Eingabe von einem Buchstaben kommt's zu einer Dauerschleife.

    Wenn's nach dem Sinn geht, sollen eigentlich nur Zahlen eingegeben werden.

    #include <stdio.h>
    #include <conio.h>
    
    int main()
    {
    	int zahl=0;
    
    	do{
    	printf("\n\nBitte Zahl eingeben: ");
    	scanf_s("%d", &zahl);
    		if(zahl != 0)
    		{
    			printf("\n\nDie eigegebene Zahl lautet: %d",zahl);
    			printf("\n\nDie eigegebene Zahl lautet: %.2lf",(double)zahl);
    			printf("\n\nDie eigegebene Zahl lautet: %5d",zahl);
    		}	
    		else
    		{
    			return 0;
    		}
    	}
    	while(true);
    
    }
    

    Danke schonmal! 🙂



  • Anscheinend ist es C und nicht C++. Ich verschiebe Dich mal ins C-Forum. Da hat's mehr C-Fachleute.



  • Dieser Thread wurde von Moderator/in volkard aus dem Forum C++ (auch C++0x) in das Forum C (C89 und C99) verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Die C-ler würdens vermutlich wegen des trues gern zurückschubbsen. 😉

    @haeki: Ne conio.h hab ich nicht, aber wenn ich das scanf_s durch scanf ersetze, funktioniert das Programm bei mir ganz normal und bricht wie gewollt ab wenn ich eine 0 eingebe.



  • Dobi schrieb:

    Die C-ler würdens vermutlich wegen des trues gern zurückschubbsen. 😉

    @haeki: Ne conio.h hab ich nicht, aber wenn ich das scanf_s durch scanf ersetze, funktioniert das Programm bei mir ganz normal und bricht wie gewollt ab wenn ich eine 0 eingebe.

    Danke erstmal! Das klappt ja bei mir auch alles. Das Problem ist, dass ich ne Endlosschleife bekomme, wenn ich z.B. "a" eingebe. "Scanf_s" hab ich, weil scanf angeblich veraltet ist... funktioniert zwar, aber Visual meckert trotzdem bissl dann...



  • Bei mir brichts ab wenn ich a eingebe. Willst du es nicht mal mit cin und co. versuchen?



  • Du mußt auch den Rückgabewert von scanf() auswerten - wenn das was anderes als 1 zurückgibt, konnte es die Eingabe nicht verdauen und du mußt geeignete Gegenmaßnahmen ergreifen (in C++ hätte ich jetzt ignore() vorgeschlagen, hier hilft wohl getc() in einer Schleife).



  • CStoll schrieb:

    Du mußt auch den Rückgabewert von scanf() auswerten - wenn das was anderes als 1 zurückgibt, konnte es die Eingabe nicht verdauen und du mußt geeignete Gegenmaßnahmen ergreifen (in C++ hätte ich jetzt ignore() vorgeschlagen, hier hilft wohl getc() in einer Schleife).

    Cin und co? 🙂

    Bin komplett neu bei C. Kann bissl php, aber C ist komplettes Neuland bis auf die Logik.



  • Wenn du schon MSVC zum C compilieren benutzt, achte darauf, dem Compiler dies auch explizit mitzuteilen.

    #ifdef __cplusplus
    #error ich versuche gerade, C Code mit einem C++ Compiler zu übersetzen, was Schrott ist
    #endif

    #pragma warning( disable:4996 )

    Was macht eigentlich dein Code, wenn der Anwender mal einen Buchstaben eingibt?
    Ich habe mal davon gehört, dass Anwender nicht immer das machen, was ihnen gesagt wird.

    Nutze scanf statt scanf_s, weil C Sprachstandard.
    Nutze %f für double bei printf weil C Sprachstandard.
    Lasse den typecast bei printf weg weil implizit schon C Sprachstandard (derjenige, der dir das gezeigt hat, hat keine Ahnung). /*edit: habe gerade bemerkt, dass zahl ein int ist, dann OK, obwohl designmäßig typecasts meistens Schrott sind*/
    Ersetze true durch 1 und verschiebe while an die Stelle von do.



  • Wutz schrieb:

    Wenn du schon MSVC zum C compilieren benutzt, achte darauf, dem Compiler dies auch explizit mitzuteilen.

    #ifdef __cplusplus
    #error ich versuche gerade, C Code mit einem C++ Compiler zu übersetzen, was Schrott ist
    #endif

    #pragma warning( disable:4996 )

    Was macht eigentlich dein Code, wenn der Anwender mal einen Buchstaben eingibt?
    Ich habe mal davon gehört, dass Anwender nicht immer das machen, was ihnen gesagt wird.

    Nutze scanf statt scanf_s, weil C Sprachstandard.
    Nutze %f für double bei printf weil C Sprachstandard.
    Lasse den typecast bei printf weg weil implizit schon C Sprachstandard (derjenige, der dir das gezeigt hat, hat keine Ahnung).
    Ersetze true durch 1 und verschiebe while an die Stelle von do.

    Das sind doch mal wahre Worte. 🙂

    mit

    #ifdef __cplusplus
    #error ich versuche gerade, C Code mit einem C++ Compiler zu übersetzen, was Schrott ist
    #endif

    #pragma warning( disable:4996 )

    konnte ich zwar jetzt nicht so viel anfangen, aber den Rest versuche ich morgen mal umzusetzen. Danke auf jeden Fall! 🙂



  • Wutz schrieb:

    Wenn du schon MSVC zum C compilieren benutzt, achte darauf, dem Compiler dies auch explizit mitzuteilen.

    #ifdef __cplusplus
    #error ich versuche gerade, C Code mit einem C++ Compiler zu übersetzen, was Schrott ist
    #endif

    #pragma warning( disable:4996 )

    Das lag wohl an der Müdigkeit, dass ich's nicht gleich verstanden hab. 🙂

    Aber das mit dem typecast verstehe ich nicht... ich muss es doch casten, damit ich es mit zwei Nachkommastellen ausgeben kann, oder?



  • Bei der Standardeingabe, aus der du mit scanf zu lesen versuchst, handelt es sich um einen sog. Eingabestrom. Du kannst dir dir das etwa wie eine Warteschlange vorstellen: Wenn der Benutzer per Tastatur etwas eingibt, stellen sich die eingegebenen Zeichen hinten an, bis du sie vorne der Reihe nach durch Leseoperationen wieder herausgeholt hast (Stromumleitungen mal beiseite gelassen).

    Zum Beispiel: Der Benutzer gibt "123" ein und drückt Enter. Im Eingabestrom befinden sich jetzt die Zeichen:

    | 1 | 2 | 3 | \n |
    +---+---+---+----+
    

    Wenn du jetzt (beispielsweise mit getchar()) ein Zeichen einliest, wird das aus dem Eingabestrom herausgeholt, dessen Inhalt nun so aussieht:

    | 2 | 3 | \n |
    +---+---+----+
    

    So weit, so gut. scanf("%d", &x); versucht jetzt, eine ganze Zahl aus der Standardeingabe zu lesen. In diesem Fall ginge das wunderbar, weil sofort Zahlen kommen; der Eingabestrom sähe danach aus wie folgt:

    | \n |
    +----+
    

    D.h., der Zeilenumbruch befindet sich noch im Eingabestrom, während die als Zahl gelesenen Zeichen herausgeholt wurden. Sagen wir, der Benutzer schiebt jetzt noch ein paar Zeichen hinterher, beispielsweise "123abc" und drückt wieder Enter. Der Eingabestrom sieht jetzt aus wie folgt:

    | \n | 1 | 2 | 3 | a | b | c | \n |
    +----+---+---+---+---+---+---+----+
    

    ...und scanf kann wieder einlesen (scanf ignoriert whitespaces). Eingelesen wird 123, danach steht noch

    | a | b | c | \n |
    +---+---+---+----+
    

    im Strom, weil scanf das Zeichen 'a' nicht mehr in die Zahl einfließen lassen konnte.

    Wenn jetzt scanf erneut vorbeikommt (oder gleich jemand "abc" eingibt) und eine Zahl einzulesen versucht, gibt es ein Problem: die nächsten Zeichen im Eingabestrom passen nicht! Und scanf kann nicht viel mehr machen als zu sagen: "Tut mir leid, die Zeichen passen nicht, versuch etwas anderes."

    Wie geht man also damit um? Es gibt eine Reihe von Möglichkeiten, aber letztendlich läuft es immer darauf hinaus, die blockierenden Zeichen aus dem Eingabestrom zu holen. Da Benutzereingabe in der Regel zeilenweise interpretiert wird, ist eine einfache Variante, alles bis zum nächsten Zeilenumbruch zu ignorieren:

    char c;
    int x;
    
    /* scanf gibt die Anzahl der erfolgreich eingelesenen Argumente zurück */
    if(scanf("%d", &x) == 1) {
      /* Erfolg */
    } else {
      /* Misserfolg */
    }
    /* Bis zum Zeilenumbruch ignorieren. */
    do { c = getchar(); } while(c != EOF && c != '\n');
    

    Andere Möglichkeiten sind, gleich die ganze Zeile aus dem Eingabestrom in einen Puffer zu holen und daraus (beispielsweise mit sscanf) zu parsen oder alle Zeichen, die nicht passen, zu ignorieren (in diesem Fall scanf("%*[0123456789]");). In manchen Fällen reicht es auch, mit einer Fehlermeldung abzubrechen - das ist regelmäßig dann der Fall, wenn man statt direkter Benutzereingaben eher erwartet, dass einem eine Datei oder ein Socket nach stdin gepipet werden. Allerdings greift man in solchen Fällen regelmäßig auch zu komplexeren Parserkonstrukten als scanf.



  • seldon schrieb:

    Bei der Standardeingabe, aus der du mit scanf zu lesen versuchst, handelt es sich um einen sog. Eingabestrom. Du kannst dir dir das etwa wie eine Warteschlange vorstellen: Wenn der Benutzer per Tastatur etwas eingibt, stellen sich die eingegebenen Zeichen hinten an, bis du sie vorne der Reihe nach durch Leseoperationen wieder herausgeholt hast (Stromumleitungen mal beiseite gelassen).

    Zum Beispiel: Der Benutzer gibt "123" ein und drückt Enter. Im Eingabestrom befinden sich jetzt die Zeichen:

    | 1 | 2 | 3 | \n |
    +---+---+---+----+
    

    Wenn du jetzt (beispielsweise mit getchar()) ein Zeichen einliest, wird das aus dem Eingabestrom herausgeholt, dessen Inhalt nun so aussieht:

    | 2 | 3 | \n |
    +---+---+----+
    

    So weit, so gut. scanf("%d", &x); versucht jetzt, eine ganze Zahl aus der Standardeingabe zu lesen. In diesem Fall ginge das wunderbar, weil sofort Zahlen kommen; der Eingabestrom sähe danach aus wie folgt:

    | \n |
    +----+
    

    D.h., der Zeilenumbruch befindet sich noch im Eingabestrom, während die als Zahl gelesenen Zeichen herausgeholt wurden. Sagen wir, der Benutzer schiebt jetzt noch ein paar Zeichen hinterher, beispielsweise "123abc" und drückt wieder Enter. Der Eingabestrom sieht jetzt aus wie folgt:

    | \n | 1 | 2 | 3 | a | b | c | \n |
    +----+---+---+---+---+---+---+----+
    

    ...und scanf kann wieder einlesen (scanf ignoriert whitespaces). Eingelesen wird 123, danach steht noch

    | a | b | c | \n |
    +---+---+---+----+
    

    im Strom, weil scanf das Zeichen 'a' nicht mehr in die Zahl einfließen lassen konnte.

    Wenn jetzt scanf erneut vorbeikommt (oder gleich jemand "abc" eingibt) und eine Zahl einzulesen versucht, gibt es ein Problem: die nächsten Zeichen im Eingabestrom passen nicht! Und scanf kann nicht viel mehr machen als zu sagen: "Tut mir leid, die Zeichen passen nicht, versuch etwas anderes."

    Wie geht man also damit um? Es gibt eine Reihe von Möglichkeiten, aber letztendlich läuft es immer darauf hinaus, die blockierenden Zeichen aus dem Eingabestrom zu holen. Da Benutzereingabe in der Regel zeilenweise interpretiert wird, ist eine einfache Variante, alles bis zum nächsten Zeilenumbruch zu ignorieren:

    char c;
    int x;
    
    /* scanf gibt die Anzahl der erfolgreich eingelesenen Argumente zurück */
    if(scanf("%d", &x) == 1) {
      /* Erfolg */
    } else {
      /* Misserfolg */
    }
    /* Bis zum Zeilenumbruch ignorieren. */
    do { c = getchar(); } while(c != EOF && c != '\n');
    

    Andere Möglichkeiten sind, gleich die ganze Zeile aus dem Eingabestrom in einen Puffer zu holen und daraus (beispielsweise mit sscanf) zu parsen oder alle Zeichen, die nicht passen, zu ignorieren (in diesem Fall scanf("%*[0123456789]");). In manchen Fällen reicht es auch, mit einer Fehlermeldung abzubrechen - das ist regelmäßig dann der Fall, wenn man statt direkter Benutzereingaben eher erwartet, dass einem eine Datei oder ein Socket nach stdin gepipet werden. Allerdings greift man in solchen Fällen regelmäßig auch zu komplexeren Parserkonstrukten als scanf.

    Wow, erstmal danke, dass du mir sone ausführliche Antwort schreibst. Ich werd mir das ganze morgen dann mal ausführlich und in Ruhe anschauen! Super cool auf jeden Fall, danke! 🙂



  • Ich schlage vor die Benutzereingaben zunächst als String einzulesen - am besten mit fgets, da man dabei die Pufferlänge begrenzen kann.

    char line[100];
    int  x;
    
    if( fgets( line, 99, stdin) != NULL) {
      printf( "%s\n", line);
    
      x = atoi(line);
    
      ...
    }
    

    Zur Ausgabe kann man es dann auch in int, float oder was auch immer konvertieren.
    Leider gibt z.B. atoi null zurück wenn es keine Zahl ist, sodas man das gesondert
    feststellen muss. Alternativ eine eigene Funktion schreiben.

    Der Sinn eine int Variable als float zu casten und dann auszugeben erschliesst
    sich mir nicht.
    Die Nachkommastellen sind immer null ...

    Achja: Wenn ich mich nicht täusche kompiliert VS eine .c Datei anders als eine .cpp Datei 😉
    (Den Namen der Quelldatei hatte der Fragesteller bisher nicht verraten.)

    Bem: siehe auch http://www.c-plusplus.net/forum/294041


Log in to reply