如何在C语言中对结构体数组进行排序?

7

我有一个如下所示的结构体数组:

typedef struct _my_data_ 
{
  unsigned int id;
  double latitude;
  double longitude;
  unsigned int content_len;
  char* name_dyn;
  char* descr_dyn;
} mydata;

我希望按照ID字段升序排序它。我知道可以使用qsort函数对数组进行排序,但我不确定在对结构体进行排序时如何正确使用它。


6
你是否编写了一个函数,可以告诉你两个给定结构体中哪一个具有较大或较小的“id”字段? - sarnold
1个回答

27

你需要一个结构体比较函数,其函数原型与 qsort() 期望的函数原型相匹配,即:

int md_comparator(const void *v1, const void *v2)
{
    const mydata *p1 = (mydata *)v1;
    const mydata *p2 = (mydata *)v2;
    if (p1->id < p2->id)
        return -1;
    else if (p1->id > p2->id)
        return +1;
    else
        return 0;
}

如果你需要更复杂的排序标准,这仍然是一个很好的基础,因为你可以使用相同的框架添加第二个排序标准:

int md_comparator(const void *v1, const void *v2)
{
    const mydata *p1 = (mydata *)v1;
    const mydata *p2 = (mydata *)v2;
    if (p1->latitude < p2->latitude)
        return -1;
    else if (p1->latitude > p2->latitude)
        return +1;
    else if (p1->longitude < p2->longitude)
        return -1;
    else if (p1->longitude > p2->longitude)
        return +1;
    else
        return 0;
}

显然,这适用于您需要的任何条件。如果需要调用函数(strcmp()?)来比较值,请调用一次,但将返回值分配给本地变量,并使用两次:

int md_comparator(const void *v1, const void *v2)
{
    const mydata *p1 = (mydata *)v1;
    const mydata *p2 = (mydata *)v2;
    int rc;
    if (p1->latitude < p2->latitude)
        return -1;
    else if (p1->latitude > p2->latitude)
        return +1;
    else if (p1->longitude < p2->longitude)
        return -1;
    else if (p1->longitude > p2->longitude)
        return +1;
    else if ((rc = strcmp(p1->name_dyn, p2->name_dyn)) < 0)
        return -1;
    else if (rc > 0)
        return +1;
    else
        return 0;
}

此外,当数据成员为无符号整数时,该模板可以正常工作,并且在比较有符号整数时避免了溢出问题。请注意,您可能会看到的快捷方式是:

int md_comparator(const void *v1, const void *v2)   /* BAD */
{                                                   /* BAD */
    const mydata *p1 = (mydata *)v1;                /* BAD */
    const mydata *p2 = (mydata *)v2;                /* BAD */
    return(p1->id - p2->id);                        /* BAD */
}                                                   /* BAD */

如果id是无符号的,那么差值永远不会为负数,这是不好的;如果整数是有符号的、具有大的幅度和相反的符号,则存在溢出的风险。


非常感谢您的回答,我现在就要尝试这段代码。 - beta
1
对“如果id是无符号的”进行冥想:由于返回值是int,因此unsigned值将被转换为int,因此它可能变为负数。但是,如果idunsigned longunsigned long long,并且sizeof(long)> sizeof(int),那么您将容易受到一些非常奇怪的行为的影响。明确返回±1机制始终有效的观察仍然准确;广泛使用的“如果比较两个无符号量,则减法不起作用”的说法并不严格准确。 - Jonathan Leffler

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