【问题标题】:Sorting structure with C qsort()使用 C qsort() 排序结构
【发布时间】:2014-04-01 17:46:12
【问题描述】:

在实现 C 中内置的 qsort() 以按存储的 int 值 (hitCount) 对结构数组进行排序时遇到了麻烦。

我的结构:

typedef struct words {
    const char *word;
    int hitCount;
} words;

我正在尝试使用 Microsoft (http://support.microsoft.com/kb/73853) 给出的示例。

所以我在顶部:

typedef int (*compfn)(const void*, const void*);

及比较方法:

int compare (words *a, words *b) {
    if (a->hitCount > b->hitCount) {
        return -1;
    } else if (a->hitCount < b->hitCount) {
        return 1;
    } else {
        return 0;
    }
}

然后在另一个方法中,我调用 qsort 并用我的数组名称和其他详细信息替换 Microsoft 示例:

qsort((void *) &output, outputLength, sizeof(words), (compfn)compare);

这会导致分段错误。

我不完全了解如何使用 qsort,所以我假设我从 Microsoft 的示例中改编它的地方我做错了。

我希望我已经包含了这个错误,并且可以对我应该做什么以使其正常工作得到一些启发。

非常感谢!

【问题讨论】:

  • 什么是输出?显示调用函数的部分代码
  • 包含qsort的完整方法:pastebin.com/y6ziACWy
  • 什么是outputLengthoutput的定义在哪里?
  • 除非你们都知道它的作用并且确实需要它,否则不要进行任何显式转换。让编译器警告你,不要闭嘴。此外,始终使用-Wall -Wextra -pedantic 进行编译
  • @Matt:请不要使用任何外部代码。相反,请提供MCVE。周围更好。尤其是当您在 cmets 中隐藏该链接时。

标签: c sorting struct qsort


【解决方案1】:

您必须将数组而不是数组的地址传递给 qsort。

qsort( output, ... );

您的比较函数必须返回一个 int 并接受两个 const void* 参数。 将您的函数 int compare (words *a, words *b) 转换为不同的(但正确的)类型,然后由 qsort() 调用将导致未定义的行为。

比较函数必须是:

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

然后你将 a 和 b 转换为正确的类型:

((words*)a)->hitCount < ((words*)b)->hitCount

【讨论】:

  • 数组的地址是数组的第一个元素的地址,所以没有坏处!
  • @Deduplicator &output 是指针的地址,不是数组的地址。
  • char array[2];assert((void*)array == (void*)&amp;array);
  • @Deduplicator 告诉我标准在哪里保证数组的地址和数组一样?下次它不是代码将破坏。像您这样的假设会导致代码在多年后中断。
  • 假设char * p = malloc(10); 然后&amp;p != p 为真。 @Deduplicator
【解决方案2】:

我怀疑 outputLength 计算不正确。一个完整的工作示例:

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

typedef struct words {
    const char *word;
    int hitCount;
} words;

int compare(const void * left, const void * right) {
    const words * a = (const words *) left;
    const words * b = (const words *) right;
    if (a->hitCount > b->hitCount) {
        return -1;
    } else if (a->hitCount < b->hitCount) {
        return 1;
    } else {
        return 0;
    }
}

int main() {
    struct words output[] = {
      { "hello", 314 },
      { "world", 42 },
      { "answer", 42 }
    };
    int outputLength = sizeof(output) / sizeof(output[0]);
    int i;

    output[0].word = "hello";
    output[0].hitCount = 314;
    output[1].word = "world";
    output[1].hitCount = 42;
    qsort(output, outputLength, sizeof(words), compare);

    for (i = 0; i < outputLength; ++i) {
        printf("%d %s\n", output[i].hitCount, output[i].word);
    }

    return 0;
}

【讨论】:

  • 对于我正在做的事情,我从哈希表创建了一个数组。因此数组的长度与哈希表中仍未设置为空的结构数量相同。我已按照您的建议进行了更改,但仍然出现分段错误。
  • 然后我怀疑错误在于您如何从哈希表初始化数组。可能是时候学习如何使用调试器了。 ericlippert.com/2014/03/05/how-to-debug-small-programs
  • 与数组无关。方法外部的条件是否会导致问题。还是谢谢。
【解决方案3】:

标准库函数qsort的原型是

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

注意compare 函数的签名。 您不能对指向不同签名函数的指针进行类型转换并使其正常工作。因此,对比较函数进行类型转换是行不通的。它必须具有与qsort 原型中声明的签名相同的签名。将您的 compare 函数更改为 -

int compare(const void *a, const void *b) {
    int c = ((words *) a)->hitCount;
    int d = ((words *) b)->hitCount;

    if(c > d) return -1;
    if(c < d) return 1;
    return 0;
}

qsort 的第一个参数base 是包含要排序的元素的缓冲区的基地址。此外,任何指针类型都与void * 变量兼容,因此您不需要转换基地址。因此,您应该将qsort 函数称为 -

qsort(output, outputLength, sizeof output[0], compare);

【讨论】:

    【解决方案4】:

    得到它的工作:

        int compare (const void *a, const void *b) {
            if (((words *)a)->hitCount > ((words *)b)->hitCount) {
                return -1;
            } else if (((words *)a)->hitCount < ((words *)b)->hitCount) {
                return 1;
            } else {
                return 0;
            }
        }
    

    并调用排序:

        qsort(output, outputLength, sizeof(words), compare);
    

    感谢大家的帮助,但大部分归功于“自我”。

    【讨论】:

    • &amp;outputoutput 的更正只能被 OP 识别,因为 OP 没有发布 output 的声明。
    • 我喜欢减法简化,而不是一堆比较。返回取消引用的 p 和 q 的差。就我而言,我刚刚想出了: return ((struct filerec *)p)->fsize - ((struct filerec *)q)->fsize;但直到我在 (struct filerec *)q 和 p 周围添加括号后它才起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-23
    • 2021-07-16
    • 2014-05-21
    • 2021-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多