返回主菜单保持循环菜单 [英] Go Back To Main Menu Keeps Looping Menus

查看:41
本文介绍了返回主菜单保持循环菜单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当程序第一次启动时,我可以成功地从主菜单中选择任何选项.但是,当我从任何子菜单中选择返回主菜单选项时,它将返回主菜单,但无论我之后再次按下哪个选项,它都会继续循环该菜单.只允许我选择返回主菜单选项.如何将选择重置到它不会继续循环的地方?我尽可能地缩短了代码,以便它仍然可以编译但也可以证明错误.提前致谢.

When the program first starts I can select any option from the main menu successfully. However, when I select the go back to main menu option from any of the submenus it'll go back to the main menu but no matter what option I press again afterwards it will continue to loop that menu. Only allowing me to select the go back to main menu option. How do I reset the selection to where it won't continue to loop? I have shortened the code as best as possible so that it can still compile but also demonstrate the error. Thank you in advance.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

int main()
{
    //declare all working variables: mOption, FManOption, COption...etc...
    int MOption = 0;
    int FManOption = 0;
    int FOption = 0;
    int COption = 0;
    int userChoice = 0;

    //declarations for all arrays of struct
    //declare a pointer to an array of struct using malloc() for fisherman, fish, catch

    //process:
    printf("Please select 1 to start the program or 0 to quit: ");
    scanf("%d", &userChoice);
    while(userChoice != 1 && userChoice != 0)
    {
        printf("Invalid selection! Please type a 1 or a 0: ");
        scanf("%d", &userChoice);
    }//end (userChoice != 1 && userChoice != 0)
    if(userChoice != 1)
        printf("Thank you for wasting my time! Have a great day!");
    else
    {

      MOption = mainMenu();


        switch(MOption)
        {
            case 1: FManOption = FishermanMenu();
                    while(FManOption != 3)
                    {
                        switch(FManOption)
                        {
                            case 1: getFisherman();//get a fisherman
                                    //count fisherman
                                    break;
                            case 2: //prompt for a ssn, validate, search
                                    //if found display everything about this fisherman
                                    break;
                            case 3: FManOption = mainMenu();

                                    //reset FManOption
                                    break;
                            default: printf("\nInvalid selection! Please select from one of the menu options\n");
                        }//end switch(FManOption)
                    }//end while(FManOption != 3)
                    break;
        }
    }
}

int mainMenu()
{
    int Option;

    printf("\n-------Welcome to the Fishing Tournament Main Menu!-------\n");
    printf("1 - Fisherman menu\n");
    printf("2 - Fish menu\n");
    printf("3 - Tournament(Catch) menu\n");
    printf("4 - Close Tournament (determine winner)\n");
    printf("5 - Quit Program\n\n");
    printf("Please select a menu option: ");
    scanf("%d", &Option);
    if(Option > 5 || Option < 1)
        do /* check scanf() return value for input errors */
        {
            printf("\nInvalid selection! Please select from one of the menu options\n");
            printf("1 - Fisherman menu\n");
            printf("2 - Fish menu\n");
            printf("3 - Tournament(Catch) menu\n");
            printf("4 - Close Tournament (determine winner)\n");
            printf("5 - Quit Program\n\n");
            printf("Please select a menu option: ");
            scanf("%d", &Option);
        }
        while(Option > 5 || Option < 1);

    return Option; /* finally return the final correct option */
}//end main menu

int FishermanMenu()
{
    int ManOption;
    printf("\n-------Fisherman Menu-------\n");
    printf("1 - Register fisherman\n");
    printf("2 - Search fisherman\n");
    printf("3 - Go back to main menu\n");
    printf("Please select a menu option: ");
    scanf("%d", &ManOption);
    if(ManOption > 5 || ManOption < 1)
        do /* check scanf() return value for input errors */
        {
            printf("\nInvalid selection! Please select from one of the menu options\n");/* handle input error */
            printf("1 - Register fisherman\n");
            printf("2 - Search fisherman\n");
            printf("3 - Go back to main menu\n");
            printf("Please select a menu option: ");
            scanf("%d", &ManOption);
        }
        while(ManOption > 5 || ManOption < 1);
    return ManOption; /* finally return the final correct option */
}//end Fisherman Menu

