Nur Zahlen zulassen in einer while schleife

Ferdi01

Grünschnabel
Warum bekomme ich eine Endlosschleife, wenn ich bei diesem Code einen Buchstabe eingebe? Wie muß ich das anders schreiben?

C:
#include <stdio.h>

int main()
{
    int auswahl = 0;

    do
    {
        printf("\n" );
        printf("\n1 – Auswahl1" );
        printf("\n2 – Auswahl2");
        printf("\n3 – Auswahl3");
        printf("\n" );
        printf("\nGeben Sie Ihre Wahl ein:" );
        scanf( "%d", &auswahl );
    }while ( auswahl < 1 || auswahl > 3 );
}
 
Zuletzt bearbeitet:

Padawan

Erfahrenes Mitglied
Hallo Ferdi01,

Versuchs mal mit folgendem Code:
C:
#include <stdio.h>

int main()
{
   int auswahl = 0;
   do
   {
      printf("\n");
      printf("\n1 – Auswahl1");
      printf("\n2 – Auswahl2");
      printf("\n3 – Auswahl3");
      printf("\n" );

      printf("\n Geben Sie Ihre Wahl ein:" );
      scanf( "%i", &auswahl );

   } while ( (auswahl < 1 || auswahl > 3) && getchar() != '\n'  );

   return 0;
}

Es lag an dem
C:
getchar() != '\n'

Es lag anscheinend an der Pufferung der Eingaben. Wenn man einen Buchstaben eingibt unt enter drückt, befindet sich das Enter bzw. dessen ASCII-Zeichen noch im Buffer, wahrscheinlich auch die "Endlosschleife".

Näheres kann hier, unter "4.1.2 Probleme und deren Behandlung mit »scanf()«" nachgelesen werden.
 

Ferdi01

Grünschnabel
nee, hat so nicht funktioniert, wird abgefragt ob auswahl <1 und >4 oder der Zeilenumbruch ist. Es läuft immer einmal durch wenn es eine Zahl ist bei Buchstaben geht es wieder in zu do. Ich ab es anders gelöst.

C:
#include <stdio.h>

int main()
{
   int auswahl = 0;
   do
   {
      printf("\n");
      printf("\n1 – Auswahl1");
      printf("\n2 – Auswahl2");
      printf("\n3 – Auswahl3");
      printf("\n" );

      printf("\n Geben Sie Ihre Wahl ein:" );
      scanf( "%i", &auswahl );
      getchar();
   } while ( (auswahl < 1 || auswahl > 3) );

   return 0;
}



funktioniert etwas besser.
Wenn ich nur Enter drücke, dann wartet das Programm noch auf eine Zahl bzw. Buchstabe mit Enter.
Kann man das abstellen, oder muss man damit leben.
 

Padawan

Erfahrenes Mitglied
du must ja auch
C:
&& getchar() != '\n'
in die while abfrage reinpacken, sonst klapt es nicht.

Wie schon in meiner vorherigen Post geschrieben, die Verlinkte seite anschauen. Dort gibt es mehrere verschiedenen Möglichkeiten, wie dein Sachverhalt gelöst werden kann.

EDIT: Dein Code klapt bei mir mit der getchar() lösung
 

Ferdi01

Grünschnabel
bei deiner Lösung, macht er auch wenn ich 4 eingebe weiter. Er soll aber bei 4 und größer in der Schleife bleiben.
 

Padawan

Erfahrenes Mitglied
Anscheinend war das mit dem getchar, nicht ganz sinnvoll.
Mit dem Code Funktioniert es bei mir:
C:
#include <stdio.h>
int fauswahl(int);

int main()
{
   int auswahl;
   do
   {
      printf("\n");
      printf("\n1 – Auswahl1");
      printf("\n2 – Auswahl2");
      printf("\n3 – Auswahl3");
      printf("\n 0 – Ende");

      printf("\n" );

      printf("\n Geben Sie Ihre Wahl ein:" );
      scanf( "%d", &auswahl );
   }
   while((auswahl < 1 || auswahl > 3));

   fauswahl(auswahl);

   return 0;
}

int fauswahl(int menueAuswahl)
{
   printf("---------- %i ----------", menueAuswahl);
}
 
