tags:

views:

54

answers:

3

here a a piece of code that is supposed to loop over and over until the user inputs a number between 0 and 15

        int input;

        do
        {

                printf("Enter a number => ");
                scanf("%d",&input);
                printf("\n %d \n",input);

        }
        while (!(input>0 && input <15));

However if the user puts in something like "gfggdf" it results in the loop repeating over and over and over and never prompting the user for input again... the console looks like this

Enter a number =>
0
Enter a number =>
0
Enter a number =>
0
Enter a number =>
0
Enter a number =>
0
Enter a number =>
0
(looping indefinitely)

After looking through this book, it seems I need to do something like this to prevent this from happening.

int input, error, status;
char skip_ch;

do
{
        error = 0;

        printf("Enter a number => ");
        status = scanf("%d",&input);
        printf("\n %d \n",input);

        if(status!=1)
        {
                error=1;
        }
        else if(input < 0 || input >15){
                error = 1;
        }

        do
        {
                scanf("%c",&skip_ch);
        }while(skip_ch != '\n');
}
while (error);

I understand needing to check for the scanf status to make sure it was valid input, What I don't understand the the inner do-while loop. I feel like the book never really explained to me why scanf needs to be called several times like that, it's like somehow the scanf buffer got filled up with a bunch of garbage and it's somehow cleaning it out by just looping through it a million times for you.

Could anyone please explain this black magic to me?

+2  A: 

That's why there are so many answers to similar questions that recommend not using scanf().

Also, you should check the return value from scanf() because it tells you when something has gone wrong - and no value could be converted.

The normal recommended solution is a combination of fgets() to read lines of input and sscanf() to parse the line once it is read.

The second loop in your example goes around reading the characters up to and including a newline, thus resynchronizing scanf() and skipping any bogus data. It is a long-winded way of doing it; it would be simpler to use getchar() instead:

int c;
while ((c = getchar()) != EOF && c != '\n')
    ;

Note the use of 'int c;' - the type is crucially not 'char' or 'unsigned char' because it must store all possible char values plus EOF.

Jonathan Leffler
A: 

First of all, avoid using scanf(), use fgets() instead.

To evaluate your condition (first code). 0 > 0 is false. 0 < 15 is true. false && true is false. !false is true. That's why it's looping indefinitely.

Ruel
Yes, I was expecting for it to loop because inputting the characters would result in input ==0, the unexpected part was that it didn't allow the user to try and enter something else, and it instead went into an infinite loop (I probably should have clarified that). Jerry Coffin explained to me what is going on though, the previous input is stuck in the scanf buffer because the "status" of the scanf was set to 0 to signify an error, and it didn't clear the buffer (because of the error)
Danny
+1  A: 

The original code loops indefinitely because the invalid data (the "gfggdf") is not removed from the input buffer when scanf fails to convert it to an integer -- it's left in the input buffer, so the next call to scanf looks at the same data, and (of course) still can't convert it to an integer, so the loop executes yet again, and the results still haven't changed.

The:

do {
        scanf("%c",&skip_ch);
}while(skip_ch != '\n');

simply reads one character at a time (and ignores it) until it gets to the end of the line. If you prefer to get rid of the garbage in the input buffer without a loop, you can use something like: scanf("%*[^\n]");

Another possibility that's (probably more) widely used is to start by reading an entire line, then attempting to convert what's in the line. Whether it converts or not, you've already read all the data to the end of the line so the next attempt at reading/converting will start from the next line whether the conversion worked or not. This does have one potential weakness: if you got a really long line of input, you could end up reading and storing a lot of data for which you have no real use. It's rarely a big deal, but you should still be aware of it.

Jerry Coffin