C-Programmierung: Einfache Ein- und Ausgabe

Wohl kein Programm kommt ohne Ein- und Ausgabe aus. In C ist die Ein-/Ausgabe allerdings kein Bestandteil der Sprache selbst. Vielmehr liegen Ein- und Ausgabe als eigenständige Funktionen vor, die dann durch den Linker eingebunden werden. Die wichtigsten Ein- und Ausgabefunktionen werden Sie in diesem Kapitel kennenlernen.

Die Funktion printf haben wir bereits in unseren bisherigen Programmen benutzt. Zeit also, sie genauer unter die Lupe zu nehmen. Die Funktion printf hat die folgende Syntax:

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

Bevor wir aber printf diskutieren, sehen wir uns noch einige Grundbegriffe von Funktionen an. In einem späteren Kapitel werden Sie dann lernen, wie Sie eine Funktion selbst schreiben können.

In den beiden runden Klammern befinden sich die Parameter. In unserem Beispiel ist der Parameter const char *format . Die drei Punkte dahinter zeigen an, dass die Funktion noch weitere Parameter erhalten kann. Die Werte, die der Funktion übergeben werden, bezeichnet man als Argumente. In unserem „Hallo Welt“-Programm haben wir der Funktion printf beispielsweise das Argument "Hallo Welt" übergeben.

Außerdem kann eine Funktion einen Rückgabewert besitzen. In diesem Fall ist der Typ des Rückgabewertes int . Den Typ der Rückgabe erkennt man am Schlüsselwort, das vor der Funktion steht. Eine Funktion, die keinen Wert zurückgibt, erkennen Sie an dem Schlüsselwort void .

Die Bibliotheksfunktion printf dient dazu, eine Zeichenkette (engl. String) auf der Standardausgabe auszugeben. In der Regel ist die Standardausgabe der Bildschirm. Als Übergabeparameter besitzt die Funktion einen Zeiger auf einen konstanten String. Was es mit Zeigern auf sich hat, werden wir später noch sehen. Das const bedeutet hier, dass die Funktion den String nicht verändert. Über den Rückgabewert liefert printf die Anzahl der ausgegebenen Zeichen. Wenn bei der Ausgabe ein Fehler aufgetreten ist, wird ein negativer Wert zurückgegeben.

Als erstes Argument von printf sind nur Strings erlaubt. Bei folgender Zeile gibt der Compiler beim Übersetzen deshalb eine Warnung oder einen Fehler aus:

 printf(55); // falsch

Da die Anführungszeichen fehlen, nimmt der Compiler an, dass es sich bei 55 um einen Zahlenwert handelt. Geben Sie dagegen 55 in Anführungszeichen an, interpretiert der Compiler dies als Text. Bei der folgenden Zeile gibt der Compiler deshalb keinen Fehler aus:

 printf("55"); // richtig

Formatelemente von printf

Bearbeiten

Die printf-Funktion kann auch mehrere Parameter verarbeiten, diese müssen dann durch Kommata voneinander getrennt werden.

Beispiel:

#include <stdio.h>

int main()
{
  printf("%i plus %i ist gleich %s.\n", 3, 2, "Fünf");
  return 0;
}

Ausgabe:

3 plus 2 ist gleich Fünf.

Die mit dem %-Zeichen eingeleiteten Formatelemente greifen nacheinander auf die durch Komma getrennten Parameter zu (das erste %i auf 3, das zweite %i auf 2 und %s auf den String "Fünf").

Innerhalb von format werden Umwandlungszeichen (engl. conversion modifier) für die weiteren Parameter eingesetzt. Hierbei muss der richtige Typ verwendet werden. Die wichtigsten Umwandlungszeichen sind:

Zeichen Umwandlung
%d oder %i int
%c einzelnes Zeichen
%e oder %E double im Format [-]d.ddd e±dd bzw. [-]d.ddd E±dd
%f double im Format [-]ddd.ddd
%o int als Oktalzahl ausgeben
%p die Adresse eines Zeigers
%s Zeichenkette ausgeben
%u unsigned int
%lu long unsigned
%x oder %X int als Hexadezimalzahl ausgeben
%% Prozentzeichen

