简体   繁体   English

如何在 C 中使 qsort 函数更通用

[英]How to make qsort function in C, more generic

Consider the following code snippet:考虑以下代码片段:

typedef int (*FNPTR)(const void* p1, const void* p2);

typedef struct {
    char name[100];
    int age;
} Person_t;


void display(Person_t p[], size_t size)
{
    for (size_t i = 0; i < size; i++) {
        printf("%s: %d\n", p[i].name, p[i].age);
    }
}

int compare_by_age(const void* p1, const void* p2)
{
    return ((Person_t *)p1)->age - ((Person_t *)p2)->age;
}

int compare_by_name(const void* p1, const void* p2)
{
    return strcmp(((Person_t *)p1)->name, ((Person_t *)p2)->name);
}

FNPTR compare(int choice)
{
    return (choice == 0) ? compare_by_age : compare_by_name;
}

int main()
{
    Person_t p[] = {
        {"John", 21},
        {"Albert", 23}
    };

    size_t size = sizeof(p)/sizeof(p[0]);

    qsort(p, size, sizeof(p[0]), compare(1));
    display(p, size);
    ...
}

One disadvantage of this program is, if we keep adding fields to the Person structure, then we have to write "n" compare functions.这个程序的一个缺点是,如果我们不断向 Person 结构添加字段,那么我们必须编写“n”个比较函数。 Is it possible to create and return relevant compare function inside the compare itself, based on the choice(without adding "n" compare functions).是否可以根据选择(不添加“n”个比较函数)在比较本身内部创建并返回相关的比较函数。 Something similar to C++ lambads so that we can create and return a function within another function in C (or any other better logic).类似于 C++ lambads 的东西,以便我们可以在 C 中的另一个函数(或任何其他更好的逻辑)中创建和返回一个函数。

TBH, I think your best option is to write a custom sort function instead of shoehorning something into qsort() . TBH,我认为您最好的选择是编写自定义排序函数,而不是将某些东西硬塞到qsort() But below are some ways to do it.但下面是一些方法。

Note: This solution is (probably) not thread safe注意:此解决方案(可能)不是线程安全的

It's probably impossible to do in a very neat way.以非常简洁的方式可能无法做到。 This is C after all.毕竟这是C。 But you could use a global variable like this:但是您可以使用这样的全局变量:

enum field { NAME, AGE } field;

int compare(const void* p1, const void* p2)
{
    switch(field) {
    case AGE: 
        return ((Person_t *)p1)->age - ((Person_t *)p2)->age;        
    case NAME:
        return strcmp(((Person_t *)p1)->name, ((Person_t *)p2)->name);
    }
}

Not the sweetest solution, but at least you don't have to write tons of compare functions.不是最甜蜜的解决方案,但至少您不必编写大量比较函数。

If you really want to be McGyver to avoid globals, you could use NULL as a sentinel for first argument to trigger configuration mode and then use p2->age as the configuration parameter, but I strongly advice against it.如果你真的想成为 McGyver 来避免全局变量,你可以使用 NULL 作为第一个参数的哨兵来触发配置模式,然后使用p2->age作为配置参数,但我强烈建议不p2->age I'm showing it just to show that it's possible, but this is just silly:我展示它只是为了表明它是可能的,但这只是愚蠢的:

int compare(const void* p1, const void* p2)
{
    static enum field field;

    if(!p1) { 
        switch((Person_t *)p2)->age) {
        case NAME: field = NAME; break;
        case AGE: field = AGE; break;
        }
        return 0; // Just to exit
    }

    switch(field) {
    case AGE: 
        return ((Person_t *)p1)->age - ((Person_t *)p2)->age;    
    case NAME:
        return strcmp(((Person_t *)p1)->name, ((Person_t *)p2)->name);
    }
}

A third option, also not a very good one, is to send the data in via the Person_t structs:第三种选择,也不是一个很好的选择,是通过 Person_t 结构发送数据:

typedef struct {
    char name[100];
    int age;
    // Extra fields that the compare function can use
} Person_t;

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

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