Using and dereferencing (void**)
Asked Answered
D

3

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.

Dairymaid answered 23/9, 2013 at 4:34 Comment(8)
See this C FAQGenisia
WHy on earth would you want to use a void* you don't have to?Ejective
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.Dairymaid
Why don't templates work? Oh, nevermind, I see you didn't tag this as C++.Ejective
Mostly because I've never used c++ before, there's no reason I couldn't, if that would make the job significantly easier.Dairymaid
Read also "Swapping 2 string pointers with a function whose parameters are void **" very interesting answer written by H2CO3Carmencita
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?"Carmencita
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.Prosecute
C
8

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);
Carmencita answered 23/9, 2013 at 4:41 Comment(0)
G
3

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

Reference: C FAQ Question 4.9

Gallagher answered 23/9, 2013 at 4:40 Comment(0)
L
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.

Loverly answered 23/9, 2013 at 4:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.