Weitere Formate und genauere Erläuterungen finden Sie in der Referenz dieses Buches.

Beispiel:

#include <stdio.h>

int main()
{
  printf("Integer: %d\n", 42);
  printf("Double: %.6f\n", 3.141);
  printf("Zeichen: %c\n", 'z');
  printf("Zeichenkette: %s\n", "abc");
  printf("43 Dezimal ist in Oktal: %o\n", 43);
  printf("43 Dezimal ist in Hexadezimal: %x\n", 43);
  printf("Und zum Schluss geben wir noch das Prozentzeichen aus: %%\n");

  return 0;
}

Nachdem Sie das Programm übersetzt und ausgeführt haben, erhalten Sie die folgende Ausgabe:

Integer: 42
Double: 3.141000
Zeichen: z
Zeichenkette: abc
43 Dezimal ist in Oktal: 53
43 Dezimal ist in Hexadezimal: 2b
Und zum Schluss geben wir noch das Prozentzeichen aus: %

Neben dem Umwandlungszeichen kann eine Umwandlungsangabe weitere Elemente zur Formatierung erhalten. Dies sind maximal:

  • ein Flag
  • die Feldbreite
  • durch einen Punkt getrennt die Anzahl der Nachkommstellen (Längenangabe)
  • und an letzter Stelle schließlich das Umwandlungszeichen selbst

Unmittelbar nach dem Prozentzeichen werden die Flags (dt. Kennzeichnung) angegeben. Sie haben die folgende Bedeutung:

  • - (Minus): Der Text wird links ausgerichtet.
  • + (Plus): Es wird auch bei einem positiven Wert ein Vorzeichen ausgegeben.
  • Leerzeichen: Ein Leerzeichen wird ausgegeben, wenn der Wert positiv ist.
  • # : Welche Wirkung das Kennzeichen # hat, ist abhängig vom verwendeten Format: Wenn ein Wert über %x als Hexadezimal ausgegeben wird, so wird jedem Wert ein 0x vorangestellt (außer der Wert ist 0).
  • 0 : Die Auffüllung erfolgt mit Nullen anstelle von Leerzeichen, wenn die Feldbreite verändert wird.

Im folgenden ein Beispiel, das die Anwendung der Flags zeigt:

#include <stdio.h>

int main()
{
   printf("Zahl 67:%+i\n", 67);
   printf("Zahl 67:% i\n", 67);
   printf("Zahl 67:%#x\n", 67);
   printf("Zahl 0:%0x\n", 0);
   return 0;
}

Wenn das Programm übersetzt und ausgeführt wird, erhalten wir die folgende Ausgabe:

Zahl 67:+67  
Zahl 67: 67
Zahl 67:0x43
Zahl 0:0

Feldbreite

Bearbeiten

Hinter dem Flag kann die Feldbreite (engl. field width) festgelegt werden. Das bedeutet, dass die Ausgabe mit der entsprechenden Anzahl von Zeichen aufgefüllt wird. Beispiel:

int main()
{
  printf("Zahlen rechtsbündig ausgeben: %5d, %5d, %5d\n",34, 343, 3343);
  printf("Zahlen rechtsbündig ausgeben, links mit 0 aufgefüllt: %05d, %05d, %05d\n",34, 343, 3343);
  printf("Zahlen linksbündig ausgeben: %-5d, %-5d, %-5d\n",34, 343, 3343);
  return 0;
}

Wenn das Programm übersetzt und ausgeführt wird, erhalten wir die folgende Ausgabe:

Zahlen rechtsbündig ausgeben:    34,   343,  3343
Zahlen rechtsbündig ausgeben, links mit 0 aufgefüllt: 00034, 00343, 03343
Zahlen linksbündig ausgeben: 34   , 343  , 3343 

In Zeile 4 haben wir anstelle der Leerzeichen eine 0 verwendet, so dass nun die Feldbreite mit Nullen aufgefüllt wird.

