Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

entering a string into scanf with a while < 0 condition causes infinite loop

I have a "minutes" variable that I'd like the user to enter a positive number into.

int main(void)
{
    float minutes;
    minutes = -1;
    printf("Find out how many bottles worth of water your showers use!\n");
    printf("How many minutes do you spend in the shower? ");
    scanf("%f", &minutes);
    while(minutes < 0)
    {
        printf("Please enter a positive number: ");
        scanf("%f", &minutes);
    }
}

It works as intended for numbers. If minutes >= 0, it accepts it, and if minutes < 0 it keeps asking. If I enter a string it infinitely loops

printf("Please enter a positive number: "); 

and never gives me a chance to enter a new value. Why is this and how can I fix it? Thanks!

like image 321
Nicholas Hassan Avatar asked Jul 11 '16 16:07

Nicholas Hassan


People also ask

Which condition causes infinite for loop?

Infinite Loops A common infinite loop occurs when the condition of the while statement is set to true . Below is an example of code that will run forever. It is not necessary to test any infinite loops. An infinite loop will run forever, but the program can be terminated with the break keyword.

What causes infinite loop in C?

while loop represents the infinite condition as we provide the '1' value inside the loop condition. As we already know that non-zero integer represents the true condition, so this loop will run infinite times. We can also use the goto statement to define the infinite loop.

What is the problem with scanf () to read a string?

Explanation: The problem with the above code is scanf() reads an integer and leaves a newline character in the buffer. So fgets() only reads newline and the string “test” is ignored by the program. 2) The similar problem occurs when scanf() is used in a loop.

How do you avoid an infinite loop?

To avoid ending up in an infinite loop while using a for statement, ensure that the statements in the for() block never change the value of the loop counter variable. If they do, then your loop may either terminate prematurely or it may end up in an infinite loop.


2 Answers

If you don't enter a numerical value, whatever you types stays in the input buffer. You can check for this by reading the return value of scanf, which tells you the number of items read. If it is 0, you can use getchar to read characters until the next newline to flush the buffer.

int main(void)
{
    int rval, c;
    float minutes;
    minutes = -1;
    printf("Find out how many bottles worth of water your showers use!\n");
    printf("How many minutes do you spend in the shower? ");
    rval = scanf("%f", &minutes);
    if (rval == 0) {
        while (((c = getchar()) != '\n') && (c != EOF));
    }
    while(minutes < 0)
    {
        printf("Please enter a positive number: ");
        rval = scanf("%f", &minutes);
        if (rval == 0) {
            while (((c = getchar()) != '\n') && (c != EOF));
        }
    }
}
like image 73
dbush Avatar answered Sep 27 '22 20:09

dbush


The %f conversion specifier tells scanf to stop reading input as soon as it sees a character that isn't part of a legal floating-point constant (i.e., something that isn't a digit, decimal point, or sign). That bad character gets left in the input stream, so the next call to scanf fails, and the next, and the next, etc.

You should always check the return value of scanf - it will tell you how many items were successfully read and assigned from the input stream. In this case, you're expecting a single item, so you should get a return value of 1. If you get a return value of 0, then it means the input is not a proper floating point value, and that bad input has to be cleared somehow. Here's one possible solution:

if ( scanf( "%f", &minutes ) == 1 )
{
  // process minutes as normal
}
else
{
  // clear everything up to the next whitespace character
  while ( !isspace( getchar() ) )
    ; // empty loop 
}

The only problem with this is that scanf is kind of dumb, and if you type something like 123fgh, it will convert and assign the 123 while leaving fgh in the input stream; you would probably want to reject that whole input completely.

One solution is to read the input as text, and then convert it using strtod:

char buffer[BUFSIZE]; // where BUFSIZE is large enough to handle expected input
...
if ( fgets( buffer, sizeof buffer, stdin ) )
{
  char *chk; // chk will point to the first character *not* converted; if
             // it's anything other than whitespace or the string terminator,
             // then the input was not a valid floating-point value.
  double tmp = strtod( buffer, &chk );
  if ( isspace( *chk ) || *chk == 0 )
  {
    minutes = tmp;
  }
  else
  {
    // input was not a proper floating point value
  }
}

This has the benefit of not leaving crap in the input stream.

like image 33
John Bode Avatar answered Sep 27 '22 21:09

John Bode