推荐答案

问题是你永远卡在这个 while 循环中:

The problem is that you get stuck in this while loop forever:

while (FManOption != 3)

这个循环只有当你在渔夫"里面时才有意义.子菜单,但您应该离开此循环,并在用户选择返回主菜单"后将程序返回到之前的状态.

This loop only makes sense when you are inside the "Fisherman" sub-menu, but you should leave this loop and return the program to its previous state after the user selects "Go back to main menu".

不要尝试以程序的状态(例如用户当前是在主菜单还是子菜单中)的方式编写代码,而是由程序的控制流暗示更容易将程序状态显式存储在变量中,例如:

Instead of attempting to write your code in such a way that the state of the program (e.g. whether the user is currently in the main menu or a sub-menu) is implied by the control flow of the program, it is often easier to store the program state explicitly in a variable, for example like this:

enum menu_state
{
    MENUSTATE_MAIN,
    MENUSTATE_FISHERMAN,
    MENUSTATE_FISH,
    MENUSTATE_TOURNAMENT_CATCH,
    MENUSTATE_CLOSE_TOURNAMENT
};

int main( void )
{
    [...]
    if (userChoice != 1)
        printf("Thank you for wasting my time! Have a great day!");
    else
    {
        enum menu_state ms = MENUSTATE_MAIN;

        for (;;) //infinite loop, equivalent to while(true)
        {
            switch ( ms )
            {
                case MENUSTATE_MAIN:
                    switch ( mainMenu() )
                    {
                        case 1:
                            printf( "opening fisherman menu\n" );
                            ms = MENUSTATE_FISHERMAN;
                            break;
                        case 2:
                            printf( "opening fish menu\n" );
                            ms = MENUSTATE_FISH;
                            break;
                        case 3:
                            printf( "opening tournament(catch) menu\n" );
                            ms = MENUSTATE_TOURNAMENT_CATCH;
                            break;
                        case 4:
                            printf( "opening close tournament menu\n" );
                            ms = MENUSTATE_CLOSE_TOURNAMENT;
                            break;
                        case 5:
                            //quit program
                            exit( EXIT_SUCCESS );
                        default:
                            fprintf( stderr, "unexpected error\n" );
                            exit( EXIT_FAILURE );
                    }
                    break;
                case MENUSTATE_FISHERMAN:
                    switch ( FishermanMenu() )
                    {
                        case 1:
                            printf( "Register fisherman not yet implemented.\n" );
                            break;
                        case 2:
                            printf( "Search fisherman not yet implemented.\n" );
                            break;
                        case 3:
                            //change program state back to main menu
                            ms = MENUSTATE_MAIN;
                            break;
                        default:
                            fprintf( stderr, "unexpected error\n" );
                            exit( EXIT_FAILURE );
                    }
                    break;
                case MENUSTATE_FISH:
                    printf( "Fish menu not yet implemented, returning to main menu.\n" );
                    ms = MENUSTATE_MAIN;
                    break;
                case MENUSTATE_TOURNAMENT_CATCH:
                    printf( "Tournament(catch) menu not yet implemented, returning to main menu.\n" );
                    ms = MENUSTATE_MAIN;
                    break;
                case MENUSTATE_CLOSE_TOURNAMENT:
                    printf( "Close tournament not yet implemented, returning to main menu.\n" );
                    ms = MENUSTATE_MAIN;
                    break;
                default:
                    fprintf( stderr, "unexpected error\n" );
                    exit( EXIT_FAILURE );
            }
        }
    }
}

还值得注意的是,您的函数mainMenuFishermanMenu 包含不必要的代码重复.您可以通过以下方式简化 mainMenu 函数:

It is also worth noting that your functions mainMenu and FishermanMenu contain unnecessary code duplicaton. You can simplify the function mainMenu the following way:

