时间:2010-02-09 11:37:04

标签: c pointers

qsort(bt->rw[t], bt->num[t], 
      sizeof(TRELLIS_ATOM *), 
      (int (*)(const void *,const void *))compare_wid);

bt->rw[t]是指向结构指针的指针,bt->[num]int,我不明白第四个参数是什么,除了compare_wid是在某处定义的函数,如下所示:

static int compare_wid( TRELLIS_ATOM* a, TRELLIS_ATOM* b )
{
   ...
   return x;
}

8 个答案:

答案 0 :(得分:22)

我会稍微了解该行的含义,但在此之前,让我们了解为什么qsort()需要其所需类型的最终参数的一些基础知识。 qsort()是一个可以对任何类型的数据进行排序的函数。

您提供:

  • 一个基指针,指向一个连续数据块的开头,
  • 块中元素的数量
  • 一个数据成员的大小,
  • 一个比较两个数据值的函数。

由于排序算法通常不依赖于正在排序的数据的类型,因此可以在不知道它正在排序的数据类型的情况下编写qsort()。但是为了能够做到这一点,qsort()接受void *参数,这意味着C中的“通用指针”。

假设您有一系列未分类的int值:

#define N 1024
int data[N] = { 10, 2, 3, -1, ... } /* 1024 values */

然后您可以致电qsort()

对其进行排序
qsort(data, N, sizeof data[0], compare_int);
传递给data时,

int *的类型为qsort(),而qsort()的第一个参数属于void *类型。由于任何对象指针都可以在C中转换为void *,这是可以的。接下来的两个论点也没问题。最后一个参数compare_int应该是一个函数,它接受两个const void *参数并返回int。该qsort()函数将使用&data[0]&data[N-1]之间的成对指针调用该函数。

声明一个函数f()“需要两个const void *参数并返回int”:

int f(const void *, const void *);

如果想要声明一个我们可以设置为f的函数指针,则指针被声明为:

int (*pf)(const void *, const void *);
pf = f;

括号是必需的,因为否则pf将是返回int *的函数。现在,pf是指向返回int的函数的指针。

回到我们的int排序算法,从上面我们可以将compare_int()定义为:

int compare_int(const void *a, const void *b)
{
    const int *the_a = a;
    const int *the_b = b;
    if (*the_a > *the_b) return 1;
    else if (*the_a < *the_b) return -1;
    else return 0;
}

在撰写compare_int()时,我们知道传递的指针是int *伪装成void *,因此我们将它们转换回int *,这是正常的,然后我们比较数字。

现在,我们将注意力转向相关代码:

static int compare_wid( TRELLIS_ATOM* a, TRELLIS_ATOM* b )

表示compare_wid是一个带有两个TRELLIS_ATOM *参数的函数,并返回int。正如我们刚才看到的那样,qsort()的最后一个参数应该是一个类型为的函数:

int (*)(const void *, const void *)

即,一个函数接受两个const void *参数并返回int。由于类型不匹配,程序员会将compare_wid()强制转换为qsort()所需的类型。

然而,这有问题。类型函数:

int (*)(TRELLIS_ATOM *, TRELLIS_ATOM *)

不等同于类型函数:

int (*)(const void *, const void *)

因此无法保证演员是否有效。将compare_wid()声明为:

更容易,更正确,更标准
static int compare_wid(const void *a, const void *b);

compare_wid()的定义应如下所示:

static int compare_wid(const void *a, const void *b)
{
    const TRELLIS_ATOM *the_a = a;
    const TRELLIS_ATOM *the_b = b;
    ...
    /* Now do what you have to do to compare the_a and the_b */
    return x;
}

如果你这样做,你就不需要在调用qsort()时进行演员表了,你的程序不仅更容易阅读,而且更正。

如果您无法更改compare_wid(),请编写另一个功能:

static int compare_stub(const void *a, const void *b)
{
    return compare_wid(a, b);
}

并使用qsort()(不使用强制转换)代替compare_stub()致电compare_wid()

编辑:基于许多错误的答案,这是一个测试程序:

$ cat qs.c
#include <stdio.h>
#include <stdlib.h>

struct one_int {
    int num;
};

