qsort() 和铸造操作

qsort() and casting operation

考虑一个指向结构的指针数组。以下代码取自您可能会找到的示例 here。我想对这两排演员表进行解释。我不熟悉这个 "double casting".

int myptrstructcmp(const void *p1, const void *p2)
{
    struct mystruct *sp1 = *(struct mystruct * const *)p1;
    struct mystruct *sp2 = *(struct mystruct * const *)p2;

我认为应该是:

int myptrstructcmp(const void *p1, const void *p2)
{
    struct mystruct *sp1 = (struct mystruct *)p1;
    struct mystruct *sp2 = (struct mystruct *)p2;

假设您要对 int 的数组进行排序。您的比较器将传递一对伪装成 void *int *;添加了一层间接。

如果您正在对 struct mystruct * 的数组进行排序,您的比较器将被 struct mystruct ** 伪装成 void *;添加了一层间接。

What is the meaning of struct mystruct * const *? Without the const* it fails to cast correctly. Why?

'without the const * it fails to cast correctly' 是什么意思?没有 const,它工作正常。如果没有第二个 *,它就无法工作,因为函数被传递了一个 struct mystruct **(给予或接受一些常量),如果你省略了第二个星号,你就是在滥用类型系统。

考虑:

struct mystruct
{
    int i;
};

int myptrstructcmp(const void *p1, const void *p2);
int myptrstructcmp(const void *p1, const void *p2)
{
    struct mystruct *sp1 = *(struct mystruct **)p1;
    struct mystruct *sp2 = *(struct mystruct **)p2;

    if (sp1->i < sp2->i)
        return -1;
    else if (sp1->i > sp2->i)
        return +1;
    else
        return 0;
}

这编译得很好。当您在 ** 之间添加 const 时,它也可以正常编译。就个人而言,我不会在转换中包含 const。我要做的是对 sp1sp2 指针进行常量限定:

    struct mystruct const *sp1 = *(struct mystruct **)p1;
    struct mystruct const *sp2 = *(struct mystruct **)p2;

或:

    const struct mystruct *sp1 = *(struct mystruct **)p1;
    const struct mystruct *sp2 = *(struct mystruct **)p2;

这承诺不会修改它们在函数中指向的对象,这实际上对 qsort() 的正确性能至关重要。

这是您的线索 — 在您参考的 C FAQ 中:

If, on the other hand, you're sorting pointers to structures, you'll need indirection,

您正在对指向结构的指针列表进行排序。必须按排序在列表中处理指针,这意味着您通过引用(通过指向指针的指针)传递列表中的指针以进行比较。

来自man qsort

[...] comparison function [..] which is called with two arguments that point to the objects being compared.

"被比较的对象" 是传递给 qsort() 的数组的元素,因为这个要排序的数组包含 指针 比较函数接收指向指针的指针作为参数。

要从传递给比较函数的参数中获取数组的元素,这些参数需要取消引用。

比较函数接收指向数组元素的指针。如果您的数组包含某种类型 T 的元素,那么您的比较函数将接收 const void * 指针,这些指针应被解释为 const T * 指针。

在你的例子中 T 等于 struct mystruct *(因为你的数组包含指针)。在那种情况下 const T * 等同于 struct mystruct * const*,这正是您在代码中看到的。

为了让您的代码更容易理解,您实际上可以在函数中引入一个 typedef 名称 T 并将您的 conversions/dereferences 拆分为单独的步骤

int myptrstructcmp(const void *p1, const void *p2)
{
  typedef mystruct *T;             // array elements are pointers
  const T *pel1 = p1, *pel2 = p2;  // pointers to array elements
  T sp1 = *pe1, sp2 = *pe2;        // values of array elemnts

这等同于您在原始代码中的内容;