4    Rechnen mit C und Operatoren

Nachdem Sie nun die grundlegenden Datentypen von C kennengelernt haben, erfahren Sie nun, wie Sie Werte mit scanf() einlesen und mithilfe der arithmetischen Operatoren und Standardfunktionen Berechnungen mit diesen Werten durchführen können. Denn bis jetzt können Sie mit C noch nicht sehr viel anfangen, denn zwei wichtige Sachen fehlen Ihnen noch: Das Einlesen von Zahlenwerten über die Tastatur und das Rechnen mit diesen Zahlen. Dies soll sich nun ändern.

4.1    Werte formatiert einlesen mit scanf()

Damit Sie auf den nächsten Seiten etwas mehr Praxisbeispiele erstellen können, werden Sie in Kürze das Nötigste zur Funktion scanf() kennenlernen. Mit dieser Funktion können Sie formatiert von der Standardeingabe einlesen. Die Standardeingabe ist normalerweise die Tastatur, aber dies kann sich auch anders verhalten, beispielsweise wenn Sie einen Pi über ein RS232-Kabel mit einem PC verbinden. In diesem Fall ist die Standardeingabe auf dem Pi das RS232-Kabel zum PC und die Standardausgabe auf dem PC der Pi. scanf() ist das Gegenstück zu printf() (siehe Abschnitt 2.2, »Die Funktion printf()«). scanf() ist ebenfalls in der Header-Datei stdio.h deklariert, weshalb Sie auch hier wieder diesen Header inkludieren müssen.

Die Funktion scanf() gibt EOF (end of file) zurück, wenn ein Fehler bei der Konvertierung der Eingabewerte aufgetreten ist. EOF ist meistens -1; dies kann sich aber auch anders verhalten, und EOF kann auch 0 sein, z. B. unter macOS. Ansonsten gibt scanf() die Anzahl der erfolgreich eingelesenen Eingabewerte zurück. Auch hier kann dieser Wert 0 sein, z. B. wenn das angegebene Umwandlungszeichen (oder auch der Platzhalter) mit dem %-Zeichen nicht mit dem Typ der Eingabe übereinstimmt. Mit dem Rückgabewert von scanf() können Sie also praktisch die Eingabe auf Korrektheit testen, was Sie auch immer tun sollten.

Sie können fast überall dieselben Formatelemente mit den Prozentzeichen für die Basisdatentypen verwenden, die Sie von printf() her kennen. Bei double müssen Sie beispielsweise %lf für scanf() verwenden, wie auch bei printf(). Allerdings erwartet die Funktion scanf() im Gegensatz zu printf() die Speicheradresse der Variablen, weshalb folgender Funktionsaufruf zu einer Fehlermeldung führen kann:

scanf("%d", iVar); // Fehler!!!

Manchmal prüft der Compiler nicht, dass Sie versehentlich eine Zahl anstatt einer Adresse eingesetzt haben, und schreibt Ihre Eingabe in die entsprechende Speicheradresse, die in der Variablen steht. In diesem Fall haben Sie auf ein ungültiges Segment zugegriffen und bekommen dann bei der Ausführung Ihres Programmes einen Abbruch mit der Meldung »segmentation fault«. Unter Windows würde stattdessen eine allgemeine Schutzverletzung ausgelöst. Bei scanf() müssen Sie also aufpassen.

Die Adresse einer Variablen erhalten Sie mit dem Adressoperator &. Daher müssen Sie scanf() wie folgt verwenden:

scanf("%d", &iVar);

Sie können den Adressoperator natürlich auch bei der Ausgabe von printf() nutzen, womit Sie die Speicheradresse einer Variablen ausgeben.

Hierzu folgt nun ein Beispiel, das ausnahmsweise auf ein Konstrukt vorgreift, das an dieser Stelle noch nicht behandelt wurde, nämlich eine if‐Überprüfung. Die if-Anweisung selbst lernen Sie ausführlicher in Abschnitt 5.1, »Bedingte Anweisungen«, kennen. Da das Überprüfen der Eingabe des Anwenders ein für das Gesamtverständnis essenzieller Aspekt ist, ist dieser Vorgriff sicher sinnvoll und vertretbar.

00  // Kapitel4/scanf_beispiel.c
01 #include <stdio.h>