int mainMenu( void )
{
    for (;;) //repeat forever, until input is valid
    {
        int option;
        printf("\n-------Welcome to the Fishing Tournament Main Menu!-------\n");
        printf("1 - Fisherman menu\n");
        printf("2 - Fish menu\n");
        printf("3 - Tournament(Catch) menu\n");
        printf("4 - Close Tournament (determine winner)\n");
        printf("5 - Quit Program\n\n");
        printf("Please select a menu option: ");
        scanf("%d", &option);

        if ( 1 <= option && option <= 5 )
            return option;

        printf("\nInvalid selection! Please select from one of the menu options\n");
    }
}

然而,在尝试使用 scanf 的结果之前,始终检查函数 scanf 是否成功是很重要的.这可以通过检查 scanf 的返回值来完成.此外,在使用 scanf 之后,重要的是丢弃该行其余部分的输入.否则,如果用户例如输入 "12dfghoh",则对 scanf 的所有后续调用都将失败,因为它将无法删除 dfghoh 尝试读取数字时从输入流中读取.

However, it is important to always check whether the function scanf succeeded before attempting to use the result of scanf. This can be accomplished by checking the return value of scanf. Also, after using scanf, it is important to discard the input from the rest of the line. Otherwise, if the user for example enters "12dfghoh", then all subsequent calls to scanf will fail, because it won't be able to remove dfghoh from the input stream when trying to read a number.

因此,这是我检查 scanf 的返回值并丢弃该行中所有剩余输入的代码:

Therefore, here is the code in which I check the return value of scanf and also discard all remaining input in the line:

int mainMenu( void )
{
    for (;;) //repeat forever, until input is valid
    {
        int option, c;

        printf("\n-------Welcome to the Fishing Tournament Main Menu!-------\n");
        printf("1 - Fisherman menu\n");
        printf("2 - Fish menu\n");
        printf("3 - Tournament(Catch) menu\n");
        printf("4 - Close Tournament (determine winner)\n");
        printf("5 - Quit Program\n\n");
        printf("Please select a menu option: ");
        if (
            scanf("%d", &option) == 1 && //make sure scanf succeeded
            1 <= option && option <= 5
        )
        {
            return option;
        }

        printf("\nInvalid selection! Please select from one of the menu options\n");

        //discard remainder of line, which may contain bad input
        //and prevent the next call of scanf to succeed
        do
        {
            c = getchar();
        }
        while ( c != EOF && c != '\n' );
    }
}

另一方面,对于基于行的输入,最好使用 fgets 而不是 scanf,因为这样您就不必处理从输入流中删除错误输入的问题.有关更多信息,请参阅此链接:

On the other hand, for line-based input, it would probably be better to use fgets instead of scanf, because then you don't have to deal with removing bad input from the input stream. See this link for further information:

远离 scanf() 的初学者指南

使用 fgets 函数,函数 mainMenu 看起来像这样:

Using the fgets function, the function mainMenu would look like this:

//NOTE: the following header must be added
#include <ctype.h>

int mainMenu( void )
{
    for (;;) //repeat forever, until input is valid
    {
        char buffer[1024], *p;
        long option;

        printf("\n-------Welcome to the Fishing Tournament Main Menu!-------\n");
        printf("1 - Fisherman menu\n");
        printf("2 - Fish menu\n");
        printf("3 - Tournament(Catch) menu\n");
        printf("4 - Close Tournament (determine winner)\n");
        printf("5 - Quit Program\n\n");
        printf("Please select a menu option: ");

        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            printf( "unexpected input error!\n" );

            //since this type of error is probably not recoverable,
            //don't try again, but instead exit program
            exit( EXIT_FAILURE );
        }

        option = strtol( buffer, &p, 10 );

        if ( p == buffer )
        {
            printf( "error converting string to number\n" );
            continue;
        }

        //make sure remainder of line contains only whitespace,
        //so that input such as "12dfghoh" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "unexpected input encountered!\n" );
                continue;
            }
        }

        //make sure input is in the desired range
        if ( option < 1 || option > 5 )
        {
            printf( "input must be between 1 and 5\n" );
            continue;
        }

        return option;
    }
}

