使用qsort对结构体数组进行排序需要帮助

24

现在,我已经看到了各种例子,但是我不明白它们的含义。

这是我的结构。

typedef struct profile{
    char gender[1];
    double soc;
       . . .
} PROFILE;

我将要按照社会保障号码(soc)排序。

我知道需要一个比较函数,但我不知道如何得出我所需要的确切函数。


4
“double” 看起来对于社会安全号码来说是一个相当毫无意义的类型。如果您希望允许输入非严格数字值,应该使用“char [10]”,否则应该使用“uint32_t”。 - R.. GitHub STOP HELPING ICE
1
不要让那些唱衰者打扰你。double可能不是最理想的选择,但对于存储9位数值来说完全足够。至少你不会遇到舍入小数表示的问题。 - Mark Ransom
6
@Mark Ransom:我觉得“nay-sayer”这个词并不适合用于指出错误的设计/代码!社会保障号码什么时候变成了分数表示法了!请你改正一下。 - Mitch Wheat
1
@Mark Ransom:我认为 Stack Overflow 中没有任何规定禁止在与问题无直接关系的主题上提供未经请求的建议。如果有,我已经多次违反了这个规定。而且,我不同意你的看法。Double 明显是错误的。 - JeremyP
1
@Mark Ransom:是的,它可以工作,但特别是当您查看美国社会保险号码的验证要求时,这并没有太多意义。顺便说一下,英国相当于SSN的是NI号码,实际上以两个字母开头。 - JeremyP
显示剩余4条评论
4个回答

39
这是一个关于在C语言中使用qsort对结构体数组进行排序的示例。
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int price;
    int id;
} order;

int compare(const void *a, const void *b) {
  
    order *orderA = (order *)a;
    order *orderB = (order *)b;
  
    return (orderB->price - orderA->price);
}

int main() {
    order list[6];

    srand(time(NULL));

    printf("Before sorting\n");
    for (int i = 0; i < 6; i++) {   
        list[i].price = rand() % 10;
        list[i].id = i; 
        printf("Order id = %d Price = %d\n", list[i].id, list[i].price);            
    }

    qsort(list, 6, sizeof(order), compare);

    printf("AFTER sorting\n");
    for (int n = 0; n < 6; n++) {
        printf("Order id = %d Price = %d\n", list[n].id, list[n].price);    
    }       
    return 0;
}

非常有帮助,特别是 compare() 方法。 :) - gsamaras
2
您的“比较”函数不正确:价格之间的差异可能大于类型“int”的范围。例如:"-3"和"INT_MAX"会产生"INT_MAX + 3",这个表达式超出了"type int"的范围,调用了未定义的行为,在大多数当前硬件上评估为负数,尽管比较应返回正数。 - chqrlie
1
这将按从大到小的顺序排序。如果您想要相反的顺序,只需在返回表达式中更改orderA和orderB即可,对吗? - Matheus Rotta
没错,也就是说你应该将它改为return(orderA->price - orderB->price) - volkan g
1
一个安全的比较函数应该返回(orderA->price > orderB->price) - (orderA->price < orderB->price)。请参见https://en.cppreference.com/w/c/algorithm/qsort以获取示例。 - qwr
显示剩余2条评论

14

你的 Soc 应该几乎肯定不是double类型,但无论如何,以下是比较函数需要返回的示例:

int compare(const void *p1, const void *p2)
{
    const struct profile *elem1 = p1;    
    const struct profile *elem2 = p2;

   if (elem1->soc < elem2->soc)
      return -1;
   else if (elem1->soc > elem2->soc)
      return 1;
   else
      return 0;
}

感谢指出const void *.

这里有一个完整的示例(已存档):使用C qsort()函数对结构进行排序


