Loading

【转载】flag标志什么?哦,它标志代码馊了

  几乎每次在代码中发现flag变量,我总是能嗅到一股馊味。不管你闻没闻到,反正我闻到了。

  在代码中,flag通常作为标志变量的名字。但问题在于,不是所有的问题或代码都需要使用这种标志变量,更不是使用标志变量一定要用flag这个名字。需要使用标志变量的问题只有一小部分而已,况且即使使用标志变量,取flag这个名字也往往是一种草率的下策。

  然而由于基础不扎实及某些书籍的误导等缘故,不少初学者往往特别善于滥用flag。几乎在任何场合他们都有能力把这个flag使用上且用得出神入化般的别扭,就如同用if语句和goto语句构造循环结构一样,甚至比这更坏。这种情况是由于不熟悉基本的代码结构所致。

另一种滥用flag的情况则是,由于最初的设计千疮百孔漏洞百出,等到发现时用flag打上丑陋的补丁。

  下面开始阅读代码,请自备口罩。

样本代码1

#include <stdio.h>
#define N 10
int main()
{
 int num [N],number,flag=1,c;
 char name[N][8];
 /*……*/
 while(flag==1)
    {printf("\ninput number to look for:");
     scanf("%d",&number);
     search(number,num,name);
     printf("continue ot not(Y/N)?");
     getchar();
     c=getchar();
     if(c=='N'||c=='n')
       flag=0;
    }
  /*……*/  
  return 0;
}

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

这段代码中的while语句是明显的缺陷,因为从这个程序段的功能来说使用do-while才是自然的表达。

#include <stdio.h>
#define N 10
int main( void )
{
   int num [N],number,flag=1,c;
   char name[N][8];
   /*……*/
   do{
       printf("\ninput number to look for:");
       scanf("%d",&number);
       search(number,num,name);
       printf("continue ot not(Y/N)?");
       getchar();
       c=getchar();
       if(c=='N'||c=='n')
         flag=0;
       }
   while(flag==1);
    /*……*/  
   return 0;
}

其次flag标志变量根本没有必要,代码完全可以写成

#include <stdio.h>
#define N 10
int main( void )
{
   int num [N],number,flag=1,c;
   char name[N][8];
   /*……*/
   do{
      printf("\ninput number to look for:");
      scanf("%d",&number);
      search(number,num,name);
      printf("continue ot not(Y/N)?");
      getchar();
      c = getchar() ;
   }
   while(!(c=='N'||c=='n'));
    /*……*/  
   return 0;
}

最后,getchar();也用得十分蹩脚,它的用意是读入前面调用scanf("%d",&number)输入一个十进制整数后输入的<CR>(回车换行)。但实际上如果程序使用者输入十进制整数后又顺手按了一个<SP>空格再回车,这个“getchar();”就会因为读入了空格而导致后面的“c = getchar() ;”读取N、n、Y、y这几个文字字符的企图遭到失败。更好的写法应该是

#include <stdio.h>
#define N 10
int main( void )
{
   int num [N],number,flag=1,c;
   char name[N][8];
   /*……*/
   do{
       printf("\ninput number to look for:");
       scanf("%d",&number);
       search(number,num,name);
       printf("continue ot not(Y/N)?");
       scanf("\n%c",&c );
   }
   while(!(c=='N'||c=='n'));
    /*……*/  
   return 0;
}

与“样本代码1”对比一下,不难发现样本代码1中的flag有多么拙劣。

样本代码2

#define N 10
char str[N];
int main()
{
/*……*/
int i,flag;
for(flag=1;flag==1;)
  {scanf("%s",&str);
   if(strlen(str)>N)
    printf("string too long,input again!");
   else
    flag=0;
  }
/*……*/
}

* ——谭浩强 ,《C程序设计(第四版)学习辅导》,清华大学出版社,2010年7月,p85*

这段代码的flag用得更加可惊可愕,它甚至不如写成功能等价的

#define N 10
char str[N];
int main(void)
{
   /*……*/
   int i,flag;
   do {
      scanf("%s",&str);
      if(strlen(str)>N)
        printf("string too long,input again!");
      else
        break;
   }
   while(1);
   /*……*/
}

由此不难看出那个flag有多么多余。然而这个写法依旧啰嗦,更简洁的写法是:

#define N 10
char str[N];
int main(void)
{
   /*……*/
   do {
      scanf("%s",&str);
   }
   while(strlen(str)>N && printf("string too long,input again!"));
   /*……*/
}

这写法虽然简洁,然而依然带着样本代码中原来的毛病,这个毛病就是scanf("%s",&str);中的&str。因为str的定义是 char str[10];,而与转换格式%s相对应的参数应该是char *类型的指针,所以这个调用应该写为scanf("%s",str);。

  即便如此,代码依然是错误的。原代码企图在strlen(str)>N的情况下重新输入,这是幼稚的一厢情愿,而且有着双重的错误。从逻辑来说,strlen(str) 等于N时就已经是越界使用数组了。所以strlen(str)>N这个关系表达式不应该使用“>”运算。从实践的角度来说strlen(str)>=N时的越界已然对内存中的数据存储造成了破坏,程序以后的行为是建立在错误的基础上,最后的结果必然是错误的,至少是不可靠的。而结果不可靠,对于程序来说就是一种错误。

  那么,这段代码应该怎样写呢?由于样本代码2要解决的问题是对十个字符排序,这里的要求是输入10个字符。这根本不用写一个循环语句反复折腾,只需简单地

#define N 10
char str[N];
int main(void)
{
   /*……*/
   int i;
   scanf("%10c" , str ) ;
   /*……*/
}

即可。

  当然,也许有人会指责scanf("%10c" , str )没有用上N这个宏,这个指责确实有道理。其实用N这个宏来写scanf("%10c" , str )也并非什么难事,代码可以这样写:

#define N 10
#define FMT(x) ("%"NUM(x)"c")
#define NUM(x) #x
char str[N];
int main(void)
{   
   /*……*/ 
   int i;
   scanf( FMT(N) , str ) ;
   /*……*/
}

转载自https://www.cnblogs.com/pmer/archive/2011/11/10/2245012.html

posted @ 2021-06-09 15:48  Yangtai  阅读(261)  评论(0编辑  收藏  举报