但是,您不能简单地将代码中的函数 mainMenu 替换为我上面的代码,因为在代码中混合 scanffgets不太好用.这是因为 scanf 不会一次从输入流中读取一行,而只会提取读取数字所需的数量,并保留该行的其余部分,包括换行符, 在缓冲区中.因此,如果您在 scanf 之后立即使用 fgetsfgets 将读取 scanf 没有的行的其余部分提取,通常是一个只包含换行符的字符串.

However, you cannot simply replace the function mainMenu in your code with my code above, because mixing scanf and fgets in your code will not work very well. This is because scanf will not read one line at a time from the input stream, but will only extract as much as it needs to read the number and will leave the rest of the line, including the newline character, in the buffer. Therefore, if you use fgets immediately after scanf, fgets will read the remainder of the line that scanf did not extract, which will be often a string containing nothing else than the newline character.

因此,如果您决定使用 fgets(我推荐),那么您应该在程序中的任何地方使用它,而不是将它与 scanf 混合使用,例如这个:

Therefore, if you decide to use fgets (which I recommend), then you should use it everywhere in your program and not mix it with scanf, for example like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>

int mainMenu(void);
int FishermanMenu(void);
int get_int_from_user( const char *prompt );

enum menu_state
{
    MENUSTATE_MAIN,
    MENUSTATE_FISHERMAN,
    MENUSTATE_FISH,
    MENUSTATE_TOURNAMENT_CATCH,
    MENUSTATE_CLOSE_TOURNAMENT
};

int main( void )
{
    int user_choice;

    for (;;) //loop forever until input is valid
    {
        user_choice = get_int_from_user(
            "Please select 1 to start the program or 0 to quit: "
        );

        if ( user_choice == 0 )
        {
            printf("Thank you for wasting my time! Have a great day!");
            exit( EXIT_SUCCESS );
        }

        if ( user_choice == 1 )
        {
            //input is valid, so break infinite loop
            break;
        }

        printf( "Invalid selection!\n" );
    }

    enum menu_state ms = MENUSTATE_MAIN;

    for (;;) //main program loop
    {
        switch ( ms )
        {
            case MENUSTATE_MAIN:
                switch ( mainMenu() )
                {
                    case 1:
                        printf( "opening fisherman menu\n" );
                        ms = MENUSTATE_FISHERMAN;
                        break;
                    case 2:
                        printf( "opening fish menu\n" );
                        ms = MENUSTATE_FISH;
                        break;
                    case 3:
                        printf( "opening tournament(catch) menu\n" );
                        ms = MENUSTATE_TOURNAMENT_CATCH;
                        break;
                    case 4:
                        printf( "opening close tournament menu\n" );
                        ms = MENUSTATE_CLOSE_TOURNAMENT;
                        break;
                    case 5:
                        //quit program
                        exit( EXIT_SUCCESS );
                    default:
                        fprintf( stderr, "unexpected error\n" );
                        exit( EXIT_FAILURE );
                }
                break;
            case MENUSTATE_FISHERMAN:
                switch ( FishermanMenu() )
                {
                    case 1:
                        printf( "Register fisherman not yet implemented.\n" );
                        break;
                    case 2:
                        printf( "Search fisherman not yet implemented.\n" );
                        break;
                    case 3:
                        //change program state back to main menu
                        ms = MENUSTATE_MAIN;
                        break;
                    default:
                        fprintf( stderr, "unexpected error\n" );
                        exit( EXIT_FAILURE );
                    }
                break;
            case MENUSTATE_FISH:
                printf( "Fish menu not yet implemented, returning to main menu.\n" );
                ms = MENUSTATE_MAIN;
                break;
            case MENUSTATE_TOURNAMENT_CATCH:
                printf( "Tournament(catch) menu not yet implemented, returning to main menu.\n" );
                ms = MENUSTATE_MAIN;
                break;
            case MENUSTATE_CLOSE_TOURNAMENT:
                printf( "Close tournament not yet implemented, returning to main menu.\n" );
                ms = MENUSTATE_MAIN;
                break;
            default:
                fprintf( stderr, "unexpected error\n" );
                exit( EXIT_FAILURE );
        }
    }
}

