Incompatible type pointer compiler warning for qsort 4th argument

I am trying to use the standard qsort library to sort an array of wide characters:

 wchar_t a = L'a'; wchar_t a1 = L'Γ€'; wchar_t b = L'z'; wchar_t chararray[] = {b, a, a1}; length = wcslen(chararray); qsort(chararray, length, sizeof(wchar_t), wcscoll); 

Now I think the functions involved have these prototypes:

 int wcscoll(const wchar_t *ws1, const wchar_t *ws2); void qsort(void *base, size_t num, size_t size, int (*comp_func)(const void *, const void *)) 

The results are fully expected, but why do I get a compiler warning " passing argument 4 of 'qsort' from incompatible pointer type " ? And how can I use wcscoll to match the prototype?

The warning disappears if I define and pass a separate comparison function:

 int widecharcomp(const void *arg1, const void *arg2) { return wcscoll(arg1, arg2); } 

... but it looks like it should have error handling if the arguments are not of type wchar_t * .

+4
source share
4 answers

There are two problems: you mixed up wchar_t and wchar_t* , and you tried passing wchar_t* as void* .

You first told qsort sort the wchar_t array. But wcscoll does not compare wchar_t , it compares wide character strings that are of type wchar_t* . The fact that your comparison seems to have worked is due to your test data, which turned out to work well with both interpretations.

If you want to sort the characters, you need to call the corresponding function (I do not know the wide API-characters to tell you which one). If you want to sort strings, you need to select an array of strings (of type wchar_t * ).

Also, even if you have a wchar_t* array, you won’t be able to wcscoll as an argument in qsort . The fact is that there is no guarantee that wchar_t* and void* have the same representation. Some machines have word pointers that have a different representation of byte pointers; on such a machine, qsort will pass byte pointers to array elements to wcscoll , and this will not work because wcscoll expects byte pointers. The solution is to write a trivial wrapper function that performs the conversion if necessary. An optional wrapper is often needed with qsort .

+4
source

You have made a very right path. The gcc documentation for strcoll and wcscoll gives an example similar to this as the correct way to use strcoll or wcscoll with qsort .

  /* This is the comparison function used with qsort. */ int compare_elements (char **p1, char **p2) { return strcoll (*p1, *p2); } /* This is the entry point---the function to sort strings using the locale collating sequence. */ void sort_strings (char **array, int nstrings) { /* Sort temp_array by comparing the strings. */ qsort (array, nstrings, sizeof (char *), compare_elements); } 

This example really raises the warning you want to get rid of, but you can get it again by changing char** to const void* in the compare_elements arguments, and then explicitly typing const char** .

You are right to observe that this is unsafe, but type safety is not one of C.'s strengths. C has nothing to do with generics or templates, so the only way qsort can work on an arbitrary type is by its comparison function for accepting void* s. It is up to the programmer to make sure that the comparison function is not used in a context where arguments that are not the expected type can be passed to it.

However, there is an error in the code . What the comparison function receives is not the elements that need to be compared, but rather pointers to the elements that need to be compared. Therefore, if the elements are strings, this means that the pointer is a pointer. Therefore when you write

 return wcscoll(arg1, arg2); 

In fact, you pass wscoll a wchar_t** when it expects wchar_t* . The correct way to do this by suppressing the warning would be as follows:

 int widecharcomp(const void *arg1, const void *arg2) { return wcscoll(*(const w_char_t**)arg1, *(const w_char_t**)arg2); } 

how uglier than that.

Edit:

Just looked at the top bit of your code. Your mistake here is really twofold. You are trying to use wcscoll to sort characters. This is a function for sorting strings (which in C are pointers to zero-terminated sequences of characters). It was written above, assuming you are trying to sort the lines. If you want to sort characters, then wcscoll not a suitable function to use, but everything related to qsort is still applicable.

+8
source

You have already coded your solution (however, see other answers and changes at the end of this question with the choice of the used comparison function and the data transferred to qsort() ).

You can drop the wrapper function by pointing the function pointer to qsort() to the appropriate type, but I think using a wrapper is the best solution in terms of serviceability. If you really want to avoid the wrapper function (you may have encountered a measurable run into the primary problem), you can do this:

 qsort(chararray, length, sizeof(wchar_t), (int(*)(const void*,const void*))wcscoll); 

Or make it more readable with typedef for the type of the comparison function:

 typedef int (*comp_func_t)(const void *, const void *); /* ... */ qsort(chararray, length, sizeof(wchar_t), (comp_func_t) wcscoll); 

Unfortunately, direct C qsort() cannot be typical, so it cannot have "error handling if the arguments are not of type wchar_t". You, the programmer, are responsible for passing the correct data, sizes, and comparison function to qsort() .


Edit:

To solve some of the problems mentioned in other answers about types passed by the comparison function, a procedure is used here that can be used to sort wchar_t using the current locale matching sequence. The library may have something better, but I don't know about it at the moment:

 int wchar_t_coll( const void* p1, const void* p2) { wchar_t s1[2] = {0}; wchar_t s2[2] = {0}; s1[0] = * (wchar_t*)p1; s2[0] = * (wchar_t*)p2; return wcscoll( s1, s2); } 

Also note that the chararray you pass to wcslen() is not completed correctly - you will need 0 at the end of the initializer:

 wchar_t chararray[] = {b, a, a1, 0}; 
+2
source

You cannot pointer to another type, your current solution is as good as

0
source

All Articles