qsort
被编写为可以对任何类型的数据进行排序。大部分排序都是在内存中移动项目,这可以通过移动表示数据的字节来完成:您不需要知道两个数组元素中字节的含义来交换它们,您只需要知道在哪里它们是什么以及每个字节有多少字节。所以qsort
可以接受一个void *
该部分位于数组的开头和一个size_t
它告诉它每个元素中有多少字节,并且它不关心该元素的原始类型是什么void *
is.
然而,qsort
无法仅根据字节来确定排序顺序中两个元素中哪一个更早。它可以纯粹按照字节中位的二进制值对所有数组元素进行排序,但有人可能希望以相反的顺序对数组进行排序,或者按字母顺序对字符串进行排序,忽略大小写,或者按浮点解释进行排序。其中每一个的排序都与仅按二进制值排序不同。所以qsort
需要一个辅助例程。这是传递给它的比较例程。
Since qsort
只给出一个void *
,它不知道数组元素的原始类型是什么,因此无法将该类型传递给比较例程。比较例程必须将void *
到正确的类型。每个比较例程都是根据其比较元素的类型进行定制的。它传递的指针不指向随机地址;它们指向正在排序的数组中的元素。
给定函数声明int comparator(const void *pLhs, const void *pRhs)
, 分配int *lhs = (int *) pLhs;
转换为void *
范围pLhs
to an int *
多变的lhs
. Then *lhs
可以用作int
,所以可以rhs
.
您显示的示例代码中存在三个错误或缺陷。首先,比较例程传递一个const void *
。预计不会更改所指向的值。所以它应该将指针转换为const int *
, not int *
.
其次,在 C 中强制转换是不必要的。简单地做const int *lhs = pLhs;
将工作。编译器会自动转换const void *
to const int *
初始化时lhs
。 (在 C++ 中,这样的强制转换可能是必要的。)在 C 中,在不必要的情况下不鼓励进行此类强制转换,因为在某些情况下,如果强制转换不存在,它们可以通过逃避编译器将生成的警告消息来隐藏错误。事实上,这已经发生在这里。通过强制转换,Apple Clang 11 不会发出初始化警告。如果没有强制转换,编译器会警告初始化会丢弃const
预选赛。
Third, return *lhs > *rhs;
是不够的。这qsort
如果左参数应排序在右参数之前,则比较应返回负值;如果它们就排序顺序而言相同,则比较应返回零;如果左参数应排序在右参数之后,则比较应返回正值。因此升序的正确代码可能是:
return *lhs < *rhs ? -1 : *lhs == *rhs ? 0 : +1;
对于降序排列,它可以是:
return *lhs > *rhs ? -1 : *lhs == *rhs ? 0 : +1;
如果您还不熟悉条件运算符,您可以使用:
if (*lhs > *rhs)
return -1;
else if (*lhs == *rhs)
return 0;
else
return +1;