Jetzt können wir zwar schon Stringkonstanten auf dem Bildschirm ausgeben, aber irgendwie fehlt doch noch etwas zur Eingabe (Input) um mehr aus unseren Programmen zu machen. Also auf zum Input-Befehl Namens scanf, das Gegenstück zu printf.C C++ C/C++ Adressoperator Format ANSI C formatiertes einlesen scanf printf Formatiertes einlesen mit scanf - Adressoperator Format Kapitel 6: Formatiertes einlesen mit scanf

Jetzt können Sie zwar schon Stringkonstanten auf dem Bildschirm ausgeben, aber irgendwie fehlt doch noch etwas zur Eingabe (Input) um mehr aus den Programmen zu machen. Also auf zum Input-Befehl Namens scanf(), das Gegenstück zu printf().

6.1. Der Adressoperator &            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis



Hinweis
 

Zuerst noch einen Hinweis für die absoluten Anfänger in der Programmiersprache C. Einiges wird Ihnen in diesem Kapitel ein wenig unklar sein. Aber wenn Sie das Buch komplett durchgelesen haben, wird sich so einiges von selber klären. Aber Sie werden Erleuchtung finden, versprochen!

Hierzu zunächst die Syntax der Funktion:

#include <stdio.h>     //benötigter include für diesen Befehl

int scanf(const char *format, ...);

Mit der Funktion scanf() können Werte unterschiedlicher Datentypen formatiert eingelesen werden. Eingelesen wird dabei von der Standardeingabe (stdin). Mit Standardeingabe ist normalerweise die Tastatur gemeint. Hierzu ein Beispiel mit scanf():

#include <stdio.h>

int main ()
{
   int i;               /* Ein ganzzahliger Datentyp */
   printf("Bitte geben Sie eine Zahl ein : ");
   scanf("%d",&i);      /* Wartet auf die Eingabe */
   printf("Die Zahl die Sie eingegeben haben war %d\n",i);
   return 0;
}

Wenn das Programm korrekt abläuft, wird nach einer Zahl gefragt. Jetzt gibt der Anwender eine Zahl ein und drückt ENTER. Anschließend gibt das Programm die Zahl, die eingegeben wurde, auf dem Bildschirm aus und wird beendet.

Abbildung 6.1: Programm mit der Funktion scanf() bei der Ausführung
Abbildung 6.1: Programm mit der Funktion scanf() bei der Ausführung

Bildlich können Sie sich diesen Vorgang folgendermaßen vorstellen:

Abbildung 6.2: Programm Ein- Ausgabe mit scanf() und printf()
Abbildung 6.2: Programm Ein- Ausgabe mit scanf() und printf()

scanf() ist ähnlich aufgebaut wie printf(). Wie bei printf() werden hier zwei Klammern und zwei Hochkommata verwendet. Es wird also formatiert eingelesen. Das Formatzeichen %d steht für die formatierte Eingabe einer dezimalen Zahl. Was aber bedeutet hier das Zeichen "&"?

6.2. Probleme und deren Behandlung mit scanf()            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Den Adressoperator "&" jetzt schon besser zu verstehen kann nicht schaden. Später wenn das Thema der Pointer (Zeiger) besprochen wird, kann dieses Vorverständnis von Nutzen sein. Sollte Ihnen dieser Abschnitt ein wenig fremd anmuten, ist das kein Grund zur Sorge. Das Thema Pointer wird später noch ausführlich behandelt.

Eine Variable kann in die vier folgenden Einzelteile zerlegt werden:

Im Programmbeispiel oben heißt das konkret, der Datentyp ist int, der Name ist i, die Adresse wird während der Laufzeit zugewiesen, darauf haben Sie keinen Einfluss. Die Speicheradresse sei hier z.B. 0000:123A. Der Wert ist der, den Sie mit scanf() noch eingeben mussten. Wurde jetzt z.B. 5 eingegeben, ist dieser Speicherplatz wie folgt belegt:

Abbildung 6.3: Stellt eine Variable im Speicher da.
Abbildung 6.3: Stellt eine Variable im Speicher da.

Das &-Zeichen ist nichts anderes als der Adressoperator. Dies bedeutet hier, der Variablen i vom Typ int mit der Speicheradresse 0000:123A wird der Wert 5 zugewiesen. Oder einfacher: Verschicken Sie eine E-Mail an jemanden, ohne die E-Mail-Adresse anzugeben?!

Achtung
 

Ein häufiger Fehler, den Anfänger machen, ist das Weglassen des Adressoperators &.

Was im Falle zum Einlesen eines Strings (Zeichenkette) richtig wäre, ist bei anderen Datentypen wie Dezimal- oder Gleitpunktzahlen wieder falsch:

/* FALSCH, da Adressoperator & fehlt */
scanf("%d",zahl);

/* Richtig, eine Zeichenkette benötigt keinen Adressoperator */
scanf("%s",string);

Auch wenn scanf() das Gegenstück zu printf() ist und sich beide in ihrer Schreibweise ähneln, sollten Sie nicht auf die Idee kommen, Folgendes zu schreiben:

/* FALSCH */
scanf("Bitte geben sie eine Zahl ein: %d\n",&zahl);

Das funktioniert deshalb nicht, weil scanf() für die Standardeingabe programmiert ist und printf() für die Standardausgabe. Wobei die Standardausgabe auf der Kommandozeile auch umgeleitet werden kann. Aber dazu später mehr.

Ein häufiges Problem, das auftritt, wenn Sie scanf() für die Eingabe verwenden, ist die Pufferung. Diese ist je nach System und Anwendung, Zeilen- oder Vollgepuffert. Dies gilt wiederum nicht für die Standardfehlerausgabe (stderr), die laut ANSI C, niemals vollgepuffert sein darf. Bevor ich weiter erkläre, folgendes Programm zum testen:

#include <stdio.h>

int main()
{
   char a,b,c;

   printf("1. Buchstabe : ");
   scanf("%c",&a);
   printf("2. Buchstabe : ");
   scanf("%c",&b);
   printf("3. Buchstabe : ");
   scanf("%c",&c);
   printf("Sie gaben ein : %c %c %c ",a,b,c);
   return 0;
}

Folgendes könnte nun vom Programm auf den Bildschirm ausgegeben werden:

Abbildung 6.4: Das Problem mit der Funktion scanf()-Hier unter Linux
Abbildung 6.4: Das Problem mit der Funktion scanf()-Hier unter Linux

Was ist hier passiert? Warum wird der zweite Buchstabe immer übersprungen? Wie gesagt das Problem liegt hier an der Pufferung. Und in C gibt es keinen Befehl (wie etwa chomp bei Perl), um das letzte Zeichen zu entfernen. In diesem Beispiel wurde als erster Buchstabe "a" eingegeben und ENTER gedrückt. Und dieses ENTER (ASCII-Code = 10 = \n = newline) befindet sich immer noch im Puffer der Standardeingabe und wird automatisch für das zweite Zeichen verwendet. Was können Sie dagegen tun? Hierzu ein paar Möglichkeiten, die allerdings auch systemabhängig sind:

6.2.1 Möglichkeit 1
Sie benutzen die Funktion fflush() zum entleeren des Tastaturpuffers. Was aber möglicherweise nicht auf jedem Betriebssystem (speziell nicht unter Linux) so funktioniert:

#include <stdio.h>

int main()
{
   char a,b,c;
   printf("1. Buchstabe : ");
   scanf("%c",&a);
   fflush(stdin);
   printf("2. Buchstabe : ");
   scanf("%c",&b);
   fflush(stdin);
   printf("3. Buchstabe : ");
   scanf("%c",&c);
   printf("Sie gaben ein : %c %c %c ",a,b,c);
   return 0;
}

6.2.2 Möglichkeit 2
Sie benutzten eine do while-Schleife und ziehen das newline-Zeichen aus dem Puffer heraus:

#include <stdio.h>

int main()
{
   char a,b,c;
   printf("1. Buchstabe : ");
   do {scanf("%c",&a);} while ( getchar() != '\n' );
   printf("2. Buchstabe : ");
   do {scanf("%c",&b);} while ( getchar() != '\n' );
   printf("3. Buchstabe : ");
   do {scanf("%c",&c);} while ( getchar() != '\n' );
   printf("%c %c %c\n",a,b,c);

   return 0;
}

Mehr zur do while Schleife finden Sie im entsprechenden Kapitel. Dieses Beispiel lässt sich natürlich auch mir einer while()-Schleife als Makro verpackt realisieren:

#define Fflush(int_keyboard_buffer)\
   while(( (int_keyboard_buffer) = getchar()) != '\n')

Mehr zu diesem Beispiel erfahren Sie im Kapitel der Präprozessor-Direktiven.

6.2.3 Möglichkeit 3 Sie verwenden scanf() erst gar nicht und greifen auf eine der vielen anderen Standardeingaben-Funktionen zurück. Ideal wäre es beispielsweise die Funktion fgets() zum Einlesen zu verwenden und diese Eingabe mit der Funktion sscanf() in ein entsprechendes Format zu konvertieren.

Hinweis
 

Im Laufe dieses Buches wird noch des Öfteren die Funktion scanf() verwendet. Sollte etwas nicht so funktionieren, wie es sollte, beziehen Sie sich auf dieses Kapitel.

Achtung
 