Standardmäßig erfolgt die Ausgabe rechtsbündig. Durch Voranstellen des Minuszeichens kann die Ausgabe aber auch linksbündig erfolgen, wie in Zeile 5 zu sehen ist.

Nachkommastellen

Bearbeiten

Nach der Feldbreite folgt, durch einen Punkt getrennt, die Genauigkeit. Bei %f werden ansonsten standardmäßig 6 Nachkommastellen ausgegeben. Diese Angaben sind natürlich auch nur bei den Gleitkommatypen float und double sinnvoll, weil alle anderen Typen keine Nachkommastellen besitzen.

Beispiel:

#include <stdio.h>

int main()
{
  double betrag1 = 0.5634323;
  double betrag2 = 0.2432422;
  printf("Summe: %.3f\n", betrag1 + betrag2);

  return 0;
}

Wenn das Programm übersetzt und ausgeführt wurde, erscheint die folgende Ausgabe auf dem Bildschirm:

Summe: 0.807

Auch die Funktion scanf haben Sie bereits kennengelernt. Sie hat eine vergleichbare Syntax wie printf:

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

Die Funktion scanf liest einen Wert ein und speichert diesen in den angegebenen Variablen ab. Doch Vorsicht: Die Funktion scanf erwartet die Adresse der Variablen. Deshalb führt der folgende Funktionsaufruf zu einem Fehler:

 scanf("%i", x); /* Fehler */

Richtig dagegen ist:

 scanf("%i",&x);

Mit dem Adressoperator & erhält man die Adresse einer Variablen. Diese kann man sich auch ausgeben lassen:

#include <stdio.h>

int main(void)
{
  int x = 5;

  printf("Adresse von x: %p\n", &x);
  printf("Inhalt der Speicherzelle: %d\n", x);

   return 0;
}

Kompiliert man das Programm und führt es aus, erhält man z.B. die folgende Ausgabe:

Adresse von x: 0022FF74
Inhalt der Speicherzelle: 5

Die Ausgabe der Adresse kann bei Ihnen variieren. Es ist sogar möglich, dass sich diese Angabe bei jedem Neustart des Programms ändert. Dies hängt davon ab, wo das Programm (vom Betriebssystem) in den Speicher geladen wird.

Mit Adressen werden wir uns im Kapitel Zeiger noch einmal näher beschäftigen.

Für scanf können die folgenden Platzhalter verwendet werden, die dafür sorgen, dass der eingegebene Wert in das "richtige" Format umgewandelt wird:

Zeichen Umwandlung
%d vorzeichenbehafteter Integer als Dezimalwert
%i vorzeichenbehafteter Integer als Dezimal-,
Hexadezimal oder Oktalwert
%e, %f, %g Fließkommazahl
%o int als Oktalzahl einlesen
%s Zeichenkette einlesen
%x Hexadezimalwert
%% erkennt das Prozentzeichen

getchar und putchar

Bearbeiten

Die Funktion getchar liefert das nächste Zeichen vom Standard-Eingabestrom. Ein Strom (engl. stream) ist eine geordnete Folge von Zeichen, die als Ziel oder Quelle ein Gerät hat. Im Falle von getchar ist dieses Gerät die Standardeingabe -- in der Regel also die Tastatur. Der Strom kann aber auch andere Quellen oder Ziele haben: Wenn wir uns später noch mit dem Speichern und Laden von Dateien beschäftigen, dann ist das Ziel und die Quelle des Stroms eine Datei.

Das folgende Beispiel liest ein Zeichen von der Standardeingabe und gibt es aus. Eventuell müssen Sie nach der Eingabe des Zeichens <Enter> drücken, damit überhaupt etwas passiert. Das liegt daran, dass die Standardeingabe üblicherweise zeilenweise und nicht zeichenweise eingelesen wird.

int c;
c = getchar();
putchar(c);

