Master C

Your first C program

5 / 193
Contents

Contents

Master C

0 of 193 complete

11 min read

scanf, and reading what somebody types

printf's mirror image, the ampersand it insists on, and the return value nobody checks until it bites them.

A program that always prints the same thing is an expensive way of owning a piece of paper. Useful programs read something first: a number typed at a keyboard, a file piped in, the output of another program.

When a program starts, it is handed two channels worth caring about now. Standard output is where printf writes. Standard input is a stream of characters flowing in. Typing at a terminal feeds standard input, and so does a file redirected into the program; your code cannot tell the difference and does not need to.

The library function that reads from standard input is scanf, and it is printf's mirror image. You hand it a format string saying what you expect to find, and it picks those things out of the stream.

One thing about it is different, and it is the thing everybody gets wrong the first time.

int n;
scanf("%d", &n);
Reading one whole number. Note the ampersand, which printf never needs.

int n; sets aside room for a whole number and puts nothing in it. Whatever bytes were at that spot are still there. &n is the address of n: the place in memory where it lives, rather than the value currently sitting there.

printf is given values because it only has to look at them. scanf has to change n, and to change something you must know where it is. Handing scanf the value of n would tell it nothing about where to put the answer.

The box below is given some input to read, shown beside the code. It is editable, exactly like the code is, and the program cannot tell that it was not typed by a person.

Try CodeC

Change the input from 7 to 21 and run it again. Then try a negative number, and then a number with a decimal point in it.

Two values in one call

A format string can ask for more than one thing, and then it needs one address per thing, in the same order. %d skips over any spaces, tabs and new lines before the number it is looking for, which means the layout of the input barely matters.

Try CodeC

Put the 4 on its own line in the input and run it again. Then delete the space between the two %d in the format string; it still works, because %d skips leading whitespace by itself.

Numbers with a decimal point

To read a double, C's number with a fractional part, the specification is %lf. This is the one asymmetry worth memorising: printf writes a double with %f, and scanf reads one with %lf. The reason is an old rule about how values are passed to functions, and the practical effect is that a %f in a scanf call is a bug even though it looks right.

Try CodeC

Change the input to 0.5, then to 40. Then change it to the word soon and see what a program does when the input is not the shape it was promised.

Did it actually read anything?

That last experiment is the reason for this section. scanf returns a number, and it is not the value it read: it is how many of the things you asked for it managed to fill in. Ask for two and get two, it returns 2. Ask for one and find a word where a number should be, it stops, returns 0, and leaves your variable exactly as it found it.

Try CodeC

Change the input to the word twelve and run it again. Then delete the input entirely and look at what got comes back as when there was nothing there at all.

With no input left, scanf returns EOF, a constant from stdio.h that stands for end of file and is negative, usually -1. So the three answers are: the count if it worked, a smaller count or 0 if the input did not match, and a negative number if the input ran out.

Now look again at the first line of that snippet. It says int n = -1; rather than int n; for a reason. If scanf fails and never touches n, then printing n means printing whatever bytes were lying at that address: a different number on different days, a bug that vanishes when you go looking for it. Giving a variable a value before anything can go wrong costs nothing.

Asking before reading

Prompting is just printing before you read. There is one wrinkle: output is buffered, meaning printf's characters may sit in memory until there is a reason to send them, so a prompt with no new line at the end can arrive after the program has already started waiting. fflush(stdout); forces everything waiting out immediately.

Try CodeC

Change the input to 12 and 0.85 and run it again. The prompts and the answer all appear together here because nothing is waiting at a real keyboard.

One character at a time

Below scanf sits something simpler. getchar() reads exactly one character from standard input and returns its numeric code, or EOF if there is nothing left. It takes no address, because it hands the answer straight back.

Try CodeC

Change the input to a lower case a and run it again, then to a space, then to the digit 0. The code you get back is the number the machine really stores.

That 65 for a capital A is not a quirk of getchar. A character in C is a small whole number, and the printing works because %c asks printf to write the character with that code rather than the number itself. Text in C is numbers all the way down, which the next section makes precise.

You write scanf("%d", n); and forget the ampersand. What happens?

With warnings on, the compiler tells you the argument is the wrong type, and you fix it in ten seconds. With warnings off, it may well build. Then scanf takes the number sitting in n, treats it as an address, and writes four bytes there. If n happened to hold 7, it writes to memory address 7, which belongs to nobody, and the program dies with a segmentation fault. If it holds something plausible, it quietly corrupts whatever was there instead. This is the single best argument for -Wall.

Check yourself

scanf("%d %d", &a, &b) is given the input 5 apples. What does it return, and what is in b?

All lessons in Master C