题目: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 ; }