Qsort比较函数

Posted

技术标签:

【中文标题】Qsort比较函数【英文标题】:Qsort Comparison Function 【发布时间】:2012-12-13 03:05:09 【问题描述】:

我是 C 初学者,我正在尝试了解 qsort 函数所需的比较函数。

第一部分:语法

一个简单的建议用法是这样的(我也包含了一些 main() 代码来打印结果):

#include <stdio.h>
#include <stdlib.h>

int values[] =  40, 10, 100, 90, 20, 25, 12, 13, 10, 40 ;

int compare(const void *a, const void *b)

    const int *ia = (const int *)a; // casting pointer types 
    const int *ib = (const int *)b;
    return *ia  - *ib; 


int main()

    int n;
    for (n=0; n<10; n++)
    
        printf("%d ",values[n]);
    
    printf("\n");
    qsort(values, 10, sizeof(int), compare);
    for (n=0; n<10; n++)
    
        printf("%d ",values[n]);
    
    printf("\n");
    system("pause");
    return 0;

我不明白为什么您需要比较功能中的所有额外内容,所以我将其简化为:

int compare (int *a, int *b)
    
        return *a-*b; 
    

这仍然有效,并产生相同的结果。谁能向我解释我删除了什么,为什么它仍然有效?

第二部分:为什么是指针?

另外,我真的需要使用指针吗?为什么我不能像这样直接比较“a”和“b”(这不起作用):

int compare (int a, int b)
        
            return a-b; 
        

由于某种原因,使用多维数组,我能够摆脱不使用指针的情况,并且由于某种原因它起作用了!到底是怎么回事? (多维数组按每个子数组第2项排序的示例代码):

#include <stdio.h>
#include <stdlib.h>

int values[7][3] =  40,55, 10,52, 100,8, 90,90, 20,91, 25,24 ;

int compare(int a[2], int b[2])

    return a[1] - b[1];


int main()

    int n;
    for (n=0; n<6; n++)
    
        printf("%d,",values[n][0]);
        printf("%d ",values[n][1]);
    
    printf("\n");
    qsort(values, 6, sizeof(int)*3, compare);
    for (n=0; n<6; n++)
    
        printf("%d,",values[n][0]);
        printf("%d ",values[n][1]);
    
    printf("\n");
    system("pause");
    return 0;

我真的很高兴多维数组排序能够正常工作,因为这是我的最终目标,但我不知道我是如何让它工作的(除了运气不好和切碎代码)所以我真的很想解释一下为什么我提供的一些例子有效,为什么有些不有效!

【问题讨论】:

【参考方案1】:

这仍然有效,并产生相同的结果。谁能向我解释我删除了什么,为什么它仍然有效?

您在 C 中调用未定义的行为。请参阅 C99 6.3.2.3 Pointers/8:

指向一种函数的指针可以转换为指向另一种函数的指针 键入并再次返回;结果应与原始指针比较。如果一个转换 指针用于调用类型与指向类型不兼容的函数, 行为未定义。

在 C++ 中,这个程序完全是病态的:http://ideone.com/9zRYSj

它仍然“碰巧工作”,因为compare 函数需要一对指针;在您的特定平台上sizeof(void*)sizeof(int*) 相同,因此调用int(void *, void *) 类型的函数指针实际上包含指向int(int *, int *) 类型函数的指针实际上与在此特定时间点在特定平台上强制转换的指针类型相同。

另外,我真的需要使用指针吗?为什么我不能像这样直接比较“a”和“b”(这不起作用):

因为qsort 对任意两种类型都有一个通用的比较函数;不仅仅是int。所以它不知道指针被解引用到什么类型。

由于某种原因,使用多维数组,我能够摆脱不使用指针的情况,并且由于某种原因它起作用了!怎么回事!

这是因为以下原型是相同的:

    int foo(int *a, int *b); int foo(int a[], int b[])

也就是说,数组在传递给函数时衰减为指针。像您一样显式指定数组的长度:

int foo(int a[2], int b[2])

使编译器使sizeof 和其他编译时间位将该项视为二元素数组;但是当它下降到机器级别时,该函数仍然接受一对指针。

在任何这些情况下,传递一个不采用一对void *s 的比较函数都会导致未定义的行为。 “未定义行为”的一个有效结果是“它似乎起作用了”。另一个有效结果是“它在星期二工作”或“它格式化硬盘”。不要依赖这种行为。

