Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 929
crockeea Avatar asked Sep 23 '13 04:09

crockeea


People also ask

Can you dereference a void pointer?

A void pointer cannot be dereferenced. We get a compilation error if we try to dereference a void pointer. This is because a void pointer has no data type associated with it. There is no way the compiler can know what type of data is pointed to by the void pointer.

What is a void pointer can you dereference a void pointer without knowing its type?

You cannot. Dereferencing a void pointer requires an explicit cast beforehand. You can ofcourse cast it to any particular type and then dereference it without knowing its original type, but why you would want to do that is beyond me.

Why is pointer arithmetic not applicable on void?

Since void is an incomplete type, it is not an object type. Therefore it is not a valid operand to an addition operation. Therefore you cannot perform pointer arithmetic on a void pointer.


2 Answers

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);
like image 108
Grijesh Chauhan Avatar answered Oct 14 '22 00:10

Grijesh Chauhan


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

Reference: C FAQ Question 4.9

like image 2
Yu Hao Avatar answered Oct 14 '22 00:10

Yu Hao