int mainMenu( void )
{
    for (;;) //repeat forever, until input is in desired range
    {
        int option;

        option = get_int_from_user(
            "\n-------Welcome to the Fishing Tournament Main Menu!-------\n"
            "1 - Fisherman menu\n"
            "2 - Fish menu\n"
            "3 - Tournament(Catch) menu\n"
            "4 - Close Tournament (determine winner)\n"
            "5 - Quit Program\n\n"

            "Please select a menu option: "
        );

        //make sure input is in the desired range
        if ( option < 1 || option > 5 )
        {
            printf( "input must be between 1 and 5\n" );
            continue;
        }

        return option;
    }
}

int FishermanMenu()
{
    for (;;) //repeat forever, until input is in desired range
    {
        int option;

        option = get_int_from_user(
            "\n-------Fisherman Menu-------\n"
            "1 - Register fisherman\n"
            "2 - Search fisherman\n"
            "3 - Go back to main menu\n"
            "Please select a menu option: "
        );

        //make sure input is in the desired range
        if ( option < 1 || option > 3 )
        {
            printf( "input must be between 1 and 3\n" );
            continue;
        }

        return option;
    }
}

int get_int_from_user( const char *prompt )
{
    for (;;) //loop forever until user enters a valid number
    {
        char buffer[1024], *p;
        long l;

        puts( prompt );

        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "unrecoverable error reading from input\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL )
        {
            int c;

            printf("line input was too long!\n");

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF)
                {
                    fprintf( stderr, "unrecoverable error reading from input\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        errno = 0;
        l = strtol( buffer, &p, 10 );

        if ( p == buffer )
        {
            printf( "error converting string to number\n" );
            continue;
        }

        if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
        {
            printf( "number out of range error\n" );
            continue;
        }

        //make sure remainder of line contains only whitespace,
        //so that input such as "12dfghoh" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto next_outer_loop_iteration;
            }
        }

        return l;

next_outer_loop_iteration:
        continue;
    }
}

在上面的代码中,我创建了一个新函数 get_int_from_user,它执行广泛的输入验证.

In the above code, I created a new function get_int_from_user, which performs extensive input validation.

另一个问题是,菜单处理函数mainMenuFishermanMenu 简单地将用户输入的数字传回main 功能.这些函数自己解释和处理输入会更有意义.

Another problem is that it is not meaningful for the menu handling functions mainMenu and FishermanMenu to simply pass the number that the user entered back to the main function. It would be more meaningful for these functions to interpret and handle the input themselves.

正如在另一个答案中已经建议的那样,您可以更改函数 mainMenuFishermanMenu 以将程序的新状态返回到 main,因为这将是 main 需要的唯一信息,假设输入由函数 mainMenuFishermanMenu 解释和处理.

As already suggested in the other answer, you could change the functions mainMenu and FishermanMenu to instead return the new state of the program back to main, as that would be the only information main needs, assuming that the input is interpreted and handled by the functions mainMenu and FishermanMenu.

在这种情况下,您的程序代码将如下所示:

In that case, the code of your program would instead look like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>

int mainMenu(void);
enum menu_state FishermanMenu(void);
enum menu_state get_int_from_user( const char *prompt );

enum menu_state
{
    MENUSTATE_MAIN,
    MENUSTATE_FISHERMAN,
    MENUSTATE_FISH,
    MENUSTATE_TOURNAMENT_CATCH,
    MENUSTATE_CLOSE_TOURNAMENT,
    MENUSTATE_QUIT
};

