[C语言] 实现简单的菜单式互动程序2

2019-07-31  本文已影响0人  入梦瞌睡

0.前言

  1. 在上一篇文章《[C语言] 实现简单的菜单式互动程序》中,已经讲述了如何使用c语言实现一个简单的菜单式互动程序(一个模拟ATM机的小程序)
  2. 正如上一篇文章的总结所说,所有的代码全部放入main函数中,会导致:
    ①代码冗杂; ②不利于阅读; ③不利于移植
  3. 因此,本文在对上一篇文章的代码进行一定改进的同时,也着重于分享介绍:应该如何将main函数中冗杂的代码,按照模块化的思想,通过自定义函数,让整个程序更加干净整洁

1.程序编写目的

1.1练习函数的定义以及使用
1.2通过训练培养代码的模块化思想
1.3训练程序编写逻辑

2.实现该程序所需要的技术

2.1自定义函数

自定义函数格式:

函数返回值类型 函数名(参数,参数....){

}

实际的例子:

int add(int a,int b){//a和b是形式参数
     int result = 0;
     result = a + b;
     return result;//返回值
}

2.2输入检测判断

使用该技术的原因:当按照菜单提示进行操作的选择时,用户可能会不小心输入错误,(如输入字母、1至5以外的数字等)因此需要加入输入检测判断的功能,具体如图所示:


输入不合法.png

2.3主函数的编写方式

如果代码全部塞入主函数中,就会导致主函数过于的冗杂,那是不是让主函数尽可能简单就是好的函数呢?
答案是否定的。

void main(){
     login();
     showMenu();
     choice();
}

这么写确实主函数含简洁,但是别人看到这个主函数并不知道这个程序的目的和意义。
这种写法除了欠缺注释以外,还缺少逻辑结构的展现
主函数的编写,主要是要做到:

3.技术的使用方式

程序的主要思路可以参考上一篇文章《如何用c实现简单的菜单式互动程序》
地址:https://www.jianshu.com/p/c78e8feaf426
这篇文章会以该地址中给出的代码(后文会称为“旧代码”)为基础进行改进,主要阐述如何使用函数、主函数如何编写、以及一些细节优化问题

3.1如何将不同模块更改为函数

函数的特点在于可以反复调用
因此,一般需要函数化的代码,就是那些需要重复使用的代码

3.1.1重复使用的代码要要编写成函数

遇到要重复使用的代码优先考虑将其定义在函数中,然后只需要调用该函数即可,
例如,在“旧代码”中,会有:

printf("***********************************\n"); 
printf("****  1.取款         2.存款  ******\n");
printf("****  3.显示余额             ******\n");
printf("****  4.设置密码     5.退出  ******\n");
printf("***********************************\n");

“旧代码”中这个菜单只会显示一次,如果用户输入内容过多,可能会出现用户看不到菜单的情况,因此在这次的改进中,会多次显示这个菜单,而且每次操作完成后会对控制台进行清屏重新显示,以保证用户界面的整洁。
而这个重复多次显示,则需要用到自定义函数

void showMenu() {
    printf("***********************************\n");
    printf("****  1.取款         2.存款  ******\n");//开始存、取款 
    printf("****  3.显示余额             ******\n");
    printf("****  4.设置密码     5.退出  ******\n");//请输入原始、新密码
    printf("***********************************\n");
}

由于是简单的显示,因此不需要返回值,也不需要参数,这种函数应该属于最简单的函数

3.1.2比较复杂的操作要编写成函数

在“旧代码”中,出现了:

while (flag == 'Y') {
         printf("请输入取款金额!\n");//取款成功,当前余额为
         scanf_s("%d", &money);
         if (money > remains) {
                    printf("余额不足,是否重新输入?(Y/N)\n");
                    while ((ch = getchar()) != '\n');//清空缓存区
                    scanf_s("%c", &flag);
         } else {
                     remains = remains - money;
                      printf("取款成功!\n");
                      flag = 'N';
                  }
        }

这一大段代码负责实现取款功能,这么一大段代码放在switch语句中的一个case里,本身就显得很臃肿,而switch又在main里面,那这个main函数就会变得很难看了
像这种一大段代码完成一个(复杂)操作的情况,就可以把这一大段代码编写成一个函数

void withdraw() {
    int outMoney = 0;
    while (1) {
        printf("请输入取款金额:");
        scanf_s("%d", &outMoney);
        if (outMoney > 0 && outMoney <= totalMoney) {
            totalMoney -= outMoney;
            printf("取款成功,余额为:%d", totalMoney);
            //提示是否继续
            if (!ifContinue()) {//不继续
                return;
            }
        }
        else {
            printf("余额不足,");
            if (!ifContinue()) {
                return;
            }
        }
    }
}

而switch语句中,则会变成:

case 1:
    withdraw();//取款
    break;

可见会变得十分简洁

大家可以对比一下使用函数与不使用函数二者的观感:

 case 1:
                while (flag == 'Y') {
                    printf("请输入取款金额!\n");//取款成功,当前余额为
                    scanf_s("%d", &money);
                    if (money > remains) {
                        printf("余额不足,是否重新输入?(Y/N)\n");
                        while ((ch = getchar()) != '\n');//清空缓存区
                        scanf_s("%c", &flag);
                    }
                    else {
                        remains = remains - money;
                        printf("取款成功!\n");
                        flag = 'N';
                        //while ((ch = getchar()) != '\n');//清空缓存区
                    }
                }
                //存款成功,当前余额为20->余额不足,是否继续Y/N
                break;
            case 2:
                printf("请输入存款金额!\n");
                scanf_s("%d", &money);
                remains = remains + money;
                //while ((ch = getchar()) != '\n');//清空缓存区
                printf("存款成功!\n");
                break;
            case 3:
                printf("您的余额为:%d\n", remains);
                break;
            case 4:
                printf("请输入新密码\n");
                scanf_s("%d",&password);
                printf("设置成功!\n");
                break;
            case 5:
                break;
            default:
                break;
            }

