简体   繁体   English

在C中对结构数组进行排序

[英]Sorting an array of structures in C

Let's say I have structure like this: 假设我有这样的结构:

typedef struct MyStruct{
  char *string1;
  int number1, number2, number3;
  char string2[11], string3[9];
  char *string4;
  char *string5;
}MyStruct;

Programs prompts user to choose by what field it should sort the data. 程序提示用户选择应该对数据进行排序的字段。 I am having trouble thinking of a way to sort array effectively. 我无法想到一种有效排序数组的方法。 Do I really need to write separate sorting functions for each field? 我真的需要为每个字段编写单独的排序函数吗? There must be some other way, because writing 8 functions, where 2 would suffice, doesn't look rational. 必须有其他一些方法,因为编写8个函数,其中2就足够了,看起来不合理。

Look up qsort() from <stdlib.h> . <stdlib.h>查找qsort() It takes a comparator function. 它需要一个比较器功能。 You can write separate comparator functions for the different sort orders, but still use the standard library qsort() to do the sorting. 您可以为不同的排序顺序编写单独的比较器函数,但仍使用标准库qsort()进行排序。

For example: 例如:

int ms_cmp_string1(const void *vp1, const void *vp2)
{
    const MyStruct *ms1 = vp1;
    const MyStruct *ms2 = vp2;

    int cmp = strcmp(ms1->string1, ms1->string2);
    if (cmp != 0)
        return cmp;
    else if (ms1->number1 < ms2->number1)
        return -1;
    else if (ms1->number1 > ms2->number1)
        return +1;
    //...other comparisons as required...
    else
        return 0;
}

This is a decent outline for comparators. 对于比较器来说,这是一个不错的大纲。 This one sorts on string1 and then by number1 . 这个在string1排序,然后在number1上排序。 You can either write variants that sort on different fields, or devise a scheme that applies the various possible tests in an order of your choosing. 您可以编写在不同字段上排序的变体,也可以设计一个按您选择的顺序应用各种可能测试的方案。 But the basic outline works pretty well and is suitable for passing to qsort() without any casts necessary. 但基本大纲工作得很好,适合传递给qsort()而不需要任何强制转换。

You don't need to write 8 functions if only 2 are needed. 如果只需要2个,则不需要编写8个函数。 Build your own qsort function and send a last parameter containing the member offset to the compare function, then, in your compare function, cast pointer + offset to the right type. 构建自己的qsort函数并将包含成员偏移量的最后一个参数发送到compare函数,然后在compare函数中,将指针+偏移量转换为正确的类型。

Something like: 就像是:

int comp_int(const void *pa, const void *pb, size_t offset)
{
    const int *a = (const int *)((const char *)pa + offset);
    const int *b = (const int *)((const char *)pb + offset);

    return *a - *b;
}

int comp_string(const void *pa, const void *pb, size_t offset)
{
    const char *a = (const char *)pa + offset;
    const char *b = (const char *)pb + offset;

    return strcmp(a, b);
}

void swap(void *v[], int a, int b)
{
    void *temp;

    temp = v[a];
    v[a] = v[b];
    v[b] = temp;
}

void sort(void *v[], int left, int right, size_t offset, int (*comp)(const void *, const void *, size_t))
{
    int i, last;

    if (left >= right) return;
    swap(v, left, (left + right) / 2);
    last = left;
    for (i = left + 1; i <= right; i++) {
        if ((*comp)(v[i], v[left], offset) < 0)
            swap(v, ++last, i);
    }
    swap(v, left, last);
    sort(v, left, last - 1, offset, comp);
    sort(v, last + 1, right, offset, comp);
}

offsetof can help offsetof可以帮助

Here is a sample of using qsort from my another answer: 以下是我在另一个答案中使用qsort的示例:

struct stringcase { char* string; void (*func)(void); };

void funcB1();
void funcAzA();

struct stringcase cases [] = 
{ { "B1", funcB1 }
, { "AzA", funcAzA }
};

struct stringcase work_cases* = NULL;
int work_cases_cnt = 0;

// comparator function
int stringcase_cmp( const void *p1, const void *p2 )
{
  return strcasecmp( ((struct stringcase*)p1)->string, ((struct stringcase*)p2)->string);
}

// prepare the data for searching
void prepare() {
  // allocate the work_cases and copy cases values from it to work_cases
  qsort( cases, i, sizeof( struct stringcase ), stringcase_cmp );
}

Using some macros: 使用一些宏:

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

struct data {
    int x, y, z;
};

#define comp(member) comp_##member
#define comp_build(member)                          \
int comp_##member(const void *pa, const void *pb)   \
{                                                   \
    const struct data *a = pa, *b = pb;             \
    return (a->member < b->member)  ? -1 : (a->member > b->member); \
}

comp_build(x)
comp_build(y)
comp_build(z)

int main(void)
{
    #define ROWS 3

    struct data v[] = {
        {3, 2, 1},
        {1, 3, 2},
        {2, 1, 3}
    };
    int i;

    puts("Unsorted");
    for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
    qsort(v, ROWS, sizeof(struct data), comp(x));
    puts("Sorted by x");
    for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
    puts("Sorted by y");
    qsort(v, ROWS, sizeof(struct data), comp(y));
    for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
    puts("Sorted by z");
    qsort(v, ROWS, sizeof(struct data), comp(z));
    for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);

    return 0;
}

如果你正在使用GNU C库,那么有一个名为qsort_r()的扩展,它允许你将一个额外的参数传递给比较函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM