Format Specifiers
printf and scanf (and their many relatives like fprintf, sprintf, and sscanf) don't know the types of their arguments the way a normal C function does — they figure out how to interpret each argument from a format string containing %-prefixed placeholders called format specifiers. Getting these right is essential: the compiler generally can't check them for you.
Common format specifiers
Specifier | Type | Meaning |
|---|---|---|
| int | Signed decimal integer |
| unsigned int | Unsigned decimal integer |
| double (printf) / float* (scanf) | Decimal floating-point |
| double | Same as |
| char (widened to int) | A single character |
| char * | A null-terminated string |
| unsigned int | Hexadecimal (lower/upper case letters) |
| unsigned int | Octal |
| void * | A pointer value, implementation-defined format |
| (none) | A literal percent sign |
#include <stdio.h>
int main(void) {
int age = 30;
unsigned int count = 5u;
double price = 19.99;
char grade = 'A';
char *name = "Ada";
int hex_value = 255;
printf("Age: %d\n", age);
printf("Count: %u\n", count);
printf("Price: %f\n", price);
printf("Grade: %c\n", grade);
printf("Name: %s\n", name);
printf("Hex: %x, Octal: %o\n", hex_value, hex_value);
printf("Pointer: %p\n", (void *)name);
printf("100%% done\n");
return 0;
}printf vs scanf for floating-point
A quirk that surprises a lot of beginners: printf("%f", someDouble) and printf("%lf", someDouble) behave identically, because when a float is passed to a variadic function like printf, it's automatically promoted to double — so %f already expects a double. But scanf receives a pointer, and pointers aren't promoted, so scanf truly does distinguish %f (expects float *) from %lf (expects double *). Mixing these up with scanf is a classic, hard-to-spot bug.
float f;
double d;
scanf("%f", &f); /* correct: %f for a float* */
scanf("%lf", &d); /* correct: %lf for a double* */
/* scanf("%f", &d); WRONG -- undefined behavior, writes only 4 bytes into an 8-byte double */Length modifiers
Length modifiers are prefixes placed before the conversion letter to target a different-sized integer type:
Modifier | Example | Targets |
|---|---|---|
|
| short int |
|
| signed char |
|
| long int |
|
| long long int |
|
| size_t (the type returned by |
|
| intmax_t |
|
| ptrdiff_t |
#include <stdio.h>
int main(void) {
long long population = 8000000000LL;
size_t array_size = sizeof(population);
printf("Population: %lld\n", population);
printf("Size in bytes: %zu\n", array_size);
return 0;
}