Menu

Formatangaben in C: die vollständige printf- und scanf-Referenz

Jede C-Formatangabe an einem Ort - was %d, %s, %c, %f, %p und der Rest bedeuten, die Längenmodifikatoren l, ll, h und z, Breite, Genauigkeit und Flags, und welche sich zwischen printf und scanf unterscheiden.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Eine Formatangabe sagt printf, wie ein Wert zu Text wird, und scanf, wie Text zu einem Wert wird. Es ist der eine Teil von C, in dem der Compiler dir standardmäßig nicht helfen kann - die Argumenttypen sind für die Funktion unsichtbar -, diese Seite ist also die Tabelle zum Nachschlagen.

Die allgemeine Form einer Angabe ist:

%[Flags][Breite][.Genauigkeit][Länge]Umwandlung

Nur das % und der Umwandlungsbuchstabe sind erforderlich. %-8.2lf benutzt alle fünf Teile.

Die Umwandlungstabelle

AngabeBedeutungprintf-Argumentscanf-Argument
%dvorzeichenbehaftete Dezimalzahlintint *
%ivorzeichenbehaftete Ganzzahlint (wie %d)int * (akzeptiert auch 0x, 0-Oktal)
%uvorzeichenlose Dezimalzahlunsigned intunsigned int *
%fDezimale Gleitkommazahldoublefloat *
%Fwie %f, INF/NAN großdouble-
%e / %Ewissenschaftliche Notationdoublefloat *
%g / %Gkürzere von %f / %edoublefloat *
%a / %Ahexadezimale Gleitkommazahldoublefloat *
%ceinzelnes Zeichenint (befördertes char)char *
%sZeichenkettechar * ('\0'-terminiert)char * (Puffer)
%pZeigeradressevoid *void **
%x / %Xvorzeichenlos hexadezimalunsigned intunsigned int *
%ovorzeichenlos oktalunsigned intunsigned int *
%nbisher geschriebene Zeichen ablegenint *int *
%%ein wörtliches %keinespasst auf ein wörtliches %
%[...]Zeichenmenge (nur scanf)-char *

Zwei Einträge verdienen eine Warnung. %n schreibt in den Speicher, statt auszugeben, und ist der Mechanismus hinter der Formatzeichenketten-Schwachstelle - lass niemals Benutzertext in eine Formatzeichenkette gelangen. Und %p verlangt ein void *; ein int * direkt zu übergeben ist undefiniert, auch wenn jeder reale Compiler die Adresse trotzdem ausgibt.

Längenmodifikatoren

Der Längenmodifikator steht zwischen der Genauigkeit und dem Umwandlungsbuchstaben und sagt, wie groß das Argument tatsächlich ist.

Vorzeichenbehaftete Umwandlungen (d i) und vorzeichenlose (u x o) nehmen unterschiedliche Typen - %lu will ein unsigned long, kein long, und das falsch zu machen ist undefiniertes Verhalten, kein kosmetischer Schnitzer.

ModifikatorMit d i (printf / scanf)Mit u x o (printf / scanf)
hhint (aus signed char) / signed char *unsigned int (aus unsigned char) / unsigned char *
hint (aus short) / short *unsigned int (aus unsigned short) / unsigned short *
(keiner)int / int *unsigned int / unsigned int *
llong / long *unsigned long / unsigned long *
lllong long / long long *unsigned long long / unsigned long long *
zvorzeichenbehafteter Größentyp / Zeiger daraufsize_t / size_t *
jintmax_t / intmax_t *uintmax_t / uintmax_t *
tptrdiff_t / ptrdiff_t *vorzeichenloses Gegenstück zu ptrdiff_t

Und für die Gleitkomma- und Zeichenfamilien:

ModifikatorMitprintf-Typscanf-Typ
(keiner)f e g adoublefloat *
lf e g adouble (akzeptiert, ohne Wirkung)double *
Lf e g along doublelong double *
lc swint_t / wchar_t *wchar_t *

Die Kombinationen, die du tatsächlich tippst:

%zu ist die Angabe, die man sich merken sollte. sizeof, strlen und jede Größe aus der Standardbibliothek liefern size_t, das auf einem 64-Bit-System 8 Bytes hat, während int 4 hat. printf("%d", strlen(s)) ist ein echter Bug, der auf manchen Plattformen zu funktionieren scheint - nimm %zu.

Die Modifikatoren hh und h existieren hauptsächlich für scanf, wo die Größe des Ziels zählt. In printf wird ein short vor dem Aufruf zu int befördert, %d würde es also ohnehin korrekt ausgeben; %hd dokumentiert lediglich die Absicht.

Typen fester Breite

Für <stdint.h>-Typen wie int32_t sind die portablen Angaben Makros aus <inttypes.h>:

PRId32 expandiert zu den für deine Plattform richtigen Buchstaben, und benachbarte Zeichenkettenliterale werden aneinandergehängt, weshalb das Format in drei Teile zerfällt. Es ist hässlich; die Alternative - nach long long casten und %lld benutzen - ist oft lesbarer und immer korrekt.

Flags

Flags stehen unmittelbar hinter dem % und ändern die Darstellung. Sie gelten nur für printf.

FlagWirkungBeispielAusgabe
-hinter dem Wert auffüllen statt davor%-6d| bei 4242 |
+bei vorzeichenbehafteten Werten immer ein Vorzeichen ausgeben%+d bei 42+42
Leerzeichenein Leerzeichen dort, wo ein + stünde% d bei 42 42
0mit Nullen statt Leerzeichen auffüllen%06d bei 42000042
#alternative Form: 0x bei %x, 0 bei %o, Punkt bei %g behalten%#x bei 2550xff

