Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

348
Views
qsort: Cast the comparator function itself or the parameters in the body of comparator function?

There are a couple of obvious ways to use qsort: cast in the comparator:

int cmp(const void *v1, const void *v2) 
{
    const double *d1 = v1, *d2 = v2;
    ⋮
}

qsort(p, n, sizeof(double), cmp);

or cast the comparator:

int cmp(const double *d1, const double *d2) 
{
    ⋮
}

qsort(p, n, sizeof(double), (int (*)(const void *, const void *))cmp);

I tend to use the former, more for aesthetic reasons than anything else. Are there any technical reasons for preferring one over the other?

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

You should avoid the latter case because it's not valid.

For two function types to be compatible, the return types must be compatible and the corresponding parameter types must be compatible. A const void * is not compatible with a const double * therefore the function types are not compatible. Calling a function through an incompatible pointer type results in undefined behavior.

Note that just because two types may be implicitly converted doesn't mean they are compatible. Taking the example of const double * and const void *, conversion between the two types can be performed without a cast, however the representation of the two types need not be the same.

This means that the way a const double * is passed to a function may be different from how a const void * is passed to a function. So by calling a function of type int (*)(const double*, const double*) as if it had type int (*)(const void*, const void*), the parameters could be passed in an incorrect way.

While x64 and ARM systems will typically use the same representation for all pointer types, you might get away with doing the former, but there's still no guarantee of that. Modern compilers will often assume undefined behavior will not happen and perform optimizations based on that fact.

The former case is the proper method as the function's signature is compatible with what the qsort function expects.

over 4 years ago · Santiago Trujillo Report

0

In addition to dbush excellent answer, it should be noted that the case of an alternate comparison function with a prototype of int cmp(const char *s1, const char *s2), such as strcmp is not as clear cut as the one in the question. The C Standard specifies that:

6.2.5 Types

[...] A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.

So pointers to functions with prototypes int cmp(const void *v1, const void *v2) and int cmp(const char *v1, const char *v2) are not compatible but the calling sequence is quite unlikely to be different even on those extremely rare targets where int cmp(const double *v1, const double *v2) would be problematic (early Cray systems and CPUs lacking byte addressability).


You do not provide the code for the comparison functions: it is a common mistake to simply return the difference of values (*d1 - *d2). This does not work for floating point values and neither does it for int values as the subtraction may overflow.

Here is an implementation for increasing order that works for all number types:

int cmp(const void *v1, const void *v2) {
    const int *p1 = v1, *p2 = v2;
    return (*p1 > *p2) - (*p1 < *p2);
}

For floating point types, special handling of NaN values may be needed:

// sort by increasing values, with NaN after numbers
int cmp(const void *v1, const void *v2) {
    const double *p1 = v1, *p2 = v2;
    if (isnan(*p1)) {
        return isnan(*p2) ? 0 : 1;
    } else
    if (isnan(*p2)) {
        return -1;
    } else {
        return (*p1 > *p2) - (*p1 < *p2);
    }
}
over 4 years ago · Santiago Trujillo Report

0

As an addendum, there is another strategy to call qsort: create an intermediary qsort required prototype function that calls a type-enabled comparison function.

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

static int double_cmp(const double *d1, const double *d2)
    { return (*d1 > *d2) - (*d2 > *d1); }

static int double_void_cmp(const void *v1, const void *v2)
    { return double_cmp(v1, v2); }

int main(void) {
    double p[] = { 2.18, 6.28, 3.14, 1.20, 2.72, 0.58, 4.67, 0.0, 1, 1.68 };
    const size_t n = sizeof p / sizeof *p;
    size_t i;
    qsort(p, n, sizeof *p, &double_void_cmp);
    for(i = 0; i < n; i++) printf("%s%.2f", i ? ", " : "", p[i]);
    fputs(".\n", stdout);
    return EXIT_SUCCESS;
}

Although this has its own problems, one can use double_cmp as a comparator for other non-qsort things. Also, it doesn't require any casts or explicit assignments, per my interpretation of ISO 9899 6.3.2.3,

A pointer to void may be converted to or from a pointer to any incomplete or object type . . . and back again.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!