8.7.1 任务模块化
咱们来更具体的考虑菜单程序须要执行的任务。该程序须要获取用户的响应,而且须要基于该响应选择一系列的动做。并且,程序还应该提供一种方法让用户能够回到菜单以作更多的选择。C的switch语句是筛选动做的一个很方便的工具,由于每一个用户选择可对应于一个特定的case标签。可使用while语句来提供对菜单的重复访问。可使用伪代码按照下列方式描述该过程:函数
get choice工具
while choice is not 'q'ui
switch to desired chice and execute it 设计
get next choice调试
8.7.2 使执行更顺利code
程序顺利执行的目标:处理正确输入时顺利执行和处理错误输入时顺利执行。队列
例如,您能作的一件事是让“获取选项”部分筛选掉不合适的响应,从而仅使正确的响应被传送到switch语句。这代表须为输入过程提供一个只返回正确响应的函数。将其与while循环、switch语句相结合会产生下列的程序结构:
开发
#include <stdio.h> char get_choice (void); void count(void); int main(void) { int choice ; while((choice=get_choice())!='q') { switch(choice) { case 'a' : printf("Buy low ,sell high.\n"); break; case 'b' : putchar('\a'); /*ANSI*/ break; case 'c' : count(); break; default : printf("program error!\n"); break; } } return 0; }
定义get_choice()函数使其只返回值‘a' 'b' 'c'和'q'。咱们令实际的菜单选项十分简单,以使您把精力集中在程序结构上;很快咱们会讨论count()函数。default语句是方便调试的。若是get_choice函数没能将其返回值限制为预期值,则default语句可使您了解发生了一些可疑的事情。get
get_choice()函数
下面的伪代码是这个函数的一个可能的设计:
show choice
get response
while response is not acceptable
prompt for more response
get response
下面是一个虽然简单但使用起来不太方便的实现:
char get_choice (void) { int ch; printf("Enter the letter of your choice:\n"); printf("a.advice b.bell \n"); printf("c.count d.quit\n"); ch=getchar(); while ((ch<'a' || ch>'c') && ch !='q') { printf("Please respond with a,b,c or q.\n"); ch=getchar(); } return ch; }
问题在于,使用缓冲输入时,函数会将回车键产生的每一个换行符都做为一个错误的响应对待。要使程序界面更加顺畅,该函数应该跳过换行符。
要实现这一目标为多种方法。一种方法是用一个名为get_first()的新函数代替getchar();该函数读取一行的第一个字符并将其他字符丢弃掉。此方法还有一个优势,就是将由act组成的输入行看做是输入了一个简单的a,而不是将其做为由表明count的c所产生的一个有效的响应。记住这一目标,您就能够将输入函数重写为以下的形式:
char get_choice (void) { int ch; printf("Enter the letter of your choice:\n"); printf("a.advice b.bell \n"); printf("c.count d.quit\n"); ch=get_first(); /*get_first()函数*/ while ((ch<'a' || ch>'c') && ch !='q') { printf("Please respond with a,b,c or q.\n"); ch=get_first(); /*get_first()函数*/ } return ch; } char get_first(void) { int ch; ch=getchar(); /*读取下一个字符*/ while(getchar()!='\n') continue; /*跳过本行的剩余部分*/ return ch; }
8.7.3 混合字符和数字输入
建立菜单提供了将字符输入与数值输入相混合会产生问题的另外一个实例。例如,假设count()函数(选项C)以下所示:
void count (void) { int n,i; printf("Count how far?Enter an integer: \n"); scanf("%d",&n); for (i=1;i<=n;i++) printf("%d\n",i); }
若是您经过输入3进行响应,则scanf()将读取3并留下一个换行符,把它做为输入队列中的下一个字符。对get_choice()的下一次调用会致使get_first()返回此换行符,从而致使不但愿的动做。
解决该问题的一种方法是重写get_first()使其返回下一个非空白字符,而不是简单的返回它遇到的下一个字符 。第二种方法是由count()函数本身来负责处理换行符。
void count(void) { int n,i; printf("Count how far?Enter an integer: \n"); n=get_int(); for(i=1;i<=n;i++) printf("%d",i); while(getchar()!='\n') continue; }
此函数使用了程序清单8.7中的get_int()函数;回忆一下,该函数检查有效输入并给用户从新尝试的机会。
程序清单8.8 显示了最终的菜单程序
/*menuette.c--菜单技术*/ #include <stdio.h> char get_choice (void); char get_first (void); int get_int (void); void count (void); int main (void) { int choice; void count (void); while ((choice=get_choice())!='q') { switch (choice) { case 'a':printf("Buy low ,sell high.\n"); break; case 'b':putchar('\a'); break; case 'c':count(); break; default:printf("Program error!\n"); break; } } printf("Bye.\n"); return 0; } void count(void) { int n,i; printf("Count how far?Enter an integer: \n"); n=get_int(); for (i=1;i<=n;i++) printf("%d\n",i); while(getchar()!='\n') continue; } char get_choice(void) { int ch; printf("Enter the letter of your choice:\n"); printf("a.advice b.bell\n"); printf("c.count q.quit\n"); ch = get_first (); while((ch<'a' || ch>'c') && ch!='q') { printf("Please respond with a,b,c, or q.\n"); ch=get_first(); } return ch; } char get_first(void) { int ch; ch=getchar(); while(getchar()!='\n') continue; return ch; } int get_int(void) { int input; char ch; while(scanf("%d",&input)!=1) { while((ch=getchar())!='\n') putchar(ch); //剔除错误的输入 printf(" is not an integer.\nPlease enter an "); printf("integer value,such as 25,-178,or 3: "); } return input; }
让菜单界面按照您所但愿的那样顺利工做是很难的,但在你开发了一种可行的方法后,您就能够在多种状况下重用该界面。
另外要注意的一点是在面临较复杂的任务时,每一个函数如何将任务指派给其余函数,这样就可使程序更加模块化。