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; }
讓菜單界面按照您所但願的那樣順利工做是很難的,但在你開發了一種可行的方法後,您就能夠在多種狀況下重用該界面。
另外要注意的一點是在面臨較複雜的任務時,每一個函數如何將任務指派給其餘函數,這樣就可使程序更加模塊化。