C Format Specifier Cheat Sheet for printf and scanf
printf and scanf trust the format string: if a conversion does not match its argument, the behaviour is undefined even when the output looks right. Choose a C type below to see the exact conversion for each function, the GCC -Wformat warning a wrong choice produces, and a short program you can run.
The one rule to remember
printf receives values after default argument promotion, so float arrives as double and char or short arrive as int. scanf receives pointers, which are never promoted, so its conversion must describe the exact object type: %f for float *, %lf for double *.
Interactive printf and scanf specifier selector
Pick printf or scanf and a C type, try any conversion, and see whether GCC's -Wformat check accepts it. printf receives promoted values (float becomes double); scanf receives addresses, so float * needs %f and double * needs %lf.
printf("%f", value);
Correct match
printf takes double with %f; %lf is accepted since C99.
Recommended
%f
Passed as
double
-Wformat preview · GCC, x86-64 Linux
$ gcc -std=c17 -Wall -Wextra main.c -o main
(no -Wformat warning)
Safe printf snippet for double
#include <stdio.h>
int main(void) {
double pi = 3.14159265358979;
printf("pi = %.5f\n", pi);
return 0;
}
stdout: pi = 3.14159
Length modifiers and width/precision explorer
Modifier
printf argument
scanf target
hh
signed/unsigned char (value promoted to int)
signed char * / unsigned char *
h
short (value promoted to int)
short * / unsigned short *
(none)
int, unsigned int, double (float promoted)
int *, unsigned int *, float *
l
long, unsigned long; no effect on %f
long *, unsigned long *, double * (%lf)
ll
long long, unsigned long long
long long *, unsigned long long *
z
size_t
size_t *
t
ptrdiff_t
ptrdiff_t *
j
intmax_t, uintmax_t
intmax_t *, uintmax_t *
L
long double
long double *
Format
Argument
Output
Meaning
[%5d]
42
[ 42]
Minimum width 5, right-aligned.
[%-5d]
42
[42 ]
- flag left-aligns.
[%05d]
42
[00042]
0 flag pads with zeros.
[%.2f]
3.14159
[3.14]
Precision: 2 digits after the point.
[%8.3f]
3.14159
[ 3.142]
Width 8, precision 3 (rounded).
[%.3s]
"format"
[for]
Precision on %s caps characters printed.
[%+d]
42
[+42]
+ flag always prints the sign.
[%#x]
255
[0xff]
# flag adds the 0x prefix.
For scanf, a width is a maximum: %63s reads at most 63 characters into char buf[64], leaving room for the terminating NUL.
printf vs scanf: type promotion reference
Recommended conversions for 64-bit Linux (LP64). The PRI and SCN macros from <inttypes.h> expand to the right conversion on every platform.
printf is variadic, so a float argument undergoes default argument promotion to double. %f and %lf (accepted since C99) both print a double. scanf receives pointers, which are never promoted, so it needs %f for float * and %lf for double *.
Which format specifier prints a size_t value?
Use %zu, added in C99. size_t is unsigned long on 64-bit Linux but unsigned long long on 64-bit Windows, so %lu or %llu is not portable.
Why does scanf need & for an int but not for a char array?
scanf stores into objects, so it needs their addresses: &count for an int. An array name such as buf already converts to a pointer to its first element, so scanf("%63s", buf) needs no &. Give %s a width one less than the buffer size.
What does -Wformat check?
When the format string is a literal, GCC and Clang compare each conversion with the type of its argument; -Wall enables -Wformat. A mismatch is undefined behaviour even when the program appears to print the right value.