Thread: case, menu system, loops infinitely on invalid input

  1. #1
    Registered User
    Join Date
    Apr 2011
    Posts
    23

    case, menu system, loops infinitely on invalid input

    I am having a problem with a menu system, it's a sample menu system that I am putting together to make sure it will work, theoretical reasons, just trying to learn.

    When an invalid value is entered it will go into an infinite loop and the program has to be forcefully terminated.

    Can someone explain to me how to prevent this.

    Code:
    /* using an infinite loop and switch statement
       to implement a menu system */
    #include <stdio.h>
    #include <stdlib.h>
    
    #define DELAY 150000
    
    int menu(void);
    void delay(void);
    
    int main()
    {
    
        while (1)
        {
            /* get user's selection and branch based on the input */
    
            switch(menu())
            {
                case 1:
                {
                    puts("\nExecuting choice 1");
                    delay();
                    break;
                }
                case 2:
                {
                    puts("\nExecuting choice 2");
                    delay();
                    break;
                }
                case 3:
                {
                    puts("\nExecuting choice 3");
                    delay();
                    break;
                }
                case 4:
                {
                    puts("\nExecuting choice 4");
                    delay();
                    break;
                }
                case 5:  /* exit program */
                {
                    puts("\nExiting program now...\n");
                    delay();
                    exit(0);
                }
                default:
                {
                    puts("\nInvalid choice, try again");
                    delay();
                }
            }/* end of switch */
    
        } /* end of while */
    
    }
    
    /* displays a menu and inputs user selection */
    int menu(void)
    {
        int reply;
    
        puts("\nEnter 1 for task A");
        puts("Enter 2 for task B");
        puts("Enter 3 for task C");
        puts("Enter 4 for task D");
        puts("Enter 5 to exit program");
    
        scanf("%d", &reply);
    
        return reply;
    }
    
    void delay(void)
    {
        long x;
        for (x = 0; x < DELAY; x++)
            ;
    }
    Thanks in advance.

  2. #2
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Either change how you read numbers (read the FAQ), or check the return value of scanf and/or clear out any invalid input.


    Quzah.
    Hope is the first step on the road to disappointment.

  3. #3
    Registered User
    Join Date
    Apr 2011
    Posts
    23
    While I appreciate the concise answer, could you point me towards what I need to look at in the FAQ, I've gone through a bunch of it and have no been able to find an answer that related to C, but I have found a C++ answer, but this doesn't help me at all.

    edit: actually I found my answer. A bit more involved than I would have though, but it works well.
    Last edited by ModeSix; 05-07-2011 at 11:52 PM.

  4. #4
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Cprogramming.com FAQ > How do I get a number from the user (C)

    scanf is picky, and if you don't give it what you tell it to expect, it doesn't like it. If you tell it to read a number, and there's a letter there, it leaves it there, which is what's happening to you.


    Quzah.
    Hope is the first step on the road to disappointment.

  5. #5
    Registered User
    Join Date
    Apr 2011
    Posts
    23
    I finally realized what was happening, and found information about converting strings to integers.

    Ended up using this code for my menu() function and it does the trick. Thanks for pointing me in the right direction.

    Code:
    int menu(void)
    {
        int reply;
        char buf[BUFSIZ];
    
        puts("\nEnter 1 for task A");
        puts("Enter 2 for task B");
        puts("Enter 3 for task C");
        puts("Enter 4 for task D");
        puts("Enter 5 to exit program");
    
        if(fgets(buf, sizeof(buf), stdin) != NULL)
        {
            reply = atoi(buf);
        }
    
        return reply;
    }

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    You can do it that way or you can use fgetc() and make the numbers in your cases in to characters ... as in case '1':

  7. #7
    Registered User
    Join Date
    Apr 2011
    Posts
    23
    Quote Originally Posted by CommonTater View Post
    You can do it that way or you can use fgetc() and make the numbers in your cases in to characters ... as in case '1':
    Interesting idea. I will try this and see how it works out.

    Code:
    char menu(void)
    {
        char reply;
        //char buf[BUFSIZ];
    
        puts("\nEnter 1 for task A");
        puts("Enter 2 for task B");
        puts("Enter 3 for task C");
        puts("Enter 4 for task D");
        puts("Enter 5 to exit program");
    
        fgetc(reply);
    
        return reply;
    }
    
    Error output:
    I:\Ctutorial\cin21days\cin21days\day13\list1307\main.c||In function 'menu':|
    I:\Ctutorial\cin21days\cin21days\day13\list1307\main.c|74|warning: passing argument 1 of 'fgetc' makes pointer from integer without a cast|
    i:\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.4.1\..\..\..\..\include\stdio.h|352|note: expected 'struct FILE *' but argument is of type 'char'|
    I:\Ctutorial\cin21days\cin21days\day13\list1307\main.c|74|warning: 'reply' is used uninitialized in this function|
    ||=== Build finished: 0 errors, 2 warnings ===|
    Apparently I am not understand the implementation of fgetc()
    Last edited by ModeSix; 05-08-2011 at 03:04 AM.

  8. #8
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    You could always read the man page.
    Code:
    reply = fgetc( stdin );
    Or just:
    Code:
    return fgetc( stdin );
    Or you could just merge the two functions, since I don't really see any reason to split them:
    Code:
    do
    {
        print your menu options here
        switch( fgetc( stdin ) )
        {
            ...your cases...
        }
    while( invalidinput == true );
    Quzah.
    Hope is the first step on the road to disappointment.

  9. #9
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by quzah View Post
    Code:
    while( invalidinput == true );
    Quzah.
    LOL... a menu that only works on bad entries... I'm thinking ... while (invalidinput == false);

    But yes, there is no reason --and little sense-- in splitting an obvious single result into two pieces. He should put the whole thing in main.

  10. #10
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by ModeSix View Post
    Apparently I am not understand the implementation of fgetc()
    The single most important skill a programmer can develop is ... looking "stuff" up...
    The second most important skill is learning to read error messages... the error output you posted told you fgetc() expected a file pointer.

  11. #11
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by CommonTater View Post
    LOL... a menu that only works on bad entries... I'm thinking ... while (invalidinput == false);
    No, I had this in mind:
    Code:
    invalidinput = true;
    do
    {
        printf( "enter 1, everything else is invalid: " );
        fflush( stdout )
    
        switch( fgetc( stdin ) )
        {
            case '1':
                invalidinput = false;
                printf( "yay\n" );
            break;
            default:
                printf( "invalid input. try entering 1 again!\n" );
            break;
        }
    } while( invalidinput == true );
    Edit - Oh, I see what you mean. It will quit after one valid action. I guess he threw me off with the way he was splitting the two functions. I was trying to combine his printing the menu choices with the menu, and was thinking of it as validating input. I guess I mixed up what I wanted to do.


    Quzah.
    Last edited by quzah; 05-08-2011 at 03:20 PM.
    Hope is the first step on the road to disappointment.

  12. #12
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by quzah View Post
    No, I had this in mind:
    No silly... like this...
    Code:
    invalidinput = false;
    do
    {
        printf( "enter 1, everything else is invalid: " );
        fflush( stdout )
    
        switch( fgetc( stdin ) )
        {
            case '1':
                printf( "yay\n" );
            break;
            default:
                invalidinput = true;
                printf( "invalid input. try entering 1 again!\n" );
            break;
        }
    } while( invalidinput == false );

  13. #13
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    It still doesn't let you run the menu repeatedly, and will bail out on the first invalid attempt. I wanted a nice single loop here, but it's probably for the best to keep validating input separate from the actual menu.

    edit - I suppose it just needs to have a case to exit the loop as a valid option, and everything else just says to re-enter it:
    Code:
    while( !done )
        get input
        switch input
            case done break;
            case ... break;
            default: say try again

    Quzah.
    Last edited by quzah; 05-08-2011 at 04:36 PM.
    Hope is the first step on the road to disappointment.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. loops, menu loops
    By gloworm in forum C Programming
    Replies: 17
    Last Post: 04-12-2010, 07:59 PM
  2. Replies: 3
    Last Post: 03-16-2010, 02:29 PM
  3. noob question re: switch case & loops
    By scooglygoogly in forum C++ Programming
    Replies: 13
    Last Post: 08-25-2008, 11:08 AM
  4. Menu System using switch and case.
    By Spectrum48k in forum C Programming
    Replies: 3
    Last Post: 07-23-2002, 08:23 AM
  5. Help me in while loop with case menu
    By playboy1620 in forum C Programming
    Replies: 1
    Last Post: 02-20-2002, 11:07 PM