Geben wir über die Tastatur "hallo" ein, so erhalten wir durch den Aufruf von getchar zunächst das erste Zeichen (also das "h"). Durch einen erneuten Aufruf von getchar erhalten wir das nächste Zeichen, usw. Die Funktion putchar(c) ist quasi die Umkehrung von getchar: Sie gibt ein einzelnes Zeichen c auf der Standardausgabe aus. In der Regel ist die Standardausgabe der Monitor.

Zugegeben, die Benutzung von getchar hat hier wenig Sinn, außer man hat vor, nur das erste Zeichen einer Eingabe einzulesen. Häufig wird getchar mit Schleifen benutzt. Ein Beispiel dafür werden wir noch später kennenlernen.

Escape-Sequenzen

Bearbeiten

Eine spezielle Darstellung kommt in C den Steuerzeichen zugute. Steuerzeichen sind Zeichen, die nicht direkt auf dem Bildschirm sichtbar werden, sondern eine bestimmte Aufgabe erfüllen, wie etwa das Beginnen einer neuen Zeile, das Darstellen des Tabulatorzeichens oder das Ausgeben eines Warnsignals. So führt beispielsweise

printf("Dies ist ein Text ");
printf("ohne Zeilenumbruch");

nicht etwa zu dem Ergebnis, dass nach dem Wort „Text“ eine neue Zeile begonnen wird, sondern das Programm gibt nach der Kompilierung aus:

Dies ist ein Text ohne Zeilenumbruch

Eine neue Zeile wird also nur begonnen, wenn an der entsprechenden Stelle ein \n steht. Die folgende Auflistung zeigt alle in C vorhandenen Escape-Sequenzen:

  • \n (new line) = bewegt den Cursor auf die Anfangsposition der nächsten Zeile.
  • \t (horizontal tab) = Setzt den Tabulator auf die nächste horizontale Tabulatorposition. Wenn der Cursor bereits die letzte Tabulatorposition erreicht hat, dann ist das Verhalten unspezifiziert (vorausgesetzt eine letzte Tabulatorposition existiert).
  • \a (alert) = gibt einen hör- oder sichtbaren Alarm aus, ohne die Position des Cursors zu ändern
  • \b (backspace) = Setzt den Cursor ein Zeichen zurück. Wenn sich der Cursor bereits am Zeilenanfang befindet, dann ist das Verhalten unspezifiziert.
  • \r (carriage return, dt. Wagenrücklauf) = Setzt den Cursor an den Zeilenanfang
  • \f (form feed) = Setzt den Cursor auf die Startposition der nächsten Seite.
  • \v (vertical tab) = Setzt den Cursor auf die nächste vertikale Tabulatorposition. Wenn der Cursor bereits die letzte Tabulatorposition erreicht hat, dann ist das Verhalten unspezifiziert (wenn eine solche existiert).
  • \" " wird ausgegeben
  • \' ' wird ausgegeben
  • \?  ? wird ausgegeben
  • \\ \ wird ausgegeben
  • \0 ist die Endmarkierung einer Zeichenkette


Jede Escape-Sequenz symbolisiert ein Zeichen auf einer Implementierung und kann in einer Variablen des Typs char gespeichert werden.

Beispiel:

#include <stdio.h>

int main(void)
{
  printf("Der Zeilenumbruch erfolgt\n");
  printf("durch die Escape-Sequenz \\n\n\n");
  printf("Im Folgenden wird ein Wagenrücklauf (carriage return) mit \\r erzeugt:\r");
  printf("Satzanfang\n\n");
  printf("Folgende Ausgabe demonstriert die Funktion von \\b\n");
  printf("12\b34\b56\b78\b9\n");
  printf("Dies ist lesbar\n\0und dies nicht mehr.");      /* erzeugt ggf. eine Compiler-Warnung */
  return 0;
}

Erzeugt auf dem Bildschirm folgende Ausgabe:

 Der Zeilenumbruch erfolgt
 durch die Escape-Sequenz \n

 Satzanfangen wird ein Wagenrücklauf (carriage return) mit \r erzeugt:
 
 Folgende Ausgabe demonstriert die Funktion von \b
 13579
 Dies ist lesbar