17. Einfache Ein-/Ausgabe

printf(format, arg1, ...)

gibt die Argumente gemäß dem Formatstring auf die Standardausgabe (das ist ohne weitere Vorkehrungen der Bildschirm) aus. Die Formatstrings sehen weitgehend wie in die ersten Operanden für den String-Prozentoperator in Python aus. Für die zusätzlichen Typen von C gibt es einige Formatcodes mehr:

  • %u Unsigned integer wird dezimal ausgegeben
  • %c Ein char (oder sonstiger integer) wird als Zeichen ausgegeben
  • %l. Bei Ausgabe von long ints muss ein l-Modifier vor dem Formatcode stehen

Erschöpfend Auskunft über weitere Möglichkeiten gibt man 3 printf oder das glibc-Manual.

Ein paar Beispiele:

char user[]="msdemlei";
char format[]="%d-%d=%d";
int pagecount=5;
printf("%d --- ", pagecount);
printf("%10s hat %4d Seiten gedruckt\n", user, pagecount);
printf("Wahr ist: 129=%d%d", 1, 29);
printf(format, 5, 2, 5-2);

printf weiß nichts über die Typen seiner Argumente – ob das floats, chars, oder longs sind, muss allein aus dem Formatstring hervorgehen (gcc -Wall warnt allerdings, wenn man da groben Unfug versucht). Die Regeln zur Promotion von Argumenten (zu denen wir noch kommen werden), verhindern zwar häufig echte Katastrophen, dennoch werden Werte je nach Formatcode ganz verschieden ausgegeben. Das Programm

#include <stdio.h>

int main(void)
{
  int i=0x400+'d';
  unsigned long l=(1<<31)+'c';

  printf("%d, %c, %ld, %lu\n", i, i, l, l);
  return 0;
}

gibt z.B. 

1124, d, -2147483549, 2147483747

aus.

Wilde Dinge passieren, wenn mehr Formatcodes als Argumente vorhanden sind – das kann bis zum Programmabsturz reichen.

  printf("%s %s %s %s\n");

kann z.B. einfach sinnlosen Quatsch ausgeben, aber auch einen Segmentation Fault auslösen.

Nochmal hinweisen möchte ich auf die Möglichkeit der I/O-Umleitung in allen ernstzunehmenden shells:

examples> meinprogramm > ausgabe.txt < eingabe.txt

schreibt alles, was meinprogramm ausgibt, in die Datei ausgabe.txt und nimmt die Eingabe aus eingabe.txt.

Nebenbei: Wer hexadezimal drucken möchte, kann das mit dem Formatcode %x tun – in C wie in Python.

Die Formatstrings gelten im Wesentlichen auch fürs Einlesen mit scanf. Aber: Skalare Argumente für scanf müssen ein & haben (warum, sehen wir später).

scanf("%d", &zahl);
 liest einen Integer,
scanf("%s", str);
 einen String (aber: Dafür nicht scanf nehmen, sondern fgets).

Bei scanf ist es sehr wichtig, die Längencodes korrekt zu verwenden, weil die oben erwähnte Promotion der Argumente hier nicht greift.

Das Programm

#include <stdio.h>

int main(void)
{
  int i;
  short s;
  double d;
  float f;

  scanf("%d %hd %lf %f", &i, &s, &d, &f);
  printf("%d %d %f %f\n", i, s, d, f);
  scanf("%hd %d %f %lf", &i, &s, &d, &f); /* Falsch! */
  printf("%d %d %f %f\n", i, s, d, f);
  return 0;
}

gibt mit der Eingabe

500000 50000 3.1415 2.71
500000 5000 3.1415 2.71

heute und auf einer i386-Architektur

500000 -15536 3.141500 2.710000
458752 5000 3.141499 584860314976236483507101602781593600.000000

aus. Abgesehen davon, dass nicht das rauskommt, was wir wollen, kann die zweite Zeile auch zum Absturz führen und wird auf verschiedenen Maschinen verschiedene Dinge tun. Unter MacOS X/PPC beispielsweise gibt das gleiche Programm

500000 -15536 3.141500 2.710000
-1591697120 0 50.112022 2.088750

aus.

Das Einlesen nichttrivialer Geschichten mit scanf ist etwas kryptisch, weil es allerlei Regeln gibt, wie mit blanks umgegangen wird und auch Schablonen angegeben werden können.

Ein Beispiel:

#include <stdio.h>

int main(void)
{
  int zahl, res;

  res = scanf("Zahl: %d", &zahl);
  printf("%d, %d\n", zahl, res);
  return 0;
}
Eingabe            Ausgabe
Quatsch            -1073743480, 0
Zahl:3             3, 1
Zahl:      8       8, 1

Leerzeichen werden also überlesen, der Rückgabewert von scanf ist die Zahl der tatsächlich gelesenen Dinge. Variablen, die nicht gelesen werden, werden nicht verändert – in diesem Fall bleibt zahl uninitialisiert.

Unformatierte E/A: fgets(str, n, stdin); liest eine bis zu n Zeichen lange Zeile nach str, fputs(str, stdout); gibt einen String aus.

fgetc(stdin); liest ein Zeichen und gibt EOF zurück, wenn es keine mehr gibt (Falle: EOF ist nicht als char zu repräsentieren), fputc(c, stdout) gibt ein Zeichen aus.

Beispiel:

#include <stdio.h>

int main(void)
{ int c; /* int ist wichtig! */

  c = fgetc(stdin);
  if (c==EOF) {
    printf("Ende der Eingabe.");
  }
  return 0;
}

Übungen zu diesem Abschnitt

Ihr solltet euch wenigstens an den rötlich unterlegten Aufgaben versuchen

(1)

Probiert folgendes Programm, macht Eingaben und beobachtet die Ausgabe. Macht das so lange, bis ihr versteht, was der Rechner tut.

#include <stdio.h>

int main(void)
{
  char str[80];
  int res;

  res = scanf(" %s", str);
  printf("%d %s\n", res, str);
  res = scanf("%s ", str);
  printf("%d %s\n", res, str);
  res = scanf(" %s ", str);
  printf("%d %s\n", res, str);
  return 0;
}

(2)

Erklärt das -15536 und die zweite Zeile in der i386-Ausgabe des Beispiels mit dem misslungenen Formatstring.


Markus Demleitner

Copyright Notice