Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
Vorwort des Gutachters
1 Einstieg in C
2 Das erste Programm
3 Grundlagen
4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
5 Basisdatentypen
6 Operatoren
7 Typumwandlung
8 Kontrollstrukturen
9 Funktionen
10 Präprozessor-Direktiven
11 Arrays
12 Zeiger (Pointer)
13 Kommandozeilenargumente
14 Dynamische Speicherverwaltung
15 Strukturen
16 Ein-/Ausgabe-Funktionen
17 Attribute von Dateien und das Arbeiten mit Verzeichnissen (nicht ANSI C)
18 Arbeiten mit variabel langen Argumentlisten – <stdarg.h>
19 Zeitroutinen
20 Weitere Headerdateien und ihre Funktionen (ANSI C)
21 Dynamische Datenstrukturen
22 Algorithmen
23 CGI mit C
24 MySQL und C
25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
26 Paralleles Rechnen
27 Sicheres Programmieren
28 Wie geht’s jetzt weiter?
A Operatoren
B Die C-Standard-Bibliothek
Stichwort

Download:
- ZIP, ca. 10,3 MB
Buch bestellen
Ihre Meinung?

Spacer
<< zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch
Buch: C von A bis Z

C von A bis Z
3., aktualisierte und erweiterte Auflage, geb., mit CD und Referenzkarte
1.190 S., 39,90 Euro
Galileo Computing
ISBN 978-3-8362-1411-7
Pfeil 4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
Pfeil 4.1 Formatierte Eingabe mit »scanf()«
Pfeil 4.1.1 Der Adressoperator
Pfeil 4.1.2 Probleme und deren Behandlung mit »scanf()«
Pfeil 4.1.3 Überprüfen auf das richtige Format
Pfeil 4.1.4 Zusammenfassung zu »scanf()«
Pfeil 4.2 Formatierte Ausgabe mit »printf()«

Um auf den nächsten Seiten etwas mehr mit den Programmen machen zu können, benötigen Sie Kenntnisse in der einfacheren (hier formatierten) Ein- und Ausgabe. Die in diesem Kapitel vorgestellten Funktionen printf() und scanf() sind recht gut für diesen Einstieg geeignet, stellen allerdings keinesfalls das Nonplusultra in C dar. Ganz im Gegenteil: Beide Funktionen sind eher als sicherheitskritisch zu betrachten und in der Praxis mit Vorsicht zu genießen (Stichwort: »Format String Exploits«).

4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«


Galileo Computing - Zum Seitenanfang

4.1 Formatierte Eingabe mit »scanf()« Zur nächsten ÜberschriftZur vorigen Überschrift


Hinweis

Zuerst noch ein 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 ganz durchgelesen haben, wird sich vieles von selbst klären. Sie werden Erleuchtung finden, versprochen!


Betrachten wir zunächst die Syntax dieser Funktion:

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