1
-1 因为你不能在不进行复杂转换的情况下将此传递给 qsort,参数应该是 const,并且可以更简单地编写为 return (int)(elem1->soc - elem2->soc); - Chris Lutz
@Mitch - 那么发帖人正在使用一个奇怪的编译器。但是你已经修复了你的代码,所以我撤回了我的负评。 - Chris Lutz
1
@ChrisLutz:在一般情况下,简单的减法方法是不正确的。请参阅我的答案以获取反例。 - chqrlie
MS文章现在可以在https://jeffpar.github.io/kbarchive/kb/073/Q73853/ 找到。请注意,比较不使用空指针。 - Alan Corey

8
严格版本的比较器需要两个常量void指针:
int compare(const void *v1, const void *v2)
{
    const struct profile *p1 = v1;
    const struct profile *p2 = v2;
    if (p1->gender > p2->gender)
        return(+1);
    else if (p1->gender < p2->gender)
        return(-1);
    else if (p1->soc > p2->soc)
        return(+1);
    else if (p1->soc < p2->soc)
        return(-1);
    else
        return(0);
}

这首先比较性别字段,然后是社会保障字段。这就是如何处理任何多部分比较的方法。


你的比较函数有性别歧视!(此外,“soc”比较可以写成“return (int)(p1->soc - p2->soc);”。如果OP(明智地)将“soc”的类型更改为“int”,则无需进行强制转换。) - Chris Lutz
2
@Chris:恰恰相反 - 我的函数非常有礼貌;你不知道吗?女士优先于绅士。只要没有溢出,减法机制就可以工作;比较机制则无论如何都可以工作。 - Jonathan Leffler
2
通过研究最近的一个问题,我对@ChrisLutz的建议感到惊讶,他的声望得到了+1。为JL点赞! - Weather Vane
你为什么要创建你期望 v1 和 v2 为的指针?(它们不能是标准变量吗)。你为什么要将指针设置为 const?(我没有使用 const 也可以正常工作。) - Connor
1
@Connor:你可以使用适当类型的局部变量,但这会复制可能是大型结构体的不必要副本。代码不会修改它所查看的内容(也不能修改它),因此指针应该指向常量数据。接口中的const指针是标准规定的。你可以取消const性质,但为什么要这样做?这样做可以工作,但不够安全。 - Jonathan Leffler
1
@Connor:你可以使用适当类型的局部变量,但这会产生一个不必要的副本,可能是一个很大的结构体。代码不会修改它所查看的内容(也不能修改),所以指针应该指向常量数据。接口中的const指针是标准规定的。你可以去掉const属性,但为什么要这样做呢?虽然可以工作,但不够安全。 - Jonathan Leffler

4
使用qsort()函数并传递比较函数来对数组进行排序。
这里有一个可以针对price成员的所有可能值产生正确结果的函数:
typedef struct profile {
    char gender[1];
    double soc;
    int price;
    ...
} PROFILE;

int compare_price(const void *a, const void *b) {
    const PROFILE *oa = a;
    const PROFILE *ob = b;

    return (oa->price > ob->price) - (oa->price < ob->price);
}

int compare_soc(const void *a, const void *b) {
    const PROFILE *oa = a;
    const PROFILE *ob = b;

    return (oa->soc > ob->soc) - (oa->soc < ob->soc);
}

注意:

  • 对值进行简单的减法运算,如果差异不符合 int 类型,则会产生错误的结果。例如,-2INT_MAX 不能使用减法方法正确比较。浮点数也无法使用这种方法。

  • 上述方法适用于所有可比较的类型,包括除 NaN 之外的 double

如果您想处理 NaN,请按以下方式将它们分组到末尾:

#include <math.h>

int compare_soc_nan_at_the_end(const void *a, const void *b) {
    const PROFILE *oa = a;
    const PROFILE *ob = b;

    if (isnan(oa->soc)) {
        return isnan(ob->soc) ? 0 : 1;
    } else
    if (isnan(ob->soc)) {
        return -1;
    } else {
        return (oa->soc > ob->soc) - (oa->soc < ob->soc);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接