Как это сделать?
Я думаю, что лучше всего написать две пары функций.В каждой паре один будет тривиальным, сводя на нет результат вызова другого в паре.Например, у вас есть «имя по возрастанию» и «имя по убыванию»;вы, вероятно, будете реализовывать «имя по возрастанию» полностью с прототипом int cmp_name_asc(const void *p1, const void *p2);
, а затем реализуете другое в паре как:
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
, чего вы не можете сделать.
The '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
?).
Должен ли код сортироваться по имени и значению?
При дальнейшей проверке кажется, что вы, возможно, намереваетесь провести сравнение двух частей, с сортировкой по имени по имени и по значению во втором,или значение first и name second, и для каждого из этих сравнений, по возрастанию или по убыванию.Это правильно?
Если это так, вы все равно можете создавать маленькие функции, показанные ранее, но сделайте их 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;
Ваш код синтаксического анализа выберет правильные имена функций для назначения 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 (используя массив указателей на функции и устанавливая указатель на ноль, чтобы указывать больше не нужно)сравнения).Самое сложное - это установить правильные указатели функций в 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", "", $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);
}
Вывод из этой программы:
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