现在的位置: 首页 > 综合 > 正文

flag标志什么?哦,它标志代码馊了——(二)

2012年12月30日 ⁄ 综合 ⁄ 共 4774字 ⁄ 字号 评论关闭

样本代码3

题目:写一个判(判断?)素数的函数,在主函数输入一个整数,输出是否为素数的信息。 

View Code

#include <stdio.h>
int main ()
{int prime (int);
int n;
printf ("input an integer:");
scanf ("%d",&n);
if (prime(n))
printf ("%d is a prime. \n",n);
else
printf ("%d is not a prime. \n",n);
return 0;
}
int prime (int n )
{int flag=1,i;
for (i=2;i<n/2&&flag==1;i++)
if (n%i==0)
flag=0;
return (flag);
}

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

  这段代码用于判断一个整数是否是素数,其中也用到了flag。

  不过在讨论这个flag的得失利弊之前,必须要指出的是,prime()这个函数根本上就是错的。因为prime(4),prime(1) ,prime(0)的返回值都是1,也就是说prime()把0,、1、4都判定为素数。更让人喷饭的是,甚至对于负整数,prime()也会判断为素数,这非常滑稽。代码作者在写prime()函数时犯了两个非常浅薄的错误:第一,压根没考虑到n可能不是正整数的情况;第二,误把i<=n/2写成了i<n/2。而把“<=”误写为“<”是初学者才会犯的一个毛病,后果是导致循环次数错误。

  prime()函数没有错误的写法应该是:

View Code

int prime (int n )
{int flag=1,i;
if (n<=1)
return 0;
for (i=2;i<=n/2 && flag==1;i++)
if (n%i==0)
flag=0;
return (flag);
}

  修改过的代码中,flag依然如阑尾一般啰嗦无效。这个flag的作用之一是通过对“flag==1”求值结束for循环语句,然而这是一种虽然可行却很怪异的结束循环语句的方法,表明代码作者根本不懂得循环语句可以用break语句或return语句这两种正规方法提前结束,但却可以写一本《C程序设计(第四版)学习辅导》。就这个函数而言,由于“n%i==0”时已经可以确定n是否为素数,所以用return语句结束循环更加直截了当些。

  这个题目的正确写法是

#include <stdio.h>

#define TRUE  1
#define FALSE 0

int be_prime( int );

int main ( void )
{
  int n;

  printf ("输入一个整数:");
  scanf ("%d",&n);

  printf ("%d%s是素数。\n" , 
           n , (be_prime(n)==TRUE)?"":"不" ) ;

  return 0;
}

int be_prime ( int n )
{
  int i;

  if ( n <= 1 )
      return FALSE;

  for ( i = 2 ; i <= n / 2 ; i++ )
     if ( n % i == 0 )
        return FALSE;

  return  TRUE ;
}

 样本代码4

  写一个函数,输入一个十六进制数,输出相应的十进制数。

View Code

#include <stdio.h>
#include <stdlib.h>
#define MAX 1000
int main()
{ int htoi(char s[]);
int c,i,flag,flag1;
char t[MAX];
i=0;
flag=0;
flag1=1;
printf("input a HEX number:");
while((c=getchar())!='\0'&&i<MAX&&flag1)
{if(c>='0'&&c<='9'||c>='a'&&c<='f'||c>='A'&&c<='F')
{flag=1;
t[i++]=c;
}
else if(flag)
{t[i]='\0';
printf("decimal number%d\n",htoi(t));
printf("continue or not?");
c=getchar();
if(c=='N'||c=='n')
flag1=0;
else
{flag=0;
i=0;
printf("\ninput a HEX number:");
}
}
}
return 0;
}
int htoi(char s[])
{ int i,n;
n=0;
for(i=0;s[i]!='\0';i++)
{if(s[i]>='0'&&s[i]<='9')
n=n*16+s[i]-'0';
if(s[i]>='a'&&s[i]<='f')
n=n*16+s[i]-'a'+10;
if(s[i]>='A'&&s[i]<='F')
n=n*16+s[i]-'A'+10;
}
return(n);
}

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

  这个题目本身就不伦不类,“输入一个十六进制数,输出相应的十进制数”这个要求其实用三行代码就可以完成:

int n;
scanf("%x",&n);
printf("相应的十进制数为%d\n",n);

   对照代码,发现真正的要求应该是“输入十六进制数形式的字符序列,输出它所表示的数的十进制的值”。为了一个如此简单的要求,在main()函数中竟然使用了两个flag,并构造了一个无比复杂的while语句,令人吐血。下面就来分析一下这两个flag的利弊得失。

  首先来看flag1: 

View Code

  flag1=1;
while((c=getchar())!='\0'&&i<MAX && flag1 )
{
if (/*……*/)
{ /*……*/
}
else
if( flag )
{ /*……*/
if(c=='N'||c=='n')
flag1=0;
else
{ /*……*/
}
}
}

  很显然,flag1的作用无非是用来结束while循环语句,没有其他作用。既然如此,为什么不采用简单明了的break语句呢?对比一下下面的写法

 

  while( (c=getchar())!='\0' && i<MAX )
  {
    if (/*……*/)
    { /*……*/
    }
    else 
      if( flag )
      { /*……*/
          if(c=='N'||c=='n')
             break ;
          else
          { /*……*/
          }
      } 
  }   

    就不难发现,flag1纯粹是画蛇添足的写法,应该删除。

  删除了flag1之后,再来看flag。 