Flags lassen sich kombinieren: %-+8.2f bedeutet Mindestbreite 8, zwei Nachkommastellen, immer mit Vorzeichen, hinter der Zahl aufgefüllt. - schlägt 0, wenn du beide schreibst.

Breite und Genauigkeit

Breite ist eine Mindestfeldgröße - die Ausgabe wird aufgefüllt, um sie zu erreichen, und nie abgeschnitten, um hineinzupassen. Genauigkeit (hinter einem Punkt) bedeutet für jede Familie etwas anderes:

UmwandlungGenauigkeit bedeutet
%f %eStellen nach dem Komma (Voreinstellung 6)
%gsignifikante Stellen insgesamt (Voreinstellung 6)
%smaximale Zeichenzahl zur Ausgabe
%d %i %u %x %oMindestanzahl Ziffern, mit Nullen aufgefüllt

Ein * anstelle einer der Zahlen holt sie aus einem int-Argument, das vor dem Wert selbst gelesen wird. So baust du Tabellen, deren Spaltenbreiten zur Laufzeit berechnet werden.

In scanf hat die Breite eine andere und viel wichtigere Aufgabe: Sie begrenzt, wie viel Eingabe eine Umwandlung verbraucht, und das ist das Einzige, was zwischen %s und einem Pufferüberlauf steht.

Wo printf und scanf sich unterscheiden

Sie sehen gleich aus und widersprechen sich an vier Stellen. Jede davon ist eine aktive Fehlerquelle:

Situationprintfscanf
double-Wert%f (float wird zu double befördert)%lf - %f meint float *
BreiteMindestfeldgröße, füllt aufmaximal verbrauchte Eingabe, schneidet ab
ArgumenteWerteZeiger - &x oder ein Arrayname
Ein Leerzeichen im Formatgibt ein Leerzeichen ausüberspringt beliebigen Leerraum

Probiere 2.5 hallo. Das Format, das sie liest, und das Format, das sie ausgibt, benutzen für dasselbe double verschiedene Buchstaben, und die 15 in %15s deckelt die Eingabe auf 15 Zeichen plus Terminator - genau die Größe von word.

Die scanf-Zeichenmenge

%[...] liest eine Menge von Zeichen statt eines durch Leerraum begrenzten Wortes. Es ist die Art, mit scanf eine ganze Zeile zu lesen, wobei fgets das bessere Werkzeug bleibt:

[^\n] bedeutet „alle Zeichen außer einem Zeilenumbruch". %[0-9] würde nur Ziffern lesen, %[abc] nur diese drei Buchstaben. Anders als %s überspringt eine Zeichenmenge keinen führenden Leerraum, ein verirrter Zeilenumbruch im Puffer lässt sie also überhaupt nichts treffen.

Es falsch zu machen ist undefiniertes Verhalten

printf kann den wirklichen Typ deines Arguments nicht sehen. Es liest so viele Bytes, wie die Angabe impliziert, gedeutet so, wie die Angabe es sagt. Eine Fehlpaarung ist also kein Formatierungsschluckauf - es ist das Lesen von Speicher als das Falsche:

printf("%d\n", 3.5);        /* gibt Müll aus: liest die Bits eines double als int */
printf("%s\n", 42);         /* behandelt 42 als Adresse - Absturz */
printf("%d\n", strlen(s));  /* size_t als int gelesen - auf 64 Bit falsch */
printf("%f\n", 3);          /* int als double gelesen - Müll */

Die Verteidigung kostet ein Flag. Kompiliere mit -Wall, und GCC prüft jede wörtliche Formatzeichenkette gegen ihre Argumente:

gcc -Wall -Wextra program.c -o program

Es meldet jede der vier obigen Zeilen zur Übersetzungszeit. Das ist die wertvollste Warnung in C, und im Editor oben ist sie standardmäßig an.

Für die praktische Seite - ausgerichtete Tabellen, Gleitkommazahlen lesbar ausgeben, Eingaben ohne den klassischen Zeilenumbruch-Bug lesen - siehe printf und scanf.

Häufig gestellte Fragen

Was bedeutet %d in C?

%d ist die Umwandlungsangabe für eine vorzeichenbehaftete Dezimalzahl - ein int. In printf gibt es das Argument als Ziffern zur Basis 10 aus; in scanf liest es Ziffern zur Basis 10 in ein int *. %i bedeutet in printf dasselbe, in scanf akzeptiert %i aber auch 0x-Hexadezimal und Oktal mit führender Null.

Was ist der Unterschied zwischen %f und %lf in C?

In printf gibt es keinen praktischen Unterschied: float-Argumente werden zu double befördert, %f gibt also beide aus, und C99 akzeptiert %lf als Synonym. In scanf ist der Unterschied entscheidend - %f schreibt in ein float * (4 Bytes) und %lf in ein double * (8 Bytes). %f für ein double zu benutzen beschädigt Speicher.

Wie gebe ich in C ein size_t oder ein long long aus?

%zu für size_t (was sizeof und strlen liefern) und %lld / %llu für long long / unsigned long long. %d dafür zu benutzen ist undefiniertes Verhalten - es funktioniert zufällig auf 32-Bit-Systemen und spinnt auf 64-Bit-Systemen, weshalb der Bug so lange überlebt.

Was passiert, wenn man in C die falsche Formatangabe benutzt?

Es ist undefiniertes Verhalten, kein Rundungsfehler. printf liest das Argument als das, was die Angabe behauptet, printf("%d", 3.5) gibt also Müll aus und printf("%s", 42) folgt der Zahl 42 als Adresse und stürzt meist ab. Kompiliere mit -Wall, wodurch GCC Formatzeichenketten gegen die Argumente prüft.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S