垃圾代碼評析——關於《C程序設計伴侶》6.2(三)


前文鏈接:http://www.cnblogs.com/pmer/archive/2012/11/05/2754784.html

【樣本】

  •  輸出二維數組結果

  排序完成之后,我們對於數據的處理就已經完成了,最后的任務就是將處理后的數據輸出,告訴用戶數據的處理結果。自然地,要將二維數組中的數據輸出,我們還是利用for語句來循環遍歷實現:

1. //
2. 
3. int main() 
4. {
5.    //
6.    
7.    // 輸出結果數據 
8.    for(int i = 0; i < classnum;++i)
9.    {
10.       printf("average score is %.2f :\n",
11.          getaver(scores[i]));
12.       
13.       for(int j = 0; j < stnum; ++j)
14.       {
15.          if(0 == scores[i][j])
16.             break;
17.          printf("%4d",scores[i][j]);
18.       }
19.       printf("\n");
20.    }
21.    return 0;
22. }

 

  因為此時的二維數組已經是排序完成的,我們只需要利用for語句循環遍歷所有數據將其全部輸出就可以了。在輸出的時候,我們還順帶輸出了各個班級的平均成績,這時我們使用“scores[i]”調用getaver()函數,這也說明它其實就是各個一維數組的首地址。
    ——陳良喬 ,《C程序設計伴侶》,人民郵電出版社,2012年10月,p105~107

【評析】

  大體上這段代碼還過得去,如果是初學者的習作應該能得70分。但在書里這樣寫有些說不過去了。
  首先,把這么長的代碼擠在main()中不妥,很業余。抽象為一個函數完成這個功能為好。
  其次,代碼的

10.       printf("average score is %.2f :\n",
11.          getaver(scores[i]));

 

  居然再一次計算了各個班的平均成績。作為專業程序員,如果沒有特別過硬的理由,這絕對是一種奇恥大辱。這一方面反應了作者在思考代碼時沒有很好地進行數據結構的設計(整天把“算法才是王道”掛在嘴邊的都愛犯這種毛病),另一方面反應了作者在算法方面也缺乏全盤考慮就開始寫代碼了,寫到最后也顧不了那么多了,只好在這里隨手打了一塊難看的補丁。
  代碼中還有幾處很業余的地方,例如

15.          if(0 == scores[i][j])
16.             break;

 

  這種寫法顯然是因為作者不懂得&&運算符的運算規則的緣故。(在該書77頁,作者是這樣講解&&運算的:“例如:(a>0)&&(b>0) 在計算這個邏輯表達式的值的時候,會根據小括號確定的運算次序(或者表達式的默認運算順序),首先計算a>0和b>0這兩個關系表達式的值,然后邏輯運算符“&&”會根據這兩個關系表達式的值最終得出整個邏輯表達式的值。”)不懂得細節就寫不出漂亮的代碼,神在細節當中。
  其實那個內層循環語句應該這樣寫:

      for(int j = 0; ( j < stnum ) && ( 0 != scores[i][j] ); ++j)
      {
         printf("%3d ",scores[i][j]);
      }

 

  這里的另一處改動是把printf()中的實參"%4d"改成了"%3d ",這樣即使scores[i][j]達到四位輸出也不至於亂套。
  最后一處

19.       printf("\n");

 

  還是寫

       putchar ('\n');

 

 為好。當然,這只是一個小細節。

【重構】

  絕對不要上來就寫 “6個班,每個班的人數不等,但最多不超過100個” 這種傻乎乎的代碼,理由很簡單,根本無法測試!因此這里將這個條件改為“4個班,每個班的人數不等,但最多不超過3個”。這樣便於測試,將來修改這兩個常數也很容易。
  其次,既然原來的代碼使用了qsort()標准函數,這里也將同樣使用。
  第三,設定分數不可能為負值,輸入負值表示該班級分數輸入完畢。
  下面邊分析邊寫代碼。
  首先,用枚舉常量描述問題的規模:

enum {
       STNUM = 3 ,     
       CLNUM = 4 
     };

 

   使用如下的數據結構描述班級:

typedef
struct {
         int scores[STNUM] ;
         double average ;
       } 
CLASS ;

 

   這樣,所有的班級就構成了一個數組。

CLASS classes[CLASS_NUM];

 

   總體思路非常簡單:

int main( void )
{
   CLASS classes[CLASS_NUM];
   //輸入成績
   //計算平均分
   //調用qsort()函數排序
   //輸出排序后各班成績 
   return 0;
}

 

   下面是完成之后的代碼:

#include <stdio.h>
#include <stdlib.h>

enum {
       STUDENT_NUM = 3 ,     
       CLASS_NUM   = 4 
     };
     
typedef
struct {
         int scores[STUDENT_NUM] ;
         double average ;
       } 
CLASS ;    

#define SIZE(A) ( sizeof(A) / sizeof((A)[0]))

void input_gr( CLASS [] , size_t );
void input_cl( int   [] , size_t );
void cal_gr ( CLASS [] , size_t );
double cal_cl ( int   [] , size_t ) ;
int cmp( const void * , const void * );
void output_gr( CLASS [] , size_t );
void output_cl( int   [] , size_t );

int main( void )
{
   CLASS classes[CLASS_NUM];
   
   input_gr( classes , SIZE(classes) );//輸入成績
   cal_gr ( classes , SIZE(classes) );//計算平均分
   qsort( classes , SIZE(classes) , sizeof(CLASS) , cmp  );//調用qsort()函數排序
   output_gr( classes , SIZE(classes) );//輸出排序后各班成績 
   return 0;
}

void output_cl( int  scr [] , size_t n)
{
   size_t i ;
   for( i = 0 ; (i < n)&&( scr[i] >= 0 )  ; i ++ )
      printf( "%3d " , scr[i] );

   putchar('\n') ;
}

void output_gr( CLASS cl[] , size_t n)
{
   size_t i ;
   for( i = 0 ; i < n ; i ++ )
   {
      output_cl( cl[i].scores , SIZE(cl[i].scores) );
      printf( "平均分為:%f。\n" , cl[i].average ) ;
   }   
}

int cmp( const void *elem1 , const void *elem2 )
{
   if(((CLASS *)elem1)->average > ((CLASS *)elem2)->average )
      return 1;

   if(((CLASS *)elem1)->average < ((CLASS *)elem2)->average )
      return -1;

   return 0;
}

double cal_cl ( int   scr[] , size_t n )
{
   size_t i  ;
   int total ;
   
   if( n == 0 )
      return 0.;
   
   for( 
        i = 0 , total = 0 ; 
        ( i < n ) && ( scr[i] >= 0 ) ; 
        i ++ 
      )
   {   
      total += scr[i] ;
   }
   return (double)total/(double)i;
}

void cal_gr ( CLASS cl[] , size_t n )
{
   size_t i ;
   for( i = 0 ; i < n ; i ++ )
      cl[i].average = cal_cl ( cl[i].scores , SIZE(cl[i].scores) ) ;
}

void input_cl( int  scr [] , size_t n)
{
   size_t i ;
   for( i = 0 ; i < n ; i ++ )
   {
      scanf( "%d" , scr + i );
      if( scr[i] < 0 )
         return ;
   }   
}

void input_gr( CLASS cl[] , size_t n)
{
   size_t i ;
   for( i = 0 ; i < n ; i ++ )
   {
      printf( "輸入%d班成績,輸入負數表示結束:\n" , i + 1 ) ;
      input_cl( cl[i].scores , SIZE(cl[i].scores) );
   }
}

 


免責聲明!

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



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