【讨论】:

有趣的是,数组分解为指针。这是可以依赖的,还是其中也存在固有的错误?您如何建议修改多维排序以使其可靠? @Dlinet:始终让您的比较函数采用const void*。将该指针转换为const int*。然后像往常一样使用operator[]【参考方案2】:

我不明白为什么您需要在比较中添加所有额外的东西 函数,所以我将其简化为这个

您是否使用const 限定符取决于您。您不应修改比较器中的值。但是有可能抛弃const 并打破你对编译器的承诺。


qsort 需要一个函数指针,它接受两个 const void * 作为参数,这就是为比较器函数传递指针的原因:

   void qsort(void *base, size_t nmemb, size_t size,
                  int(*compare)(const void *, const void *));

因此传递ab 会导致将 解释为指针,这显然是错误的。


它在不为多维数组传递指针的情况下工作,因为当你传递数组时,它们会衰减为指针。因此,您拥有的以下比较器是可以的。

int compare (int a[2], int b[2])

    return a[1] - b[1];

【讨论】:

【参考方案3】:

答案很简单:来自qsorthttp://pubs.opengroup.org/onlinepubs/009695399/functions/qsort.html的手册,函数指针的原型是:

int comp(const void *a, const void *b)

这个原型关联的typedef可以这样声明

typedef int (*comp_qsort_funct_t) ( const void *, const void * )

其中comp_qsort_funct_t是函数指针的类型

qsort 的比较函数原型使用 const 变量,因为这是一个很好的实践/设计,因为数据不会被修改。

使用不同的原型可能会导致意外结果,具体取决于编译器/平台。或者只是编译失败,注释中提供的示例:http://ideone.com/9zRYSj

所以你不应该使用不同的原型。

【讨论】:

这是 C,不是 C++。在 C++ 中,这是完全非法的;不仅仅是“糟糕的设计实践”ideone.com/9zRYSj 我只是在解释为什么原型使用 const 变量......我不明白你对 C++ 的看法......请解释一下 在 C 中,此代码是未定义的行为。在 C++ 中,这段代码完全是格式错误的。您的参考是 C++ 源代码,但您说的是 C。请澄清这种差异,我会将 -1 更改为 +1。 所提供的链接是关于 C 而不是 C++...您刚刚阅读了 URL 的域吗?并且提供的代码不是格式错误的,这是在C中声明函数原型的方式 原型不是病态的;对qsort 的调用是。你指向了一个页面标题和内容中写着“C++ Reference”的页面;但这是 C 和 C++ 显着不同的领域;因此,仅替换 C++ 参考可能会使读者感到困惑。【参考方案4】:

我想确认提出原始问题的 Dlinet 的观察结果。

C 编译器确实会接受所有形式的比较函数,即使没有 const,甚至使用 int * 代替 void *。但是,我的 C++ 编译器拒绝这些变化,只接受 const void * 形式。

C 编译器甚至接受 int 形式,根本不使用指针。我检查并发现比较函数的 int 形式正在接收 pointer 值,而不是数组的 int。结果是数组没有排序,但保持相同的顺序。而且我认为这一切都是您所期望的。

因此,您似乎可以在函数定义中使用 int * 而不是 void *,然后不需要在函数内进行强制转换。

这是否是一种好的编程习惯尚无定论,有些程序员说是,有些人说不是。在我看来,无论编程是否良好,减少混乱将是支持使用 int * 而不是 void * 的一点。但是,C++ 编译器不允许您这样做。

【讨论】:

我不明白这个迟到的答案是如何得到赞成的;这是完全错误的。 C 编译器接受所有类型的非法代码。有问题的 C 代码会导致未定义的行为。这不是良好的编程习惯,没有知道如何用 C 编程的人说它是。

以上是关于Qsort比较函数的主要内容,如果未能解决你的问题,请参考以下文章

在 qsort 比较函数上出现错误

如何从 stdlib 为 qsort 编写比较函数?

qsort函数实现对任意数据的排序

qsort()函数(C)

有没有一种方法可以在不使用大量分支语句的情况下为无符号整数编写 qsort 比较函数?

如何将长双打与 qsort 以及关于 NaN 进行比较?