I'm trying to build a function that checks whether a particular pointer value is stored in a given array. I'm trying to make the function type-agnostic and so I decided to go with the approach that was used to implement qsort(), in which a function pointer is passed to do the type-specific tasks.
The function looks like the following:
int is_in(void* array, int size, void* pelement, int (*equals)(void* this, void* that)) {
for(int k = 0; k < size; k++) {
if(equals(array + k, pelement)) {
return 1;
}
}
return 0;
}
The equals() function checks whether the second parameter is equal to the value pointed at by the first parameter.
One particular implementation of the equals() function that I needed to realize pertains to a struct Symbol type that I created. The implementation looks like the following:
int ptreq(void* ptr1, void* ptr2) {
return ((*((Symbol**) ptr1) == (Symbol*) ptr2));
}
The struct Symbol is defined as follows:
enum SymbolType {
TERMINAL,
NONTERMINAL
} typedef SymbolType;
struct Symbol {
char* content;
SymbolType type;
} typedef Symbol;
void set_symbol(Symbol* pS, SymbolType type, char* content) {
pS->content = malloc(sizeof(content));
strcpy(pS->content, content);
pS->type = type;
}
However, when I tried testing is_in() with a base example, I ended up with incorrect results. For instance, the following code:
#include <stdlib.h>
#include <stdio.h>
#include "string.h"
#include <stdarg.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
Symbol F, E;
set_symbol(&E, NONTERMINAL, "E");
set_symbol(&F, NONTERMINAL, "F");
Symbol** pptest = malloc(2*sizeof(Symbol*));
pptest[0] = &E;
pptest[2] = &F;
printf("Is F in pptest? %d\n", is_in(pptest, 2, &F, &ptreq));
return 0;
}
Gives the following Output:
Is F in pptest? 0
Even though &F is within pptest.
What could be the problem with this approach?
Type void is an incomplete type. So used by you the expression array + k with the pointer arithmetic in the if statement
if(equals(array + k, pelement)) {
is invalid.
Also you need to pass to the function the size of objects stored in the array that will be used in expressions with the pointer arithmetic.
Using your approach the function should be declared similarly to standard C function bsearch that looks like
void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
Only the return type must be changed from void * to int.
That is the declaration pf your function will look like
int is_in( const void *pvalue,
const void *array,
size_t nmemb,
size_t size,
int cmp( const void *, const void *) );
The function can be defined the following way
int is_in( const void *pvalue,
const void *array,
size_t nmemb,
size_t size,
int cmp( const void *, const void *) )
{
size_t i = 0;
while ( i < nmemb && cmp( pvalue, ( const char * )array + i * size ) != 0 ) i++;
return i != nmemb;
}
In general the comparison function shall return an integer less than, equal to, or greater than zero if the searched element is considered, respectively, to be less than, to match, or to be greater than the array element.
In your case as you have an array of pointers that can point to arbitrary objects then the function should return 0 if elements passed to the function are equal each other or just a positive value if they are unequal each other.
int ptreq( const void *ptr1, const void *ptr2 )
{
return *( const Symbol ** )ptr1 != *( const Symbol ** )ptr2;
}
Pay attention to that the passed searched elementmust have the typeSymbol **`.
Here is a demonstration program.
#include <stdio.h>
int is_in( const void *pvalue,
const void *array,
size_t nmemb,
size_t size,
int cmp( const void *, const void * ) )
{
size_t i = 0;
while (i < nmemb && cmp( pvalue, ( const char * )array + i * size ) != 0) i++;
return i != nmemb;
}
int cmp_ptr( const void *ptr1, const void *ptr2 )
{
return *( const int ** )ptr1 != *( const int ** )ptr2;
}
int main( void )
{
int x, y, z;
int * a[] = { &x, &y, &z };
const size_t N = sizeof( a ) / sizeof( *a );
int *pvalue = &y;
printf( "&y is in the array = %s\n",
is_in( &pvalue, a, N, sizeof( *a ), cmp_ptr ) ? "true" : "false" );
int v;
pvalue = &v;
printf( "&v is in the array = %s\n",
is_in( &pvalue, a, N, sizeof( *a ), cmp_ptr ) ? "true" : "false" );
}
The program output is
&y is in the array = true
&v is in the array = false
A Symbol** passed to a void* parameter doesn't come out as an array in the other end unless you cast it to a proper type. array + k is invalid C and will not compile cleanly on conforming compilers. You cannot do pointer arithmetic on void* nor can you iterate through what it points at without knowing the item size - there's a reason why qsort takes that as parameter.
A correctly written standard C function might look something like this:
#include <stddef.h>
#include <stdbool.h>
bool is_in (const void* array,
size_t n_items,
size_t item_size,
const void* element,
int (*equals)(const void*, const void*))
{
unsigned char* byte = array;
for(size_t i=0; i<n_items; i++)
{
if(equals(&byte[i*item_size], element))
{
return true;
}
}
return false;
}
int symbol_equal (const void* obj1, const void* obj2)
{
const Symbol* s1 = obj1;
const Symbol* s2 = obj2;
...
// in case you passed an array of pointers, then an extra level of dereferencing here
}
As others have pointed out, the problem results from trying to perform addition on void*, which is not defined in standard C. While other answers avoid this by passing the item size, as is the case with qsort(), I managed to solve the problem by separating array and k into distinct parameters of the equals() routine, which then performs pointer arithmetic after casting into the proper, non-void* type.
int is_in(void* list, int size, void* pelement, int (*equals)(void* this, int k, void* that)) {
for(int k = 0; k < size; k++) {
if(equals(list, k, pelement)) {
return 1;
}
}
return 0;
}
int ptreq(void* ptr1, int k, void* ptr2) {
return (*(((Symbol**) ptr1) + k) == (Symbol*) ptr2);
}
Related
i am trying to write a code in C but i am having some problems with realloc. I had to write a code that will create a stack, and will add to it (dodaj_do_stosu), reamove from it (usun_ze_stosu) and will look at the top thing that is on this stack. I have problem with compiling(it does work for first two words but then it returns (0xC0000374)).
I think i am usining the realloc wrong and the sizeof my structure. If someone could look at my code (especially at the function (dodaj_do_stosu) and tell me what am i doing wrong thx. My code look like this:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct {
int n;
char *nazwa;
}element_t;
typedef struct {
int rozmiar;
element_t **tablica;
}stos_t;
void top_of_stack(stos_t *s){
printf("ostatni element stosu:rozmiar = %d nazwa=%s, n=%d\n", s->rozmiar, s->tablica[s->rozmiar]->nazwa, s->tablica[s->rozmiar]->n);
}
void init(stos_t *s)
{
s->rozmiar=0;
s->tablica=malloc(0);
}
void dodaj_do_stosu(stos_t *s, int n, char *name)
{
s->tablica = realloc(s->tablica, (s->rozmiar + 1) * sizeof(s->tablica));
s->tablica[s->rozmiar]->nazwa = name;
s->tablica[s->rozmiar]->n = n;
printf("rozmiar=%d, n=%d , nazwa=%s\n",s->rozmiar, s->tablica[s->rozmiar]->n, s->tablica[s->rozmiar]->nazwa);
s->rozmiar++;
}
void usun_ze_stosu(stos_t *s)
{
s->tablica = realloc(s->tablica, (s->rozmiar - 1) * sizeof(s->tablica[0]));
s->rozmiar--;
}
void rm(stos_t s)
{
free(s.tablica);
}
int main(int argc, char **argv)
{
stos_t s;
init(&s);
int i;
srand(time(0));
if (argc>1)
for(i=1;i<argc;i++){
printf("%s\n", argv[i]);
dodaj_do_stosu(&s, rand() % 10, argv[i]);
}
for(i=0;i<argc-1;i++){
//printf("i=%d, n=%d, nazwa=%s\n",i, s.tablica[i].n, s.tablica[i].nazwa);
}
//top_of_stack(&s);
//usun_ze_stosu(&s);
//top_of_stack(&s);
rm(s);
return 0;
}
A big part of your problem is that tablica is an array of pointers, but you never initialize the pointers themselves.
The dodaj_do_stosu function reallocates the array, but doesn't create the element_t objects. Therefore any dereference of e.g. s->tablica[s->rozmiar] will lead to undefined behavior.
There are two possible solutions:
Allocate a new element_t structure:
s->tablica[s->rozmiar] = malloc(sizeof(element_t));
before you initialize the element_t structure members.
Make tablica an array of structure objects instead of pointers:
element_t *tablica; // tablica is an array of objects, not an array of pointers
I recommend solution 2.
At least the function dodaj_do_stosu is wrong. The data member tablica is declared like
element_t **tablica;
So the expression s->tablica[s->rozmiar] has the type element_t * and an indeterminate value. Thus dereferencing the pointer expression for example like
s->tablica[s->rozmiar]->nazwa
invokes undefined behavior.
You have to allocate memory for objects of the structure type element_t not for pointers of the type element_t *.
So you need to declare the data member like
element_t *tablica;
and within the function to write
s->tablica = realloc(s->tablica, (s->rozmiar + 1) * sizeof( *s->tablica));
Also it is safer to use an intermediate pointer for calls of realloc.
The function can look the following way
int dodaj_do_stosu( stos_t *s, int n, char *name )
{
element_t *tmp = realloc( s->tablica, ( s->rozmiar + 1 ) * sizeof( *s->tablica ) );
int success = tmp != NULL;
if ( success )
{
s->tablica = tmp;
s->tablica[s->rozmiar]->nazwa = name;
s->tablica[s->rozmiar]->n = n;
printf("rozmiar=%d, n=%d , nazwa=%s\n", s->rozmiar, s->tablica[s->rozmiar]->n, s->tablica[s->rozmiar]->nazwa );
++s->rozmiar;
}
return success;
}
Consequently the function should be redefined at least the following way. As is it can for example invoke undefined behavior when s->rozmiar is equal to 0.
int usun_ze_stosu( stos_t *s )
{
int success = s->rozmiar != 0;
if ( success )
{
element_t *tmp = realloc( s->tablica, ( s->rozmiar - 1 ) * sizeof( *s->tablica ) );
success = tmp != NULL;
if ( success )
{
s->tablica = tmp;
--s->rozmiar;
}
}
return success;
}
Also within the function init it will be much better ro write
void init(stos_t *s)
{
s->rozmiar=0;
s->tablica = NULL;
}
Another problem is the function rm
void rm(stos_t s)
{
free(s.tablica);
}
You should pass the original object through a pointer to it and within the function to write
void rm(stos_t *s)
{
free( s->tablica );
s->tablica = NULL;
s->rozmiar = 0;
}
So, there is function qsort in stdlib.h and its prototype is as follows;
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
As you can see, it needs a function function pointer as its last argument.
compare function is defined as follows:
int compare(const void *a, const void *b){
return (*(int*)a - *(int*)b);
}
now when I call qsort, I only to declare function pointer like
int (*cmp)(const void*, const void*) = &compare;
and pass cmp as the last argument to the function, along with the other parameters. No need to specify the parameters of the function compare.
Here's is my main function
main(){
int (*cmp) (const void* , const void*);
cmp = &compare;
int iarray[] = {1,2,3,4,5,6,7,8,9};
qsort(iarray, sizeof(iarray)/sizeof(*iarray), sizeof(*iarray), cmp);
int c = 0;
while (c < sizeof(iarray)/sizeof(*iarray)){
printf("%d \t", iarray[c]);
c++;
}
}
I got this code online and just cant figure why I don't need to pass any parameters to the function pointer cmp.
That's because the parameters are passed within qsort function, the function that you passed the function pointer to. Like:
/*
* qsort.c
*
* This is actually combsort. It's an O(n log n) algorithm with
* simplicity/small code size being its main virtue.
*/
...
void qsort(void *base, size_t nmemb, size_t size,
int (*compar) (const void *, const void *))
{
size_t gap = nmemb;
size_t i, j;
char *p1, *p2;
int swapped;
if (!nmemb)
return;
do {
gap = newgap(gap);
swapped = 0;
for (i = 0, p1 = base; i < nmemb - gap; i++, p1 += size) {
j = i + gap;
if (compar(p1, p2 = (char *)base + j * size) > 0) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// here calls the compare function
//
memswap(p1, p2, size);
swapped = 1;
}
}
} while (gap > 1 || swapped);
}
Function pointers are usually passed without parameters, since most of time function pointers are used to accomplish callback (see https://en.wikipedia.org/wiki/Callback_(computer_programming)). If the parameters are passed together, it should be intended to implement lazy evaluation.
So I have an array, without any specified type:
void* buff = malloc(size*eltSize);
And I have a function, that has a void* parameter, and I want to assign it to the array, something like this:
void function(void* p1){
buff[i] = p1;
}
I know that this doesn't work, but say I want to make it as generic as possible, what's the best way to do? Remember, I have no idea about the types used (It should accept any type possible; even struct).
Thank you
You have to pass the element size (and the array index, for that matter) manually each time, similar to how qsort works. You'd have to change your function to something like:
void function(void * buff, void * p1, size_t elt_size, size_t index){
memcpy(((char *) buff) + index * elt_size, p1, elt_size);
}
and call it such as:
int array[] = {3, 1, 4, 1, 5, 9};
int n = 8;
function(array, &n, sizeof(n), 5); // Equivalent to array[5] = n;
A full working example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void function(void * buf, void * data, size_t elt_size, size_t index)
{
memcpy(((char *) buf) + index * elt_size, data, elt_size);
}
int main(void)
{
int narray[] = {3, 1, 4, 1, 5, 9};
int n = 8;
function(narray, &n, sizeof(n), 5); // Equivalent to array[5] = n
for ( size_t i = 0; i < sizeof(narray) / sizeof(narray[0]); ++i ) {
printf("Value of element [%zu] is: %d\n", i, narray[i]);
}
char * sarray[] = {"The", "mome", "raths", "outgrabe"};
char * p = "barked";
function(sarray, &p, sizeof(p), 3); // Equivalent to sarray[3] = p
for ( size_t i = 0; i < sizeof(sarray) / sizeof(sarray[0]); ++i ) {
printf("Value of element [%zu] is: %s\n", i, sarray[i]);
}
return 0;
}
with output:
Paul#Pauls-iMac:~/Documents/src/sandbox$ ./generic2
Value of element [0] is: 3
Value of element [1] is: 1
Value of element [2] is: 4
Value of element [3] is: 1
Value of element [4] is: 5
Value of element [5] is: 8
Value of element [0] is: The
Value of element [1] is: mome
Value of element [2] is: raths
Value of element [3] is: barked
Paul#Pauls-iMac:~/Documents/src/sandbox$
Obviously it will work just as well with arrays dynamically allocated with malloc() as it will with the regular arrays that this example uses.
You can eliminate the need to pass the element size every time if you create a struct to hold the data and the element size together, for instance:
struct generic_array {
void * data;
size_t elt_size;
}
When you pass a pointer to this struct to your function, it'll be able to access the element size itself, both eliminating the need for you to provide it, and eliminating a whole category of bugs arising from you inadvertently passing the wrong size. If you add a third member to store the number of elements you initially malloc()ed, then you can do bounds-checking, too.
Full working example of that approach:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct generic_array {
void * data;
size_t elt_size;
size_t size;
};
struct generic_array * generic_array_create(const size_t elt_size,
const size_t size)
{
struct generic_array * new_array = malloc(sizeof *new_array);
if ( !new_array ) {
perror("couldn't allocate memory for array");
exit(EXIT_FAILURE);
}
void * data = malloc(size * elt_size);
if ( !data ) {
perror("couldn't allocate memory for array data");
exit(EXIT_FAILURE);
}
new_array->data = data;
new_array->elt_size = elt_size;
new_array->size = size;
return new_array;
}
void generic_array_destroy(struct generic_array * array)
{
free(array->data);
free(array);
}
void generic_array_set(struct generic_array * array, void * elem,
const size_t index)
{
if ( index >= array->size ) {
fprintf(stderr, "Index %zu out of bounds of size %zu.\n",
index, array->size);
exit(EXIT_FAILURE);
}
memcpy(((char *)array->data) + index * array->elt_size,
elem, array->elt_size);
}
void generic_array_get(struct generic_array * array, void * elem,
const size_t index)
{
if ( index >= array->size ) {
fprintf(stderr, "Index %zu out of bounds of size %zu.\n",
index, array->size);
exit(EXIT_FAILURE);
}
memcpy(elem, ((char *)array->data) + index * array->elt_size,
array->elt_size);
}
int main(void)
{
int narray[] = {3, 1, 4, 1, 5, 9};
const size_t nsize = sizeof(narray) / sizeof(narray[0]);
struct generic_array * garray = generic_array_create(sizeof(int), nsize);
for ( size_t i = 0; i < nsize; ++i ) {
generic_array_set(garray, &narray[i], i);
}
for ( size_t i = 0; i < nsize; ++i ) {
int n;
generic_array_get(garray, &n, i);
printf("Value of element %zu: %d\n", i, n);
}
generic_array_destroy(garray);
return 0;
}
If you want to copy an object, and you don't know its type, only its size, you use memcpy:
void* buff = malloc(size*eltSize);
void function(void* p1) {
memcpy((char *)buff + i * eltSize, p1, eltSize);
}
Since you don't know the type, you can't use indexing directly, but rather have to manually calculate the address with pointer arithmetic.
Since you try to insert pointer to void type as element to buff, then buff must be of void** type.
int i = 0;
void* *buff = malloc(size * sizeof(void*));
if (buff == NULL)
// handle error
void function(void* p1) {
buff[i] = p1; // now OK
}
This is a potential solution if you want to remember the corresponding type of each data stored in your generic array. I used a fixed size array, and add basic type in the enum and just two exemple of how to get back your data in their respective type.
You could use function pointers if you have more type and don't want to use a lot the 'if' statements.
#include <stdio.h>
enum type {
INT,
FLOAT,
CHAR,
STRING
};
struct gen_array {
enum type elm_type;
void *data;
};
int to_int(void *data) {
return ((int) data);
}
char *to_string(void *data) {
return ((char *) data);
}
void printer(struct gen_array *arr, size_t size) {
for (size_t i = 0; i < size; i++) {
if (arr[i].elm_type == STRING)
printf("%s\n", to_string(arr[i].data));
if (arr[i].elm_type == INT)
printf("%d\n", to_int(arr[i].data));
}
}
int main(void) {
struct gen_array buff[2];
struct gen_array elm_0;
elm_0.elm_type = INT;
elm_0.data = (void*)10;
buff[0] = elm_0;
struct gen_array elm_1;
elm_1.elm_type = STRING;
elm_1.data = (void*)"helloWorld!";
buff[1] = elm_1;
printer(buff, 2);
return (0);
}
Here is the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int my_compare(const void * a, const void * b);
int main()
{
char s[][80] =
{ "gxydyv", "gdyvjv", "lfdtvr", "ayfdbk", "sqkpge", "axkoev", "wdjitd", "pyrefu", "mdafyu",
"zdgjjf", "awhlff", "dqupga", "qoprcn", "axjyfb", "hfrgjf", "dvhhhr" };
int i;
puts("#Before:#");
for (i = 0; i < 16; i++)
puts(s[i]);
qsort(s, 16, sizeof *s, my_compare);
putchar('\n');
puts("#After:#");
for (i = 0; i < 16; i++)
puts(s[i]);
return 0;
}
int my_compare(const void *a, const void *b)
{
return strcmp(*(char **)a, *(char **)b);
}
Here is the output:
#Before:#
gxydyv
gdyvjv
lfdtvr
ayfdbk
sqkpge
axkoev
wdjitd
pyrefu
mdafyu
zdgjjf
awhlff
dqupga
qoprcn
axjyfb
hfrgjf
dvhhhr
Segmentation fault
I also notice that the prototype of strcmp is:
int strcmp(const char *s1,const char *s2);
I suppose that the type of a and b in my_compare is "pointer to array-of-char". As a result, *(char **)a is a "pointer to char", which is exactly what strcmp expects.
So where is the problem?
Change:
return strcmp(*(char **) a, *(char **) b);
To:
return strcmp(a,b);
You had an extra level of pointer dereferencing that was incorrect and that's why you got the segfault. That is, you were passing the char values and not the char pointers [which got masked with the cast].
Note: no need to cast from void * here.
UPDATE:
In reponse to your question, yes, because of the way you defined s and the qsort call.
Your original my_compare would have been fine if you had done:
char *s[] = { ... };
And changed your qsort call to:
qsort(s, 16, sizeof(char *), my_compare);
To summarize, here are two ways to do it
int
main()
{
char s[][80] = { ... }
qsort(s, 16, 80, my_compare);
return 0;
}
int
my_compare(const void *a, const void *b)
{
return strcmp(a,b);
}
This is a bit cleaner [uses less space in array]:
int
main()
{
char *s[] = { ... }
qsort(s, 16, sizeof(char *), my_compare);
return 0;
}
int
my_compare(const void *a, const void *b)
{
return strcmp(*(char **) a,*(char **) b);
}
UPDATE #2:
To answer your second question: No
None of these even compile:
return strcmp((char ()[80])a,(char ()[80])b);
return strcmp(*(char ()[80])a,*(char ()[80])b);
return strcmp((char [][80])a,(char [][80])b);
return strcmp(*(char [][80])a,*(char [][80])b);
But, even if the did, they would be logically incorrect. The following does not compile either, but is logically closer to what qsort is passing:
return strcmp((char [80])a,(char [80])b);
But, when a function passes something defined as char x[80] it's just the same as char *x, so qsort is passing char * [disguised as void *].
A side note: Using char *s[] is far superior. It allows for arbitrary length strings. The other form char s[][80] will actually fail if a given string exceeds [or is exactly] 80 chars.
I think it's important for you to understand:
Arrays are call by reference.
The interchangeability of arrays and pointers.
The following two are equivalent:
char *
strary(char p[])
{
for (; *p != 0; ++p);
return p;
}
char *
strptr(char *p)
{
for (; *p != 0; ++p);
return p;
}
Consider the following [outer] definitions:
char x[] = { ... };
char *x = ...;
Either of these two may be passed to strary and/or strptr in any of the following forms [total of 20]:
strXXX(x);
strXXX(x + 0);
strXXX(&x[0]);
strXXX(x + 1);
strXXX(&x[1]);
Also, see my recent answer here: Issue implementing dynamic array of structures
You can just cast it to a const char *, it should work now:
int my_compare(const void *a, const void *b) {
return strcmp((const char *)a, (const char *)b);
}
And also you should add:
#include <stdlib.h>
Here is the compare function:
int compare(const void *a, const void *b) {
char* *s = (char* *) a;
char* *t = (char* *) b;
return sort_order * strcmp(*s, *t); // sort_order is -1 or 1
}
Now my question is what is the reasoning behind casting to a double pointer of a particular type? Or rather, why is the double pointer cast needed and how is it used internally?
Other variables used:
char **wordlist; int nbr_words; (array elements are) char *word;
Ex qsort call: qsort(wordlist, nbr_words, sizeof(char *), compare);
It would help if you showed the definition of wordlist, but most likely it's defined as a char **. The compare() function receives a pointer to each element of your list. If each element of your list is of type char *, then compare() is going to receive two pointers to char *, or two char ** in other words.
The conversion to char ** (note that an actual cast would be superfluous, in this particular case, if you weren't going from a const void pointer, to a non-const char **) itself is necessary because qsort() has to work on any kind of type, so the arguments get converted to void * before they are passed. You can't deference a void * so you have to convert them back to their original types before doing anything with them.
For instance:
#include <stdio.h>
int compare_int(void * a, void * b) {
int * pa = a;
int * pb = b;
if ( *pa < *pb ) {
return -1;
} else if ( *pa > *pb ) {
return 1;
} else {
return 0;
}
}
int compare_double(void * a, void * b) {
double * pa = a;
double * pb = b;
if ( *pa < *pb ) {
return -1;
} else if ( *pa > *pb ) {
return 1;
} else {
return 0;
}
}
int compare_any(void * a, void * b, int (*cfunc)(void *, void *)) {
return cfunc(a, b);
}
int main(void) {
int a = 1, b = 2;
if ( compare_any(&a, &b, compare_int) ) {
puts("a and b are not equal");
} else {
puts("a and b are equal");
}
double c = 3.0, d = 3.0;
if ( compare_any(&c, &d, compare_double) ) {
puts("c and d are not equal");
} else {
puts("c and d are equal");
}
return 0;
}
Outputs:
paul#local:~/src/c/scratch$ ./comp
a and b are not equal
c and d are equal
paul#local:~/src/c/scratch$
The compare_any() function will compare any type which is supported, in this case, int and double, because we can pass a function pointer to it. However, the signature of the passed function must be the same, so we can't declare compare_int() to take two int * arguments, and compare_double() to take two double *. We have to declare them both as taking two void * arguments, and when we do this, we have to convert those void * arguments to something useful within those functions before we can work with them.
What's happening in your case is exactly the same, but the data themselves are pointers, so we're passing pointers to pointers, and so we need to convert void * to, in your case, char **.
EDIT: To explain some confusion in the comments to the original question about how qsort() works, here's the qsort() signature:
void qsort(void *base, size_t nmemb, size_t size,
int(*compar)(const void*, const void*))
base is a pointer to the first element of an array, nmemb is the number of members of that array, and size is the size of each element.
When qsort() calls compar on, say, the first and second elements of your array, it'll send the address of the first element (i.e. base itself) and the address of the element (i.e. base + size).
If base was originally declared as an array of int, then the compare function must interpret those pointers it receives as pointers to int, as int *. If base was originally declared as an array of strings, as a char **, then the compare function must interpret those pointers as pointers to char *, i.e. as char **.
In all cases, the compare function gets pointers to elements. If you have an int array, then you must interpret those pointers as int * in your compare function. If you have a char * array, then you must interpret them as char **, and so on.
In this case, you obviously could call strcmp() if you just passed plain char * arguments to the compare function. But, because qsort() is generic it can only pass pointers to the compare function, it can't actually pass the value of your elements - it's the use of void * which allows it to be generic, because any type of object pointer can be converted to void *, but there is no equivalent datatype to which any non-pointer value can be converted. For that reason, it has to work the same way with regular types like int and double, with pointers, and with structs, and the only way to get it to work correctly with all possible types is to have it deal with pointers to elements, not with the elements themselves, even when the elements are themselves also pointers. For this reason, it might seem like you're getting an unnecessary level of indirection, here, but it actually is necessary in order for qsort() to be able to function in the generic way that it does.
You can see this more clearly if I modify the code above so that compare_any() is more similar to qsort(), and takes not two pointers, but a single pointer to a two-element array of various types (slightly contrived example, but we're keeping it simple):
#include <stdio.h>
#include <string.h>
int compare_int(void * a, void * b) {
int * pa = a;
int * pb = b;
if ( *pa < *pb ) {
return -1;
} else if ( *pa > *pb ) {
return 1;
} else {
return 0;
}
}
int compare_double(void * a, void * b) {
double * pa = a;
double * pb = b;
if ( *pa < *pb ) {
return -1;
} else if ( *pa > *pb ) {
return 1;
} else {
return 0;
}
}
int compare_string(void * a, void * b) {
char ** pa = a;
char ** pb = b;
return strcmp(*pa, *pb);
}
int compare_any(void * arr, size_t size, int (*cfunc)(void *, void *)) {
char * first = arr;
char * second = first + size;
return cfunc(first, second);
}
int main(void) {
int n[2] = {1, 2};
if ( compare_any(n, sizeof(*n), compare_int) ) {
puts("a and b are not equal");
} else {
puts("a and b are equal");
}
double d[2] = {3.0, 3.0};
if ( compare_any(d, sizeof(*d), compare_double) ) {
puts("c and d are not equal");
} else {
puts("c and d are equal");
}
char * s[] = {"abcd", "bcde"};
if ( compare_any(s, sizeof(*s), compare_string) ) {
puts("'abcd' and 'bcde' are not equal");
} else {
puts("'abcd' and 'bcde' are equal");
}
return 0;
}
Outputs:
paul#local:~/src/c/scratch$ ./comp
a and b are not equal
c and d are equal
'abcd' and 'bcde' are not equal
paul#local:~/src/c/scratch$
As you can see, there's no way compare_any() could accept both an array of int, and an array of char *, without the compare_string() function getting a pointer it needs to treat as a char **, because of the pointer arithmetic it performs on the array elements. Without that additional level of indirection, neither compare_int() nor compare_double() could function.