使用 qsort 和 void 指针问题对结构进行排序
Sorting structures with qsort and void pointer issues
我的任务是读取存储在字符串和双精度对中的数据,让用户输入排序依据(格式为 "n-v+"),然后使用 qsort 对其进行排序。我在编写比较函数时遇到问题。
我收到以下错误:
- 警告:取消引用“void *”指针
- 错误:void 值没有被忽略,因为它应该被忽略
- 警告:控制到达非空函数的末尾[-Wreturn-type]
下面是函数
的部分代码
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;
}
}
}
怎么做?
我认为最好写两对函数。在每一对中,一个将是微不足道的,否定调用对中另一个的结果。例如,您有 'name ascending' 和 'name descending';您可能会使用原型 int cmp_name_asc(const void *p1, const void *p2);
完整实现 'name ascending',然后将另一个实现为:
int cmp_name_des(const void *p1, const void *p2)
{
return -cmp_name_asc(p1, p2);
}
与 cmp_value_asc
和 cmp_value_des
对类似。
int cmp_value_des(const void *p1, const void *p2)
{
return -cmp_value_asc(p1, p2);
}
如果您真的对开销的轻微差异感到厌烦,您可以使用调用两次的静态内联函数来解决它,一次在 cmp_value_des()
和 cmp_value_asc()
中,但是几乎肯定是不值得的。
然后你只需要编写两个函数:
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);
}
然后,当您解析参数时,您可以控制调用四个函数中的哪一个。这比使用那些全局变量和复制结构(尽可能避免)等要容易得多。
为什么会出错?
您收到“warning: dereferencing ‘void *’ pointer
”消息是因为您:
record first;
first=*p;
您需要使用:
record first = *(const record *)p;
获得正确的复制行为 — 将 void *
转换为正确的指针类型,然后取消引用它。除了这个复制信息;你不应该这样做(它完全不必要地减慢了速度)。你应该使用:
const record *firstp = (const record *)p;
除了强制转换在 C 中并不重要(它在 C++ 中很重要,如果您粗心大意地在您的 C++ 代码中以这种方式排序——那也不是一个好主意)。另请注意,这使得初始化成为变量定义的一部分,而不是稍后作为单独的赋值。这通常是一种很好的技术——尤其是在 C++ 中。
同样,您可能在同一行遇到“error: void value not ignored as it ought to be
”错误。由于 p
是 void *
,*p
是 void
,而您试图使用 void
值的结果——您不能这样做.
“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…
}
并且因为在 else if
块之后既没有 else
子句也没有 return 0;
或任何其他内容,原则上该函数可以退出而不返回值,这是警告告诉你的。
我还会观察到您似乎没有使用 tip2
(tip1 == 2
应该是 tip2 != 0
吗?)。
代码是否应该按名称和值排序?
进一步审查,您似乎打算进行两部分比较,
首先按名称排序,然后按值排序,或者先按值排序,然后按名称排序,并且对于这些比较中的每一个,升序或降序。对吗?
如果是这样,您仍然可以创建之前显示的小函数,但要创建它们 static
因为您不会在比较代码之外使用它们。然后您可以决定创建函数指针来完成这项工作:
static int (*compare1)(const void *p1, const void *p2) = cmp_name_asc;
static int (*compare2)(const void *p1, const void *p2) = cmp_value_des;
您的解析代码将 select 正确的函数名称分配给 compare1
和 compare2
。您的比较函数将变为:
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;
}
不得不使用文件范围变量 compare1
和 compare2
有点麻烦,但比在问题代码中尝试全部内联更麻烦。
很容易将其扩展为按三个标准或 1..N 个标准进行排序(使用指向函数的指针数组,并将指针设置为 null 以指示不再进行比较)。最难的部分是在 compare1
和 compare2
以及等价物中设置正确的函数指针。
工作代码
下面是显示上述大部分建议的代码(我并不担心降序比较器函数中的最小性能损失):
#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", "", , , , , ) }'
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);
}
该程序的输出:
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
我的任务是读取存储在字符串和双精度对中的数据,让用户输入排序依据(格式为 "n-v+"),然后使用 qsort 对其进行排序。我在编写比较函数时遇到问题。
我收到以下错误:
- 警告:取消引用“void *”指针
- 错误:void 值没有被忽略,因为它应该被忽略
- 警告:控制到达非空函数的末尾[-Wreturn-type]
下面是函数
的部分代码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;
}
}
}
怎么做?
我认为最好写两对函数。在每一对中,一个将是微不足道的,否定调用对中另一个的结果。例如,您有 'name ascending' 和 'name descending';您可能会使用原型 int cmp_name_asc(const void *p1, const void *p2);
完整实现 'name ascending',然后将另一个实现为:
int cmp_name_des(const void *p1, const void *p2)
{
return -cmp_name_asc(p1, p2);
}
与 cmp_value_asc
和 cmp_value_des
对类似。
int cmp_value_des(const void *p1, const void *p2)
{
return -cmp_value_asc(p1, p2);
}
如果您真的对开销的轻微差异感到厌烦,您可以使用调用两次的静态内联函数来解决它,一次在 cmp_value_des()
和 cmp_value_asc()
中,但是几乎肯定是不值得的。
然后你只需要编写两个函数:
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);
}
然后,当您解析参数时,您可以控制调用四个函数中的哪一个。这比使用那些全局变量和复制结构(尽可能避免)等要容易得多。
为什么会出错?
您收到“warning: dereferencing ‘void *’ pointer
”消息是因为您:
record first;
first=*p;
您需要使用:
record first = *(const record *)p;
获得正确的复制行为 — 将 void *
转换为正确的指针类型,然后取消引用它。除了这个复制信息;你不应该这样做(它完全不必要地减慢了速度)。你应该使用:
const record *firstp = (const record *)p;
除了强制转换在 C 中并不重要(它在 C++ 中很重要,如果您粗心大意地在您的 C++ 代码中以这种方式排序——那也不是一个好主意)。另请注意,这使得初始化成为变量定义的一部分,而不是稍后作为单独的赋值。这通常是一种很好的技术——尤其是在 C++ 中。
同样,您可能在同一行遇到“error: void value not ignored as it ought to be
”错误。由于 p
是 void *
,*p
是 void
,而您试图使用 void
值的结果——您不能这样做.
“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…
}
并且因为在 else if
块之后既没有 else
子句也没有 return 0;
或任何其他内容,原则上该函数可以退出而不返回值,这是警告告诉你的。
我还会观察到您似乎没有使用 tip2
(tip1 == 2
应该是 tip2 != 0
吗?)。
代码是否应该按名称和值排序?
进一步审查,您似乎打算进行两部分比较, 首先按名称排序,然后按值排序,或者先按值排序,然后按名称排序,并且对于这些比较中的每一个,升序或降序。对吗?
如果是这样,您仍然可以创建之前显示的小函数,但要创建它们 static
因为您不会在比较代码之外使用它们。然后您可以决定创建函数指针来完成这项工作:
static int (*compare1)(const void *p1, const void *p2) = cmp_name_asc;
static int (*compare2)(const void *p1, const void *p2) = cmp_value_des;
您的解析代码将 select 正确的函数名称分配给 compare1
和 compare2
。您的比较函数将变为:
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;
}
不得不使用文件范围变量 compare1
和 compare2
有点麻烦,但比在问题代码中尝试全部内联更麻烦。
很容易将其扩展为按三个标准或 1..N 个标准进行排序(使用指向函数的指针数组,并将指针设置为 null 以指示不再进行比较)。最难的部分是在 compare1
和 compare2
以及等价物中设置正确的函数指针。
工作代码
下面是显示上述大部分建议的代码(我并不担心降序比较器函数中的最小性能损失):
#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", "", , , , , ) }'
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);
}
该程序的输出:
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