scanf (and why you should avoid using it)

I believe scanf() is one of the most common pitfalls that most novice C programmers encounter. In our first steps with the C programming language we usually try to write some simple interactive programs, like a program that asks for our name (“What is your name?”) and then sends some greetings in a more personalized fashion (“Hello, Giannis!”), or even some simple game.

In most books, students are encouraged to use the scanf() function as the standard method to read the user’s input. And it really does a good job, as long as the input given by the user is what was expected by the author of the program. Or else, things may get a little tricky especially for the newcomer. Here is an example, a simple program that prompts the user to enter a number, and then it print out whether it was a even or an odd number. Here, our hypothetical programmer intended to apply some sort of defensive programming, and check whether the user’s input was recognized as a number. The program will keep on asking until a valid number is entered:

#include <stdio.h>

int main()
{
int number, result;

do
{
printf(“Give me a number: “);
result = scanf(“%d”, &number );
if (result < 1)
printf(“You didn’t type a number!n”);
}
while (result < 1);

if (number % 2 == 0)
printf(“%d is odd.n”, number);
else
printf(“%d is even.n”, number);
}

At first sight it looks fine. If we try to run it we’ll get what we expected:

giannis@giannis-desktop:~$ ./a.out
Give me a number: 133
133 is odd.
giannis@giannis-desktop:~$ ./a.out
Give me a number: 44
44 is even.

It looks okay! But what happens if the user enters some invalid input? Let’s find out:

Give me a number: abc
You didn't type a number!
Give me a number: You didn't type a number!
Give me a number: You didn't type a number!
Give me a number: You didn't type a number!
...

And it goes like this until we terminate the program with Ctrl-C. Not quite what expected, eh?

What did go wrong here? Well, let’s take a step-by-step course. At first, the user is prompted to enter a number. Then scanf is called to read an integer number from standard input. The program blocks and waits for the user to type something and press Enter. After that, scanf reads the first character from standard input, which is ‘a’, which is not a digit. As a result, scanf puts back the character and bails out. The error message is displayed and we start all over again to ask the user for new input. However, when scanf is called on second time, it doesn’t block at all, because the input buffer already contains the previously entered data. It tries once more to parse the user’s input, and it fails once again, and the loop infinitely continues for ever.

It turns out that the problem here is that if some invalid input is entered, it never leaves the input buffer. What we need here is to find a solution, so that the user input is always taken off from the input buffer, either when it is valid or not. The “proper” way to achieve this, is to first read a whole line of input into a temporary buffer using fgets(), and then parse the buffer using sscanf(), a close-cousin of scanf(). This is how it will look like:

...
do
{
printf("Give me a number: ");
char st[1024];
fgets( st, sizeof(st), stdin );
result = sscanf(st, "%d", &number );
if (result < 1)
printf("You didn't type a number!n");
}
while (result < 1);
...

And now let’s try to test the new improved version of the program:

giannis@giannis-desktop:~$ ./a.out
Give me a number: rfdasf
You didn't type a number!
Give me a number: fasfdsfgsd
You didn't type a number!
Give me a number: 25
25 is odd.

Now we got somewhere, didn’t we? Another, less elegant, way to solve the same problem would be to still use scanf() but also meticulously take care to remove the invalid line of data from the buffer. In C-terms, that would be:

...
do
{
printf("Give me a number: ");
result = scanf("%d", &number );
if (result < 1)
{
printf("You didn't type a number!n");
char st[1024];
fgets( st, sizeof(st), stdin );
}
}
while (result < 1);
...

But as I said before, I personally find this version kind of ugly, because, we are using fgets() only to read and ignore a whole line from the input buffer, which is something we can avoid if we use sscanf().

I believe most C programmers have been puzzled at their first steps, and I hope that these little headaches will not discourage new programmers from continueing their efforts. For these efforts will be greatly rewarded!

This entry was posted in Uncategorized. Bookmark the permalink.

13 Responses to scanf (and why you should avoid using it)

  1. Wattie says:

    Somebody should rewrite the scanf and include it in the newer compilers. Should be better solution, aint it?

  2. wITTus says:

    No.
    This would break downward compatibility.

    Nice article.
    Thank You.

  3. Ning says:

    Good article!! This is exactly what i’m looking for
    I have some difficulty in get and parse HTTP request, and fgets is the right key
    Thanx!

  4. Jeshwanth says:

    Thank you…

  5. davej says:

    It should also be mentioned that scanf is often a security risk (buffer overflow) if it is used to read a string.

  6. Benson says:

    Hi,

    Could you explain to me what is char?
    I thought char onli hold 256 int?

    How come you can char[1024]?
    What does char * str mean?

  7. torakora says:

    Nice article about fgets and sscanf. But, can’t you use getchar() after scanf so that it takes the remaining input from the buffer and clear the buffer up?

  8. balaji says:

    Thank you…
    I tried all the way for validation….i couldnt stop my while loop …thank you

  9. randomperson says:

    Thank you so much! I had the exactly same issue that you gave here as an example, and I was asking other programmers only to be ignored because that was too “basic” for them. :(
    But I found your blog and finally I understand what went wrong! :D

  10. Pepo says:

    In the first coding example:

    if (number % 2 == 1)

    Shouldn’t this be:

    if (number % 2 == 0)

    ?

    • giannis says:

      That’s correct! Thanks, I will update it.

    • CS_stud_12 says:

      if (number % 2 == 1)
      number is odd
      else
      number is even

      • Swagato Barman Roy says:

        To make it cleaner, the ==1 part is redundant. You can write
        if(number%2) printf(“The number is odd.”);
        else printf(“The number is even.”);

        Even better, after you acquire the number, pack it into a single line code.
        number%2? printf(“The number is odd.”): printf(“The number is odd.”);

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>