int scanf(const char * restric 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 der Funktion scanf():

/* scanf1.c */
#include <stdio.h>

int main (void) {
   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 Taste Enter. Anschließend gibt das Programm die Zahl, die eingegeben wurde, auf dem Bildschirm aus und wird beendet.

Abbildung 4.1 Eine einfache Zahleneingabe mit »scanf()«

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

Abbildung 4.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 »&«?


Galileo Computing - Zum Seitenanfang

4.1.1 Der Adressoperator »&« Zur nächsten ÜberschriftZur vorigen Überschrift

Den Adressoperator »&« jetzt schon besser zu verstehen, kann nicht schaden. Später, wenn das Thema »Zeiger« (Pointer; siehe Kapitel 12) besprochen wird, kann dieses Vorverständnis von Nutzen sein. Sollte dieser Abschnitt Ihnen ein wenig fremd vorkommen, ist das kein Grund zur Sorge.

Eine Variable kann in die vier folgenden Einzelteile zerlegt werden:

  • Datentyp
  • Name der Variable
  • Speicheradresse der Variable
  • Wert der Variable

Im Programmbeispiel von oben heißt das konkret: Der Datentyp ist int, der Name ist i, und 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 4.3 Eine Variable im Speicher (vereinfacht dargestellt)

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


Hinweis

Die Attribute einer Variable wurden hier nur vereinfacht dargestellt. Neben den hier erwähnten Attributen gibt es noch das Zugriffsrecht, den Gültigkeitsbereich und die Lebensdauer einer Variablen. Aber darauf gehe ich erst später in Kapitel 9 ein.



Achtung

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


Was beim Einlesen einer Zeichenkette richtig ist, ist bei anderen Datentypen wie Ganz- oder Gleitpunktzahlen wieder falsch:

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

/*
   Richtig, denn 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.


Galileo Computing - Zum Seitenanfang

4.1.2 Probleme und deren Behandlung mit »scanf()« Zur nächsten ÜberschriftZur vorigen Überschrift

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, sollten Sie folgendes Programm testen:

/* scanf2.c */
#include <stdio.h>

int main(void) {
   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 4.4 Ein mögliches Problem mit »scanf()« unter Linux

Was ist hier passiert? Warum wird der zweite Buchstabe immer übersprungen? Wie gesagt, das Problem ist hier die 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 Taste Enter gedrückt. Dieses Taste 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? Hier gibt es ein paar Möglichkeiten, die allerdings auch systemabhängig sind:

Möglichkeit 1

Sie benutzen die Funktion fflush() zum Entleeren des Tastaturpuffers. Möglicherweise gelingt dies nicht auf jedem Betriebssystem (speziell nicht unter Linux):

/* scanf3.c */
#include <stdio.h>

int main(void) {
   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;
}

Möglichkeit 2

Sie benutzen eine do while-Schleife und ziehen das Newline-Zeichen aus dem Puffer heraus:

/* scanf4.c */
#include <stdio.h>

int main(void) {
   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 in Abschnitt 8.9.

Möglichkeit 3

Sie verwenden scanf() erst gar nicht (wie dies in der Praxis aus Sicherheitsgründen zu empfehlen ist) und greifen auf eine der vielen anderen Standardeingabe-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 (siehe auch Abschnitt 16.23). Ein entsprechendes Beispiel könnte so aussehen:

/* scanf5.c */
#include <stdio.h>

int main(void) {
   char ch;
   char buf[2];

   printf("Ein Zeichen bitte : ");
   fgets(buf, 2, stdin);
   sscanf(buf, "%c", &ch);
   printf("Das Zeichen : %c\n",ch);
   return 0;
}

Hinweis

In diesem Buch wird noch des Öfteren die Funktion scanf() verwendet. Falls etwas nicht so funktioniert, wie es sollte, beziehen Sie sich auf die drei gezeigten Möglichkeiten in diesem 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. Abgesehen davon ist scanf() (und auch printf()) ein guter Kanidat für Format String Exploits. Viele Compiler monieren scanf() auch als unsichere Funktion. Der Compiler von Microsoft VC++ z. B. rät, stattdessen die Funktion scanf_f()zu verwenden. Beachten Sie hierbei allerdings, das scanf_f() keine Standard-C-Funktion und somit auch nicht portabel ist.



Galileo Computing - Zum Seitenanfang

4.1.3 Überprüfen auf das richtige Format Zur nächsten ÜberschriftZur vorigen Überschrift

Um sicherzugehen, dass der Benutzer auch das Richtige eingegeben hat, können (müssen) Sie den Rückgabewert von scanf() überprüfen:

/* scanf6.c */
#include <stdio.h>

int main(void) {
   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 dabei immer die Anzahl der erfolgreich gelesenen Werte. Der Wert 0 hingegen wird zurückgegeben, wenn es zu keiner Übereinstimmung mit dem geforderten Formatzeichen gekommen ist. In diesem Beispiel erwartet scanf() die Eingabe eines Zeichens (%c) und einer Dezimalzahl (%d). Wenn beide Eingaben richtig gemacht wurden, 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:

/* scanf7.c */
#include <stdio.h>

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

   printf("Bitte zwei Zahlen eingeben: ");
   check = scanf("%d %d",&a ,&b);
   fflush(stdin);  /* unter Linux entfernen */
   //getchar();    /* für Linux             */
   /* Ist check gleich 2, war die Eingabe richtig. */
   if(check == 2)
      printf("Beide Zahlen richtig %d und %d\n",a ,b);
   /* ... nicht richtig, also war 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() im Beispiel 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 4.5 Die Überprüfung des Rückgabewertes von »scanf()«


Galileo Computing - Zum Seitenanfang

4.1.4 Zusammenfassung zu »scanf()« topZur vorigen Überschrift

Die Funktion scanf() liest zeichenweise eine Folge von Eingabefeldern 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. Für den Fall, dass bei der Eingabe schon ein Fehler auftrat, bevor die Daten überhaupt gelesen werden konnten, wird EOF zurückgegeben.

Folgende Zeichen werden bei scanf() als Eingabefelder akzeptiert:

  • alle Zeichen bis zum nächsten Whitespace
  • alle Zeichen bis zu einer bestimmten Feldbreite von n
  • alle Zeichen bis zu dem ersten Zeichen, das nicht mehr in ein entsprechendes Format konvertiert werden konnte

Whitespace

Ein Whitespace ist ein Leerzeichen, ein Tabulator oder eine Zeilenschaltung.



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. Den Großteil dieser Begriffe werden Sie aber auf den nächsten Seiten noch genauer kennenlernen. Ich habe die Aufteilung der einzelnen Themen bewusst in dieser Form vorgenommen. Wenn Sie dieses Buch durchgearbeitet haben, werden Sie kein Anfänger mehr sein und hin und wieder das eine oder andere Thema nachschlagen wollen. Dann wird es Ihnen leichter fallen, Informationen zur Funktion scanf() kompakt in ein oder zwei Kapiteln zu finden, anstatt im ganzen Buch verstreut danach suchen zu müssen.




Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z

 C von A bis Z
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Spielend C++ lernen






 Spielend C++ lernen


Zum Katalog: C++ von A bis Z






 C++ von A bis Z


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++11 programmieren






 C++11 programmieren


Zum Katalog: Computergeschichte(n) – nicht nur für Geeks






 Computergeschichte(n)
 nicht nur für Geeks


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Katalog: Java 7 – Mehr als eine Insel







 Java 7 –
 Mehr als eine Insel


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de