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("想輸入幾組數據:\n");
    scanf("%d",&m);
    printf("請輸入相應數字\n");

    for (i = 0;i<m;i++)        //輸入相應的數值
    {    
    printf("%d、",i+1);
        scanf("%d",&w[i]);
    }
    
    printf("您要的n-1位數字如下:\n");

    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);
    }

    return 0;
}

評析:


  先說一下題目。這個題目比較好,程序的要求和問題的范圍非常明確,而且給出了樣例。美中不足的是“無符號整數”這個詞,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 0;
}

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM