简体   繁体   English

具有qsort和void指针问题的排序结构

[英]Sorting structures with qsort and void pointer issues

My task is to read data that's stored in string and double pairs, have the user input what it should be sorted by (in the form "n-v+") and then sort it with qsort. 我的任务是读取存储在字符串和双对中的数据,让用户输入应该对数据进行排序的形式(“ n-v +”形式),然后使用qsort对其进行排序。 I'm having issues with writing my comparison function. 我在编写比较功能时遇到问题。

I'm getting the following errors: 我收到以下错误:

  • warning: dereferencing 'void *' pointer 警告:取消引用“ void *”指针
  • error: void value not ignored as it ought to be 错误:空值不应该被忽略
  • warning: control reaches end of non-void function [-Wreturn-type] 警告:控制到达非无效函数的结尾[-Wreturn-type]

Here's the part of code with the function 这是带有功能的代码部分

typedef struct {
char name[32];
double value;
} record;

int sign1=0; //1 is +, -1 is -
int tip1=0; //1 is n, 2 is v
int sign2=0; //1 is +, -1 is -
int tip2=0; //1 is n, 2 is v

//comparison function
int compa(const void*p,const void*d){ //1 means first, -1 means second
    int result=0;
    record first;
    first=*p;
    record second;
    second=*d;
    if(tip1==1){ //compare for n first
        result=strcmp(first.name,second.name);  //first goes smaller
        if(result!=0) return sign1*result;
        else { //then for v
            if (first.value>second.value)result=-1; 
            else result=1;  
            return sign2*result;
            }
        }
    else if (tip1==2){//compare for v first
        if (first.value>second.value){
            result=-1; 
            return sign1*result;}
        else if (first.value<second.value) {
            result=1;
            return sign1*result;}
        else{ //then for n
            result=strcmp(first.name,second.name);  //first goes smaller
            if(result!=0) return sign2*result;
            else return sign2;
            }
        }   
}

How to do it? 怎么做?

I think it's probably best to write two pairs of functions. 我认为最好编写两对函数。 In each pair, one will be trivial, negating the result of calling the other one in the pair. 在每对中,一个将是微不足道的,从而消除了在该对中调用另一个的结果。 For example, you have 'name ascending' and 'name descending'; 例如,您有“名称升序”和“名称降序”; you will probably implement 'name ascending' in full with prototype int cmp_name_asc(const void *p1, const void *p2); 您可能会使用原型int cmp_name_asc(const void *p1, const void *p2);来完全实现“名称升序” int cmp_name_asc(const void *p1, const void *p2); , and then implement the other in the pair as: ,然后将配对中的另一个实现为:

int cmp_name_des(const void *p1, const void *p2)
{
    return -cmp_name_asc(p1, p2);
}

and similarly for the pair of cmp_value_asc and cmp_value_des . 同样适用于cmp_value_asccmp_value_des

int cmp_value_des(const void *p1, const void *p2)
{
    return -cmp_value_asc(p1, p2);
}

If you're really antsy about the mild difference in overhead, you can work around it with a static inline function that you call twice, once each in cmp_value_des() and cmp_value_asc() , but that is almost certainly not worthwhile. 如果您真的对开销的细微差别感到不满,可以使用一个静态内联函数来解决该问题,该内联函数调用两次,分别在cmp_value_des()cmp_value_asc() ,但这几乎绝对不值得。

That then leaves you with writing two functions: 然后,您需要编写两个函数:

int cmp_value_asc(const void *p1, const void *p2)
{
    const record *r1 = p1;
    const record *r2 = p2;
    if (r1->value < r2->value)
        return -1;
    else if (r1->value > r2->value)
        return +1;
    else
        return 0;
}

int cmp_name_asc(const void *p1, const void *p2)
{
    const record *r1 = p1;
    const record *r2 = p2;
    return strcmp(r1->name, r2->name);
}

Then, when you're parsing the arguments, you control which of the four functions is called. 然后,在解析参数时,您可以控制调用四个函数中的哪个。 This is much easier that futzing with those global variables, and making copies of the structures (avoid that whenever possible) and so on. 与那些全局变量融合在一起并制作结构的副本(尽可能避免这种情况)等等,这要容易得多。

Why the errors? 为什么会出错?

You were getting the ' warning: dereferencing 'void *' pointer ' messages because you have: 您收到“ warning: dereferencing 'void *' pointer ”消息,因为您有:

record first;
first=*p;

You'd need to use: 您需要使用:

record first = *(const record *)p;

to get the right copying behaviour — convert the void * to the correct pointer type and then dereference it. 若要获得正确的复制行为—将void *转换为正确的指针类型,然后取消引用它。 Except this copies the information; 除此之外,复制信息; you shouldn't do that (it slows things down wholly unnecessarily). 您不应该这样做(它会完全不必要地降低速度)。 You should be using: 您应该使用:

 const record *firstp = (const record *)p;

except that the cast is not crucial in C (it would be in C++, if you were careless enough to sort this way in your C++ code — that would be a bad idea, too). 除了强制转换在C中不是至关重要的(如果在C ++代码中,如果您很粗心,无法以这种方式进行排序,那么C ++将是C ++,这也不是一个好主意)。 Note too that this makes the initialization part of the variable definition, rather than as separate assignment later. 还要注意,这使初始化成为变量定义的一部分,而不是以后作为单独的分配。 That's usually a good technique — especially in C++. 通常这是一种好技术,尤其是在C ++中。

Similarly, you were probably getting the ' error: void value not ignored as it ought to be ' errors on the same lines. 同样,您可能会收到' error: void value not ignored as it ought to be ,在同一行上error: void value not ignored as it ought to be 'error。 Since p is a void * , *p is a void , and you were trying to use the result of a void value — which you can't do. 由于p是一个void **pvoid ,并且您试图使用void值的结果—您不能这样做。

The ' warning: control reaches end of non-void function [-Wreturn-type] ' warning is because your comparison function has the structure: warning: control reaches end of non-void function [-Wreturn-type]warning: control reaches end of non-void function [-Wreturn-type] ”警告是因为您的比较函数具有以下结构:

if (tip1 == 1)
{
    …code that returns a value…
}
else if (tip1 == 2)
{
    …code that returns a value…
}

and because there is neither an else clause nor a return 0; 并且因为既没有else子句也没有return 0; nor anything else after the else if block, the function can, in principle, exit without returning a value, which is what the warning tells you. else if块之后也没有其他任何内容,该函数原则上可以退出而不返回值,这是警告告诉您的内容。

I'll also observe you don't seem to use tip2 (should tip1 == 2 be tip2 != 0 ?). 我还将观察到您似乎并没有使用tip2 (应该将tip1 == 2设为tip2 != 0吗?)。

Is the code supposed to sort on name and value? 代码应该按名称和值排序吗?

On further scrutiny, it seems like you might be intending to have two part comparisons, with sorting by name first and value second, or value first and name second, and for each of those comparisons, either ascending or descending. 在进一步的审查中,您似乎打算进行两部分比较,首先按名称排序,然后按值排序,或者首先按值排序,然后按名称排序,然后对每个比较进行升序或降序排序。 Is that correct? 那是对的吗?

If so, you can still create the small functions shown before, but make them static since you won't be using them outside the comparison code. 如果是这样,您仍然可以创建前面显示的小功能,但是将它们设为static因为您不会在比较代码之外使用它们。 You can then decide to create function pointers to do the work: 然后,您可以决定创建函数指针来完成工作:

static int (*compare1)(const void *p1, const void *p2) = cmp_name_asc;
static int (*compare2)(const void *p1, const void *p2) = cmp_value_des;

Your parsing code will select the correct function names for assignment to compare1 and compare2 . 您的解析代码将为分配给compare1compare2选择正确的函数名称。 Your comparison function then becomes: 您的比较函数将变为:

int cmpa(const void *p1, const void *p2)
{
    int rc = (*compare1)(p1, p2);        // Or int rc = compare1(p1, p2);
    if (rc == 0)
        rc = (*compare2)(p1, p2);
    return rc;
}

