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

2012年(1)

2011年(21)

分类: C/C++

2011-11-15 21:08:05

样本代码3

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

  1. #include <stdio.h>

  2. int main ()

  3.  {int prime (int);

  4.   int n;

  5.   printf ("input an integer:");

  6.   scanf ("%d",&n);

  7.   if (prime(n))

  8.     printf ("%d is a prime. \n",n);

  9.   else

  10.     printf ("%d is not a prime. \n",n);

  11.   return 0;

  12.   }

  13. int prime (int n )

  14.  {int flag=1,i;

  15.   for (i=2;i<n/2&&flag==1;i++)

  16.     if (n%i==0)

  17.       flag=0;

  18.   return (flag);

  19. }

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

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

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

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

  1. int prime (int n )

  2.  {int flag=1,i;

  3.   if (n<=1)

  4.      return 0;

  5.   for (i=2;i<=n/2 && flag==1;i++)

  6.     if (n%i==0)

  7.       flag=0;

  8.   return (flag);

  9. }

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

这个题目的正确写法是

  1. #include <stdio.h>



  2. #define TRUE 1
  3. #define FALSE 0



  4. int be_prime( int );



  5. int main ( void )

  6. {

  7.   int n;



  8.   printf ("输入一个整数:");

  9.   scanf ("%d",&n);



  10.   printf ("%d%s是素数。\n" ,

  11.            n , (be_prime(n)==TRUE)?"":"不" ) ;



  12.   return 0;

  13. }



  14. int be_prime ( int n )

  15. {

  16.   int i;



  17.   if ( n <= 1 )

  18.       return FALSE;



  19.   for ( i = 2 ; i <= n / 2 ; i++ )

  20.      if ( n % i == 0 )

  21.         return FALSE;



  22.   return TRUE ;

  23. }

样本代码4

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

  1. #include <stdio.h>

  2. #include <stdlib.h>

  3. #define MAX 1000

  4. int main()

  5. { int htoi(char s[]);

  6.   int c,i,flag,flag1;

  7.   char t[MAX];

  8.   i=0;

  9.   flag=0;

  10.   flag1=1;

  11.   printf("input a HEX number:");

  12.   while((c=getchar())!='\0'&&i<MAX&&flag1)

  13.    {if(c>='0'&&c<='9'||c>='a'&&c<='f'||c>='A'&&c<='F')

  14.     {flag=1;

  15.      t[i++]=c;

  16.     }

  17.     else if(flag)

  18.     {t[i]='\0';

  19.      printf("decimal number%d\n",htoi(t));

  20.      printf("continue or not?");

  21.      c=getchar();

  22.      if(c=='N'||c=='n')

  23.        flag1=0;

  24.      else

  25.        {flag=0;

  26.         i=0;

  27.         printf("\ninput a HEX number:");

  28.        }

  29.     }

  30.    }

  31.   return 0;

  32. }

  33. int htoi(char s[])

  34. { int i,n;

  35.   n=0;

  36.   for(i=0;s[i]!='\0';i++)

  37.    {if(s[i]>='0'&&s[i]<='9')

  38.      n=n*16+s[i]-'0';

  39.     if(s[i]>='a'&&s[i]<='f')

  40.      n=n*16+s[i]-'a'+10;

  41.     if(s[i]>='A'&&s[i]<='F')

  42.      n=n*16+s[i]-'A'+10;

  43.    }

  44.   return(n);

  45. }

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

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

int n;

scanf("%x",&n);

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

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

首先来看flag1

  1. flag1=1;

  2.   while((c=getchar())!='\0'&&i<MAX && flag1 )

  3.   {

  4.     if (/*……*/)

  5.     { /*……*/

  6.     }

  7.     else

  8.       if( flag )

  9.       { /*……*/

  10.           if(c=='N'||c=='n')

  11.              flag1=0;

  12.           else

  13.           { /*……*/

  14.           }

  15.       }

  16.   }

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

  1. while( (c=getchar())!='\0' && i<MAX )

  2.   {

  3.     if (/*……*/)

  4.     { /*……*/

  5.     }

  6.     else

  7.       if( flag )

  8.       { /*……*/

  9.           if(c=='N'||c=='n')

  10.              break ;

  11.           else

  12.           { /*……*/

  13.           }

  14.       }

  15.   }

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

删除了flag1之后,再来看flag

  1. int c,i=0,flag = 0;

  2.   char t[MAX];

  3.   /*……*/

  4.   while( (c=getchar())!='\0' && i<MAX )

  5.    {

  6.      if(c>='0'&&c<='9'||c>='a'&&c<='f'||c>='A'&&c<='F')

  7.      {

  8.         flag=1;

  9.         t[i++]=c;

  10.      }

  11.     else

  12.        if(flag)

  13.        {

  14.           t[i]='\0';

  15.           /*……*/

  16.           if((c=getchar())=='N'||c=='n')

  17.              break ;

  18.           else

  19.           {

  20.            flag = 0;

  21.            i=0;

  22.            /*……*/

  23.        }

  24.     }

  25.    }

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

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

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

  1. #define MAX 1000

  2.   /*……*/

  3.   char t[MAX];

  4.   /*……*/

  5.   while((c=getchar())!='\0'&&i<MAX)

  6.   /*……*/

  7.   printf("decimal number%d\n",htoi(t));

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

 

  1. #include <limits.h>

  2. #define HEX_BIT (4)

  3. #define MAX ( sizeof ( unsigned ) * (CHAR_BIT/ HEX_BIT) )

 

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

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

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

  1. do

  2. {

  3.        //跳过非十六进制形式的字符

  4.        //读取最多MAX个十六进制数形式的字符

  5.        //询问用户是否继续

  6. }

  7. while(继续);

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

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

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

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

  1. char hex_str[ MAX + 1 ] ;

  2. scanf("%8[0123456789ABCDEFabcdef]%*[^\n]",hex_str);

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

改正后的代码为:

  1. #include <stdio.h>

  2. #include <limits.h>



  3. #define HEX_BIT 4 //一个十六进制数字符占4位

  4. #define MAX ( sizeof( unsigned ) * ( CHAR_BIT / HEX_BIT ) )

  5. #define HEX_CHAR "0123456789ABCDEFabcdef"



  6. unsigned htoi(char []);



  7. int main( void )

  8. {

  9.   char hex_str[ MAX + 1 ] ; // + 1 for \0

  10.   char yn;



  11.   do

  12.   {

  13.      printf("请输入一个十六进制数:");

  14.      scanf("%*[^"HEX_CHAR"]");

  15.      scanf("%8[" HEX_CHAR"]%*[^\n]",hex_str);

  16.      printf("%s的十进制为%u\n",hex_str,htoi(hex_str));

  17.      printf("继续(Y/N)?");

  18.      scanf(" %c",&yn);

  19.   }

  20.   while(!(yn=='N'||yn=='n'));



  21.   return 0;

  22. }



  23. unsigned htoi(char s[])

  24. {

  25.    unsigned i , n = 0 ;

  26.    

  27.    for( i = 0 ; s[i] != '\0' ; i++ )

  28.    {

  29.       n *= 16 ;

  30.       if(s[i]>='0'&&s[i]<='9')

  31.          n += s[i] - '0' ;

  32.       else if(s[i]>='a'&&s[i]<='f')

  33.          n += s[i] - 'a' + 10 ;

  34.       else if(s[i]>='A'&&s[i]<='F')

  35.          n += s[i] - 'A' + 10 ;

  36.    }

  37.    

  38.    return n ;

  39. }
阅读(4110) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~