C — смешанная Qsort со структурой (String и Double)

У меня есть почти полный код, мне нужно Qsort кое-что, сначала у меня есть массив целых чисел, массив чисел с плавающей запятой, а затем у меня есть структура с char и двойным комбинированным массивом. Нужно как-то qsort их, но я застрял.

Вот мой код, у меня нет проблем с сортировкой целых чисел и чисел с плавающей запятой, но я не могу выполнить сортировку структур.

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

#define LEN 20

typedef struct product
{
    double price;
    char name[LEN];
} product;


int ComparFuncInt(const void *x, const void *y);
int ConparFuncFloat(const void *x, const void *y);
int ComparFuncStructName(const void *x, const void *y);
int ComparFuncStructPrice(const void *x, const void *y);
/* void PrintIntegerArray(int *numbers, int len);
void PrintFloatArray(float *numbers, int len);
void PrintStructArray(product *items, int len);
*/
int main(void)
{
    unsigned option;

    /* initialized variables to be used with functions */
    int numArr1[] = {15, 25, 3, 19, 22, 17, -54, 0, 9};
    float numArr2[] = {76.40f, 11.2f, 235.4f, 76.50f, 341.6f};
    product prices[] = {{0.75f, "Milk"}, {0.99f, "Yogurt"}, {3.19f, "Cucumber"},
                        {1.09f, "Orange"}, {0.80f, "Bread"}, {0.99f, "Juice"}};
    int numCount = sizeof(numArr1) / sizeof(int);
    float floatCount = sizeof(numArr2) / sizeof(float);
    double doubleCount = sizeof(struct product) / sizeof(double);
    char charCount = sizeof(struct product) / sizeof(char);


    while (1)
    {
        printf("\n\nSelect your action!\n\n");
        printf("1. Sort integers (numArr1)\n");
        printf("2. Sort decimals (numArr2)\n");
        printf("3. Sort structures by price\n");
        printf("4. Sort structures by name\n");
        printf("0. Exit\n");
        scanf("%u", &option);
        switch (option)
        {
            case 1:
                qsort(numArr1, (size_t)numCount, sizeof(int), ComparFuncInt);
                for (int i = 0; i < numCount; printf("%3d", numArr1[i]), i++);
                break;
            case 2:
                qsort(numArr2, (size_t)floatCount, sizeof(float), ConparFuncFloat);
                for (int j = 0; j < floatCount; printf("%.2f ", numArr2[j]), j++);
                break;
            case 3:
                qsort(prices, (size_t)doubleCount, sizeof(double), ComparFuncStructPrice);
                for (int k = 0; k < doubleCount; printf("%.2f ", prices[k].price), k++);
                break;
            case 4:
                qsort(prices, (size_t)charCount, sizeof(char), ComparFuncStructName);
                for (int l = 0; l < charCount; printf("%s", prices[l].name), l++);
                break;
            case 0:
                exit(1);
                break;
            default:
                printf("Only selections from 1 to 4 and 0 are accepted\n");
        }
    }
    return EXIT_SUCCESS;
}


int ComparFuncInt(const void *x, const void *y){
    if(* (int*)x > *(int*)y) return 1;
    else if(* (int*)x < *(int*)y) return -1;
    else return 0;
}
int ConparFuncFloat(const void *x, const void *y){
    if(* (float *)x > *(float *)y) return 1;
    else if(* (float *)x < *(float *)y) return -1;
    else return 0;
}
int ComparFuncStructName(const void *x, const void *y){
    const char *pa = *(const char**)x;
    const char *pb = *(const char**)y;
    return strcmp(pa,pb);
}
int ComparFuncStructPrice(const void *x, const void *y){
    if(* (float *)x > *(float *)y) return 1;
    else if(* (float *)x < *(float *)y) return -1;
    else return 0;
}

Я хочу сделать так, как когда пользователь выбирает 3, он идет и сортирует массив PRICES по продуктам по ценам, и если пользователь выбирает 4, он должен сортировать по именам, в обоих случаях он должен выводить как цену, так и имя, просто разница в том, что это был отсортирован.


