【问题标题】:Is there any restriction about the compare function used in qsort()qsort() 中使用的比较函数是否有任何限制
【发布时间】:2018-04-03 15:43:12
【问题描述】:

我编写了这段代码 sn-p 以将字符串数组排序为最小化它们连接的顺序:

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

int cmpstr(const void* p1, const void* p2){
    int p1l = strlen((const char*)p1);
    int p2l = strlen((const char*)p2);
    int r = strncmp((const char*)p1, (const char*)p2, p1l<p2l?p1l:p2l);
    if(r == 0 && p1l != p2l){
        if(p1l < p2l){
            return cmpstr(p1, (const char*)p2 + p1l);
        }
        return cmpstr((const char*)p1 + p2l, p2);
    }
    return r;
}

int main(){
    const char* arrstr[] = {"93", "936", "15", "152", "946"};
    int num = sizeof(arrstr) / sizeof(char*);
    qsort(arrstr, num, sizeof(char*), cmpstr);
    for(int i = 0; i < num; i++){
        printf("%s\n", arrstr[i]);
    }
}

这些字符串应按 15 152 936 93 946 的顺序排序。我们希望 93 介于 936946 之间,因为 936 93 93 936 和 93 946 946 93(为了清楚起见忽略添加的空格)。

但是代码没有按预期工作。尽管我对cmpstr() 的测试完全符合我的预期,但数组根本没有排序。

我做错了什么?

我注意到,当我将cmpstr() 的演员阵容从*(char* const*) 更改为(char*) 时,qsort() 无法正常工作。这是为什么呢?

【问题讨论】:

  • "qsort()中使用的比较函数有什么限制" 你试过阅读std::qsort的文档吗?
  • 没有理由声明该函数static。这看起来像是货物狂热的编程,在不理解为什么的情况下使用它。
  • @tadman:可能不明白为什么,但强烈建议只在单个编译单元中使用函数static,以免污染程序的命名空间。有时好来自货物崇拜。例如。雷鬼。
  • @Olaf 如果这是一个复杂的多文件应用程序,使用起来很容易,但这是一个完全不合适的单文件程序。我担心人们会使用这些东西,因为他们看到有人做过一次,然后将其吸收到某种仪式中。当你的函数行为异常时,这会导致混乱。
  • @tad:它会导致任何问题吗?不!所以没有造成伤害。您的论点没有意义,最好指出空参数列表是一个过时的功能并且强烈反对。应该是int main(void)

标签: c qsort


【解决方案1】:

传递给qsort 的比较函数接收要比较的两个数组元素的地址。由于每个数组元素都是char *,因此每个元素的地址都是char **。所以你错过了一级间接。

您需要将每个参数强制转换为char * const *,然后取消引用以获取指向字符串的指针:

int cmpstr(const void* p1p, const void* p2p){
    char *p1 = *(char * const *)p1p;
    char *p2 = *(char * const *)p2p;
    ...
}

编辑:

因为你想递归调用这个函数,你需要一个非递归的包装函数来围绕你的递归函数,因为它们采用的参数不一样:

// internal recursive function that takes two strings
static int cmpstr_int(const char* p1, const char* p2){
    int p1l = strlen(p1);
    int p2l = strlen(p2);
    int r = strncmp(p1, p2, p1l<p2l?p1l:p2l);
    if(r == 0 && p1l != p2l){
        if(p1l < p2l){
            return cmpstr_int(p1, p2 + p1l);
        }
        return cmpstr_int(p1 + p2l, p2);
    }
    return r;
}

// comparison function that extracts desired datatype from void * params
// and passes them to recursive function
static int cmpstr(const void* p1p, const void* p2p){
    const char *p1 = *(char * const *)p1p;
    const char *p2 = *(char * const *)p2p;
    return cmpstr_int(p1, p2);
}

【讨论】:

  • 但是 cmpstr 函数有效,当将 p1 视为 char** 时,出现段错误。现在我更困惑了。
  • @SolskGaer:要么它工作,要么你得到段错误。后者只是您的代码调用未定义行为的一种可能结果。为了加深你的知识,了解它。此外,qsort 有据可查;我建议您阅读并了解指针。因为那是你对问题的理解。
  • @Olaf 是不是只要 cmpstr 的行为正确,qsort 就应该正常工作?
  • @SolskGaer:您是在问您是否编写了正确的代码,您的程序是否正确运行?是的,这就是“正确代码”的定义。
  • @SolskGaer 为避免分段错误,您需要将“指针加长度”值存储在局部变量中,并将该变量的地址传递给对 cmpstr 的递归调用。
【解决方案2】:

您的比较函数接收指向数组元素的指针。每个元素都是一个指向 char 的指针,所以你会得到一个指向 char 的指针。

比较逻辑也有些过于复杂;这是一个工作版本:

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

int cmpstr(const void* a_, const void* b_)
{
    const char *const *a = a_;
    const char *const *b = b_;
    int la = strlen(*a);
    int lb = strlen(*b);

    if (la == lb) {
        /* same length - sort lexicographically */
        return strcmp(*a, *b);
    }

    if (la < lb) {
        /* a is shorter */
        int result = strncmp(*a, *b, la);
        if (!result) {
            /* a is a prefix of b */
            result = strcmp(*a, *b + la);
        }
        return result;
    }

    /* else, b is shorter - re-enter with arguments swapped,
       and negate the result */
    return -cmpstr(b_, a_);
}

int main() {
    const char* arrstr[] = {"93", "936", "15", "152", "946"};
    const size_t num = sizeof arrstr / sizeof *arrstr;
    qsort(arrstr, num, sizeof *arrstr, cmpstr);
    for (size_t i = 0; i < num; i++) {
        printf("%s\n", arrstr[i]);
    }
}

输出:

15
152
936
93
946

如果您认为我上面的 cmpstr() 与原来的偏差太大,请考虑这个不那么侵入性修改的代码,它使用您想要的递归比较,并使用单独的包装器对其进行调整以适应 qsort():

int compare_strings(const char *a, const char *b)
{
    int la = strlen(a);
    int lb = strlen(b);
    int r = strncmp(a, b, la<lb?la:lb);
    if (r == 0 && la != lb) {
        if (la < lb) {
            return compare_strings(a, b + la);
        }
        return compare_strings(a + lb, b);
    }
    return r;
}

int compare_strings_qsort(const void* a_, const void* b_)
{
    const char *const *a = a_;
    const char *const *b = b_;
    return compare_strings(*a, *b);
}

我仍然不得不更改您的变量名称,因为我发现 p1l 之类的东西难以阅读。我可以进一步简化一点,我认为这比原始函数和我上面的第一次尝试都更清晰(但可能需要一些 cmets):

int compare_strings(const char *a, const char *b)
{
    const int la = strlen(a);
    const int lb = strlen(b);
    const int r = strncmp(a, b, la<lb?la:lb);

    return (la == lb || r)
        ? r
        : (la < lb)
        ? compare_strings(a, b + la)
        : compare_strings(a + lb, b);
}

【讨论】:

  • 感谢您的回答,但我预期的结果是 15 152 936 946 93
  • 您能否编辑问题以解释排序标准,因为我显然误解了您要实现的目标?谢谢。
  • @Toby 哦,对了。我仍在试图弄清楚为什么 OP 认为 93 应该排在最后!
  • @Ian Abbott 对混乱感到抱歉。我昨晚太困了
猜你喜欢
  • 2021-08-04
  • 2014-10-10
  • 2023-02-05
  • 2018-03-26
  • 1970-01-01
  • 2021-05-29
  • 2021-11-26
  • 2018-09-14
相关资源
最近更新 更多