Void ** a Generic Pointer

generic programming in C with void pointer

The solution is not to use void* unless you really, really have to. The places where a void pointer is actually required are very small: parameters to thread functions, and a handful of others places where you need to pass implementation-specific data through a generic function. In every case, the code that accepts the void* parameter should only accept one data type passed via the void pointer, and the type should be documented in comments and slavishly obeyed by all callers.

Void ** a generic pointer?

No. void** is a pointer to void*, and nothing else. Only void* acts like a generic pointer.

Note that actually trying it will probably yield consistent results, but only the above is mandated by the Standard, anything else is Undefined Behaviour and may crash without mercy.

Are void * pointers meant for generic typing in C?

The purpose of a void * is to provide a welcome exception to some of C's typing rules. With the exception of void *, you cannot assign a pointer value of one type to an object of a different pointer type without a cast - for example, you cannot write

int p = 10;
double *q = &p; // BZZT - cannot assign an int * value to a double *

When assigning to pointers of different types, you have to explicitly cast to the target type:

int p = 10;
double *q = (double *) &p; // convert the pointer to p to the right type before assigning to q

except for a void *:

int p = 10;
void *q = &p; // no cast required here.

In the old days of K&R C, char * was used as a "generic" pointer type1 - the memory allocation functions malloc/calloc/realloc all returned char *, the callback functions for qsort and bsearch took char * arguments, etc., but because you couldn't directly assign different pointer types, you had to add an explicit cast (if the target wasn't a char *, anyway):

int *mem = (int *) malloc( N * sizeof *mem );

Using explicit casts everywhere was a bit painful.

The 1989/1990 standard (C89/C90) introduced the void data type - it's a data type that cannot store any values. An expression of type void is evaluated only for its side effects (if any)2. A special rule was created for the void * type such that a value of that type can be assigned to/from any other pointer type without need of an explicit cast, which made it the new "generic" pointer type. malloc/calloc/realloc were all changed to return void *, qsort and bsearch callbacks now take void * arguments instead of char *, and now things are a bit cleaner:

int *mem = malloc( sizeof *mem * N );

You cannot dereference a void * - in our example above, where q has type void *, we cannot get at the value of p without a cast:

printf( "p = %d\n", *(int *)q );

Note that C++ is different in this regard - C++ does not treat void * specially, and requires an explicit cast to assign to different pointer types. That's because C++ provides overloading mechanisms that C doesn't.


  1. Every object type should be mappable to an array of char.

  2. In K&R C, all functions had to return a value - if you didn't explicitly type the function, the compiler assumed it returned int. This made it difficult to determine which functions were actually meant to return a value vs. functions that only had side effects. The void type was handy for typing functions that weren't meant to return a value.

Confused about the pointers and generic(void) pointers in C

"void*" means "this value is a pointer to something, but I'm not telling you what it points to". Start with this statement, and everything makes sense.

So it should be clear that you can't dereference a void*, because nobody told you what it points to! If you don't know whether it points to an int, a char, or some struct, what do you think should happen if you dereference it?

In C, you can assign a void* to any pointer, for example to an int*. The compiler doesn't know what the void* points to, but when you write r = p; the compiler says "I hope you know what you are doing, I trust you. " (A C++ compiler in the same situation doesn't trust you). If the void* p did indeed point to an int, everything is fine. Otherwise, things are more or less bad.

The ?? one is wrong. You can't dereference *p. Doesn't matter what you do afterwards, *p isn't allowed. And no pointer is cast to an integer. There is an attempt to dereference a pointer, which isn't allowed. The result of the dereference couldn't be anything useful since you don't know what p points to, so you have nothing useful to cast to an int.

Now what happens in * (int *) p: p is a void * - a pointer to something, but you don't know what it points to. (int * )p is a cast: p is converted to an int*. That means the compiler will believe that (int*)p points to an int - that may or may not be true. * (int * ) p dereferences the int*: So in total, you convinced the compiler that p points to an int, and to read the int that p hopefully points to. If p did actually point to an int, it's fine. If p didn't point to an int, you're in trouble.

