问题:


  n-1位数字

  已知w是一个大于10但不大于1000000的无符号整数,若w是n(n≥2)位的整数,则求出w的后n-1位的数。
  输入:
  第一行为M,表示测试数据组数。
  接下来M行,每行包含一个测试数据。
  输出:
  输出M行,每行为对应行的n-1位数(忽略前缀0)。如果除了最高位外,其余位都为0,则输出0。
  样例:4 1023 5923 923 1000
  输出 23 923 23 0

原代码:


#include <stdio.h>
#include <math.h>
int digit(int x) //定义函数取位数
{
int i,temp;
for(i=;;i++)
{
temp =(int) pow(,i); if(x/temp==)
return i-;
}
} int main()
{
unsigned int w[],i,m,number,a,temp;
int digit(int x);
printf("想输入几组数据:\n");
scanf("%d",&m);
printf("请输入相应数字\n"); for (i = ;i<m;i++) //输入相应的数值
{
printf("%d、",i+);
scanf("%d",&w[i]);
} printf("您要的n-1位数字如下:\n"); for(i=; i <m; i++) //输出想要的n-1位数
{
a=digit(w[i]);
temp=(int)pow(,a);
number=w[i]/temp;
number=w[i]-number*temp;
printf("%d\n",number);
} return ;
}

评析:


  先说一下题目。这个题目比较好,程序的要求和问题的范围非常明确,而且给出了样例。美中不足的是“无符号整数”这个词,unsigned是C代码层面的概念,一般不宜用于描述问题。在问题层面上,应该说“非负整数”而不是“无符号整数”。

  源代码的总体结构欠佳,作者把函数定义写在了函数调用前,整个源代码结构头重脚轻。

main():

    unsigned int w[100],i,m,number,a,temp;

  变量定义太多,并不必要地使用了数组这种复杂的数据结构。

  实际上,这里只需要定义一个变量,就是测试数据组数——m。

   int digit(int x);

  函数类型声明位置不当,应该放在main()之外,main()的前面。函数的类型也不正确,最初作者想到了用unsigned,但后来又随手用了int。

    printf("想输入几组数据:\n");
scanf("%d",&m);
printf("请输入相应数字\n");

  这个写得很不错。作者没有像很多ACMer那样省去printf()输出的那两行提示信息。

    for (i = 0;i<m;i++)  //输入相应的数值
{
printf("%d、",i+1);
scanf("%d",&w[i]);
}

  完全没必要用数组,用一个int变量记录输入数据就可以了。只要在输入数据后直接求解就可以就可以保证这个int变量可以反复使用。即

    for (i = 0;i<m;i++)
{
int w ;
printf("%d、",i+1);
scanf("%d" , &w ); //输入相应的数值
//求后n-1位的数
}

  另外这里用for语句显然不如使用while语句。

    while ( m -- > 0 )
{
int w ;
printf("%d、",i+1);
scanf("%d" , &w ); //输入相应的数值
//求后n-1位的数
}

  使用while语句根本用不着那个没什么意义的i。初学者往往迷信for语言功能最强(这也往往是受到了老谭的坏影响。老谭书中滥用for语句的例子比比皆是,且不惜篇幅,大讲特讲)。

    for(i=0; i <m; i++)  //输出想要的n-1位数
{
a=digit(w[i]);
temp=(int)pow(10,a);
number=w[i]/temp;
number=w[i]-number*temp;
printf("%d\n",number);
}

  这段毛病较多。首先调用了digit()函数,

     a=digit(w[i]);

  这个函数的定义是:

int digit(int x)  //定义函数取位数
{
int i,temp;
for(i=1;;i++)
{
temp =(int) pow(10,i); if(x/temp==0)
return i-1;
}
}

功能是求x的位数。
  从算法的角度来说,求x的位数毫无必要。
  这里求的方法也是错误的,因为作者调用了pow()库函数。pow()函数的原型是

