Сортировка структур с ошибками qsort и void - PullRequest
0 голосов
/ 28 декабря 2018

Моя задача - прочитать данные, которые хранятся в строковых и двойных парах, попросить пользователя ввести то, по чему они должны быть отсортированы (в форме "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;
            }
        }   
}

1 Ответ

0 голосов
/ 28 декабря 2018

Как это сделать?

Я думаю, что лучше всего написать две пары функций.В каждой паре один будет тривиальным, сводя на нет результат вызова другого в паре.Например, у вас есть «имя по возрастанию» и «имя по убыванию»;вы, вероятно, будете реализовывать «имя по возрастанию» полностью с прототипом 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
...