scanf is the first input function most C programmers learn and one of the last they fully understand. Its failure modes are silent — it returns without reading anything and leaves garbage in your variables. Here is what is actually happening.
How scanf Parses Input
#include <stdio.h>
int main() {
int n;
printf("Enter a number: ");
int result = scanf("%d", &n);
if (result == 1) {
printf("You entered: %dn", n);
} else {
printf("scanf failed — read %d itemsn", result);
}
return 0;
}
scanf returns the number of items successfully read and assigned. Always check this return value. If the user types “abc” instead of a number, scanf returns 0, leaves n uninitialized, and the “abc” stays in the input buffer.
The Input Buffer Problem
#include <stdio.h>
int main() {
int a, b;
printf("Enter two numbers: ");
scanf("%d", &a);
scanf("%d", &b);
printf("Sum: %dn", a + b);
return 0;
}
If the user types 5 abc, the first scanf reads 5 successfully. The second scanf tries to read an integer but finds “abc” in the buffer — returns 0, leaves b uninitialized.
The problem: “abc” is still sitting in the input buffer. Every subsequent scanf call will hit it immediately and fail the same way. To clear the buffer:
/* Clear everything up to and including the newline */
int c;
while ((c = getchar()) != 'n' && c != EOF);
The Newline Left in the Buffer
This is the most common scanf gotcha:
#include <stdio.h>
int main() {
int n;
char ch;
scanf("%d", &n);
scanf("%c", &ch); /* reads the 'n' left by the first scanf */
printf("n=%d ch=%dn", n, (int)ch); /* ch is 'n' (10), not what user typed */
return 0;
}
After reading an integer, the newline the user pressed is still in the buffer. The next scanf("%c") immediately reads that newline instead of waiting for new input.
Fix: add a space before %c to skip whitespace:
scanf(" %c", &ch); /* the space skips any pending whitespace including n */
Buffer Overflow with %s
#include <stdio.h>
int main() {
char name[10];
scanf("%s", name); /* dangerous — no length limit */
/* User types 20 characters: buffer overflow */
printf("Hello, %sn", name);
return 0;
}
scanf("%s") has no bounds checking. If the user types more characters than the buffer holds, it writes past the array boundary — a classic stack buffer overflow. Always specify a width:
scanf("%9s", name); /* reads at most 9 characters, leaves room for */
Why & Is Required (And When It Is Not)
scanf needs the address of the variable to write into. This is why you must pass &variable, not variable. If you forget the &, you are passing the value of the variable as an address — undefined behaviour and almost certainly a crash. See our guide on how pointers work in C for why this address-passing is necessary.
int n;
scanf("%d", n); /* WRONG — passing value, not address */
scanf("%d", &n); /* CORRECT */
The exception: string arrays. The array name already decays to a pointer:
char name[50];
scanf("%49s", name); /* no & needed — name is already an address */
Safer Alternative: fgets + sscanf
#include <stdio.h>
#include <stdlib.h>
int main() {
char line[256];
int n;
printf("Enter a number: ");
if (fgets(line, sizeof(line), stdin) == NULL) return 1;
if (sscanf(line, "%d", &n) != 1) {
fprintf(stderr, "Invalid inputn");
return 1;
}
printf("You entered: %dn", n);
return 0;
}
fgets reads an entire line into a buffer (including the newline), consuming it completely. Then sscanf parses the buffer string. If parsing fails, the buffer is already consumed — there is nothing stuck in stdin to cause problems on the next read.
scanf Format Specifiers Reference
| Specifier | Reads | Argument type |
|---|---|---|
%d |
Decimal integer | int* |
%f |
Float | float* |
%lf |
Double | double* |
%c |
Single character | char* |
%s |
Whitespace-delimited string | char* |
%[^n] |
Everything up to newline | char* |
TL;DR
- Always check scanf’s return value — it returns the number of items read
- Failed reads leave the bad input in the buffer — clear it before retrying
- Add a space before
%cto skip the leftover newline:" %c" - Always specify a width with
%s:"%49s"for a 50-byte buffer - Pass addresses with
&— except for string arrays - Prefer
fgets + sscanfover rawscanffor robust input handling - You can practice all of these patterns in our run c online compiler — no local setup needed