Die Funktion scanf() ist nicht gegen einen Pufferüberlauf (Buffer Overflow) geschützt und somit unsicher, d.h. sie könnte für einen Hack des Programms durch eine andere Person missbraucht werden. Damit ist gemeint, dass die Funktion nicht die Anzahl der eingegebenen Zeichen überprüft und es damit zu Fehlern kommen kann, bzw. ein Fehlverhalten von außen provoziert werden kann.

6.3. Überprüfen auf das richtige Format            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Um sicher zu gehen, dass der Benutzer das Richtige eingegeben hat, können Sie den Rückgabewert von scanf() überprüfen:

#include <stdio.h>

int main()
{
   char a;
   int b,check;

   printf("Bitte Eingabe machen (Zeichen/Zahl): ");
   check=scanf("%c %d",&a,&b);
   printf("Check = %d \n",check);
   return 0;
}

Der Rückgabewert von scanf() ist immer die Anzahl der erfolgreich gelesenen Werte. In diesem Beispiel erwartet scanf() die Eingabe eines Zeichens (%c) und einer Dezimalzahl (%d). Wenn die Eingabe richtig gemacht wurde, sollte die folgende printf() Anweisung den Wert "2" ausgeben.

Mit einer kleinen Überprüfung können Sie das Programm verbessern, um ein undefiniertes Verhalten für die Weiterarbeit zu verhindern:

#include <stdio.h>

int main()
{
   int a,b,check;

   printf("Bitte zwei Zahlen eingeben: ");
   check=scanf("%d %d",&a,&b);
   fflush(stdin);  /* Unter Linux entfernen */
   //getchar()     /* für Linux */
   /* Bei check istgleich 2 wahr die Eingabe richtig */
   if(check==2)
      printf("Beide Zahlen richtig %d und %d\n",a,b);
   /* … nicht richtig, also wahr die 2. Zahl falsch */
   else if(check==1)
      {
         printf("Die 2.Zahl hat das falsche Format!!\n");
         printf("Bitte Eingabe wiederholen: ");
         /* noch ein Versuch */
         check=scanf("%d",&b);
         fflush(stdin);
         if(check)
            printf("Eingabe Ok. Ihre Zahlen %d %d\n",a,b);
         else
            printf("Leider nochmals falsch\n");
      }
   else
      printf("Die Erste oder beide Eingaben waren falsch!\n");
   return 0;
}

Bei fehlerfreier Ausführung liefert die scanf()-Funktion die Anzahl der Zeichen zurück, die erfolgreich gelesen, konvertiert und gespeichert wurden.

Wenn die erste Eingabe von scanf() schon fehlerhaft ist, wird die zweite Eingabe gar nicht mehr beachtet. Daher gibt scanf() dann 0 zurück, da gar keine Zeichen gespeichert werden konnten.

Abbildung 6.5: Programm mit Überprüfung des Rückgabewertes von scanf()
Abbildung 6.5: Programm mit Überprüfung des Rückgabewertes von scanf()

6.4. Zusammenfassung scanf()            zurück  Ein Kapitel höher  zum Inhaltsverzeichnis

Die Funktion scanf() liest zeichenweise eine folge von Eingabefelder ein. Für jedes Eingabefeld muss eine Adresse vorhanden sein. Wobei das Eingabefeld, mit dem Datentyp der Adresse übereinstimmen muss. Bei Erfolg liefert scanf() die Anzahl der erfolgreich eingelesenen Felder zurück. Konnten keine Felder korrekt eingelesen werden, gibt scanf() als Rückgabewert 0 zurück.

Folgende Zeichen werden bei scanf() als Eingabefelder akzeptiert:

Hinweis
 

Ein Whitespace ist ein Leerzeichen, Tabulator oder eine Zeilenschaltungen.

Anmerkung für den Anfänger
 

Nochmals eine Anmerkung für die absoluten Neulinge in C. Sie wurden in diesem Kapitel teilweise mit Begriffen wie Variablen, Datentypen, Format, Formatanweisungen, Feldbreite usw. bombardiert, mit denen Sie zum größten Teil wohl noch nichts anfangen können. Denn Großteil dieser Begriffe werden Sie aber auf den nächsten Seiten noch genauer kennen lernen. Die Aufteilung der einzelnen Themen wurde bewusst in dieser Form verfasst. Wenn Sie dieses Buch durchgearbeitet haben, werden Sie kein Anfänger mehr sein und hin und wieder das eine oder andere Thema nachschlage wollen. Dann wird es Ihnen leichter fallen, Informationen zur Funktion scanf(), kompakt in einen oder zwei Kapiteln zu finden, anstatt im ganzen Buch verstreut danach suchen zu müssen.

Weiter mit Kapitel 7: Formatierte Ausgabe mit printf            zum Inhaltsverzeichnis