double pow(double x, double y);

  参数和返回值都是浮点类型,这表明这个函数只是求一个近似值。把求近似值的函数用于整数领域的这种精确问题明显有问题。譬如当a为4时,(int)pow(10,a)的值可能是9999而不是10000。不过很多职业程序员也不懂得这一点,被指出后还恼羞成怒(参见 算法:求比指定数大且最小的“不重复数”问题的高效实现 、似是而非的k=sqrt(n)  )。

  求一个整数的位数其实很简单。

unsigned digit( unsigned x )
{
int pl = 1u ;
while ( ( x /= 10u ) != 0u )
{
pl ++ ;
}
return pl ;
}

  由于题目认为根本就不需要调用库函数,因此也没有预备库函数给考题者。所以,尽管作者写上了

#include <math.h>

但还是被无情地被回绝了:

In function `digit':
undefined reference to `pow'
In function `main':
undefined reference to `pow'

  回到main()中第二个for语句循环体中被打断的地方

        temp=(int)pow(10,a);

  作者再次错误地调用了pow()。原来就是想求出把高位留下,后面各位都置0的数而已。我只能说这位初学者想法是正确的,但是办法是错误的。求这个数连求原数的位数都不用,更不用说不用蹩脚地调用pow()了:

unsigned digit( unsigned x )
{
int temp = 1u ;
while ( ( x / temp ) > 10u )
{
temp *= 10u ;
}
return x % temp ;
}

  由此,不难发现main()里第二条for语句中的

 number=w[i]/temp;
number=w[i]-number*temp;

也是完全多余的。顺便说一句,即使要完成这两条语句的功能,也不应该在for的循环体内完成,而应该通过调用函数完成。

重构:


#include <stdio.h>

unsigned get_low_num( unsigned )  ;

int main( void )
{
unsigned m ; printf( "想输入几组数据:\n" );
scanf( "%u" , &m ); while ( m -- > 0u )
{
unsigned w ; printf( "请输入相应数字\n" );
scanf( "%u" , &w ); printf( "您要的n-1位数字如下:\n" );
printf( "%u\n" , get_low_num( w ) );
} return ;
} unsigned get_low_num( unsigned x )
{
unsigned temp = 1u ;
while ( ( x / temp ) > 10u )
{
temp *= 10u ;
}
return x % temp ;
}