It's a mild nuisance to have to use the file scope variables compare1 and compare2 , but much less of a nuisance than trying to do it all inline as in the code in the question. 必须使用文件作用域变量compare1compare2 ,但与尝试在问题代码中全部内联执行相比,麻烦得多。

It is pretty easy to extend this to sort by three criteria, or 1..N criteria (using an array of pointers to functions, and setting a pointer to null to indicate no more comparisons). 很容易将其扩展为按三个条件或1..N个条件进行排序(使用指向函数的指针数组,并将指针设置为null表示不再进行比较)。 The hardest part is setting the correct function pointers in compare1 and compare2 and equivalents. 最难的部分是在compare1compare2及等效项中设置正确的函数指针。

Working code 工作代码

Here is code showing most of the suggestions above (I'm not fussed about the minimal performance lost in the descending comparator functions): 以下代码显示了上面的大多数建议(我不为在比较器降序功能中损失的最低性能而烦恼):

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

typedef struct
{
    char name[32];
    double value;
} record;

static int cmp_value_asc(const void *p1, const void *p2)
{
    const record *r1 = p1;
    const record *r2 = p2;
    if (r1->value < r2->value)
        return -1;
    else if (r1->value > r2->value)
        return +1;
    else
        return 0;
}

static int cmp_name_asc(const void *p1, const void *p2)
{
    const record *r1 = p1;
    const record *r2 = p2;
    return strcmp(r1->name, r2->name);
}

static int cmp_name_des(const void *p1, const void *p2)
{
    return -cmp_name_asc(p1, p2);
}

static int cmp_value_des(const void *p1, const void *p2)
{
    return -cmp_value_asc(p1, p2);
}

static int (*compare1)(const void *p1, const void *p2) = cmp_name_asc;
static int (*compare2)(const void *p1, const void *p2) = cmp_value_des;

static int cmpa(const void *p1, const void *p2)
{
    int rc = (*compare1)(p1, p2);
    if (rc == 0)
        rc = (*compare2)(p1, p2);
    return rc;
}

static void dump_records(const char *tag, int num, const record *data);

int main(void)
{
    record data[] =
    {
        /*
        random -n 15 -c -T '{ "%C%v%2:8w", %[20:100]f },' |
        awk '{ printf("%8s%s %-14s %5.2f %s\n", "", $1, $2, $3, $4, $5) }'
        Plus three lines duplicated on name and then change the value;
        plus three lines duplicated on value and then change the name.
        */
        { "Memrgi",      66.90 },
        { "Joeeeoahnm",  98.40 },
        { "Turner",      81.40 },
        { "Rebfno",      81.40 },
        { "Rebfno",      23.19 },
        { "Tecao",       66.30 },
        { "Guvoejnard",  62.40 },
        { "Zipnoib",     32.70 },
        { "Kerjruw",     49.60 },
        { "Vebin",       51.60 },
        { "Ghost",       51.60 },
        { "Reoe",        85.00 },
        { "Yepfenen",    84.60 },
        { "Yepfenen",    82.60 },
        { "Kopl",        94.80 },
        { "Soorwzeo",    15.40 },
        { "Soorwzeo",    85.40 },
        { "Nemigiat",    29.10 },
        { "Poisson",     79.40 },
        { "Sositpv",     79.40 },
        { "Giidahroet",  71.00 },
    };
    enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };

    dump_records("Before sorting", NUM_DATA, data);

    compare1 = cmp_name_des;
    compare2 = cmp_value_asc;
    qsort(data, NUM_DATA, sizeof(data[0]), cmpa);
    dump_records("After sorting by name descending, value ascending", NUM_DATA, data);

    compare1 = cmp_value_des;
    compare2 = cmp_name_asc;
    qsort(data, NUM_DATA, sizeof(data[0]), cmpa);
    dump_records("After sorting by value descending, name ascending", NUM_DATA, data);

    compare1 = cmp_value_asc;
    compare2 = cmp_name_asc;
    qsort(data, NUM_DATA, sizeof(data[0]), cmpa);
    dump_records("After sorting by value ascending, name ascending", NUM_DATA, data);

    compare1 = cmp_name_asc;
    compare2 = cmp_value_asc;
    qsort(data, NUM_DATA, sizeof(data[0]), cmpa);
    dump_records("After sorting by name ascending, value ascending", NUM_DATA, data);

    return 0;
}

