I'm trying to use the standard library's qsort
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 completely as expected, but why am I getting the compiler warning "passing argument 4 of ‘qsort’ from incompatible pointer type
"? And how can I cast wcscoll
to fit the prototype?
The warning goes away if I define and pass in a separate comparison function:
int widecharcomp(const void *arg1, const void *arg2)
{
return wcscoll(arg1, arg2);
}
... but this one looks like it should have error handling for when the arguments are not of type wchar_t *
.
You've done pretty much the right way. 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's 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 actually does raise the warning that you want to get rid of, but again it can be gotten around by changing the char**
to const void*
in the arguments to compare_elements
, and then explicitly casting to const char**
.
You're right in observing that this is type-unsafe, but type safety is not exactly one of C's strong points. C doesn't have anything like generics or templates, so the only way that qsort can work on an arbitrary type is for its comparison function to accept void*
s. It's up to the programmer to make sure that the comparison function is not used in a context where it may be passed arguments that are not the expected type.
That said, there is an error in your code. What the comparison function receives is not the elements to be compared, but rather pointers to the elements to be compared. So if the elements are strings, that means pointer-to-pointer. So when you write
return wcscoll(arg1, arg2);
You are actually passing wscoll
a wchar_t**
when it expects a wchar_t*
. The correct way to do this, while suppressing the warning, would be:
int widecharcomp(const void *arg1, const void *arg2)
{
return wcscoll(*(const w_char_t**)arg1, *(const w_char_t**)arg2);
}
as ugly as that is.
Edit:
Just took another look at the top bit of your code. Your error is really twofold here. You're trying to use wcscoll
to sort characters. It's a function meant to sort strings (which in C are pointers to nul-terminated sequences of characters). The above was written assuming you were trying to sort strings. If you want to sort characters, then wcscoll
is not the appropriate function to use, but everything above regarding qsort
still applies.
There are two problems: you've mixed up wchar_t
and wchar_t*
, and you've tried to pass off a wchar_t*
as a void*
.
First, you've told qsort
to sort an array of wchar_t
. But wcscoll
doesn't compare wchar_t
, it compares wide character strings which have the type wchar_t*
. The fact that your comparison appears to have worked is due to your test data which just happens to work well under both interpretations.
If you wanted to sort characters, you need to call an appropriate function (I don't know the wide character API well enough to tell you which one). If you wanted to sort strings, you need to allocate an array of strings (of type wchar_t *
).
Furthermore, even if you had an array of wchar_t*
, you could not portably pass wcscoll
as an argument to qsort
. The issue 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 from byte pointers; on such a machine, qsort
would pass byte pointers to elements of the array to wcscoll
, and this wouldn't work because wcscoll
expects byte pointers. The solution is to write a trivial wrapper function that performs the conversion if necessary. A trivial wrapper is often necessary with qsort
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With