Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c

qsort

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?

like image 547
jjg Avatar asked Feb 27 '21 15:02

jjg


People also ask

How does the qsort function work in C?

The qsort() function sorts an array of num elements, each of width bytes in size, where the first element of the array is pointed to by base. The compare pointer points to a function, which you supply, that compares two array elements and returns an integer value specifying their relationship.

What is a comparator function?

A comparator is a function that takes two arguments x and y and returns a value indicating the relative order in which x and y should be sorted. It can be a 3-way comparator returning an integer, or a 2-way comparator returning a boolean.

What is the last argument of the qsort library function?

The qsort() function calls this function by the pointer passed in the last argument to qsort() each time it needs to compare two elements of the array. The comparison function takes as its arguments two pointers to elements of the array being sorted.


2 Answers

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.

like image 31
Neil Avatar answered Sep 22 '22 17:09

Neil


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.

like image 90
dbush Avatar answered Sep 20 '22 17:09

dbush