简体   繁体   中英

Why qsort doesn't work, while sorting an array of structures

I have an array of such structs

typedef union value_
    {
        double d;
        int i;
    } value;

typedef struct number_
{
    char type;
    value val;
}number;

type can be 'i' or 'd' , depending on either integer or double value is stored in union field. The array looks as following:

Integer value: -3
Double value: 4.84
Integer value: -4
Double value: 1.65
Integer value: 1
Double value: 2.54
Integer value: 2
Double value: -0.09

The main thing, is that integers and doubles go one by one. I need to sort this array using qsort in next way: integers should be sorted from lower to higher and doubles vice versa. I use the following function to compare:

int compare(const void *A, const void *B){

    number *a = (number*)A;
    number *b = (number*)B;
    
    if (a->type == b->type) {
        if (a->type =='i') {
            return a->val.i - b->val.i;
        } else {
            if (a->val.d > b->val.d) return -1;
            if (a->val.d < b->val.d) return 1;
            else return 0;
        }
    } else {
        return 0;
    }
}

It is assumed, that if the values in unions have different types, than it returns 0 without comparing, so such sequence, as int,double,int,double should be saved. But the result is following:

Integer value: -1
Integer value: -5
Double value: 0.20
Integer value: 2
Double value: -0.04
Double value: -2.69
Integer value: 4
Integer value: 5
Double value: 2.04
Integer value: 3

Why does it change doubles with integers, if it should return 0 in that case?

If you need to preserve the original list's 'interlocked pattern' of int and double types, yet sort each set of int and double values, then you can't use the standard qsort function as is on the complete list.

However, what you can do is to separate the int and double entries into new lists, sort those separately, then merge the two sorted lists back into the original, taking either an int or double entry from the relevant sorted list, depending on the type of the entry in the original list.

Here's a possible implementation (using the compare function from your code unchanged – though you'd get better performance for large lists if you made two, type-specific, comparison functions for use with the separated lists):

void printlist(number* list, size_t n)
{
    for (size_t i = 0; i < n; ++i) {
        if (list[i].type == 'i') printf("%d ", list[i].val.i);
        else printf("%lf ", list[i].val.d);
    }
    printf("\n");
}

int main()
{
    number list[] = {
        { 'i', { .i = -3} },   { 'd', { .d = 1.65} },
        { 'i', { .i = -4} },   { 'd', { .d = 4.85} },
        { 'i', { .i =  1} },   { 'd', { .d = -.09} },
        { 'i', { .i =  2} },   { 'd', { .d = 2.54} },
    };
    size_t Tcount = sizeof(list) / sizeof(*list), Icount = 0, Dcount = 0; // Counts for total, ints and doubles
    for (size_t i = 0; i < Tcount; ++i) {
        if (list[i].type == 'i') ++Icount;
        else ++Dcount;
    }
    // Display original list ...
    printlist(list, Tcount);
    number* Ilist = malloc(sizeof(number) * Icount);
    number* Dlist = malloc(sizeof(number) * Dcount);
    size_t Iindex = 0, Dindex = 0;
    // Separate lists ...
    for (size_t i = 0; i < Tcount; ++i) {
        if (list[i].type == 'i') Ilist[Iindex++] = list[i];
        else Dlist[Dindex++] = list[i];
    }
    // Sort each list separately ...
    qsort(Ilist, Icount, sizeof(number), compare);
    qsort(Dlist, Dcount, sizeof(number), compare);
    // Merge sorted lists ...
    Iindex = 0, Dindex = 0;
    for (size_t i = 0; i < Tcount; ++i) {
        if (list[i].type == 'i') list[i] = Ilist[Iindex++];
        else list[i] = Dlist[Dindex++];
    }
    // Display sorted list ...
    printlist(list, 8);
    // Clean up ...
    free(Ilist);
    free(Dlist);
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM