用函数指针设置顺序

可能你已经猜到了答案:C标准库的排序函数会接收一个比较器函数(comparator function)指针,用来判断两条数据是大于、小于还是等于。

qsort()函数看起来像这样:

用函数指针设置顺序 - 图1

qsort()函数会反复比较两个数据的大小,如果顺序颠倒,计算机会交换它们。

这就是为什么要使用比较器函数。它会告诉qsort()两个元素哪个排在前面,它会返回三种值:

用函数指针设置顺序 - 图2

下面来看一个例子,看看比较器函数在实际情况中是如何工作的。

用函数指针设置顺序 - 图3int排序聚焦

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

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

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

用函数指针设置顺序 - 图4

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

  1. int compare_scores(const void score_a, const void score_b)
  2. {
  3. }

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

用函数指针设置顺序 - 图5

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

用函数指针设置顺序 - 图6

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

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

用函数指针设置顺序 - 图7

 

用函数指针设置顺序 - 图8练习

现在轮到你了,下面描述了几种不同的排序,你能为每种排序编写比较器函数吗?为了帮你,第一个比较器函数已经写好了。

用函数指针设置顺序 - 图9

用函数指针设置顺序 - 图10

最后,假设你已经有了compare_areas()compare_names(),下面两个比较器函数你会怎么写?

用函数指针设置顺序 - 图11

 

用函数指针设置顺序 - 图12练习解答

现在轮到你了,下面描述了几种不同的排序。请为每种排序编写比较器函数。

用函数指针设置顺序 - 图13

用函数指针设置顺序 - 图14

最后,在已经有了compare_areas()compare_names()的前提下,下面两个比较器函数你会怎么写?

用函数指针设置顺序 - 图15

 

用函数指针设置顺序 - 图16轻松一刻

即使你被这题难倒了也不用担心。

本题涉及指针、函数指针,甚至还有一些数学,如果你觉得很难,那就休息一下,喝两口水,过一两小时以后再来试试。

用函数指针设置顺序 - 图17试驾

有几个比较器函数确实比较难写,有必要运行一下。需要用以下代码调用比较器函数。

用函数指针设置顺序 - 图18

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

用函数指针设置顺序 - 图19

太棒了,代码工作了!

现在试着写一些你自己的代码吧。排序函数很有用,比较器函数却很难写,不过熟能生巧,多写几个就会了。用函数指针设置顺序 - 图20

这里没有蠢问题

问:用来给字符串数组排序的比较器函数使用了char**,它是什么意思?

:字符串数组中的每一项都是字符指针(char*),当qsort()调用比较器函数时,会发送两个指向数组元素的指针,也就是说比较器函数接收到的是指向字符指针的指针,在C语言中就是char**

问:当调用strcmp()时,为什么是strcmp(*a, *b)而不是strcmp(a, b)

ab的类型是char**,而strcmp()函数需要接收char*类型的值。

问:qsort()会创建新数组吗?

:不会,qsort()在原数组上进行改动。

问:为什么我的头有点疼?

:别担心,指针很难用,如果你一点儿也不感到困扰,可能是想得还不够深。