1

I have no idea on how to look this up, even the title is confusing, even I am confused about what I'm looking for, and the question has for sure already been asked but it's so specific to be found, so here a bit of context:

int comparison(const int* a, const int* b) {
    return *a - *b;
}

int main(int argc, char const *argv[])
{
    int arr[3] = {1,6,-2};
    qsort(arr,3,sizeof(int),comparison);
    return 0;
}

Well, it does work, but the compiler gives me a warning, because qsort wants a function of type:

int(*)(const void*, const void*) 

and comparison is a function of type:

int(*)(const int*, const int*) 

I want to know why the compiler is not happy because it just has to cast the address. It should even be happy to give a type to a void* pointer. Is this really bad? Like an undefined behavior or something? Or just the compiler whining about nothing much?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Bamontan
  • 360
  • 1
  • 11
  • It is undefined behaviour if you call a function via a pointer that has a different type from the type of the actual function. So it is crucial that you pass the type of function that `qsort()` expects, because it will assume the type it expects, and if the actual function has a different type, you get UB. – Jonathan Leffler Feb 05 '21 at 04:52
  • Related: https://softwareengineering.stackexchange.com/questions/275712/why-arent-void-s-implicitly-cast-in-c And this: https://www.stroustrup.com/bs_faq2.html#void-ptr – Jerry Jeremiah Feb 05 '21 at 05:01

3 Answers3

2

After other reasons already given, there's another one. Historically there were platforms for which void * and int * had different bit arrangements, I've heard rumor of one where void * and int * were different sizes. That function pointer cast won't always work.

  const int *ia = (const int *)a;
  const int *ib = (const int *)b;

might not compile away to ia = a; but rather to something like ia = a >> 1; So there's really got to be a place for those instructions to be.

Joshua
  • 40,822
  • 8
  • 72
  • 132
1

The qsort function takes as one of its arguments a function of a type you are not passing. So you'll need to change that.

Inside the comparator, you can recast the pointers to the desired type.

Additionally, you need to dereference the values of the const int pointers you are passing into the comparator function:

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

static int 
comparator(const void *a, const void *b) 
{
    return *(const int *)a - *(const int *)b;
}

static void
printArr(int arr[], int n) 
{ 
    int i; 
    for (i = 0; i < n; ++i) {
        printf("%d ", arr[i]);
    } 
} 

int
main(int argc, const char **argv)
{
    int arr[3] = {1, 6, -2};
    qsort(arr, 3, sizeof(int), comparator);
    printArr(arr, 3);

    return EXIT_SUCCESS;
}
Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345
  • thanks for the reply, but I think I wasn't clear enough: I know what the problem is, what I want to know is why it's a problem. why can't the compiler just accept that I want to cast a void* to a int* directly, instead of having to cast it in the function. – Bamontan Feb 05 '21 at 04:44
  • Note that subtracting in the comparator function runs the risk of overflow if you have large enough values of different signs to compare. It might be better to have the body as: `{ int va = *(const int *)a; int vb = *(const int *)b; return (va > vb) - (va < vb); }` or `{ int va = *(const int *)a; int vb = *(const int *)b; if (a < b) return -1; else if (a > b) return +1; else return 0; }`. – Jonathan Leffler Feb 05 '21 at 04:47
  • 1
    It's nothing more complicated than that the `qsort` function is defined to accept a function of that type, and so that's what you have to pass to it. You could write your own sorting function that accepts a comparator of your custom type, though you lose a lot of the flexibility that the generic `qsort` already provides. Say one day you decide to change your array from storing `int` to instead storing `long` values (for whatever reason). You'd have a lot less code to change with the generic `qsort`. – Alex Reynolds Feb 05 '21 at 05:19
1

why he (the compiler) is not happy .

qsort() expects a function point of type int (*)(const void *a, const void *b), not int (*)(const int *a, const int *b). The compiler could guess its OK and beform a cast, yet it is more productive for the compiler to warn about such problems.

Or just the compiler whining about nothing much?

By warming you, you are allowed to determine the degree of the problem.


In addition to @Alex Reynolds good answer, note that *a - *b may overflow, resulting in the wrong comparison.

Instead:

int comparison(const void *a, const void *b) {
  const int *ia = (const int *)a;
  const int *ib = (const int *)b;
  return (*ia > *ab) - (*ia < *ib);
}

Good compilers recognize the (p>q) - (p<q) idiom and emit efficient code.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256