以上为“旧代码”中的写法,不使用函数,下面是使用函数的写法

        case '1':
            withdraw();//取款
            printf("取款结束\n");
            break;
        case '2':
            deposit();//存款
            printf("存款结束\n");
            break;
        case '3':
            showRemains();//显示余额
            break;
        case '4':
            setPassword();//修改密码
            printf("修改密码成功!\n");
            break;
        default:
            exitATM(EXIT_SUCCESS);
            break;
        }

对比结果一目了然,这样就能明白使用函数的原因

3.2如何编写主函数

这里就不放出“旧代码”了,“旧代码”把代码全不写在主函数中,十分臃肿,那么将每个模块函数化后,主函数应该得剩下些什么?写些什么呢?

void main() {
    //欢迎界面
    showWelcome();

    //输入密码
    bool result = loginATM();
    if (result == false) {
        exitATM(EXIT_FAILURE);
    }

    //主要操作
    while (1) {
        showMenu();
        //接收用户的选择
        
        char choice = getChoice();//获取操作
        switch (choice)
        {
        case '1':
            withdraw();//取款
            system("cls");
            printf("取款结束\n");
            break;
        case '2':
            deposit();
            system("cls");
            printf("存款结束\n");
            break;
        case '3':
            system("cls");
            showRemains();
            break;
        case '4':
            setPassword();
            system("cls");
            printf("修改密码成功!\n");
            break;
        default:
            exitATM(EXIT_SUCCESS);
            break;
        }
    }

}

这就是进行修改之后的主函数,我们可以清晰地看到每一部分的作用,另外,除了添加的注释外,换行(就是空白行)也很好地起到了将各个模块分开的任务
虽然看上去没有想象中的短,但实际上,我们可以根据主函数,得出整个程序的运行思路,这个是十分关键的

3.3如何进行输入检测

上文提到了输入检测,也有图片显示效果,那么这里先讲一下实现思路:

(1)将输入的内容放到一个char类型的数组中(每个字符一个元素)
(2)通过strlen函数判断字符串长度
(3)如果字符串长度大于1,证明输入了一长串内容,不符合要求
(4)如果字符串等于1,说明只有一个字符,判断其ASCII码即可

具体做法:

(1)先定义一个数组ch[20](20是假设最多输入了20个字符)
(2)用scanf_s("%s",ch,20);来把输入的字符串放入这个数组
(3)引入头文件stdlib.h(#include<stdlib.h>),使用strlen()函数计算字符串长度
(4)用一个int变量count来接收strlen的返回值,并判断count是否大于1,大于,则用while让其重新输入,等于1,则判断该字符串的第一个元素(ch[0])的ASCII码是否在49到53之间(‘1’的ASCII为49)

4.实际应用

将主函数分为几部分:
(1)欢迎界面
(2)密码检测
(3)主要操作
欢迎界面、密码检测需要作为一个函数,而主要操作则是将结构留在主函数中,着重将具体的操作进行函数化

void showMenu() {
    printf("***********************************\n");
    printf("****  1.取款         2.存款  ******\n");//开始存、取款 
    printf("****  3.显示余额             ******\n");
    printf("****  4.设置密码     5.退出  ******\n");//请输入原始、新密码
    printf("***********************************\n");
}
bool ifContinue() {//询问是否继续
    printf("是否继续?(y/n):\n");
    char chTemp;
    while ((chTemp = getchar()) != '\n');//这两句话用来清空缓存区
    char ch = getchar();
    
    if (ch != 'n') {
        //继续
        return true;
    }
    else {
        //退出
        return false;
    }
}


char getChoice() {//获取用户想要执行的操作的编号
    char temp[20] = {};
    //将输入的所有字符串全部从缓存中读取出来
    while (1) {
        printf("请选择操作:");
        char chTemp;
        while ((chTemp = getchar()) != '\n');//这两句话用来清空缓存区
        scanf_s("%s", temp, 20);//不用&,因为是数组
        int count = strlen(temp);
        if (count != 1) {
            printf("输入不合法,请重新");
        }
        else {//还得判断该字符正确与否
            char ch = temp[0];
            if (ch >= 49 && ch <= 53) {
                return ch;
            }
            else {
                printf("输入不合法,请重新");
            }
        }
    }

}

可见,实际上很多函数化就是将“旧代码”中对应的部分取出来放到函数中,然后做适当的修改,其他地方也是同理,就布全部贴出了
因此,在做基础训练的时候,就应该有意识地去按照模块化的思维去编写,即要以一个模块解决一个问题为目标去编写,而不是程序能运行就好

5.总结

(1)这次用编写函数的方式来完成这个程序,让我更加明白现实情况中是如何编写代码的。经过多次训练后,也逐渐掌握了哪些代码应该函数化,应该如何函数化
(2)还有一点很重要的是,这些练习让我第一次认识到应该如何在编写函数的情况下,更好地完成主函数(如何处理好主函数中的结构关系),主函数的结构的编写也是我觉得最困难的地方,需要更多地去练习

上一篇 下一篇

猜你喜欢

热点阅读