垃圾代码评析——关于《C程序设计伴侣》6.2(二)

前文链接:http://www.cnblogs.com/pmer/archive/2012/11/04/2754294.html

【样本】

  用qsort()函数对二维数组进行排序

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <string.h>
4. #include <math.h>
5. // 获得一维数组的平均值
6. float getaver(const int* st)
7. {
8.    int count = 0;
9.    int total = 0;
10.    // 只要没有遇到表示数据结束的0值,
11.    // 就一直累加所有数据的值
12.    while(0!=*st)
13.    {
14.       ++count;               // 数据个数加1
15.       total += *st;          // 累加数据
16.       ++st;  // 指向下一个数据
17.    }
18.    
19.    // 返回平均值
20.    if( 0 == count )  // 对没有元素的情况作特殊处理
21.    {
22.       return 0.0f;
23.    }
24.    else
25.    {
26.       return (float)total/count;
27.    }
28. 
29. }
30. 
31. // 通过数组的平均值大小进行比较
32. int cmp(const void* a, const void* b)
33. {
34.    // 获得两个数组的平均值
35.    float avera = getaver((int*)a);
36.    float averb = getaver((int*)b);
37.    
38.    // 返回比较结果
39.    if(fabs(avera - averb) < 0.0001)
40.    {
41.       return 0; 
42.    }
43.    else if( avera > averb )
44.    {
45.       return 1;  
46.    }
47.    else
48.    {
49.       return -1;
50.    }
51.     
52. }
53. int main() 
54. {
55.    //
56.    
57.    // 利用qsort()函数对二维数组进行排序
58.    qsort(scores,  // 数组名,也就是数组首地址
59.       classnum,   // 数组元素个数
60.       stnum*sizeof(int),        // 每个数据元素占用的空间
61.       cmp);        // 比较规则函数
62.    
63.    return 0;
64. }

   因为我们需要根据数组的平均值进行排序,所以我们首先定义了一个getaver()函数用于获取一个数组(以这个数组的首地址作为参数)的平均值。在getaver()函数中,我们利用一个while循环统计各个数据的总和以及数据个数,直到遇到表示数组数据结束的0为止(while(0!=*st))。最后getaver()返回的就是这个数组的平均值((float)total/count)。在比较规则函数cmp()函数中,我们正是利用getaver()函数来方便地夺得了两个待比较的数组的平均值,最后cmp()函数返回的是两个平均值相减的结果,也就是两个数组的比较结果。这里我们需要注意的是,cmp()函数的两个参数,在这里不再是指向两个单独的数据元素的指针,而成为了指向二维数组中的两个一维数组的指针,是这两个一维数组的首地址,所以可以经过类型转换后传递给getaver()函数直接获得两个数组的平均值。最后,qsort()函数会调用这个比较规则函数cmp()以此来实现二维数组的快速排序,而排序的结果也会重新写回这个数组。换句话说,经过qsort()函数调用后的数组,已经是排序完成的数组。
    ——陈良乔 ,《C程序设计伴侣》,人民邮电出版社,2012年10月,p105~107

【评析】

  这种头重脚轻的代码结构(把main()写在后面)非常难于阅读,使人不得不用“拿大鼎”的姿势来看代码。先来看main()。
  这个函数中只写了一个函数调用,这个调用基本没有问题。只是其中的“stnum*sizeof(int)”是非常啰嗦的写法,是“sizeof scores”的画蛇添足版。
  下面再来看cmp():

31. // 通过数组的平均值大小进行比较
32. int cmp(const void* a, const void* b)
33. {
34.    // 获得两个数组的平均值
35.    float avera = getaver((int*)a);
36.    float averb = getaver((int*)b);

  这里的a、b两个形参名我就不吐槽了。业余到什么程度大家心里有数。
  要说的是35、36行,这里两次调用了getaver()函数,求两个一维数组所有元素的平均值,注意这里都只有一个实参,这个实参是指向一维数组起始元素的指针。通常这是不行的,因为getaver()函数并不清楚数组中元素的个数。
  既然getaver()函数并不清楚数组中元素的个数,那它又如何求平均值呢?再来看一下getaver()函数。在这个函数中有这样一段代码

12.    while(0!=*st)
13.    {
14.       ++count;               // 数据个数加1

  原来它试图通过找到0确定数组中有多少个数据是有效的。然而,前文分析表明,整个二维数组中可能一个0值元素都没有。在这种情况下,程序运行的结果必然是一场灾难。
  仅此,就足矣判定这个程序死刑,MVP的代码是垃圾代码。除了做code review的反面教材,这种代码没有任何价值。
为了了解代码中的其他问题,不妨捂着鼻子把代码看完。
  现在回到cmp(),其中

38.    // 返回比较结果
39.    if(fabs(avera - averb) < 0.0001)
40.    {
41.       return 0; 
42.    }
43.    else if( avera > averb )
44.    {
45.       return 1;  
46.    }
47.    else
48.    {
49.       return -1;
50.    }

  其中的fabs(avera - averb) < 0.0001无疑是在判断avera、averb是否相等,但在这里其实有些煞有介事。写成

   // 返回比较结果
   if( avera < averb )
      return -1;  

   if( avera > averb )
      return 1;  

   return 0;

其实就很好。不但简洁,而且也不会产生其他什么问题。

 

posted @ 2012-11-05 11:15  garbageMan  阅读(2597)  评论(6编辑  收藏  举报