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
| Angabe | Bedeutung | printf-Argument | scanf-Argument |
|---|---|---|---|
%d | vorzeichenbehaftete Dezimalzahl | int | int * |
%i | vorzeichenbehaftete Ganzzahl | int (wie %d) | int * (akzeptiert auch 0x, 0-Oktal) |
%u | vorzeichenlose Dezimalzahl | unsigned int | unsigned int * |
%f | Dezimale Gleitkommazahl | double | float * |
%F | wie %f, INF/NAN groß | double | - |
%e / %E | wissenschaftliche Notation | double | float * |
%g / %G | kürzere von %f / %e | double | float * |
%a / %A | hexadezimale Gleitkommazahl | double | float * |
%c | einzelnes Zeichen | int (befördertes char) | char * |
%s | Zeichenkette | char * ('\0'-terminiert) | char * (Puffer) |
%p | Zeigeradresse | void * | void ** |
%x / %X | vorzeichenlos hexadezimal | unsigned int | unsigned int * |
%o | vorzeichenlos oktal | unsigned int | unsigned int * |
%n | bisher geschriebene Zeichen ablegen | int * | int * |
%% | ein wörtliches % | keines | passt 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.
| Modifikator | Mit d i (printf / scanf) | Mit u x o (printf / scanf) |
|---|---|---|
hh | int (aus signed char) / signed char * | unsigned int (aus unsigned char) / unsigned char * |
h | int (aus short) / short * | unsigned int (aus unsigned short) / unsigned short * |
| (keiner) | int / int * | unsigned int / unsigned int * |
l | long / long * | unsigned long / unsigned long * |
ll | long long / long long * | unsigned long long / unsigned long long * |
z | vorzeichenbehafteter Größentyp / Zeiger darauf | size_t / size_t * |
j | intmax_t / intmax_t * | uintmax_t / uintmax_t * |
t | ptrdiff_t / ptrdiff_t * | vorzeichenloses Gegenstück zu ptrdiff_t |
Und für die Gleitkomma- und Zeichenfamilien:
| Modifikator | Mit | printf-Typ | scanf-Typ |
|---|---|---|---|
| (keiner) | f e g a | double | float * |
l | f e g a | double (akzeptiert, ohne Wirkung) | double * |
L | f e g a | long double | long double * |
l | c s | wint_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.
| Flag | Wirkung | Beispiel | Ausgabe |
|---|---|---|---|
- | hinter dem Wert auffüllen statt davor | %-6d| bei 42 | 42 | |
+ | bei vorzeichenbehafteten Werten immer ein Vorzeichen ausgeben | %+d bei 42 | +42 |
| Leerzeichen | ein Leerzeichen dort, wo ein + stünde | % d bei 42 | 42 |
0 | mit Nullen statt Leerzeichen auffüllen | %06d bei 42 | 000042 |
# | alternative Form: 0x bei %x, 0 bei %o, Punkt bei %g behalten | %#x bei 255 | 0xff |
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:
| Umwandlung | Genauigkeit bedeutet |
|---|---|
%f %e | Stellen nach dem Komma (Voreinstellung 6) |
%g | signifikante Stellen insgesamt (Voreinstellung 6) |
%s | maximale Zeichenzahl zur Ausgabe |
%d %i %u %x %o | Mindestanzahl 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:
| Situation | printf | scanf |
|---|---|---|
double-Wert | %f (float wird zu double befördert) | %lf - %f meint float * |
| Breite | Mindestfeldgröße, füllt auf | maximal verbrauchte Eingabe, schneidet ab |
| Argumente | Werte | Zeiger - &x oder ein Arrayname |
| Ein Leerzeichen im Format | gibt 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.