Zuletzt bearbeitet:

Ferdi01

Grünschnabel
komisch, bei mir bekomme ich wieder die Endlosschleife. Ich hab diese gcc Version :
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)
welche Version hast du?
Kann es darum sein, das es bei dir geht und bei mir nicht.
außerdem, wenn ich deinem Code verwende bekomme folgende Fehlermeldung
test.c: In function ‘int fauswahl(int)’:
test.c:30:1: warning: no return statement in function returning non-void [-Wreturn-type]

und 0 wird nie abgefragt, weil es in der while schleife abgefangen wird.

return menueAuswahl;
in der Procedure hinzufügen, dann ist die Warnung weg. ;)
Endlosschleife bleibt.
 
Zuletzt bearbeitet:

Padawan

Erfahrenes Mitglied
ich habe die gcc version 10.2.0 unter Windos 10, pber cygwin.
Naja, soweit ich es verstanden habe, geht es darum, dass es sich um ein Menü handelt. bei den Zahlen 1, 2 und 3 soll es ja immer wieder fragen. Nach der Bedingung

C:
while((auswahl < 1 || auswahl > 3));

ist es so, dass du immer wieder in der Schleife landest, wenn du Zahlen zwischen einschließlich eins und drei eingibst.
 

Ferdi01

Grünschnabel
Wenn ich die Zahlen eingebe funktioniert das Menü richtig, es geht nur nicht wenn man ein Buchstabe eingibt, dann bin ich in der Endlosschleife.
 

Neonof

Mitglied
Warum bekomme ich eine Endlosschleife, wenn ich bei diesem Code einen Buchstabe eingebe? Wie muß ich das anders schreiben?

Hallo Ferdi01,
sehen wir uns doch zunächst einmal die Definition der Funktion scanf an:
Purpose:
Reads formatted data from the predefined stdin stream. The safer scanf_s function is also available.

Syntax:
int scanf(const char * restrict format, ...);

Declared in:
<stdio.h>

Description:
The scanf function is equivalent to fscanf with the argument stdin interposed before the arguments to scanf.

Returns:
The value of the macro EOF if an input failure occurs before any conversion. Otherwise, the scanf function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure.

Um die Arbeitsweise der Funktion besser zu verstehen, hier ein kleines Beispielprogramm:
C:
#include <stdio.h>

int main()
{
    int buffer = 0;
    int scanfret = 1;

    while (scanfret != EOF && scanfret)
    {
        printf("enter a value: ");
        scanfret = scanf("%d", &buffer);
        printf("d: %d, c: %c, ret scanf: %d\n\n", buffer, buffer, scanfret);
    }
    return 0;
}

Meine Ausgabe hierbei:
enter a value: 12b6d
d: 12, c: ♀, ret scanf: 1

enter a value: d: 12, c: ♀, ret scanf: 0

Was ist hier also gerade passiert?
Die Variable, auf die geschrieben werden soll, ist wie bei dir ein Integer. Mit dem %d wird scanf angewiesen, nur Integer einzulesen. An der Stelle, an der ein Zeichen gelesen wird, das nicht dem Formatstring entspricht, bricht die Funktion ab und gibt die Anzahl Werte zurück, die bisher erfolgreich in Variablen geschrieben wurden. Der verbleibende Puffer ist anschließend nicht geleert. Das ist hier daran ersichtlich, dass die Funktion im zweiten Durchlauf direkt ohne weitere Eingabe wieder beendet und die Rückgabe ausgegeben wurde. Solang noch etwas im Puffer verblieben ist, fragt scanf nicht nach einer erneuten Eingabe. Diesmal wurde jedoch keine Variable erfolgreich eingelesen, da die Funktion direkt beim nächsten verbleibenden Zeichen - das ja immer noch ein Buchstabe ist - wieder abgebrochen ist. Daher wird für buffer auch wieder der alte, verbliebene Wert ausgegeben. Wäre das && scanfret nicht in der Bedingung, wäre es deshalb eine Endlosschleife, da der Puffer niemals abgearbeitet wird und die Funktion immer an der selben Stelle abbricht.

