样本代码3
题目:写一个判(判断?)素数的函数,在主函数输入一个整数,输出是否为素数的信息。
#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()函数没有错误的写法应该是:
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
写一个函数,输入一个十六进制数,输出相应的十进制数。
#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:
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。
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则是代码作者心里没“数”、痴心妄想的产物
#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 ; }