Appendix B — printf and scanf formats in full
Chapter 23 taught the minimal set (%d, %s, %%) and chapter 76 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 do not match, it is outside the contract (undefined behaviour) (chapters 48 and 76).
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 — shorter output is padded, longer output is not cut | width = 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 115.1 — The skeleton of output and input conversion specifications
Output conversions — the whole list#
| conversion | argument type | output form | note |
|---|---|---|---|
%d %i | int | a decimal signed integer | in output the two are the same |
%u | unsigned int | a decimal unsigned integer | |
%b %B | unsigned int | binary — %#b puts 0b in front | added in C23 |
%o | unsigned int | octal | attach # and a 0 goes in front |
%x %X | unsigned int | hexadecimal lower and upper case | attach # and 0x/0X goes in front |
%f %F | double | 123.456000 | default precision 6 |
%e %E | double | 1.234560e+02 | exponential notation |
%g %G | double | %f or %e style depending on the exponent (%e when it is below −4 or at least the precision) | it removes trailing zeros |
%a %A | double | 0x1.edp+6 | hexadecimal floating point (C99) — the bits seen exactly |
%c | int (as a character) | one character | the argument comes promoted to int |
%s | char * | the string up to the NUL | the maximum length can be limited by precision |
%p | void * | implementation-defined notation | usually a hexadecimal address |
%n | int * | (no output) | ★ it writes the number of characters printed — avoid it for security |
%% | — | one % character |
Table 115.2 — The full list of output conversions
Flags, width, precision#
| place | notation | meaning |
|---|---|---|
| flag | - | left alignment (the default is right) |
0 | fill the spare places with zeros (ignored together with -) | |
+ | attach a sign even to positives | |
| space | one space before positives | |
# | alternative form — %#b→0b, %#o→0, %#x→0x, %#f→always a decimal point | |
| width | %8d | at least 8 places |
%*d | take the width as an argument: printf("%*d", 8, n) | |
| precision | %.3f | to three decimal places (rounded) |
%.5d | an integer to at least 5 digits (zeros in front) | |
%.4s | a string to at most 4 characters | |
%.*s | the maximum length as an argument: printf("%.*s", len, p) |
Table 115.3 — The places of flags, width and precision
%.*s is a form used often in this book — because a string that carries its length separately (chapters 67 and 105′s views) can be printed as it is, without NUL termination.
One rule has to be observed, 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 no longer matches — the very mismatch seen in chapter 67. 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 67), so this length-modifier letter is itself the contract. Get it wrong and an argument is read from the wrong place as the wrong type (undefined behaviour).
| modifier | conversions used with | output argument | input argument |
|---|---|---|---|
| (none) | d i u o x X | int / unsigned int | int * / unsigned * |
hh | d i u o x X | int (interpreted at char width) | signed char * |
h | d i u o x X | int (interpreted at short width) | short * |
l | d i u o x X | long | long * |
ll | d i u o x X | long long | long long * |
j | d i u o x X | intmax_t | intmax_t * |
z | d i u o x X | size_t | size_t * |
t | d i u o x X | ptrdiff_t | ptrdiff_t * |
L | f e g a | long double | long double * |
l | c / s | wint_t / wchar_t * | wchar_t * |
Table 115.4 — The length modifiers
Formats for the real floating types (float, double) need particular care. In output a float becomes a double by the default argument promotions, so %f alone suffices and %lf means the same. In input there is no promotion, so %f must take a float * and %lf a double *, without fail (chapter 76′s misconception box).
Formats for fixed-width integers#
For a type such as int32_t the underlying type differs by platform, so writing the format by hand can produce a mismatch. 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#
| conversion | what it reads | argument | note |
|---|---|---|---|
%d | a decimal integer | int * | it skips leading whitespace |
%i | an integer (base automatic) | int * | 0x means hex, a leading 0 octal |
%u | unsigned decimal | unsigned * | it accepts a minus sign too and wraps |
%b | a binary integer | unsigned * | added in C23 |
%o %x | octal, hexadecimal | unsigned * | |
%f %e %g | a real | float * | ★ for double it is %lf |
%c | the character as it is | char * | ★ it does not skip whitespace. Give a width and it reads that many |
%s | up to whitespace | char[] | ★ always give a maximum width (%63s) |
%[...] | characters belonging to a set | char[] | %[^,] matches characters that are not a comma. It reads whitespace too |
%p | pointer notation | void ** | 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 115.5 — The full list of input conversions
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 apply only to input#
- One space in the format means “any number of whitespace characters (none is fine too)”. A newline is whitespace too.
- An ordinary character in the format must match the input exactly. If it does not, scanning stops on the spot with a matching failure.
- The return value is the number of items successfully filled. If the input has already ended it is
EOF(a negative value), and that differs in meaning from “the format was wrong” (0). - A failed argument is not touched. Use it without checking the return value and you mistake the previous value for new input (the counterexample in chapter 100 is exactly this).
- Only
%cand%[do not skip whitespace. The accident of a newline left from the previous line going into the next%carises here — put a space in front, as in" %c", and it skips. scanfdoes not report overflow. Giving99999999999to a%dis outside the contract. If a range check is needed, use thestrtolfamily or chapter 106′s scanner.
Return values at a glance#
| function | on success | failure and boundaries |
|---|---|---|
printf fprintf | the number of characters printed | negative |
sprintf | the number of characters written (excluding the NUL) | negative |
snprintf | the number of characters that would have been needed | negative. If the return value is at least the buffer size it was truncated |
scanf sscanf | the number of items filled | 0 (matching or conversion failure) or EOF |
fgets | the buffer pointer | null (end of file or an error) |
fputs puts | a non-negative value | EOF |
Table 115.6 — The return values of the formatted and line I/O functions
Since snprintf’s return value is the number of characters needed, not the number written, the truncation check is need >= (int)sizeof buf, where need holds the return value (chapter 100).
A collection of common mistakes#
| wrong code | what happens | the fix |
|---|---|---|
printf("%d", sz) — a size_t | the width does not match on 64-bit | %zu |
printf("%s", 42) | it reads an integer as an address — usually a crash | %d |
printf(user) | the format string vulnerability (chapter 76) | printf("%s", user) |
scanf("%d", n) | a value was passed, not an address | &n |
scanf("%s", buf) | it writes past the boundary | specify a width, as in %63s |
scanf("%f", &d) — a double | only half is filled | %lf |
scanf("%c", &c) in succession | it reads the leftover newline | " %c" |
| ignoring the return value | failure passes by quietly | check the item count |
using %n | it becomes a passage for memory writes | do not use it |
Table 115.7 — Common formatting mistakes and their fixes