《Head First C 中文版》第 327 页:

假设有一个整型数组,你想升序排列它们,比较器函数应该长什么样子?

int scores[] = { 543, 323, 32, 554, 11, 3, 112 }; 

观察 qsort() 接收的比较器函数的签名,会发现它接收两个 void*,也就是两个 void 指针。我们在使用 malloc() 时碰到过它,void 指针可以保存任何类型数据的地址,但使用前必须把它转换为特定的类型。

qsort() 函数两两比较数组元素,然后以正确的顺序排列它们。qsort() 通过调用传给它的比较器函数来比较两个元素的大小。

int compare_scores(const void* score_a, const void* score_b)
{
  ...
}

值以指针的形式传给函数,因此要做的第一件事就是从指针中提取整型值。

int a = *(int*)score_a;  // 你需要把 void 指针转换为整型指针。
int b = *(int*)score_b;  // 第一个 * 就能得到保存在地址 score_b 中的整型值了。

如果 a 大于 b,需要返回正数;如果 a 小于 b,就返回负数;如果相等,返回 0 值。对整型来讲这很简单,只要将两数相减就行了:

return a - b;  // 如果 a > b,就是正数;如果 a < b,就是负数;如果 a、b 相等,就是 0 。

下面是用 qsort() 排序这个数组的方法:

qsort(scores, 7, sizeof(int), compare_scores);

然后,根据本书第 330~332 页的代码,得到以下 test_drive.c:

 1: #include <stdio.h>
 2: #include <string.h>
 3: #include <stdlib.h>
 4: 
 5: // 升序排列整型得分。
 6: int compare_scores(const void *score_a, const void *score_b)
 7: {
 8:   int a = *(int *)score_a;
 9:   int b = *(int *)score_b;
10:   return a - b;
11: }
12: 
13: // 降序排列整型得分。
14: int compare_scores_desc(const void *score_a, const void *score_b)
15: {
16:   int a = *(int *)score_a;
17:   int b = *(int *)score_b;
18:   return b - a; // 如果用第二个数减第一个,可以反转最终的排序
19: }
20: 
21: // 矩形类型。
22: typedef struct {
23:   int width;
24:   int height;
25: } rectangle;
26: 
27: // 按面积从小到大排列矩形。
28: int compare_areas(const void *a, const void *b)
29: {
30:   rectangle *ra = (rectangle *)a;
31:   rectangle *rb = (rectangle *)b;
32:   int area_a = (ra->width * ra->height);
33:   int area_b = (rb->width * rb->height);
34:   return area_a - area_b;
35: }
36: 
37: // 按字母序排列名字,区分大小写
38: int compare_names(const void *a, const void *b)
39: {
40:   char **sa = (char **)a;
41:   char **sb = (char **)b;
42:   return strcmp(*sa, *sb);
43: }
44: 
45: // 按面积从大到小排列矩形。
46: int compare_areas_desc(const void *a, const void *b)
47: {
48:   return compare_areas(b, a); // 或者也可以写 -compare_areas(a, b)。
49: }
50: 
51: // 按逆字母序排列名字,区分大小写
52: int compare_names_desc(const void *a, const void *b)
53: {
54:   return compare_names(b, a); // 或者也可以写 -compare_names(a, b)。
55: }
56: 
57: int main()
58: {
59:   int scores[] = { 543, 323, 32, 554, 11, 3, 112 };
60:   int i;
61:   qsort(scores, 7, sizeof(int), compare_scores_desc);
62:   puts("These are the scores in order:");
63:   for (i = 0; i < 7; i++) {
64:     printf("Score = %i\n", scores[i]);
65:   }
66:   char *names[] = {"Karen", "Mark", "Brett", "Molly"};
67:   qsort(names, 4, sizeof(char *), compare_names);
68:   puts("These are the names in order:");
69:   for (i = 0; i < 4; i++) {
70:     printf("%s\n", names[i]);
71:   }
72:   return 0;
73: }

编译和运行代码,将得到:

> gcc test_drive.c -o test_drive && ./test_drive
These are the scores in order:
Score = 554
Score = 543
Score = 323
Score = 112
Score = 32
Score = 11
Score = 3
These are the names in order:
Brett
Karen
Mark
Molly
>

这个结果和本书中第 333 页是一致的。但是,如果我们把上述代码第 59 行改为:

int scores[] = { 543, 323, 32, 554, 11, 3, -2147483648 };

再次编译和运行代码,将得到:

> gcc test_drive.c -o test_drive && ./test_drive
These are the scores in order:
Score = -2147483648
Score = 554
Score = 543
Score = 323
Score = 32
Score = 11
Score = 3
These are the names in order:
Brett
Karen
Mark
Molly
> 

这个结果当然不对。这是因为上述代码中第 18 行的b - a运算溢出了。要得到正确的比较器函数,只需将上述代码中第 13~19 行的compare_scores_desc函数修改如下:

// 降序排列整型得分。
int compare_scores_desc(const void *score_a, const void *score_b)
{
  int a = *(int *)score_a;
  int b = *(int *)score_b;
  if (a > b) return -1; // 注意:是降序
  if (a < b) return 1;
  return 0;
}

再次编译并运行代码,将得到:

> gcc test_drive.c -o test_drive && ./test_drive
These are the scores in order:
Score = 554
Score = 543
Score = 323
Score = 32
Score = 11
Score = 3
Score = -2147483648
These are the names in order:
Brett
Karen
Mark
Molly
> 

我们得到了正确结果。当然,就这本书讨论的问题来说,不可能发生溢出的情况。