int main( void )
{
    int user_choice;

    for (;;) //loop forever until input is valid
    {
        user_choice = get_int_from_user(
            "Please select 1 to start the program or 0 to quit: "
        );

        if ( user_choice == 0 )
        {
            printf("Thank you for wasting my time! Have a great day!");
            exit( EXIT_SUCCESS );
        }

        if ( user_choice == 1 )
        {
            //input is valid, so break infinite loop
            break;
        }

        printf( "Invalid selection!\n" );
    }

    enum menu_state ms = MENUSTATE_MAIN;

    for (;;) //main program loop
    {
        switch ( ms )
        {
            case MENUSTATE_MAIN:
                ms = mainMenu();
                break;
            case MENUSTATE_FISHERMAN:
                ms = FishermanMenu();
                break;
            case MENUSTATE_FISH:
                printf( "Fish menu not yet implemented, returning to main menu.\n" );
                ms = MENUSTATE_MAIN;
                break;
            case MENUSTATE_TOURNAMENT_CATCH:
                printf( "Tournament(catch) menu not yet implemented, returning to main menu.\n" );
                ms = MENUSTATE_MAIN;
                break;
            case MENUSTATE_CLOSE_TOURNAMENT:
                printf( "Close tournament not yet implemented, returning to main menu.\n" );
                ms = MENUSTATE_MAIN;
                break;
            case MENUSTATE_QUIT:
                return;
            default:
                fprintf( stderr, "unexpected error\n" );
                exit( EXIT_FAILURE );
        }
    }
}

enum menu_state mainMenu( void )
{
    for (;;) //repeat forever, until input is in desired range
    {
        int option;

        option = get_int_from_user(
            "\n-------Welcome to the Fishing Tournament Main Menu!-------\n"
            "1 - Fisherman menu\n"
            "2 - Fish menu\n"
            "3 - Tournament(Catch) menu\n"
            "4 - Close Tournament (determine winner)\n"
            "5 - Quit Program\n\n"

            "Please select a menu option: "
        );

        switch (option)
        {
            case 1:
                printf( "opening fisherman menu\n" );
                return MENUSTATE_FISHERMAN;
            case 2:
                printf( "opening fish menu\n" );
                return MENUSTATE_FISH;
            case 3:
                printf( "opening tournament(catch) menu\n" );
                return MENUSTATE_TOURNAMENT_CATCH;
            case 4:
                printf( "opening close tournament menu\n" );
                return MENUSTATE_CLOSE_TOURNAMENT;
            case 5:
                printf( "quitting program\n" );
                return MENUSTATE_QUIT;
            default:
                printf( "input must be between 1 and 5\n" );
                continue;
        }
    }
}

enum menu_state FishermanMenu()
{
    for (;;) //repeat forever, until input is in desired range
    {
        int option;

        option = get_int_from_user(
            "\n-------Fisherman Menu-------\n"
            "1 - Register fisherman\n"
            "2 - Search fisherman\n"
            "3 - Go back to main menu\n"
            "Please select a menu option: "
        );

        switch ( option )
        {
            case 1:
                printf( "Register fisherman not yet implemented.\n" );
                return MENUSTATE_FISHERMAN;
            case 2:
                printf( "Search fisherman not yet implemented.\n" );
                return MENUSTATE_FISHERMAN;
            case 3:
                //change program state back to main menu
                return MENUSTATE_MAIN;
                break;
            default:
                printf("input must be between 1 and 3\n");
                continue;
        }
    }
}

int get_int_from_user( const char *prompt )
{
    for (;;) //loop forever until user enters a valid number
    {
        char buffer[1024], *p;
        long l;

        puts( prompt );

        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "unrecoverable error reading from input\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL )
        {
            int c;

            printf("line input was too long!\n");

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF)
                {
                    fprintf( stderr, "unrecoverable error reading from input\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        errno = 0;
        l = strtol( buffer, &p, 10 );

        if ( p == buffer )
        {
            printf( "error converting string to number\n" );
            continue;
        }

        if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
        {
            printf( "number out of range error\n" );
            continue;
        }

        //make sure remainder of line contains only whitespace,
        //so that input such as "12dfghoh" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto next_outer_loop_iteration;
            }
        }

        return l;

next_outer_loop_iteration:
        continue;
    }
}

再想一想,我不确定我之前的建议是否正确.因为您似乎有一个严格的菜单层次结构,所以在您的情况下,不将程序的状态存储在单独的变量中可能更简单,而是让程序的控制流隐含菜单状态.在这种情况下,您的程序将如下所示:

On second thought, I'm not sure if my previous advice was correct. Because you seem to have a strict menu hierarchy, it may be simpler in your case not to store the state of the program in a separate variable, but to instead let the menu state be implied by the control flow of the program. In that case, your program would look like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>