Why is a double-void pointer required here? Dynamic generic array

But to be honest, I don't fully understand, why I need a double void pointer here.

Some background first - maybe you already know that:

A pointer of the type someType * is a pointer to some variable of the type someType or to an array of variables of the type someType.

A pointer of the type someType ** is a pointer to a variable of the type someType * - this means: A pointer to a pointer to a variable of the type someType.

A pointer of the type void * is a pointer to anything; because the compiler does not know to what kind of element this pointer points to, it is not possible to access such an element directly.

In contrast to this, it is known what variable a pointer of the type void ** points to: It points to a variable of the type void *.

Why you need void** in this position:

The key are the lines:

vec->data[vec->length] = data;
...
return vec->data[index];

In these lines, the code accesses the data vec->data points to. For this reason, vec->data cannot be void * but it must be xxx * while xxx is the type of data the pointer vec->data points to. And because vec->data points to a pointer of the type void *, xxx is void * so xxx * is void **.

vec->data = data;

Your observation is correct: vec->data is of the type void ** and data is of the type void *.

The reason is that malloc() returns some memory and the compiler does not know which kind of data is stored in this memory. So the value returned by malloc() is void * and not void **.

In the automotive industry, you would use an explicit pointer cast like this:

vec->data = (void **)data;

The expression (xxx *)y tells the compiler that the pointer y points to some data of the type xxx. So (void **) tells the compiler that the pointer points to an element of the type void *.

However, in desktop applications you often don't write the (void **).

Storing generic data in the form of void pointer in C

You are returning the node, not the data stored in your node:

void* graph_getnode_data(graph_t *graph,int id){
struct Node *node = getnode(graph,id);
if(node != NULL){
return node->data; // <---- This should fix the bug.
}
return NULL;
}

Generic copy using void pointer

argv is a pointer array. If you just want to copy the pointers you can do it like that:

 base = calloc( argc, sizeof(char *) );
copy( argv, base, argc * sizeof(char *) );

Now you have copy of the pointer array argv, but that still contains pointers to the original arguments argv[i].
If you want to create copies of argv[i] too, dont use copy() but:

 char **base = calloc( argc, sizeof(char *) );
int i;

for( i=0; i<argc; i++ )
base[i] = strdup( argv[i] );

But remember: argv[0] is the program's name and I would bet you don't want that to be part of the array. To avoid it:

 base = calloc( argc-1, sizeof(char *) );
copy( argv+1, base, (argc-1) * sizeof(char *) );

or

 char **base = calloc( argc, sizeof(char *) );
int i;

for( i=1; i<argc; i++ )
base[i-1] = strdup( argv[i] );

Why do I need to avoid casting a function pointer to a generic pointer in this case?

There's no problem with your code, and there's also no problem with that passage in the book. The "problem" is that the passage doesn't apply to your code.

the book I'm reading mentioned that casting between "void *" and a "function pointer" is illegal.

This is true. See this question/answer.

But in your code you don't have a void*. foo.function is a void(*)() (a pointer to a function taking any number of arguments and returning void), which is the same type as func_pointer. So there's no need to do any sort of casting. foo.function = method; is correct.

Edit

From the clarifications you've added to your question, it seems like the book is saying something along the lines of: "With a void* we'd be able to avoid a lot of ugly casts between types, but since ANSI C doesn't allow void* to hold function pointers we can't avoid these casts."

Simplifying one of the examples from the book:

struct Class {
void * (*ctor)(const void * self, va_list * arg_ptr);
};
struct Class self;

typedef void (*voidf)();
voidf method = ...;

*(voidf*)&self.ctor = method;

That cast is necessary because self.ctor is a void* (*)(const void*, va_list*), not a void(*)().

If you could use a void* here, you could instead do:

void *method = ...;
self.ctor = method;

Without doing any casts.



Related Topics



Leave a reply



Submit