題目:n個人圍成一圈,順序排號。從第1個人開始報數(從1到3報數),凡報到3的人退出圈子,問最后留下的是原來第幾號的那位
#include <stdio.h>
int main()
{int i,k,m,n,num[50],*p;
printf("\ninput number of person:n=");
scanf("%d",&n);
p=num;
for(i=0;i<n;i++)
*(p+i)=i+1;
i=0;
k=0;
m=0;
while(m<n-1)
{if(*(p+i)!=0)k++;
if(k==3)
{*(p+i)=0;
k=0;
m++;
}
i++;
if(i==n)i=0;
}
while(*p==0)p++;
printf("The last one NO.%d\n",*p);
return 0;
}
——譚浩強 ,《C程序設計(第四版)》學習輔導,清華大學出版社,2010年7月,p104
有些代碼一眼就能看出是初學者寫的。比如樣本中的這種,從頭到尾只有一個main()的代碼。
這種代碼並非標志了編程沒有完全入門,而是標志了編程完全沒有入門——連結構化程序設計思想的皮毛都沒掌握。
結構化程序設計思想的一個核心理念就是“層次”。良好的代碼都非常有層次感,而垃圾代碼則往往是亂糟糟地堆作一團。
只有一個main()函數有什么不行的?如果有人問這種問題我感覺還真是有點不好回答。其實沒什么不行的,就如同一個硬盤中存放了很多文件但沒有任何文件夾也沒什么不行的一樣。
要是話說到這個份上你還不理解只有一個main()有什么壞處的話,你就簡直就是在逼着我說粗話了。
假如你買了一套房子,但你在房子里並沒有用牆把房間分隔為卧室、客廳、廚房、餐廳、衛生間,而且你沒有任何壁櫥、櫃子、箱子,書桌沒有任何抽屜,你把一切生活用品都攤在你的唯一的那個房間的地上,你覺得怎么樣?
這情況和把一切代碼都堆在main()函數中一模一樣。一個源程序由若干函數組成,就如同用牆把你的房間間隔出來一樣。請注意,牆並沒有增加你房間的使用面積,相反它減少了你房間的使用面積,正如使用多個函數只可能降低程序的效率一樣。但是它們都有回報。這就是生活和寫代碼所共同遵循的辯證法,也就是結構化程序設計思想的哲學基礎。
懂得了這一點,就不難看出樣本代碼有多么丑陋了。它就如同房子沒有間隔成卧室、客廳、廚房、餐廳、衛生間一樣。
而且那個指針變量p顯得尤其滑稽可笑。因為指針這種東西要么用來構造數據結構,要么用來在函數間傳遞數據以實現間接訪問。而在這段代碼中,由於只有一個函數main(),所以不存在傳遞數據的任務。這段程序的主要的數據結構是數組,所以這個指針也不是用來構造數據結構的。由此觀之,這個指針定義得只能說是矯揉造作,煞有介事。沒有這個指針代碼更好,起碼代碼會更加簡明。代碼中的*(p+i)、*p都可以簡單地寫作num[i]、num[0],而
while(*p==0)p++;
這條語句的功能,不用指針也同樣很容易地實現。
不僅如此,代碼和題目還存在着其他的毛病:
i,j,k這種命名是最垃圾的命名風格就不多說了,題目本身也有一處漏洞,就是“n個人圍成一圈”應該說明n應該不大於50,否則代碼中num數組的尺寸來得沒頭沒腦。
但是,這段代碼最主要的問題就在於“一main到底”。
那么,應該如何避免這種丑陋的“一main到底”的代碼呢?
寫代碼是為了解決問題,而解決問題則應該首先從大處着眼。這就是結構化程序設計的精髓。
站在高處來看,解決這個問題無非是經過四個步驟:輸入人數;數組初始化(設置編號);“從1到3報數”n-1次;輸出最后剩下的那個人的編號。這些都可以簡單地用函數或基本的控制語句實現。
#include <stdio.h> #define MAX_NUM 50 #define BEGIN 1 #define END 3 #define REMOVED 0 void output( int [] , int ); void initialize( int [] , int ); void count_off ( int * , int[] , const int ) ; int main( void ) { int num[MAX_NUM]; int n; //人數 int from_this = 0 ; //從最前面的開始數 printf("輸入人數(<=50):"); scanf("%d",&n); initialize( num , n ); for(int i = 0 ; i < n - 1 ; i ++ ) //"從1到3報數" n-1次 count_off ( &from_this , num , n ) ; output( num , n ); //輸出 return 0; } void output(int num[] , int n ) { for(int i = 0 ; i < n ; i ++ ) if( num[i] != REMOVED ) { printf("剩下的是原來第%d號那位\n",num[i]); return ; } } void count_off ( int *p_this , int num[] , const int n ) { for( int i = BEGIN ; i <= END ; i ++ ) //從1到3 { while( num[*p_this] == REMOVED ) //找下一個 *p_this = (*p_this + 1) % n ; if ( i == END ) num[*p_this] = REMOVED ; //報到3的人退出 else *p_this = (*p_this + 1) % n ; } } void initialize( int num[] , int n ) { for(int i = 0 ; i < n ; i ++ ) num[i] = i + 1 ; }