Module 3 · Constants, Qualifiers, Input and Output
scanf: Reading Input Without Losing Your Mind
In this lesson
- Read an integer, a decimal, a character and a word, and print what
scanfgave back. - Explain the ampersand as the address of the box, and say what happens without it.
- Skip a leftover newline with a space, and cap a
%swith a width.
Bob types 12 34 into his program and it dies without printing anything. He removes one character from a line he did not write, and it works. Then he reads a letter after a number and gets a letter he never typed.
Both are the same lesson, learned twice. scanf uses the format language you already know from printf, mirrored, with one extra rule and one famous trap. This lesson is those two things and nothing else.
scanf is printf's mirror, with one extra rule
printf takes values and writes text. scanf takes text and writes values, which means it needs somewhere to put them.
Handing it a variable would hand it a copy of that variable's value, and writing into a copy changes nothing. So you hand it the address of the box instead, with an ampersand.
One scanf call, and what each part does
int items = scanf("format", &first, &second);
&x the address of x: where x lives, not what is in it
the one thing printf never needs and scanf always does
%d %u an int, an unsigned int
%ld %lld a long, a long long
%lf a double. %f here reads a float and ruins a double
%c exactly one character, whitespace included
%s one word, stopping at the first whitespace
%19s the same, writing at most 19 characters plus the marker
%x %o a number written in base 16 or base 8
a space in the format, skip any run of whitespace here
" %c" is a space then %c, and it is the fix for the trap
returns how many items were filled in. EOF when the input ran out
- Every specifier that fills a variable needs an address, so every one needs an ampersand.
- Whitespace means spaces, tabs and newlines, and
%d,%fand%sskip it before they start. %cis the one that does not skip, which is the whole trap.
So the shape is always the same: a format that mirrors the input, an ampersand on every target, and a returned count you look at.
Print what scanf gave back, every time
scanf returns the number of items it successfully filled in. Not the number of characters, and not whether the values are sensible.
Ignoring that number is the single most common beginner habit that turns a wrong input into a wrong answer instead of a clear message. Lesson 5 is about what to do with it; this lesson just prints it.
#include <stdio.h>
int main(void)
{
int roll = 0;
int read = scanf("%d", &roll);
printf("scanf returned %d\n", read);
printf("roll is %d\n", roll);
return 0;
}
scanf returned 1
roll is 47
That output is for the input 47. One item asked for, one item filled in, so the answer is 1.
Type a word instead of a number and the first line reads 0, which is lesson 5's whole subject.
Several values at once, and whitespace in the format
A format can ask for several values. Between them you usually write a space, which means "skip any whitespace here", and that includes the newline you pressed Enter for.
So one format reads values typed on one line or on three, without changing anything.
#include <stdio.h>
int main(void)
{
int roll = 0;
char section = '?';
double cgpa = 0.0;
int read = scanf("%d %c %lf", &roll, §ion, &cgpa);
printf("scanf returned %d\n", read);
printf("roll : %d\n", roll);
printf("section : %c\n", section);
printf("cgpa : %.2f\n", cgpa);
return 0;
}
scanf returned 3
roll : 47
section : B
cgpa : 3.78
That output is for the input 47 B 3.78. It is the same for the same three values typed on three separate lines.
The space before %c is doing real work there, and the next section is why.
%c does not skip whitespace, and a newline is whitespace
Every other specifier skips leading whitespace before it starts reading. %c does not, because sometimes a space is the character you want.
That is reasonable and it causes the most reported bug in every beginner C course. Here is the sequence.
You type 47 and press Enter. %d reads the 4 and the 7, stops at the newline, and leaves it sitting in the stream. Then %c reads the next character, which is that newline.
The fix is one space in the format string. A space there means "skip any whitespace", so the cursor moves past the newline and reads the character you meant.
This is why the second read in Example 2 above was written " %c" and not "%c".
%s stops at whitespace, and does not know when to stop writing
%s reads one word: it skips leading whitespace, then takes characters until the next whitespace. It cannot read a line with spaces in it, and reading a whole line is Module 10.
The dangerous half is what it does at the other end. A bare %s writes as many characters as arrive, whether or not there is room for them.
The fix is a width: %19s writes at most 19 characters, plus the marker C puts at the end of every piece of text. Nineteen and a marker fit in twenty boxes.
#include <stdio.h>
int main(void)
{
char word[20] = {0};
char initial = '?';
int first = scanf("%19s", word);
int second = scanf(" %c", &initial);
printf("first scanf returned %d, word is %s\n", first, word);
printf("second scanf returned %d, initial is %c\n", second, initial);
return 0;
}
first scanf returned 1, word is Progsity
second scanf returned 1, initial is R
That output is for the input Progsity R. Arrays are Module 9 and text is Module 10, so treat char word[20] as twenty boxes in a row for now.
Notice there is no ampersand on word. An array's name already is an address, which is the one exception to the ampersand rule and the reason Module 9 and Module 11 exist.
%lf for a double, always, and only here
In a printf, %f prints both a float and a double, because a float is widened on its way into the call.
In a scanf nothing is widened, because you are handing over an address and the address says nothing about the size. %f means "write four bytes here" and %lf means "write eight".
Use %f for a double and scanf writes four bytes into an eight byte box. The value is wrong, nothing is reported on the Playground, and the number you print is usually zero.
So the rule is short: in a scanf, a double is %lf, every time.
The smallest useful read. Two values, one format, and the count printed so that you can see it worked.
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int read = scanf("%d %d", &a, &b);
printf("read %d value(s)\n", read);
printf("%d + %d = %d\n", a, b, a + b);
return 0;
}
read 2 value(s)
12 + 34 = 46
That output is for the input 12 34. Remove either ampersand and the program compiles on the Playground with nothing said, then misbehaves.
The trap and its fix in one program. The two reads are identical except for one space.
#include <stdio.h>
int main(void)
{
int roll = 0;
char section = '?';
int first = scanf("%d", &roll);
int second = scanf(" %c", §ion);
printf("returned %d and %d\n", first, second);
printf("roll %d, section [%c], as a number %d\n", roll, section, section);
return 0;
}
returned 1 and 1
roll 47, section [B], as a number 66
That output is for 47 on one line and B on the next. Take the space out of the second format and the same input gives section [] over two lines, as a number 10, and the same two returned counts.
Read that last part twice. The return value cannot catch this one, because scanf did exactly what it was asked and succeeded.
A name, a roll, a section and a result, read and printed back with the widths of lesson 3.
#include <stdio.h>
int main(void)
{
char name[20] = {0};
int roll = 0;
char section = '?';
double cgpa = 0.0;
int read = scanf("%19s %d %c %lf", name, &roll, §ion, &cgpa);
printf("fields read : %d\n", read);
printf("%-10s%6d%4c%8.2f\n", name, roll, section, cgpa);
return 0;
}
fields read : 4
Nusrat 47 B 3.78
That output is for the input Nusrat 47 B 3.78. Four specifiers, four values, four counted, and one ampersand missing on purpose because name is an array.
The space before %c is still there. It is doing nothing in this input, because %d stopped on a space rather than a newline, and it costs nothing to leave it.
Where this is used
- Every problem on this platform's judge. Your program is handed a file on its standard input and your reads are how it gets in. The graded problems of this module all start with a
scanfline written for you. - A configuration parser. Reading a settings file of the shape
key valueis a loop around a two specifierscanf, and the returned count is how the loop knows to stop. Module 6 closes that loop. - The command prompt of any interactive tool. A database shell, a calculator, a game's move entry: all of them read a token, check the count, and act.
- A bank's overnight import. Millions of fixed shape records are read with one format string per record type. A record whose count comes back short goes to a reject file rather than being guessed at.
Common mistakes
1. Forgetting the ampersand.
int roll = 0;
scanf("%d", roll);
Silent on the Playground, because the check lives behind -Wall. A local gcc -Wall says warning: format '%d' expects argument of type 'int *', but argument 2 has type 'int'. scanf takes the number that is in roll and treats it as an address to write to. What happens next is undefined behaviour: on our run the write was refused and scanf returned 0, and on another machine the program is killed instead.
2. A bare %c after a number.
scanf("%d", &roll);
scanf("%c", §ion);
No message anywhere, and both reads return 1. The character read is the newline left by the Enter key, which is character 10. The fix is a space: scanf(" %c", §ion).
3. %f for a double.
double cgpa = 0.0;
scanf("%f", &cgpa);
Silent on the Playground, and scanf returns 1 as though it worked. On our run it printed 0.00 for every input. A local gcc -Wall says warning: format '%f' expects argument of type 'float *', but argument 2 has type 'double *' and suggests %lf.
4. A %s with no width.
char word[20] = {0};
scanf("%s", word);
No message at either command line. It works for every short word you test with, and a 40 character word writes 20 characters past the end of the boxes. This is the shape of a whole family of real security holes, and the fix is three characters: %19s.
Kenji is testing a calculator's two simplest buttons, on numbers big enough that the second answer will not fit where the first one does.
Input. One line with two integers a and b.
Output. Two lines: the sum, then the product.
Constraints. -100000 <= a, b <= 100000. The sum fits in an int and the product does not.
Sample. Input 3 4 gives 7 then 12.
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
/* Two printf calls. The product does not fit in an int. */
return 0;
}
Graded as sum-and-product. Module 2 lesson 2's rule about widening one operand applies here exactly.
Zara's sign-in desk has a name box nineteen letters wide, and one day somebody will type a longer name into it.
Input. At most one line holding one word with no spaces. It may be empty.
Output. Two lines: Hello, <name>! then read and what scanf returned.
Constraints. The word is 1 to 19 characters. Empty input returns -1 and greets an empty name.
Sample. Input Maria gives Hello, Maria! then read 1.
#include <stdio.h>
int main(void)
{
char name[20] = {0};
int read = scanf("%19s", name);
/* Two lines: the greeting, then what scanf gave back. */
return 0;
}
Graded as greet-name. Leave the 19 in the format exactly as it is, and leave the missing ampersand alone: an array name is already an address.
Read a roll number, a section letter and a result, then print them as three aligned lines.
Input. A roll number, a section letter and a decimal, in that order, on one line or several.
Output. Three lines: Roll, Section and CGPA, each label padded to eight characters and each value right aligned in a field of nine.
Sample. Input 47 B 3.78 gives a slip whose three values end in the same column.
#include <stdio.h>
int main(void)
{
int roll = 0;
char section = '?';
double cgpa = 0.0;
int read = scanf("%d %c %lf", &roll, §ion, &cgpa);
/* Three printf calls, then one more line printing read. */
return 0;
}
Not graded in this module. Then run it again with the three values on three separate lines and satisfy yourself that nothing changed.
Run in CompilerRead three decimal numbers and print their mean to exactly two decimal places.
Input. Three decimal numbers separated by whitespace.
Output. One line with the mean, to two decimal places.
Sample. Input 1 2 4 gives 2.33.
#include <stdio.h>
int main(void)
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
scanf("%lf %lf %lf", &a, &b, &c);
/* One printf. Divide by 3.0 and not by 3, and ask yourself why. */
return 0;
}
Not graded in this module. The hard part is the comment: work out what (a + b + c) / 3 gives when every value is a double, and then what it would give if they were int.
Common doubts
Why does
%snot need an ampersand?Because the name of an array already means the address of its first box. It is the one place the rule looks broken, and Module 9 is where it stops looking that way.
Can
scanfread a whole line with spaces in it?Not sensibly.
%sstops at the first space, and the tricks that get around it are worse than the tool for the job, which isfgetsin Module 10.Should I put a space before every specifier?
It is harmless everywhere and only necessary before
%c. Many people write it everywhere out of habit, and nobody has ever been bitten by doing so.Do I need
printfprompts like "Enter a number"?Not on a judge, and they will fail your tests, because the judge compares every character you print. Write them while you are experimenting and take them out before you submit.
What happens if I type more values than the format asks for?
The extra ones stay in the stream, waiting for the next read. Nothing is lost and nothing complains, which is exactly what the problems lesson's
line-parserdepends on.
Key takeaways
scanfwrites into boxes, so it needs an address, which is what the ampersand gives it.- It returns how many items it filled, and printing that number is a habit worth forming.
- Whitespace in the format means "skip any whitespace here", newlines included.
%cis the only specifier that does not skip whitespace, and" %c"is the fix.%stakes one word and needs a width, or it writes past the end of your boxes.- In a
scanfadoubleis%lf, and%fthere is a silent failure on the Playground.
Next you stop assuming the read worked, and make the returned count do some actual work.
End of lesson 4
Mark it done, and your progress moves with you.
Next: When Input Goes Wrong: Checking What You Read