void MainMenu( void );
void FishermanMenu( void );
void FishMenu( void );
void TournamentCatchMenu( void );
void CloseTournamentMenu( void );

int get_int_from_user( const char *prompt );

int main(void)
{
    int user_choice;

    for (;;) //loop forever until input is valid
    {
        user_choice = get_int_from_user(
            "Please select 1 to start the program or 0 to quit: "
        );

        if (user_choice == 0)
        {
            printf("Thank you for wasting my time! Have a great day!");
            exit(EXIT_SUCCESS);
        }

        if (user_choice == 1)
        {
            //input is valid, so break infinite loop
            break;
        }

        printf("Invalid selection!\n");
    }

    MainMenu();
}

void MainMenu(void)
{
    for (;;) //repeat forever, until input is in desired range
    {
        int option;

        option = get_int_from_user(
            "\n-------Welcome to the Fishing Tournament Main Menu!-------\n"
            "1 - Fisherman menu\n"
            "2 - Fish menu\n"
            "3 - Tournament(Catch) menu\n"
            "4 - Close Tournament (determine winner)\n"
            "5 - Quit Program\n\n"

            "Please select a menu option: "
        );

        switch (option)
        {
            case 1:
                FishermanMenu();
                break;
            case 2:
                FishMenu();
                break;
            case 3:
                TournamentCatchMenu();
                break;
            case 4:
                CloseTournamentMenu();
                break;
            case 5: 
                return;
            default:
                printf( "input must be between 1 and 5\n" );
                continue;
        }
    }
}

void FishermanMenu()
{
    for (;;) //repeat forever, until input is in desired range
    {
        int option;

        option = get_int_from_user(
            "\n-------Fisherman Menu-------\n"
            "1 - Register fisherman\n"
            "2 - Search fisherman\n"
            "3 - Go back to main menu\n"
            "Please select a menu option: "
        );

        switch (option)
        {
            case 1:
                printf( "Register fisherman not yet implemented.\n" );
                break;
            case 2:
                printf( "Search fisherman not yet implemented.\n" );
                break;
            case 3:
                printf( "Returning to main menu.\n" );
                return;
            default:
                printf( "input must be between 1 and 5\n" );
                continue;
        }
    }
}

void FishMenu()
{
    printf( "Fish Menu not yet implemented, please select another menu item.\n" );
}

void TournamentCatchMenu()
{
    printf( "Tournament(Catch) Menu not yet implemented, please select another menu item.\n" );
}

void CloseTournamentMenu()
{
    printf( "Close Tournament Menu not yet implemented, please select another menu item.\n" );
}

int get_int_from_user( const char *prompt )
{
    for (;;) //loop forever until user enters a valid number
    {
        char buffer[1024], *p;
        long l;

        puts( prompt );

        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "unrecoverable error reading from input\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL )
        {
            int c;

            printf("line input was too long!\n");

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF)
                {
                    fprintf( stderr, "unrecoverable error reading from input\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        errno = 0;
        l = strtol( buffer, &p, 10 );

        if ( p == buffer )
        {
            printf( "error converting string to number\n" );
            continue;
        }

        if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
        {
            printf( "number out of range error\n" );
            continue;
        }

        //make sure remainder of line contains only whitespace,
        //so that input such as "12dfghoh" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto next_outer_loop_iteration;
            }
        }

        return l;

next_outer_loop_iteration:
        continue;
    }
}

然而,即使这个解决方案更简单、更简洁,也没有之前的解决方案灵活.如果您后来决定放松菜单层次结构的严格性(例如,允许直接从一个菜单跳转到菜单层次结构中完全不同位置的另一个菜单),那么事情会很快变得混乱.

However, even if this solution is simpler and cleaner, it is not as flexible as the previous solution. If you later decide to loosen the strictness of the menu hierarchy (for example by allowing to directly jump from one menu to another menu in a completely different place in the menu hierarchy), then things would get messy very quickly.

这篇关于返回主菜单保持循环菜单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