02 int main(void) {
03 int iVar = 0;
04 printf("Bitte eine Ganzzahl eingeben: ");
05 int check = scanf("%d", &iVar);
06 if( check == EOF ) {
07 printf("Fehler bei scanf...\n");
08 return 1; // Programm beenden
09 }
10 printf("%d Wert(e) eingelesen; ", check);
11 printf("der eingegebene Wert lautet: %d\n", iVar);
12 printf("Die Adresse von iVar lautet: %p\n", &iVar);
13 return 0;
14 }

Listing 4.1    scanf_beispiel.c demonstriert das Einlesen von Zahlen mit scanf().

In Zeile (04) werden Sie durch die Ausgabe des entsprechenden Textes nach einer Zahl gefragt. scanf() wartet in Zeile (05) auf die Eingabe, die Sie mit (¢) bestätigen müssen. In Zeile (06) prüfen Sie, ob bei der Funktion scanf() ein Fehler aufgetreten ist und brechen in diesem Fall das Programm mit einer Fehlermeldung in den Zeilen (07) und (08) ab. Wenn Sie eine korrekte Ganzzahl eingegeben haben, ist in Zeile (10) der Wert check gleich 1, und in Zeile (11) geben Sie dann den mit scanf() eingegebenen Wert aus.

In Zeile (12) wird außerdem nochmals demonstriert, wie Sie die Adresse einer Variablen mit printf() ausgeben können. Als Umwandlungszeichen für eine Adresse müssen Sie %p verwenden. Das Programm bei der Ausführung sieht so aus:

Bitte eine Ganzzahl eingeben: 12345
1 Wert(e) eingelesen; der eingegebene Wert lautet: 12345
Die Adresse von iVar lautet: 000000000013ff04

Das Problem an diesem Beispiel ist allerdings, dass das zurückgegebene EOF im Fehlerfall nicht aussagt, ob auch ein gültiger Integer-Wert eingelesen wurde.

Hierzu müssen Sie den Rückgabewert auf die Anzahl der erfolgreich eingelesenen Werte prüfen. Wenn in diesem Beispiel ein gültiger Integer-Wert (und beispielsweise kein Buchstabe) eingegeben wurde, ist check gleich 1. Folglich würde eine darauf folgende Überprüfung den Fall abdecken, dass kein Fehler vor der ersten Konvertierung mit scanf() (EOF) aufgetreten ist und dass ein gültiger Wert eingegeben wurde. Dies wäre der Fall, wenn der Rückgabewert in check gleich 1 wäre. Eine solche etwas »wasserdichtere« Überprüfung mit scanf() würde daher wie folgt aussehen:

...
int check = scanf("%d", &iVar);
if( check == EOF ) {
printf("Fehler bei scanf...\n");
return 1; // Programm beenden
}
if( check != 1 ) { // Wert nicht 1, dann Fehler
printf("Fehler bei der Eingabe\n");
return 1; // Programm beenden
}
...

Sie können den Rückgabewert von scanf() also auch in einer if-Anweisung verwenden, um zu prüfen, ob die Anzahl der erfolgreich eingelesenen Werte auch mit dem geforderten (und auch der Anzahl der) Umwandlungszeichen übereinstimmt. Es ist schließlich durchaus möglich, mehrere Werte gleichzeitig mit scanf() einzulesen, wie folgendes Beispiel zeigt:

00  // Kapitel4/scanf_fehlercheck.c
01 #include <stdio.h>

02 int main(void) {
03 int iVar1 = 0, iVar2 = 0;
04 printf("Bitte zwei Ganzzahlen eingeben: ");
05 int check = scanf("%d %d", &iVar1, &iVar2);
06 if( check != 2 ) {
07 printf("Fehler: Zwei Ganzzahlen erwartet!\n");
08 return 1; // Programm beenden
09 }
10 printf("%d Wert(e) eingelesen: ", check);
11 printf("%d und %d\n", iVar1, iVar2);
12 return 0;
13 }

Listing 4.2    Das Listing prüft sämtliche Eingaben mit scanf() auf Fehler.

Das Programm bei der Ausführung sieht jetzt so aus:

Bitte zwei Ganzzahlen eingeben: 12 d
Fehler: Zwei Ganzzahlen erwartet!
*** Process returned 1 ***

Bitte zwei Ganzzahlen eingeben: 12 33
2 Wert(e) eingelesen: 12 und 33