[英]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: 我收到以下错误:
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;
}
}
}
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_asc
和cmp_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.
与那些全局变量融合在一起并制作结构的副本(尽可能避免这种情况)等等,这要容易得多。
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 *
, *p
是void
,并且您试图使用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
吗?)。
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
. 您的解析代码将为分配给
compare1
和compare2
选择正确的函数名称。 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. 必须使用文件作用域变量
compare1
和compare2
,但与尝试在问题代码中全部内联执行相比,麻烦得多。
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. 最难的部分是在
compare1
和compare2
及等效项中设置正确的函数指针。
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.