8

I would like to pass a "polymorphic" array of pointers to a function.

I can do the following without warnings:

foo (void* ptr);

bar()
{
  int* x;
  ...
  foo(x);
}

gcc apparently automatically casts x to a (void*), which is just dandy.

However, I get a warning when I do the following:

foo (void** ptr);

bar()
{
  int** x; // an array of pointers to int arrays
  ...
  foo(x);
}

note: expected ‘void **’ but argument is of type ‘int **’
warning: passing argument 1 of ‘foo’ from incompatible pointer type [enabled by default]

My question is: why is passing an (int*) as a (void*) argument not 'incompatible', but (int**) as a (void**) argument is?

Since all pointer types are the same size (right? it's been a while since I've used C), I can still do something like:

void mainFunc1(int** arr, int len)
{
    //goal is to apply baz to every int array
    foo(baz, arr, len);
}

void mainFunc2(double** arr, int len)
{
    //goal is to apply baz to every int array
    foo(qux, arr, len);
}

// I PROMISE that if I pass in a (int**) as ptr, then funcPtr will interpret its (void*) argument as an (int*)
void foo(funcPtr f, void** ptr, int len)
{
    for(int i = 0; i < len; i++)
    {
        f(ptr[i]);
    }
}

void baz(void* x) 
{
  int* y = (int*)x;
  ...
}

void qux(void* x)
{
  double* y = (double*)x;
  ...
}

The purpose for all the void pointers is so that I can use a function pointer applied to functions that will (down the stack) have different types of ptr arguments: some will take int arrays, some will take double arrays, etc.

crockeea
  • 21,651
  • 10
  • 48
  • 101
  • 3
    See this [C FAQ](http://c-faq.com/ptrs/genericpp.html) – cnicutar Sep 23 '13 at 04:42
  • WHy on earth would you want to use a void* you don't have to? – C.J. Sep 23 '13 at 04:52
  • I have a generic function (foo, above) that I'm trying to avoid duplicating, as it is somewhat more complicated than foo. It should take a list of lists and a function pointer that will be applied to each of the lists. mainFunc is just one example. – crockeea Sep 23 '13 at 04:57
  • Why don't templates work? Oh, nevermind, I see you didn't tag this as C++. – C.J. Sep 23 '13 at 05:00
  • Mostly because I've never used c++ before, there's no reason I couldn't, if that would make the job significantly easier. – crockeea Sep 23 '13 at 05:04
  • Read also ["Swapping 2 string pointers with a function whose parameters are `void **`"](http://stackoverflow.com/questions/18340892/swapping-2-string-pointers-with-a-function-whose-parameters-are-void) very interesting answer written by [H2CO3](http://stackoverflow.com/users/529758/h2co3) – Grijesh Chauhan Sep 23 '13 at 05:16
  • *`all pointer types are the same size (right? it's been a while since I've used C),`*, not always, There have been architectures where pointers to different types have different sizes: Read ["Are there are any platforms where pointers to different types have different sizes?"](http://stackoverflow.com/questions/916051/are-there-are-any-platforms-where-pointers-to-different-types-have-different-siz/1539196#1539196) – Grijesh Chauhan Sep 23 '13 at 05:20
  • Also, as has been explained many times on SO, even if all pointer variables were the same size, that would be a red herring. The most fundamental reason why you have different types for, say, `int *`, `double *` is that the pointer cannot be correctly dereferenced without that type information. Same goes for pointers with multiple levels of indirection. If `void **` could take anything, then you could assign a `long double *` to it and get really weird results when you tried to dereference it. – Crowman Oct 02 '13 at 15:37

3 Answers3

7

Note: void* is generic. but void** is not. You can assign address of any type to void* variable but void** can be assigned address of void* variable only.

void* generic;
int i;
int *ptri = &i;

generic = ptri;

or

char c;
int *ptrc = &c;

generic = ptrc;

valid but following is an error:

void**  not_generic;
int i;
int *ptri = &i;
int **ptr_to_ptr1 = &ptri;
void**  not_generic = ptr_to_ptr1;

Error: assigning int** to void**.

Yes you can do like:

void**  not_generic;
not_generic = &generic;

For generic array function simply use void* a as follows:

enum {INT, CHAR, FLOAT};
void print_array(void* a, int length, int type){
   int i = 0;
   for(i = 0; i < length; i++){
      switch(type){
         case INT: 
              printf("%d", *((int*)a + i));
              break;
         case CHAR: 
              printf("%c", *((char*)a + i));
              break;
         case FLOAT: 
              printf("%f", *((float*)a + i));
              break;
      }
   }
}

You better write this function using macros.

Call this function as:

Suppose int:

 int a[] = {1, 2, 3, 4}; 
 print_array(a, sizeof(a)/sizeof(a[0]), INT);

Suppose char:

 char a[] = {'1', '2', '3', '4'}; 
 print_array(a, sizeof(a)/sizeof(a[0]), CHAR);
Conor O'Brien
  • 987
  • 2
  • 16
  • 40
Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
2

Because there is no generic pointer-to-pointer type in C.

Reference: C FAQ Question 4.9

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
0

The short answer is: anything that resembles

<datatype>* <variable_name>

can be passed in where the parameter declaration is of type void* because void* is generic. However, void** is not. So automatic casting fails.

Hrishi
  • 7,110
  • 5
  • 27
  • 26