1

I have an almost complete code, need to Qsort some stuff, first I have array of integers, array of floats and then I have struct with char and double combined array. Need to somehow qsort them, but I am stuck.

Here is my code, I don't have problems with sorting integers and floats, but I can't complete struct sorting.

#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;
}

I want to do so as when user chooses 3, it goes and sorts array of PRICES under products by prices and if user chooses 4 it should sort by names, in both cases it should output both price and name, just difference in by what it was sorted.

  • `qsort(prices, ..., sizeof(double)` is wrong, and both `ComparFuncStructName` and `ComparFuncStructPrice` need to cast to a struct pointer. You need to pass the size of the struct to the function. A good way to avoid such mistakes is to write `qsort(array, num, sizeof *array, comparefn)`, similar to `int *x = malloc(n * sizeof *x)`. – vgru Nov 22 '18 at 12:50
  • Possible duplicate of [Need help using qsort with an array of structs](https://stackoverflow.com/questions/6105513/need-help-using-qsort-with-an-array-of-structs). You could also use the Search box next time: https://stackoverflow.com/search?q=qsort+struct. – vgru Nov 22 '18 at 12:53
  • Step 1: change `sizeof(array) / sizeof(type);` --> `sizeof array / sizeof array[0];` – chux - Reinstate Monica Nov 22 '18 at 12:56

2 Answers2

3

In the comparison functions, the pointers (x and y in your case) are pointers to elements of the array. If the array is an int, then it's a pointer to int (i.e. int *). If it's an array of structures, then they are pointers to the structure (like e.g. product *).

To access the members of the structure you of course needs to use the proper pointer-to-structure access, like e.g. the arrow operator ->.

It's also easier if you save the pointers in proper variables so you don't have to do all the casting all the time. In fact, if you want to use the passed data as pointers and not values there's no need to cast at all, because void * is implicitly convertible to almost any other pointer type (except pointers to functions).

So for your structure comparison function, you could do e.g.

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

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

Or course, since you're sorting an array of structure, the element size of the array is the size of the structure, so you need to fix that too:

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

Oh and you calculation of the number of elements in the structure is wrong as well. The formula is sizeof array / sizeof array[0]. Always. No matter the type of the array or its elements. The result of sizeof is always size_t, so you should use it as type for all sizeof operations as well.

So you need to do

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

On an unrelated and more stylistic note: Don't combine the printf call with the increment expression of the for loop. It will make the code harder to read, understand, follow and (most importantly) maintain.

Put all statements in the body of the loop instead:

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

You also don't need different iteration variables for the loops. They will be local for the loop inside their own scope, so you can reuse i for all the loops.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • "*so you don't have to do all the casting all the time.*" right, but then why the cast: `const product *a = (const product *) x;`? – alk Nov 22 '18 at 13:13
  • @alk Maybe not a big difference in the example I show, but bigger in the numeric comparison functions. And you can't really do away with the casts, but you don't need to do it *all* the time. – Some programmer dude Nov 22 '18 at 13:15
  • 2
    I meant why not just to `const product *a = x;`? – alk Nov 22 '18 at 13:16
  • For completeness, maybe mention that OP's original comparison functions such as `CompareFuncInt` should not cast away the `const` qualifier. – Ian Abbott Nov 22 '18 at 13:20
  • @gsamaras There is enough information in this answer to allow OP to fix the prices comparison function without being spoon-fed. – Ian Abbott Nov 22 '18 at 13:37
2

Change this:

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

to:

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

since the array of structs has one size, regardless of whether you'd like to sort by name or price. Moreover, the size of every element of the array is equal to the size of the struct, regardless of whether you'd like to sort by name or price.

Similar for when sorting by names.


Also, your compare functions are wrong. First, you need to covert every void pointer to a struct. Then, you need to use the relevant field of the struct to compare.

For the prices comparison, use the less and more operators. If you simply subtract, you will get wrong results.


Putting everything together (and discarding the non-struct arrays), you get:

#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);
}

Output (if I choose to sort by name):

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
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • `return (pa->price - pb->price);` returns an `int` converted from a `double`. In the unlikely case of prices beyond `INT_MAX`, or the far more likely case of prices between 0 and 1, this won't work. A good trick to use that works is `return ((pa->price > pb->price) - (pa->price < pb->price));`, which will return -1, 0 or 1. – Ian Abbott Nov 22 '18 at 13:27
  • It won't work @IanAbbott, that's why I changed it (most probably while you were writing your comment)! :) – gsamaras Nov 22 '18 at 13:28