Chinaunix首页 | 论坛 | 博客
  • 博客访问: 139439
  • 博文数量: 22
  • 博客积分: 1326
  • 博客等级: 中尉
  • 技术积分: 258
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-02 00:52
文章分类
文章存档

2012年(1)

2011年(21)

分类: C/C++

2011-11-19 23:51:47

有时候flag会以别的名字出现,但同样会把代码弄得馊不可闻。例如下面的代码

样本代码5

  1. #include <stdio.h>
  2. #include <string.h>
  3. #define N 10
  4. int main( void )
  5. {
  6. /* */
  7. int num[N],number;
  8. char name[N][8];
  9. / * */
  10. printf("\ninput number to look for:");
  11. scanf("%d",&number);
  12. search(number,num,name);
  13. /* */
  14. return 0;
  15. }

  16. void search(int n,int num[],char name[N][8])
  17. {int top,bott,mid,loca,sign;
  18. top=0;
  19. bott=N-1;
  20. loca=0;
  21. sign=1;
  22. if((n<num[0])||(n>num[N-1]))
  23.    loca=-1;
  24. while((sign==1)&&(top<=bott))
  25.   {mid=(bott+top)/2;
  26.    if(n==num[mid])
  27.      {loca=mid;
  28.       printf("No.%d,his name is %s.\n",n,name[loca]);
  29.       sign=-1;
  30.      }
  31.    else if(n<num[mid])
  32.       bott=mid-1;
  33.    else
  34.       top=mid+1;
  35.   }
  36. if(sign==1||loca==-1)
  37.       printf("%d not been found.\n",n);
  38. }

——谭浩强 ,《C程序设计(第四版)学习辅导》,清华大学出版社,20107月,p92

这段代码中的search()函数的主要功能是用二分法在已排好序的num数组中搜索n,求出nnum数组中的位置loca后输出对应在name数组中相同下标的元素name[loca]

这个search()函数的第一个毛病是缺少数组尺寸参数,char name[N][8]这个参数中的N毫无意义,函数内的N来得无缘无故。这是一个很大的毛病,这使得函数丧失通用性,而没有通用性的函数基本上就是是废品函数。但这些由于和flag无关,所以这里暂且放置不谈。

在这个函数内,locasign都属于那种flag变量,尽管没有取名为flag

loca这个变量用来记录nnum数组中的位置,如果数组中不存在则将loca的值置为-1。将loca的初值取为0极不合理,因为这表示一开始就假设n在数组中存在且为第一个元素,这是毫无道理的假设。loca合理的初值应该为-1这样,只有在找到的情况下这个值才会改变,代码可以大为简化:

  1. void search(int n,int num[],char name[N][8])
  2. {
  3.   int top = 0 ,bott = N-1,mid,loca=-1,sign = 1 ;
  4.   while((sign==1)&&(top<=bott))
  5.   {
  6.    mid=(bott+top)/2;
  7.    if(n==num[mid])
  8.      {
  9.       loca=mid;
  10.       printf("No.%d,his name is %s.\n",n,name[loca]);
  11.       sign=-1;
  12.      }
  13.    else if(n<num[mid])
  14.       bott=mid-1;
  15.    else
  16.       top=mid+1;
  17.   }
  18.   if(loca==-1)
  19.       printf("%d not been found.\n",n);
  20. }

现在不难看出,代码中的sign不过是breakreturn语句的一个拙劣的替代品而已,完全没有必要。代码可以进一步简化

  1. void search( int n , int num[] , char name[][8] )
  2. {
  3.    int top=0,bott=N-1,mid;

  4.    while( top<=bott )
  5.    {
  6.       mid=(bott+top)/2;
  7.       if( n == num[mid] )
  8.       {
  9.           printf("No.%d,his name is %s.\n",n,name[mid]);
  10.           return ;
  11.       }
  12.       else if( n < num[mid] )
  13.          bott = mid - 1 ;
  14.       else
  15.          top = mid + 1 ;
  16.   }

  17.   printf("%d not been found.\n",n);

  18. }

