Search code examples
cpointersvoid-pointerspointer-to-array

Using and dereferencing (void**)


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.


Solution

  • 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);