scanf ignoring, infinite loop

后端 未结 6 1001
遥遥无期
遥遥无期 2020-11-27 22:54
int flag = 0;
int price = 0;
while (flag==0)
{
    printf(\"\\nEnter Product price: \");
    scanf(\"%d\",&price);
    if (price==0) 
        printf(\"input not          


        
相关标签:
6条回答
  • 2020-11-27 23:08

    When you enter something that isn't a number, scanf will fail and will leave those characters on the input. So if you enter hello, scanf will see the h, reject it as not valid for a decimal number, and leave it on the input. The next time through the loop, scanf will see the h again, so it just keeps looping forever.

    One solution to this problem is to read an entire line of input with fgets and then parse the line with sscanf. That way, if the sscanf fails, nothing is left on the input. The user will have to enter a new line for fgets to read.

    Something along these lines:

    char buffer[STRING_SIZE];
    ...
    while(...) {
        ...
        fgets(buffer, STRING_SIZE, stdin);
        if ( sscanf(buffer, "%d", &price) == 1 )
            break;   // sscanf succeeded, end the loop
        ...
    }
    

    If you just do a getchar as suggested in another answer, then you might miss the \n character in case the user types something after the number (e.g. a whitespace, possibly followed by other characters).

    You should always test the return value of sscanf. It returns the number of conversions assigned, so if the return value isn't the same as the number of conversions requested, it means that the parsing has failed. In this example, there is 1 conversion requested, so sscanf returns 1 when it's successful.

    0 讨论(0)
  • 2020-11-27 23:08

    The %d format is for decimals. When scanf fails (something other a decimal is entered) the character that caused it to fail will remain as the input.

    Example.

        int va;
        scanf("%d",&va);
        printf("Val %d 1 \n", val);
    
        scanf("%d",&va);
        printf("Val %d 2 \n", val);
        return 0;
    

    So no conversion occurs.

    The scanf function returns the value of the macro EOF if an input failure occurs before any conversion. Otherwise, the scanf function returns the number of input items assigned, which can be fewer than provided for, or even zero, in the event of an early matching failure

    7.19.6. The scanf function - JTC1/SC22/WG14 - C

    So you should note that scanf returns its own form of notice for success

    int scanf(char *format)
    

    so you could have also did the following

    do {
            printf("Enter Product \n");
    }
    while (scanf("%d", &sale.m_price) == 1);
    
    if(scanf("%d", &sale.m_price) == 0)
            PrintWrongInput();
    

    Also keep in the back of your head to try to stay away from scanf. scanf or scan formatted should not be used for interactive user input. See the C FAQ 12.20

    0 讨论(0)
  • 2020-11-27 23:15

    It goes into an infinite loop because scanf() will not consumed the input token if match fails. scanf() will try to match the same input again and again. you need to flush the stdin.

    if (!scanf("%d", &sale.m_price)) fflush(stdin);

    0 讨论(0)
  • 2020-11-27 23:19

    Edit: Back when I first wrote this answer, I was so stupid and ignorant about how scanf() worked.

    • First of all let me clear something, scanf() is not a broken function, if I don't know how scanf() works and I don't know how to use it, then I probably haven't read the manual for scans() and that cannot be scanf()'s fault.
    • Second in order to understand what is wrong with your code you need to know how scanf() works.

    When you use scanf("%d", &price) in your code, the scanf() tries to read in an integer from the input, but if you enter a non numeric value, scanf() knows it isn't the right data type, so it puts the read input back into the buffer, on the next loop cycle however the invalid input is still in the buffer which will cause scanf() to fail again because the buffer hasn't been emptied, and this cycle goes on forever.

    In order to tackle this problem you can use the return value of scanf(), which will be the number of successful inputs read, however you need to discard the invalid inputs by flushing the buffer in order to avoid an infinite loop, the input buffer is flushed when the enter key is pressed, you can do this using the getchar() function to make a pause to get an input, which will require you to press the enter key thus discarding the invalid input, note that, this will not make you press the enter key twice whether or not you entered the correct data type, because the newline character will still be in the buffer. After scanf() has successfully finished reading the integer from input, it will put \n back into the buffer, so getchar() will read it, but since you don't need it, it's safe to discard it:

    #include <stdio.h>
    
    int main(void)
    {
        int flag = 0;
        int price = 0;
        int status = 0;
        while (flag == 0 && status != 1)
        {
            printf("\nEnter Product price: ");
            status = scanf("%d", &price);
            getchar();
            if (price == 0) 
                printf("input not valid\n"); 
            else 
                flag = 1;
        }   
    
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-27 23:20

    After the first number, a '\n' will be in the input buffer (the return you pressed to input the number), so in the second iteration the scanf call will fail (becouse \n isn't a number), scanf will not remove that \n from the buffer, so in the next iteration it will fail again and so on.

    You can fix that by reading the '\n' with a getchar() call after scanf.

    0 讨论(0)
  • 2020-11-27 23:26

    The "answers" that say it will because there is a '\n' in the buffer are mistaken -- scanf("%d", ...) skips white space, including newlines.

    It goes into an infinite loop if x contains 0 and scanf encounters a non-number (not just whitespace) or EOF because x will stay 0 and there's no way for it to become otherwise. This should be clear from just looking at your code and thinking about what it will do in that case.

    0 讨论(0)
提交回复
热议问题