#ifdef WRONG
static int compare(const struct one_int *a, const struct one_int *b)
{
#else
static int compare(const void *a_, const void *b_)
{
    const struct one_int *a = a_;
    const struct one_int *b = b_;
#endif
    if (a->num > b->num) return 1;
    else if (a->num < b->num) return -1;
    else return 0;
}

int main(void)
{
    struct one_int data[] = {
        { 42 },
        { 1 },
        { 100 }
    };
    size_t n = sizeof data / sizeof data[0];

    qsort(data, n, sizeof data[0], compare);
    return 0;
}

使用compare()进行编译,定义为获取两个const struct one_int *值:

$ gcc -DWRONG -ansi -pedantic -W -Wall qs.c
qs.c: In function `main':
qs.c:32: warning: passing argument 4 of `qsort' from incompatible pointer type

使用正确的定义进行编译:

$ gcc -ansi -pedantic -W -Wall qs.c
$

编辑2 :对compare_wid的最终参数使用qsort() as-it-is的合法性似乎存在一些混淆。 comp.lang.c FAQ, question 13.9有一个很好的解释(强调我的):

  

要理解为什么qsort比较函数中的好奇指针转换是必要的(以及为什么在调用qsort时函数指针的强制转换无法帮助),考虑qsort如何运作是有用的。 qsort对要排序的数据的类型或表示一无所知:它只是在一小块内存中乱窜。 (所有它知道的块是它们的大小,你在qsort的第三个参数中指定。)为了确定两个块是否需要交换,qsort调用你的比较函数。 (要交换它们,它使用相当于memcpy。)

     

由于qsort以一般方式处理未知类型的内存块,因此它使用通用指针(void *)来引用它们。当qsort调用您的比较函数时,它将作为参数传递给要比较的块的两个通用指针。由于它传递通用指针,因此比较函数必须接受通用指针,并在操作指针之前将指针转换回适当的类型(即在执行比较之前)。 void指针与结构指针的类型不同,并且在某些机器上它可能具有不同的大小或表示(这就是正确性需要这些强制转换的原因)。

如常见问题解答中所述,另请参阅this

答案 1 :(得分:5)

(int (*)(const void *,const void *))表示“将后面的内容视为指向函数的指针,该函数接受const void*类型的两个参数并返回int”。 compare_wid确实是一个可以这样对待的函数。

qsort将调用此函数在排序时执行项目之间的比较:如果返回的整数为零,则假定项目相等,否则使用整数的符号对它们进行排序。

答案 2 :(得分:3)

qsort

void qsort ( void * base, size_t num, size_t size, 
                    int ( * comparator ) ( const void *, const void * ) );
  

比较器:比较两个的功能   元素。功能如下   这个原型:

     

int comparator(const void * elem1,   const void * elem2);

     

该功能必须接受两个   指针的参数   元素,类型转换为void *。这些   参数应该归还给某些人   数据类型并进行比较。

     

此功能的返回值   应该代表elem1是否   被认为是小于,等于,或   返回大于elem2,   分别为负值,零   或者是正值。

答案 3 :(得分:2)

第四个参数包含对函数指针的显式强制转换:

int (*)(const void *,const void *)
  • int部分对应于返回类型
  • (*)部分是函数指针的符号
  • (const void *,const void *)部分是参数类型

答案 4 :(得分:0)

这是qsort实现调用的谓词函数,用于比较两个TRELLIS_ATOM*

答案 5 :(得分:0)

这是TRELLIS_ATOM类型元素的自定义排序函数(无论可能是什么)。

compare_wid应该返回一些&lt;如果*a < *b,则为0,&gt;如果*a > *b则为0,如果*a*b在逻辑上被视为相等,则为0。

答案 6 :(得分:0)

这是一个quicksort函数,它需要一个指向自定义排序处理程序例程的函数指针。因此,在第四个参数中对函数指针的参数进行类型转换。

当调用快速排序函数时,它会执行您提供的用于处理排序算法的函数指针,即TRELLIS_ATOM *aTRELLIS_ATOM *b,然后在两者之间进行比较检查,并在逻辑比较中返回1,0或-1,例如:

if (*a > *b) return 1;
if (*a == *b) return 0;
if (*a < *b) return -1;

希望这有帮助, 最好的祝福, 汤姆。

答案 7 :(得分:0)

/* Integer comparison function for use with the stdlib's Qsort. */
inline int int_compare(const void *p1, const void *p2) {
        return ( *(int*)p1 - *(int*)p2 );
}