static void dump_records(const char *tag, int num, const record *data)
{
    printf("%s (%d):\n", tag, num);
    for (int i = 0; i < num; i++)
        printf("%2d: %-12s %5.2f\n", i + 1, data[i].name, data[i].value);
}

Output from that program: 该程序的输出:

Before sorting (21):
 1: Memrgi       66.90
 2: Joeeeoahnm   98.40
 3: Turner       81.40
 4: Rebfno       81.40
 5: Rebfno       23.19
 6: Tecao        66.30
 7: Guvoejnard   62.40
 8: Zipnoib      32.70
 9: Kerjruw      49.60
10: Vebin        51.60
11: Ghost        51.60
12: Reoe         85.00
13: Yepfenen     84.60
14: Yepfenen     82.60
15: Kopl         94.80
16: Soorwzeo     15.40
17: Soorwzeo     85.40
18: Nemigiat     29.10
19: Poisson      79.40
20: Sositpv      79.40
21: Giidahroet   71.00
After sorting by name descending, value ascending (21):
 1: Zipnoib      32.70
 2: Yepfenen     82.60
 3: Yepfenen     84.60
 4: Vebin        51.60
 5: Turner       81.40
 6: Tecao        66.30
 7: Sositpv      79.40
 8: Soorwzeo     15.40
 9: Soorwzeo     85.40
10: Reoe         85.00
11: Rebfno       23.19
12: Rebfno       81.40
13: Poisson      79.40
14: Nemigiat     29.10
15: Memrgi       66.90
16: Kopl         94.80
17: Kerjruw      49.60
18: Joeeeoahnm   98.40
19: Guvoejnard   62.40
20: Giidahroet   71.00
21: Ghost        51.60
After sorting by value descending, name ascending (21):
 1: Joeeeoahnm   98.40
 2: Kopl         94.80
 3: Soorwzeo     85.40
 4: Reoe         85.00
 5: Yepfenen     84.60
 6: Yepfenen     82.60
 7: Rebfno       81.40
 8: Turner       81.40
 9: Poisson      79.40
10: Sositpv      79.40
11: Giidahroet   71.00
12: Memrgi       66.90
13: Tecao        66.30
14: Guvoejnard   62.40
15: Ghost        51.60
16: Vebin        51.60
17: Kerjruw      49.60
18: Zipnoib      32.70
19: Nemigiat     29.10
20: Rebfno       23.19
21: Soorwzeo     15.40
After sorting by value ascending, name ascending (21):
 1: Soorwzeo     15.40
 2: Rebfno       23.19
 3: Nemigiat     29.10
 4: Zipnoib      32.70
 5: Kerjruw      49.60
 6: Ghost        51.60
 7: Vebin        51.60
 8: Guvoejnard   62.40
 9: Tecao        66.30
10: Memrgi       66.90
11: Giidahroet   71.00
12: Poisson      79.40
13: Sositpv      79.40
14: Rebfno       81.40
15: Turner       81.40
16: Yepfenen     82.60
17: Yepfenen     84.60
18: Reoe         85.00
19: Soorwzeo     85.40
20: Kopl         94.80
21: Joeeeoahnm   98.40
After sorting by name ascending, value ascending (21):
 1: Ghost        51.60
 2: Giidahroet   71.00
 3: Guvoejnard   62.40
 4: Joeeeoahnm   98.40
 5: Kerjruw      49.60
 6: Kopl         94.80
 7: Memrgi       66.90
 8: Nemigiat     29.10
 9: Poisson      79.40
10: Rebfno       23.19
11: Rebfno       81.40
12: Reoe         85.00
13: Soorwzeo     15.40
14: Soorwzeo     85.40
15: Sositpv      79.40
16: Tecao        66.30
17: Turner       81.40
18: Vebin        51.60
19: Yepfenen     82.60
20: Yepfenen     84.60
21: Zipnoib      32.70

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

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