Proven C Book한국어 GitHub

Appendix B — printf and scanf formats in full

Chapter 22 learned the minimal set (%d, %s, %%) and chapter 61 dissected the format string. This appendix is a place for looking things up — read only the line you need. One rule goes first: if the format and the argument’s type go out of step it is outside the contract (undefined behaviour) (chapters 47 and 61).

The skeletons of a format specification

Output and input have similar syntax but different meanings. First the two skeletons side by side.

output (the printf family)input (the scanf family)
% [flags] [width] [.precision] [length] conversion% [*] [max width] [length] conversion
width = the minimum number of characters — it fills if short and does not cut if overwidth = the maximum number of characters to read — the means of protecting the buffer
there is a precision (.)there is no precision
* takes the width or precision as an argument* reads but does not store (suppression)

Table 99.1

Output conversions — the whole list

conversionargument typeoutput formnote
%d %iinta decimal signed integerin output the two are the same
%uunsigned inta decimal unsigned integer
%ounsigned intoctalattach # and a 0 goes in front
%x %Xunsigned inthexadecimal lower and upper caseattach # and 0x/0X goes in front
%f %Fdouble123.456000default precision 6
%e %Edouble1.234560e+02exponential notation
%g %Gdoubleautomatically the shorterit removes trailing zeros
%a %Adouble0x1.edp+6hexadecimal floating point (C99) — the bits seen exactly
%cint (as a character)one characterthe argument comes promoted to int
%schar *the string up to the NULthe maximum length can be limited by precision
%pvoid *implementation-defined notationusually a hexadecimal address
%nint *(no output)★ it writes the number of characters printed — not used, for security
%%one % character

Table 99.2

Flags, width, precision

placenotationmeaning
flag-left alignment (the default is right)
0fill the spare places with zeros (ignored together with -)
+attach a sign even to positives
spaceone space before positives
#alternative form — %#o0, %#x0x, %#f→always a decimal point
width%8dat least 8 places
%*dtake the width as an argument: printf("%*d", 8, n)
precision%.3fto three decimal places (rounded)
%.5dan integer to at least 5 digits (zeros in front)
%.4sa string to at most 4 characters
%.*sthe maximum length as an argument: printf("%.*s", len, p)

Table 99.3

%.*s is a form used often in this book — because a string that carries its length separately (chapters 58 and 90′s views) can be printed as it is, without NUL termination.

One thing must be kept, though. The width or precision argument that * takes is of type int. Pass a length carried as a size_t (as views mostly do) straight in and the variadic argument’s type goes out of step — the very mismatch seen in chapter 58. The canonical form checks the length and then casts.

if (v.size <= INT_MAX)
    printf("%.*s", (int)v.size, (const char *)v.ptr);

Length modifiers — the place that tells the type’s width

Type information does not ride along into variadic arguments (chapter 58), so this letter is itself the contract. Get it wrong and the stack is read wrongly.

modifierconversions used withoutput argumentinput argument
(none)d i u o x Xint / unsigned intint * / unsigned *
hhd i u o x Xint (interpreted at char width)signed char *
hd i u o x Xint (interpreted at short width)short *
ld i u o x Xlonglong *
lld i u o x Xlong longlong long *
jd i u o x Xintmax_tintmax_t *
zd i u o x Xsize_tsize_t *
td i u o x Xptrdiff_tptrdiff_t *
Lf e g along doublelong double *
lc / swint_t / wchar_t *wchar_t *

Table 99.4

Reals need particular care. In output a float becomes a double by the default promotion, so %f alone suffices and %lf means the same. In input there is no promotion, so %f must be float * and %lf double * without fail (chapter 61′s misconception box).

Formats for fixed-width integers

For a type such as int32_t the real type differs by platform, so writing the format by hand can go out of step. The standard put macros in <inttypes.h>.

#include <inttypes.h>

uint64_t total = 1234567890123ULL;
printf("total = %" PRIu64 "\n", total);   /* it is string concatenation */

int32_t n = 0;
sscanf(line, "%" SCNd32, &n);

The naming rule is simple — for output PRI + conversion + width (PRId32, PRIu64, PRIx16), for input SCN + conversion + width (SCNd32, SCNu64). Besides 8, 16, 32, 64, the width place also takes MAX (PRIdMAX) and PTR (PRIxPTR).

Input conversions — the whole list

conversionwhat it readsargumentnote
%da decimal integerint *it skips leading whitespace
%ian integer (base automatic)int *0x means hex, a leading 0 octal
%uunsigned decimalunsigned *it accepts a minus sign too and wraps
%o %xoctal, hexadecimalunsigned *
%f %e %ga realfloat *★ for double it is %lf
%cthe character as it ischar *★ it does not skip whitespace. give a width and it reads that many
%sup to whitespacechar[]★ always give a maximum width (%63s)
%[...]characters belonging to a setchar[]%[^,] is characters that are not a comma. it reads whitespace too
%ppointer notationvoid **only what was printed with %p is read back
%n(reads nothing)int *it writes the number of characters consumed so far
%%the % character

Table 99.5

Insert the suppression character * and it reads without storing — sscanf(s, "%*d %d", &n) throws away the first number and takes only the second. A suppressed item is not counted in the return value either.

The set specifier is close to a small tool of its own. %[abc] gathers only a, b and c; %[^,\n] gathers characters that are neither a comma nor a newline. Range notation (%[a-z]) is widely used but not guaranteed by the standard.

Rules that hold in input alone

The return values organised

functionon successfailure and boundaries
printf fprintfthe number of characters printednegative
sprintfthe number of characters written (excluding the NUL)negative
snprintfthe number of characters that would have been needednegative. if the return value is at least the buffer size it was truncated
scanf sscanfthe number of items filled0 (matching or conversion failure) or EOF
fgetsthe buffer pointernull (end of file or an error)
fputs putsa non-negative valueEOF

Table 99.6

snprintf’s return value rule matters especially — being the number of characters needed, not the number written, the truncation check is need >= (int)sizeof buf (chapter 85).

A collection of common mistakes

wrong codewhat happensthe mend
printf("%d", sz) — a size_tthe width goes out of step on 64-bit%zu
printf("%s", 42)it reads an integer as an address — collapse%d
printf(user)the format string vulnerability (chapter 61)printf("%s", user)
scanf("%d", n)a value was passed, not an address&n
scanf("%s", buf)it writes past the boundaryspecify a width, as in %63s
scanf("%f", &d) — a doubleonly half is filled%lf
scanf("%c", &c) in successionit reads the leftover newline" %c"
ignoring the return valuefailure passes by quietlycheck the item count
using %nit becomes a passage for memory writesdo not use it

Table 99.7