C语言初学者代码中的常见错误与瑕疵(3)的更多相关文章

  1. C语言初学者代码中的常见错误与瑕疵(23)

    见:C语言初学者代码中的常见错误与瑕疵(23)

  2. 一个超复杂的间接递归——C语言初学者代码中的常见错误与瑕疵(6)

    问题: 问题出处见 C语言初学者代码中的常见错误与瑕疵(5) . 在该文的最后,曾提到完成的代码还有进一步改进的余地.本文完成了这个改进.所以本文讨论的并不是初学者代码中的常见错误与瑕疵,而是对我自己 ...

  3. C语言初学者代码中的常见错误与瑕疵(5)

    问题: 素数 在世博园某信息通信馆中,游客可利用手机等终端参与互动小游戏,与虚拟人物Kr. Kong 进行猜数比赛. 当屏幕出现一个整数X时,若你能比Kr. Kong更快的发出最接近它的素数答案,你将 ...

  4. C语言初学者代码中的常见错误与瑕疵(19)

    见:C语言初学者代码中的常见错误与瑕疵(19)

  5. C语言初学者代码中的常见错误与瑕疵(14)

    见:C语言初学者代码中的常见错误与瑕疵(14) 相关链接:http://www.anycodex.com/blog/?p=87

  6. 分数的加减法——C语言初学者代码中的常见错误与瑕疵(12)

    前文链接:分数的加减法——C语言初学者代码中的常见错误与瑕疵(11) 重构 题目的修正 我抛弃了原题中“其中a, b, c, d是一个0-9的整数”这样的前提条件,因为这种限制毫无必要.只假设a, b ...

  7. C语言初学者代码中的常见错误与瑕疵(9)

    题目 字母的个数 现在给你一个由小写字母组成字符串,要你找出字符串中出现次数最多的字母,如果出现次数最多字母有多个那么输出最小的那个. 输入:第一行输入一个正整数T(0<T<25) 随后T ...

  8. 要心中有“数”——C语言初学者代码中的常见错误与瑕疵(8)

    在 C语言初学者代码中的常见错误与瑕疵(7) 中,我给出的重构代码中存在BUG.这个BUG是在飞鸟_Asuka网友指出“是不是时间复杂度比较大”,并说他“第一眼看到我就想把它当成一个数学问题来做”之后 ...

  9. C语言初学者代码中的常见错误与瑕疵(7)

    问题: 矩形的个数 在一个3*2的矩形中,可以找到6个1*1的矩形,4个2*1的矩形3个1*2的矩形,2个2*2的矩形,2个3*1的矩形和1个3*2的矩形,总共18个矩形.给出A,B,计算可以从中找到 ...

  10. C语言初学者代码中的常见错误与瑕疵(1)

    曾在豆瓣上看到过一个小朋友贴出他自己的代码(http://www.douban.com/group/topic/40293109/),当时随口指点了几句.难得这位小朋友虚心修正.从善如流,不断地改,又 ...

随机推荐

  1. QQ聊天气泡(图片拉伸不变样)、内容尺寸定制(高度随字数、字体而变)

    - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; /** QQ聊 ...

  2. Xcode插件管理

    在使用Xcode的时候,公司同事使用/// 和//TODO 就能打出很多注释信息.虽然他们帮忙给我也装了,但是我却不知道怎么弄的.今天在家无聊,过来自己实践了一把. so easy. 1.我使用的是P ...

  3. .NET基础加强,找工作之前可以看看这些............

    .NET基础知识加强: 1  变量命名规则:骆驼命名法:第一个字母小写之后的首字母大写,[对于方法名和类名首字母大写]→培养良好的命名规范. 2  构造函数:没有返回值,方法名和类名相同,每个类中都有 ...

  4. UIImage 调整图片大小

    -(UIImage *)scaleToSize:(UIImage *)img size:(CGSize)size { UIGraphicsBeginImageContext(size); [img d ...

  5. Android ExpandableListView的下拉刷新实现

    该控件的修改时根据PullToRefreshList的机制修改 下面是对ExpandableListView的扩展 package com.up91.gwy.view.componet; import ...

  6. 智能生活 科技无限 CTO VOICE 第二期 智能硬件创新创业专场演讲嘉宾招募

    生活不只有诗和远方,还有当下的痛点和需求 当可穿戴设备.虚拟现实.无人机.机器人进入人们视线甚至生活当中 下一个风口就在智能硬件领域上凸显 那么,创业者如何撕掉智能外衣,设计一款有竞争力的智能硬件? ...

  7. Speed-BI数据分析案例:2016年7月汽车销量排行榜

    据中国汽车工业协会统计分析,2016年7月,汽车产销比上月均呈下降,同比呈较快增长.1-7月,汽车产销保持稳定增长,增幅比上半年继续提升. 7月,汽车生产195.96万辆,环比下降4.38%,同比增长 ...

  8. autorelease基本概念

    // //  main.m //  01-autorelease基本概念 // //  Created by apple on 14-3-18. //  Copyright (c) 2014年 app ...

  9. 终于发现为什么SQL没有释放句柄,原来是保存句柄的变量被覆盖了,丢失了原来的句柄

    stmt = xxx ,  stmt = yyy ,  stmt.close() 之前的xxx 没有close掉

  10. Android的LinearLayout中的权重android:layout_weight

    当前EditText和Button部件只是适应了他们各自内容的大小,如下图所示: 这样设置对按钮来说很合适,但是对于文本框来说就不太好了,因为用户可能输入更长的文本内容.因此如果能够占满整个屏幕宽度会 ...