Was passiert jetzt bei deinem Code, wenn du einen Buchstaben eingibst?
C:
#include <stdio.h>

int main()
{
    int auswahl = 0;

    do
    {
        printf("\n" );
        printf("\n1 – Auswahl1" );
        printf("\n2 – Auswahl2");
        printf("\n3 – Auswahl3");
        printf("\n" );
        printf("\nGeben Sie Ihre Wahl ein:" );
        scanf( "%d", &auswahl );
    }while ( auswahl < 1 || auswahl > 3 );
}
Zunächst einmal hast du auswahl mit 0 vorinitialisiert. Der Formatstring weist scanf an, einen Integer zu erwarten und nichts anderes. Gibst du nun einen Buchstaben ein, bricht scanf ab, ohne auf auswahl zu schreiben. Es befindet sich also immer noch eine 0 darin. Der Puffer wird nicht geleert und das nächste zu lesende Zeichen bei der nächsten Iteration ist wieder der Buchstabe, bei dem die Funktion direkt wieder abbricht - daher die Endlosschleife. In deinem zweiten Ansatz schiebst du den Pointer für den stdin-Stream mit der Funktion getchar() einen weiter und übergehst damit das störende Zeichen, was für die nächste Iteration bei Bedarf eine erneute Eingabe ermöglicht. Eigentlich wäre die Funktion dazu gedacht, es auch auszuwerten. Ein direkterer Weg dazu wäre die Funktion fseek(stdin, 1, SEEK_CUR). Da getchar() aber nun mal eine Eingabe verlangt, wenn im stream nichts mehr ist, benötigst du die weitere Eingabe. Je nach Anwendungsfall kann das zusätzliche Auslesen eines Zeichens auch problematisch werden.

Ich habe für deinen Zweck zwei saubere Lösungsansätze:
Du könntest entweder die Rückgabe von scanf in deiner Abbruchbedingung abfragen, um sicherzustellen, dass eine Zahl geschrieben wurde und anschließend mit fflush(stdin) den Inputbuffer clearn, falls anschließend noch ungültige Werte eingegeben und nicht mehr abgefragt wurden, die darauf hin im Puffer verblieben.
C:
#include <stdio.h>

int main()
{
    int auswahl = 0;
    int scanfret = 0;

    do
    {
        printf("\n");
        printf("\n1 – Auswahl1");
        printf("\n2 – Auswahl2");
        printf("\n3 – Auswahl3");
        printf("\n");
        printf("\nGeben Sie Ihre Wahl ein:");
        fflush(stdin);
        scanfret = scanf("%d", &auswahl);
    } while (!scanfret || auswahl < 1 || auswahl > 3);
    fflush(stdin);
}

Die andere Möglichkeit ist, die Eingabe gleich als String einzulesen und mit atoi in eine Zahl zu konvertieren.
Purpose:
Converts a string to int, long or long long.

Syntax:
int atoi(const char *string);
long int atol(const char *string);
long long int atoll(const char *string); [C99]

Declared in:
<stdlib.h>

Description:
The atoi, atol, and atoll functions convert the initial portion of the string pointed to by string to int, long int, and long long int representation, respectively.

Except for the behavior on error, they are equivalent to

atoi: (int)strtol(string, (char **)NULL, 10)
atol: strtol(string, (char **)NULL, 10)
atoll: strtoll(string, (char **)NULL, 10)

Returns:
The converted value.
atoi gibt bei ungültiger Eingabe eine 0 zurück und durch das problemlose Einlesen als String verbleibt nichts im Puffer. Daher ist keine weitere Behandlung nötig.
C:
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int auswahl = 0;
    char buffer[10] = "";

    do
    {
        printf("\n");
        printf("\n1 – Auswahl1");
        printf("\n2 – Auswahl2");
        printf("\n3 – Auswahl3");
        printf("\n");
        printf("\nGeben Sie Ihre Wahl ein:");
        scanf("%s", buffer);
        auswahl = atoi(buffer);
    } while (auswahl < 1 || auswahl > 3);
}

Ist deine Frage damit beantwortet?

Gruß,
Neonof
 
Zuletzt bearbeitet:

Neue Beiträge