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