int (*)(const void*, const void*) and int (*)(const char*, const char*) are not compatible function pointer types.
Casting between different, non-compatible function pointer types is explicitly undefined behavior, C17 6.3.2.3/8 emphasis mine:
A pointer to a function of one type may be converted to a pointer to a function of another
type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.
So if you cast strcmp to something else, you are explicitly invoking undefined behavior. It will likely work in practice on any system where all pointer types are of equal size. But if you are going to rely on that, you might as well cook up something like this:
typedef union
{
int (*strcmp) (const char*, const char*);
int (*compare)(const void*, const void*);
} strcmp_t;
const strcmp_t hack = { strcmp };
...
qsort(str, x, y, hack.compare);
This is just as undefined behavior (and as likely to work in practice) but more readable.
You can never do qsort(str, x, y, strcmp) because again strcmp is not compatible with the function pointer type expected by qsort. Function parameter passing is done as per assignment, so the rules of simple assignment are the relevant part, from C17 6.5.11:
Constratints
...
- the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
Therefore qsort(str, x, y, strcmp) is always invalid C and this is not a quality of implementation issue. Rather, compilers letting this through without diagnostics are to be regarded as hopelessly broken.
And finally as noted in comments, strcmp only makes sense to use with bsearch/qsort in case you have a true 2D array of characters such as char str[x][y];. In my experience that's a rather rare use-case. When dealing with strings, you are far more likely to have char* str[x], in which case you must write a wrapper around strcmp anyway.
qsortpasses pointers to elements using the pointer-to operator&. So if you have an array of pointers theqsortfunction will pass pointer to pointers. And if you have an array or arrays thenqsortwill pass pointers to the arrays which will have the wrong type. So the result really depends on the data you want sorted.