结论就是,原来代码中的locasign没有任何存在的意义,因为它们除了把代码弄得更晦涩更复杂之外没有起到任何好作用。

标志变量的毛病改完了,但这个search()其实还有另外的毛病,这个毛病就是根本不应该把查找与输出这两个功能搅和在一起。完成单一任务是函数设计的一个基本原则。因此从整个程序设计的角度来说更干净的写法应该是:

  1. #include <stdio.h>

  2. #define N 10
  3. #define NOT_FOUND (-1)

  4. int search( int , int [] , size_t );

  5. int main( void )
  6. {
  7.    /* */
  8.    int num[N] , number ;
  9.    char name[N][8] ;
  10.    / * */
  11.    printf("\ninput number to look for:");
  12.    scanf("%d",&number);
  13.    {
  14.       int site = search( number , num , N );

  15.       if( site != NOT_FOUND )
  16.          printf("No.%d,his name is %s.\n" , number , name[site] );
  17.       else
  18.          printf("%d not been found.\n",n);
  19.    
  20.    }
  21.    /* */
  22.    return 0;
  23. }

  24. int search( int n, int num[] , size_t size )
  25. {
  26.    int top = 0 ,
  27.        bott = size - 1 ,
  28.        mid ;

  29.    while( top <= bott )
  30.    {
  31.       mid = ( bott + top ) / 2 ;
  32.       if( n == num[mid] )
  33.           return mid ;
  34.           
  35.       if( n < num[mid] )
  36.           bott = mid - 1 ;
  37.       else
  38.           top = mid + 1 ;
  39.    }

  40.    return NOT_FOUND ;
  41. }

通过前面的几个例子,不难看出,在代码中轻率地使用flag之类的标志变量确实能够败坏代码的味道,使得代码变馊。

那么,是否在代码中绝对不应该使用标志变量呢?却也不是。使用flag标志变量的前提首先是解决问题的算法要求使用这样的变量,离开了这个前提使用flag标志变量就难免成为东施效颦;其次,不应该生硬死板地为标志变量命名为flag,无论什么问题总是一味地flagsign,不是头脑僵化就是思维扭曲。

最后看一个例子,欣赏一下大师是如何使用标志变量的。下面的代码出自K&R,功能是统计输入的行数、单词数和字符数:

  1. #include <stdio.h>

  2. #define IN 1 /*在单词内*/
  3. #define OUT 0 /*在单词外*/
  4. /* 统计输入的行数、单词数和字符数 */
  5. main()
  6. {
  7.   int c,nl,nw,nc,state;
  8.   
  9.   state = OUT ;
  10.   nl = nw = nc = 0 ;
  11.   while((c = getchar())!= EOF){
  12.       ++nc;
  13.       if( c == '\n')
  14.          ++nl;
  15.       if( c ==' ' || c =='\n' || c =='\t')
  16.          state = OUT ;
  17.       else if(state == OUT){
  18.          state = IN ;
  19.          ++nw;
  20.       }
  21.   }
  22.   printf("%d %d %d\n", nl , nw , nc );
  23. }

这里,标志变量并没有使用丑陋的flag作为名称,而是恰当地使用了state这个与问题相贴切的名字。再加上两个漂亮的符号常量INOUT,使得代码的含义不言自明,连注释都用不着。三十多年过去了,这段代码不但没有腐朽,相反,依然还是那么清新典雅,垂范后人,令人高山仰止。

阅读(4628) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

pmerofc2012-03-12 10:36:35

GFree_Wind: 喷喷谭浩强的书挺好。很纳闷为什么把谭的书当作本科教材,耽误了不少人啊.....
我也很奇怪

GFree_Wind2012-03-11 17:17:31

喷喷谭浩强的书挺好。很纳闷为什么把谭的书当作本科教材,耽误了不少人啊