Why these two return statements are different? Why do we need to cast
the void pointer to (char **) instead of just (char *).
You may use either return statement in the comparison function. The correct choice depends on what is sorted.
The declaratopn of the function qsort looks the following way
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
The first function parameter is the address to first element of the passed array. The second parameter specifies the number of elements in the passed array. The third parameter specifiers the size of elements of the passed array. And at last the forth parameter specifies the comparison function.
The function qsort
passes to the comparison function pointers to elements of the sorted array as void pointers that are used to compare original elements of the array pointed to by the pointers within the comparison function.
For example if you have an integer array like for example
int a[5] = { 5, 3, 4, 2, 1 };
then the function qsort
to compare a pair of elements of the array as for example the first and the second elements passes to the comparison function the following expressions ( const void * )&a[0]
and ( const void * )&a[1]
. So you can compare the elements in the comparison function by casting the pointers to the type const int *
and dereferencing the obtained pointers.
Npw let's consider an array of pointers to string literals.
It can look like
char * s1[] = { "one", "two", "three", "four", "five" };
That is each element of the array has the type char *
. So the function qsort
passes pointers to elements of the array like for example &s1[0]
and &s1[1]
to the comparison function. These pointers have the type char **
.
So in fact you have
const void *a = &s1[0];
const void *b = &s1[1];
To get the elements (that are pointers) of the sorted array you need to write
char **str1 = (char **)(a);
char **str2 = (char **)(b);
return strcmp(*str1, *str2);
That is you need at first to get the original pointer of the type char **
and then to derefernce the pointers to get the original type of elements of tthe array of the type char *
.
Now let's consider another array. Instead of pointers to string literals we will declare a two-dimensional array like
char s2[][6] = { "one", "two", "three", "four", "five" };
Again the function qsort
passes to the comparison function pointers to its elements.
But now the type of elements of the array is char [6]
. A pointer to such an element has as for example in the expression &s1[0]
in turn the type char ( * )[6]
.
A value of the address of a whole array is equal to the value of the address of of its first element.
You can check that using the following code snippet
char s2[][6] = { "one", "two", "three", "four", "five" };
printf( "&s2[0] = %p\n", ( void * )&s2[0] );
printf( "&s2[0][0] = %p\n", ( void * )&s2[0][0] );
The output of this code snippet might look the following way
&s2[0] = 0x7ffcbf1712d0
&s2[0][0] = 0x7ffcbf1712d0
So logically the call of the cpmparison function can be imagined the following way
( const void * )a = &s2[0];
( const void * )b = &s2[1];
const char *c1 = a;
const char *c2 = b;
The pointers &s2[0]
and &s2[1]
and the pointers c1
and c2
have the same values. These values are addresses of the first characters of the passed sub-arrays of the two-dimensional array.
Here is a demonstration program.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int CompareWords1(const void *a, const void *b) {
char **str1 = (char **)(a);
char **str2 = (char **)(b);
return strcmp(*str1, *str2);
}
int CompareWords2(const void *a, const void *b) {
const char *c1 = ( const char * )a;
const char *c2 = ( const char * )b;
return strcmp(c1, c2);
}
int main( void )
{
const char * s1[] = { "one", "two", "three", "four", "five" };
const size_t N1 = sizeof( s1 ) / sizeof( *s1 );
for ( size_t i = 0; i < N1; i++ )
{
printf( "\"%s\" ", s1[i] );
}
putchar( '\n' );
qsort( s1, N1, sizeof( *s1 ), CompareWords1 );
for ( size_t i = 0; i < N1; i++ )
{
printf( "\"%s\" ", s1[i] );
}
putchar( '\n' );
putchar ( '\n' );
char s2[][6] = { "one", "two", "three", "four", "five" };
const size_t N2 = sizeof( s2 ) / sizeof( *s2 );
for ( size_t i = 0; i < N2; i++ )
{
printf( "\"%s\" ", s2[i] );
}
putchar( '\n' );
qsort( s2, N2, sizeof( *s2 ), CompareWords2 );
for ( size_t i = 0; i < N1; i++ )
{
printf( "\"%s\" ", s1[i] );
}
putchar( '\n' );
}
The program output is
"one" "two" "three" "four" "five"
"five" "four" "one" "three" "two"
"one" "two" "three" "four" "five"
"five" "four" "one" "three" "two"
Of cource you could write the function CompareWords2
the following way
int CompareWords2(const void *a, const void *b) {
const char ( *c1 )[6] = ( const char ( * )[6] )a;
const char ( *c2 )[6] = ( const char ( * )[6] )b;
return strcmp(*c1, *c2);
}
But it would be a bad idea to use the function when you will need to sort another two-dimensional array elements of which have some other type instead of the type char[6]
because such a function will only confuse readers of the code when they will see that the actual type of elements of the sorted array differs.