printf formats values into text on stdout; scanf parses text from stdin into variables. Both are driven by format specifiers that must match the argument types exactly.
Format specifiers
| Specifier | Type (printf) | Type (scanf) | Example output |
|---|---|---|---|
%d / %i | int | int * | -42 |
%u | unsigned int | unsigned * | 42 |
%ld / %lld | long / long long | pointer to same | 9000000000 |
%zu | size_t | size_t * | 8 |
%f | double (and float, promoted) | float * | 3.140000 |
%lf | double | double * (required) | 3.140000 |
%e / %g | double scientific / shortest | same as %f | 3.14e+00 / 3.14 |
%c | int (a char) | char * | A |
%s | char * (NUL-terminated) | char * (array) | hello |
%x / %X / %o | unsigned hex / HEX / octal | unsigned * | ff / FF / 377 |
%p | void * | void ** | 0x16d… |
%% | a literal % | — | % |
Width, precision and flags
#include <stdio.h>
int main(void) {
int n = 42;
double pi = 3.14159265;
printf("[%d] [%5d] [%-5d] [%05d] [%+d]\n", n, n, n, n, n);
printf("[%f] [%.2f] [%8.3f] [%e] [%g]\n", pi, pi, pi, pi, pi);
printf("[%x] [%X] [%#x] [%o] [%#o]\n", 255, 255, 255, 8, 8);
printf("[%s] [%10s] [%-10s] [%.3s]\n", "hello", "hi", "hi", "truncate");
printf("[%*d] [%.*f]\n", 6, n, 1, pi); /* width / precision from args */
printf("100%% done, char %c, code %d\n", 'A', 'A');
return 0;
}[42] [ 42] [42 ] [00042] [+42]
[3.141593] [3.14] [ 3.142] [3.141593e+00] [3.14159]
[ff] [FF] [0xff] [10] [010]
[hello] [ hi] [hi ] [tru]
[ 42] [3.1]
100% done, char A, code 65Reading input with scanf
#include <stdio.h>
int main(void) {
int age;
double height;
char name[20];
/* scanf needs ADDRESSES: &age, &height — but an array name already is one */
int got = scanf("%19s %d %lf", name, &age, &height);
printf("read %d items: %s is %d years, %.2f m\n", got, name, age, height);
return 0;
}read 3 items: Asha is 21 years, 1.68 mscanf returns the number of items successfully converted (or EOF). Always check it — on bad input the variables are left untouched:
#include <stdio.h>
int main(void) {
int x = -1;
if (scanf("%d", &x) != 1) {
printf("not a number, x still %d\n", x);
} else {
printf("x = %d\n", x);
}
return 0;
}not a number, x still -1Reading a whole line: fgets
#include <stdio.h>
#include <string.h>
int main(void) {
char line[64];
while (fgets(line, sizeof line, stdin) != NULL) {
line[strcspn(line, "\n")] = '\0'; /* strip the trailing newline */
printf("got \"%s\" (%zu chars)\n", line, strlen(line));
}
return 0;
}got "hello world" (11 chars)
got "second line" (11 chars)Character I/O
#include <stdio.h>
#include <ctype.h>
int main(void) {
int c; /* int, not char: must hold EOF (-1) too */
while ((c = getchar()) != EOF) {
putchar(toupper(c));
}
return 0;
}SHOUT THISPitfalls
| Mistake | Consequence | Fix |
|---|---|---|
scanf("%d", x) — missing & | Writes to a garbage address → crash | scanf("%d", &x) |
scanf("%s", buf) with no width | Buffer overflow on long input | scanf("%19s", buf) for char buf[20] |
%f to read a double | Writes only 4 bytes → wrong value | Use %lf in scanf |
gets(buf) | Unbounded — removed in C11 | fgets(buf, sizeof buf, stdin) |
scanf("%d") then fgets | fgets reads the leftover \n → empty line | Consume the rest of the line first |
printf(user_input) | Format-string vulnerability | printf("%s", user_input) |
Why is getchar's result stored in an int?
Why does printf use %f for both float and double, but scanf needs %lf for double?