C in 100 Seconds: scanf | Episode 12
Video: C in 100 Seconds: scanf — Read Numbers and Strings from the User | Episode 12 by Taught by Celeste AI - AI Coding Coach
C scanf: Read Numbers and Strings From the User
scanf("%d", &age);reads an int.scanf("%s", name);reads a word. Format specifiers like printf's, but in reverse — and the&is critical for non-arrays.
scanf is C's built-in input parser. Powerful but treacherous — buffer overflows, format mismatches, and partial reads make it a frequent bug source. Episode 13 covers the safer alternative fgets.
The basic shape
#include <stdio.h>
int main() {
int age;
printf("Enter age: ");
scanf("%d", &age);
printf("You are %d years old\n", age);
char name[50];
printf("Enter name: ");
scanf("%s", name);
printf("Hello, %s!\n", name);
return 0;
}
Two reads — one int, one word.
Format specifiers (mostly like printf)
| Type | Specifier |
|---|---|
int |
%d |
unsigned |
%u |
long |
%ld |
long long |
%lld |
float |
%f |
double |
%lf (note: lowercase L+f, required for double in scanf) |
char |
%c |
string (word) |
%s |
Note: in printf, %f covers both float and double (because float promotes). In scanf, you need %f for float and %lf for double — they take pointers to different sizes, no promotion.
The & — pass-by-pointer
int x;
scanf("%d", &x); // pass address of x
scanf writes to the variable through a pointer. You pass &x (address of x), not x. Without &, scanf would treat the value as the address — typically a segfault.
The exception: arrays.
char name[50];
scanf("%s", name); // no & — array decays to pointer
name (an array) automatically becomes &name[0] when passed. Same as %s working without &.
%s reads ONE WORD
char name[50];
scanf("%s", name); // reads until first whitespace
%s stops at the first whitespace (space, tab, newline). Input "Alice Smith" — only "Alice" is read; "Smith" stays in the input buffer for the next read.
To read a line with spaces, use fgets (next episode) or %[^\n] (read until newline).
Buffer overflow risk
char name[10];
scanf("%s", name); // input "verylongname" — overflow!
%s writes until whitespace. If input is longer than the buffer, it stomps memory. Critical security bug; the gets function was so bad it's been removed from the C standard.
The safe form: specify max length:
char name[10];
scanf("%9s", name); // read at most 9 chars (+ \0 = 10)
But fgets(name, 10, stdin) is even simpler and clearer (next episode).
Reading multiple values
int a, b;
scanf("%d %d", &a, &b); // expects two ints separated by whitespace
Format string can have multiple specifiers. The user types "5 10" and a = 5, b = 10. The space in the format matches any whitespace (including newlines) in the input.
int month, day, year;
scanf("%d/%d/%d", &month, &day, &year); // user types "5/15/2024"
Literal characters in the format must match the input. / must be where the input has /.
Return value: how many were parsed
int x;
int n = scanf("%d", &x);
if (n != 1) {
printf("That wasn't a number!\n");
}
scanf returns the number of successful conversions. For one specifier, success returns 1; failure returns 0 (no input matched) or EOF (input exhausted).
Always check the return value. If parsing fails, your variable has garbage from before — using it is a bug.
Mixing scanf and fgets
This trips up everyone:
int age;
char name[50];
scanf("%d", &age); // user types "25\n"; scanf reads 25, leaves \n
fgets(name, 50, stdin); // immediately reads the leftover \n!
After scanf("%d", ...) reads "25", the newline \n stays in the buffer. The next fgets reads it as an empty line.
Fix: consume the newline:
scanf("%d", &age);
getchar(); // discard the leftover \n
fgets(name, 50, stdin);
Or skip whitespace in scanf format strings (some specifiers like %s skip leading whitespace; %c doesn't).
The cleanest approach: just use fgets for everything and parse with sscanf.
%c reads ONE CHAR (including whitespace)
char ch;
scanf(" %c", &ch); // leading space SKIPS whitespace
The leading space in " %c" makes scanf skip whitespace before reading. Without it, %c would read the first byte (often the leftover newline from a previous scanf).
Practical input idioms
// Read an int with retry
int x;
while (scanf("%d", &x) != 1) {
printf("Invalid. Try again: ");
// discard rest of line
while (getchar() != '\n');
}
// Read multiple lines until EOF
int n;
while (scanf("%d", &n) == 1) {
printf("%d\n", n);
}
Common mistakes
Forgetting &. scanf("%d", x); — if x is int, this writes to address 5 (or whatever). Crash.
Using %lf in printf for double. Wrong — printf uses %f for both. %lf in printf is undefined behavior on some compilers; works on others.
Buffer overflow with %s. Always cap with %Ns (where N is buffer-size minus 1).
Mixing scanf("%d", ...) and fgets. Leftover newline confuses things.
Not checking return value. Failed parses leave variables uninitialized; using them is a bug.
Wrong format type. int x; scanf("%lf", &x); — passing pointer to int but expecting double. Memory stomped.
Why fgets is usually better
scanf mixes parsing and reading. For real input, separate them:
char line[100];
fgets(line, sizeof(line), stdin); // read full line, bounded
int x;
sscanf(line, "%d", &x); // parse from string
Cleaner error handling, no leftover-newline trap, no buffer overflow. Episode 13 walks through fgets in depth.
What's next
Episode 13: fgets — safe input. The right way to read user input. Bounded read, full line including spaces, predictable behavior.
Recap
scanf("%d", &x) reads an int into x. & for non-arrays; arrays decay automatically. %s reads one word; bounded with %Ns. %lf for double in scanf (vs %f in printf). Return value = successful conversions. Mixing with fgets traps you on leftover newlines. Always check return value. For real input, prefer fgets + sscanf.
Next episode: fgets.