Search code examples
cvalidationuser-inputinfinite-loopc89

Running into infinite loop with scanf


So I have a bit of code like this (Just a note this is in C89):

void inputChoice(int* choicePtr)
{
    int choice;
    printf(BLUE "\nINPUT: " RESET);                     /* Print input using the ansi colour code for Blue. */
    scanf("%d", &choice);                               /* Parse the choice. */

    while ((choice < 1) && (choice > 5))                /* Loop until choice is one of the menu options. */
    {
        /* Request for a valid menu option, printing this using the ansi colour code for Red, then resetting back to the default colour. */
        printf(RED "\nMenu Selection must be between (1 and 5) inclusive. \nPlease Re-enter. \n" RESET);
        scanf("%d", &choice);                           /* Parse the choice. */
    }
    *choicePtr = choice;                                /* Set the choice pointer to the valid choice after validation. */
}

Which gets the choice. It works fine for integers. But if someone enters anything else, for example a Character. It infinitely loops. I want to somehow check for if a character is entered.

One way I tried this was by adding this to check if a character was entered since the integer would be 0 if it didn't scan properly.

Like the below, but that doesn't work either:

scanf("%d", &choice);

while (choice == 0)
{
    printf("Invalid Choice Re-enter.");
    scanf("%d", &choice);
}


Solution

  • The expression

    while ((choice < 1) && (choice > 5))  
    

    is never true since choice can't be at the same larger than 5 and smaller than 1.

    You'll need:

    while (choice < 1 || choice > 5) 
    

    scanf will try to parse if there is anything on the buffer but will not be able to, it will continue trying causing the infinite loop, because whatever is in the buffer will remain there until it's successfully parsed.

    Since scanf will return 0 if no arguments are parsed, you can use that information to clear the buffer removing what is causing the infinite loop:

    int choice = 0;
    int c; 
    int scanned;
    //...
    if ((scanned = scanf("%d", &choice)) == EOF){ //check for EOF return
        puts("Unexpected error.");       
        //error treatment is upt to you, I would avoid the loop
        *choicePtr = scanned;
        return;
    }
    if (scanned == 0) {
        while ((c = fgetc(stdin)) != '\n' && c != EOF){}
    }
    

    in both scanfs.

    Live demo