View Code

  int c,i=0,flag = 0;
char t[MAX];
/*……*/
while( (c=getchar())!='\0' && i<MAX )
{
if(c>='0'&&c<='9'||c>='a'&&c<='f'||c>='A'&&c<='F')
{
flag=1;
t[i++]=c;
}
else
if(flag)
{
t[i]='\0';
/*……*/
if((c=getchar())=='N'||c=='n')
break ;
else
{
flag = 0;
i=0;
/*……*/
}
}
}

  首先,当flag的值为0时,如果读入的字符不是十六进制数形式的字符,程序将继续循环;如果读入十六进制数形式的字符,则flag将反复地被赋值为1,并将该字符写入t数组;之后若再次读入非十六进制数形式的字符,则被作为'\0'写入t数组,然后询问是否重新读下一个十六进制数,如果回答不是'N'或'n',则开始下一轮处理。

  不难看出,其中的“(c=getchar())!='\0'”是作茧自缚而又多此一举的循环控制条件,因为这种情况完全可以作为非十六进制数形式的字符来处理,这样程序的适应性更强。更何况从键盘输入'\0'字符是绝大多数用户并不了解的技巧。总之,无法弄清代码作者在这里的思路,就如同你永远不可能弄清头脑混乱的人在想什么一样。如果容许猜测的话,大概作者把处理字符串的常见写法稀里糊涂、移花接木地“嫁接”到了这里,这就如同给正常人安装了一条假肢一样。没人能弄清楚为什么给正常人装假肢。

  另一个循环控制条件i<MAX则是代码作者心里没“数”、痴心妄想的产物

View Code

#define MAX 1000
/*……*/
char t[MAX];
/*……*/
while((c=getchar())!='\0'&&i<MAX)
/*……*/
printf("decimal number%d\n",htoi(t));

  在这里代码作者想表达的是程序可以转换最多不超过1000位的十六进制数字形式的字符序列。然而htoi()返回值的类型是int类型。int类型最多能存储几位十六进制整数是一个小学数学问题,这里就不宣布答案了。用C语言来描述,这个MAX其实应该是

#include <limits.h>
#define HEX_BIT  (4)
#define MAX  ( sizeof ( unsigned ) * (CHAR_BIT/ HEX_BIT) ) 

   这里的MAX是unsigned类型数据类型所能存储的十六进制数的最多位数。同样,htoi()这个函数的返回值应该是unsigned类型,因为main()中输入十六进制数形式的字符序列根本没有考虑正负号。

  那么,完成“样本代码4”中的那个复杂的while语句的功能是否需要那个蹩脚的flag呢?答案是根本用不着。非但用不着,而且不用的话代码可以写得更简洁、更清晰。

  样本代码4中的那个复杂的while语句的功能可以用下面简单的伪代码说明:

do
{
	//跳过非十六进制形式的字符
	//读取最多MAX个十六进制数形式的字符
	//询问用户是否继续
}
while(继续);

   其中“跳过非十六进制字符”可以自己写函数完成,也可以使用库函数。如果使用库函数,相应的代码为:

scanf("%*[^0123456789ABCDEFabcdef]");

   其中的“*”表示读取但不存储,“^0123456789ABCDEFabcdef”表示读取非十六进制数形式的字符。

  “读取最多MAX个十六进制数形式的字符”可以用

char hex_str[ MAX + 1 ] ;
scanf("%8[0123456789ABCDEFabcdef]%*[^\n]",hex_str);

  这里的8为MAX的值。这将保证向hex_str数组中最多写入8个十六进制数形式的字符。

  改正后的代码为:

#include <stdio.h>
#include <limits.h>

#define HEX_BIT 4          //一个十六进制数字符占4位
#define MAX ( sizeof( unsigned ) * ( CHAR_BIT / HEX_BIT ) )
#define HEX_CHAR "0123456789ABCDEFabcdef"

unsigned htoi(char []);

int main( void )
{
  char hex_str[ MAX + 1 ] ;    // + 1 for \0  
  char yn;   

  do
  {
     printf("请输入一个十六进制数:");
     scanf("%*[^"HEX_CHAR"]");
     scanf("%8[" HEX_CHAR"]%*[^\n]",hex_str);
     printf("%s的十进制为%u\n",hex_str,htoi(hex_str));
     printf("继续(Y/N)?");
     scanf(" %c",&yn);
  }
  while(!(yn=='N'||yn=='n'));

  return 0;
}

unsigned htoi(char s[])
{ 
   unsigned i , n = 0 ;
   
   for( i = 0 ; s[i] != '\0' ; i++ )
   {
      n *= 16 ;
      if(s[i]>='0'&&s[i]<='9')
         n += s[i] - '0' ;
      else if(s[i]>='a'&&s[i]<='f')
         n += s[i] - 'a' + 10 ;     
      else if(s[i]>='A'&&s[i]<='F')
         n += s[i] - 'A' + 10 ;     
   }
   
   return n ; 
}

 

抱歉!评论已关闭.