What does C syntax mean?

This is from the magic array library that I use.

void sort(magic_list *l, int (*compare)(const void **a, const void **b)) { qsort(l->list, l->num_used, sizeof(void*), (int (*)(const void *,const void *))compare); } 

My question is: what is actually the last argument to qsort?

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

qsort accepts int (*comp_fn)(const void *,const void *) as an argument to the comparator, but this sorting function accepts a comparator with double pointers. One way or another, the line above converts the version of the double pointer to a single version of the pointer. Can someone help explain?

+6
source share
6 answers

On most hardware, you can assume that all pointers look the same at the hardware level. For example, in a system with flat 64-bit address pointers, there will always be a 64-bit integer. The same applies to pointers to pointers or pointers to pointers to pointers to pointers.

Therefore, any method used to call a function with two pointers will work with any function that takes two pointers. The specific type of pointers does not matter.

qsort treats pointers as a whole, as if everyone were opaque. Therefore, he does not know and does not care about how they are played. He knows what order they are in now, and uses the compare argument to determine what order they should be in.

The library you are using seems to contain lists of pointers to pointers. It has a comparison function that can compare two pointers with pointers. So he passes this to go to qsort. It is simply syntactically better than, for example,

 qsort(l->list, l->num_used, sizeof(void*), compare); /* elsewhere */ int compare(const void *ptr1, const void *ptr2) { // these are really pointers to pointers, so cast them across const void **real_ptr1 = (const void **)ptr1; const void **real_ptr2 = (const void **)ptr2; // do whatever with real_ptr1 and 2 here, eg return (*real_ptr2)->sort_key - (*real_ptr1)->sort_key; } 
+2
source

What makes you throw: it converts a pointer of type

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

type pointer

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

The latter means qsort is expected.

Such things are quite common in poor quality code. For example, when someone wants to sort an array from int s, they often write a comparison function that takes pointers to int *

 int compare_ints(const int *a, const int *b) { return (*a > *b) - (*a < *b); } 

and when it comes time to call qsort , they force it to the appropriate type to suppress compiler complaints.

 qsort(array, n, sizeof *array, (int (*)(const void *,const void *)) compare_ints); 

This is a “hack,” which leads to undefined behavior. This is obviously bad practice. What you see in your example is just a less direct version of the same “hack”.

The correct approach in such cases would be to declare the comparison function as

 int compare_ints(const void *a, const void *b) { int a = *(const int *) a; int b = *(const int *) b; return (a > b) - (a < b); } 

and then use it without any throws

 qsort(array, n, sizeof *array, compare_ints); 

In general, if you expect their comparison functions to be used as comparators in qsort (and similar functions), they should implement them with const void * parameters.

+8
source

The last qsort argument is casting a pointer to a function with two pointers, one of which accepts one pointer, which qsort will accept. It is just casting.

+3
source

It gives a pointer to a function. I suppose the reason is that the comparison can be applied to pointers that are dereferenced, and not to what they indicate.

+1
source

(int (*)(const void *,const void *))compare is a C-style cast to pass the compare function pointer to the function pointer with two const void * arguments.

0
source

The final argument is a function pointer. It indicates that it takes a pointer to a function that returns an int and takes two arguments of const void ** .

0
source

Source: https://habr.com/ru/post/896921/


All Articles