Void pointers are commonly used in libraries for "user data" in callback prototypes or as members of structs. For example, pthreads are created using a prototype like this:
Code:
void *mythread (void *data);
This is submitted to a library function, pthread_create() via a function pointer. One of the other arguments to pthread_create is a void*. The library creates a thread and calls the user function (which must have the prototype above) and passes it the void*. This means I, as the user of the library, can pass anything I want into a new thread:
Code:
struct thread_data {
int n;
char stuff[256];
};
void *mythread (void *p) {
struct thread_data *data = p; // need to do this so we can access members of the struct
do whatever with data->stuff, etc.
}
struct thread_data eg;
eg.n = 666;
strcpy(eg.stuff, "hello world");
pthread_create(mythread, (void*)&eg); // (simplified) library call
This way, I do not have to write a custom threading library for every task. So void pointers are actually extreme useful, more or less essential tools in C.