person Aleksandre Sikharulidze    schedule 22.11.2018    source источник
comment
qsort(prices, ..., sizeof(double) неверно, а ComparFuncStructName и ComparFuncStructPrice необходимо привести к указателю на структуру. Вам нужно передать размер структуры в функцию. Хороший способ избежать таких ошибок — написать qsort(array, num, sizeof *array, comparefn), аналогично int *x = malloc(n * sizeof *x).   -  person Groo    schedule 22.11.2018
comment
Шаг 1: измените sizeof(array) / sizeof(type); --› sizeof array / sizeof array[0];   -  person chux - Reinstate Monica    schedule 22.11.2018


Ответы (2)


Измените это:

qsort(prices, (size_t)doubleCount, sizeof(double), ComparFuncStructPrice);        

to:

qsort(prices, 6, sizeof(prices[0]), ComparFuncStructPrice);

поскольку массив структур имеет один размер, независимо от того, хотите ли вы сортировать по имени или цене. Более того, размер каждого элемента массива равен размеру структуры, независимо от того, хотите ли вы сортировать по имени или по цене.

Аналогично при сортировке по именам.


Кроме того, ваши функции сравнения неверны. Во-первых, вам нужно скрыть каждый указатель void на структуру. Затем вам нужно использовать соответствующее поле структуры для сравнения.

Для сравнения цен используйте все меньше и больше операторов. Если вы просто вычитаете, вы получите неверный результат.


Собрав все вместе (и отбросив массивы, не являющиеся структурами), вы получите:

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

#define LEN 20

typedef struct product
{
    double price;
    char name[LEN];
} product;


int ComparFuncStructName(const void *x, const void *y);
int ComparFuncStructPrice(const void *x, const void *y);
/* 
void PrintStructArray(product *items, int len);
*/
int main(void)
{
    unsigned option;
    product prices[] = {{0.75f, "Milk"}, {0.99f, "Yogurt"}, {3.19f, "Cucumber"},
                        {1.09f, "Orange"}, {0.80f, "Bread"}, {0.99f, "Juice"}};
    size_t pricesCount = sizeof prices / sizeof prices[0];
    while (1)
    {
        printf("\n\nSelect your action!\n\n");
        printf("3. Sort structures by price\n");
        printf("4. Sort structures by name\n");
        printf("0. Exit\n");
        scanf("%u", &option);
        switch (option)
        {
            case 3:
                qsort(prices, pricesCount, sizeof(prices[0]), ComparFuncStructPrice);
                for (size_t k = 0; k < pricesCount; printf("%f ", prices[k].price), k++);
                break;
            case 4:
                qsort(prices, pricesCount, sizeof(prices[0]), ComparFuncStructName);
                for (size_t l = 0; l < pricesCount; printf("%s ", prices[l].name), l++);
                break;
            case 0:
                exit(1);
                break;
            default:
                printf("Only selections from 1 to 4 and 0 are accepted\n");
        }
    }
    return EXIT_SUCCESS;
}

int ComparFuncStructName(const void *x, const void *y){
    const product* pa = (const product*) x;
    const product* pb = (const product*) y;
    return strcmp(pa->name, pb->name);
}
int ComparFuncStructPrice(const void *x, const void *y){
    const product* pa = (const product*) x;
    const product* pb = (const product*) y;
    return (pa->price > pb->price) - (pa->price < pb->price);
}

Вывод (если я выберу сортировку по имени):

Select your action!

3. Sort structures by price
4. Sort structures by name
0. Exit
Bread Cucumber Juice Milk Orange Yogurt 

Select your action!

3. Sort structures by price
4. Sort structures by name
0. Exit
person gsamaras    schedule 22.11.2018
comment
return (pa->price - pb->price); возвращает int, преобразованное из double. В маловероятном случае цен выше INT_MAX или в гораздо более вероятном случае цен от 0 до 1 это не сработает. Хороший прием, который работает, — это return ((pa->price > pb->price) - (pa->price < pb->price));, который вернет -1, 0 или 1. - person Ian Abbott; 22.11.2018
comment
Это не сработает @IanAbbott, поэтому я изменил его (скорее всего, пока вы писали свой комментарий)! :) - person gsamaras; 22.11.2018

В функциях сравнения указатели (в вашем случае x и y) являются указателями на элементы массива. Если массив представляет собой int, то это указатель на int (т.е. int *). Если это массив структур, то они являются указателями на структуру (например, product *).

Чтобы получить доступ к членам структуры, вам, конечно, необходимо использовать правильный доступ к указателю на структуру, например, например. оператор стрелки ->.

Также будет проще, если вы сохраните указатели в соответствующих переменных, чтобы вам не приходилось все время выполнять приведение типов. На самом деле, если вы хотите использовать переданные данные в качестве указателей, а не значений, нет необходимости в приведении вообще, потому что void * неявно преобразуется практически в любой другой тип указателя (кроме указателей на функции).

Итак, для вашей функции сравнения структур вы можете сделать, например.

int ComparFuncStructName(const void *x, const void *y){
    const product *a = x;
    const product *b = y;

    return strcmp(a->name, b->name);
}

Или, конечно, поскольку вы сортируете массив структуры, размер элемента массива равен размеру структуры, поэтому вам также нужно это исправить:

qsort(prices, charCount, sizeof prices[0], ComparFuncStructName);

Да и у вас расчет количества элементов в конструкции тоже неверен. Формула sizeof array / sizeof array[0]. Всегда. Независимо от типа массива или его элементов. Результатом sizeof является всегда size_t, поэтому его также следует использовать в качестве типа для всех операций sizeof.

Так что вам нужно сделать

size_t charCount = sizeof prices / sizeof prices[0];

Не относящееся к делу и более стилистическое примечание: не комбинируйте вызов printf с выражением приращения цикла for. Это затруднит чтение, понимание, выполнение и (что наиболее важно) сопровождение кода.

Вместо этого поместите все операторы в тело цикла:

for (int l = 0; l < charCount; l++)
    printf("%s", prices[l].name);

Вам также не нужны разные переменные итерации для циклов. Они будут локальными для цикла внутри своей области, поэтому вы можете повторно использовать i для всех циклов.

person Some programmer dude    schedule 22.11.2018
comment
так что вам не нужно все время проводить кастинг. правильно, но тогда зачем кастинг: const product *a = (const product *) x;? - person alk; 22.11.2018
comment
@alk Возможно, не большая разница в примере, который я показываю, но больше в функциях числового сравнения. И вы не можете избавиться от приведения, но вам и не нужно делать это все время. - person Some programmer dude; 22.11.2018
comment
Я имел в виду, почему бы не просто const product *a = x;? - person alk; 22.11.2018
comment
Для полноты можно упомянуть, что исходные функции сравнения OP, такие как CompareFuncInt, не должны отбрасывать квалификатор const. - person Ian Abbott; 22.11.2018
comment
@gsamaras В этом ответе достаточно информации, чтобы позволить OP исправить функцию сравнения цен без кормления с ложечки. - person Ian Abbott; 22.11.2018