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


    样本代码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 ; 
    }
    
  • 相关阅读:
    5)二叉树[1]二叉树的遍历(先序、中序、后序)
    4)线性表[顺序表和链表]
    3)链栈和链队列
    2)队列
    1)栈
    Const #define
    7)查找[2]二叉排序树以及查找
    ZooKeeper 安装与配置
    Hadoop 2.7.4 + HBase 1.2.6 + ZooKeeper 3.4.10 配置
    Hadoop的配置文件设置(非HDFS HA)
  • 原文地址:https://www.cnblogs.com/pmer/p/2250066.html
Copyright © 2020-2023  润新知