zoukankan      html  css  js  c++  java
  • C语言初学者代码中的常见错误与瑕疵(3)

    问题:


      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=1;;i++)
        {
            temp =(int) pow(10,i);
    
             if(x/temp==0)
                return i-1;
        }
    }
    
    int main()
    {
        unsigned int w[100],i,m,number,a,temp;
        int digit(int x);
        printf("想输入几组数据:
    ");
        scanf("%d",&m);
        printf("请输入相应数字
    ");
    
        for (i = 0;i<m;i++)        //输入相应的数值
        {    
        printf("%d、",i+1);
            scanf("%d",&w[i]);
        }
        
        printf("您要的n-1位数字如下:
    ");
    
        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
    ",number);
        }
    
        return 0;
    }

    评析:


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

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

     main(): 

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

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

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

       int digit(int x);
    

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

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

      这个写得很不错。作者没有像很多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
    ",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( "想输入几组数据:
    " );
       scanf( "%u" , &m );
       
       
       while ( m -- > 0u )
       {
          unsigned w ;
    
          printf( "请输入相应数字
    " );
          scanf( "%u" , &w );     
          
          printf( "您要的n-1位数字如下:
    " );
          printf( "%u
    " , get_low_num( w ) );  
       }
    
       return 0;
    }
    
    unsigned get_low_num( unsigned x )  
    {
        unsigned temp = 1u ;
        while ( ( x / temp ) > 10u )
        {
            temp *= 10u ;
        }
        return x % temp ;
    }
  • 相关阅读:
    无法连接到已配置的开发web服务器
    Java开发命名规范总结
    java包名命名规范[【转】
    github远程建了分支,本地看不到的问题
    git远程删除分支后,本地git branch -a 依然能看到的解决办法
    git主要操作命令
    Ant之build.xml配置详解【转】
    git合并指定文件到另一分支
    java最简单实现Log打印和生成日志文件
    SCF: 简单配置门面[转]
  • 原文地址:https://www.cnblogs.com/pmer/p/3425153.html
Copyright © 2011-2022 走看看