Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using qsort() to sort pointers to structs containing strings

Tags:

c

pointers

qsort

I'm not sure if this is possible to do with qsort because what I want to sort (array of pointers to struct) is not what I am comparing (strings).

Here is an abridged version of my program (assume that all the student data is in core before we call qsort() and n is the number of records to sort):

struct student {
        char lname[NAMESIZE + 1];
        char fname[NAMESIZE + 1];
        short mid;
        short final;
        short hmwks;
};

int cmp(const void *, const void *);

int
main(int argc, char **argv)
{
        int n;
        struct student *data[MAX];

        qsort(data, n, sizeof(struct student *), cmp);

        return 0;
}

int
cmp(const void *p0, const void *p1)
{
        return strcmp((*(struct student *) p0).lname,
                      (*(struct student *) p1).lname);
}
like image 985
Derek Avatar asked May 04 '12 05:05

Derek


3 Answers

What will be passed to cmp() are struct student** parameters (in the guise of void*). So change cmp() like so:

int
cmp(const void *p0, const void *p1)
{
        struct student* ps0 = *(struct student**) p0;
        struct student* ps1 = *(struct student**) p1;

        return strcmp( ps0->lname, ps1->lname);
}
like image 114
Michael Burr Avatar answered Oct 19 '22 15:10

Michael Burr


It should be something like this:

int
cmp(const void *p0, const void *p1)
{
        // pn is a pointer to an element of the array,
        // so, it's effectively a pointer to a pointer to a struct.
        // Therefore, we need to cast it appropriately to struct student **.
        // To get a pointer to a struct from it, we dereference it once,
        // hence the "*". Then we need to extract a pointer to the beginning
        // of a string, hence the "->".
        return strcmp((*(struct student **) p0)->lname,
                      (*(struct student **) p1)->lname);
}
like image 36
Alexey Frunze Avatar answered Oct 19 '22 16:10

Alexey Frunze


The other answers are correct in everything but one little detail. I've just hit this, so i'll leave it here in case someone else finds themselves struggling with an extremely pedantic compiler.

qsort() comparator accepts two const void * parameters. Which means that when you dereference them to get pointers to your actual structs, you have to keep that const-ness. So, if you were to follow every single rule of C language, your code would look like this:

int
cmp(const void *p0, const void *p1)
{
    const struct student* ps0 = *(const struct student* const*) p0;
    const struct student* ps1 = *(const struct student* const*) p1;

    return strcmp(ps0->lname, ps1->lname);
}

Note the "const pointer to const" construct - that's you telling the compiler that at no point your original pointers (p0 and p1) lose their const-ness - you first dereference them to a const * const *, and then dereference that to const *. If you simply use const **, that's a const * to * (non-const) pointer, which discards the original const-ness of const void *.

like image 31
aburakov Avatar answered Oct 19 '22 17:10

aburakov