0.前言
- 在上一篇文章《[C语言] 实现简单的菜单式互动程序》中,已经讲述了如何使用c语言实现一个简单的菜单式互动程序(一个模拟ATM机的小程序)
- 正如上一篇文章的总结所说,所有的代码全部放入main函数中,会导致:
①代码冗杂; ②不利于阅读; ③不利于移植 - 因此,本文在对上一篇文章的代码进行一定改进的同时,也着重于分享介绍:应该如何将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以外的数字等)因此需要加入输入检测判断的功能,具体如图所示:

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");
}
- 密码检测也要作为一个函数loginATM(),不过该函数只需要负责密码的输入以及检测是否正确
- 而密码正确或是错误对应的操作包含在主函数而非loginATM()中
原因在主函数中对密码正确、错误的操作进行反应,能够让人更清楚程序结构 - 同理,switch函数依然保留在主函数中,原因同上(保留主要操作的结构)
- 而那些要多次实行的操作,如输出菜单、接受用户操作、询问是否继续,这些不影响程序结构,而是注重于实际操作的代码,都需要单独写一个函数
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)还有一点很重要的是,这些练习让我第一次认识到应该如何在编写函数的情况下,更好地完成主函数(如何处理好主函数中的结构关系),主函数的结构的编写也是我觉得最困难的地方,需要更多地去练习
网友评论