One of the frequent questions I come across every now and then is, when do I use a void pointer? or what are some practical examples of void pointer?

Pointers are used to store address. People generally use pointer to “point-to-something”. It can point to standard variables, structs, functions. Let’s start with some general pointers that points to standard variable types:

int  *IntPointer;
char *CharPointer;

IntPointer can point to a integer type variable, CharPointer can point to a char type variable.

int IntegerNumber = 3;
IntPointer = &IntegerNumber;
char Character = 'A';
CharPointer = &Character;

But you can’t do:

IntPointer = &Character;      // Not valid
CharPointer = &IntegerNumber; // Not valid

Now, void pointer can “point-to-anything” i.e. you can do:

void *VoidPointer;
VoidPointer = &Character;     // Valid
VoidPointer = &IntegerNumber; // Valid

Ok, this looks fancy, I get it. But what’s the application? A real world example?

A very simple example


Let’s think of a very simple example. Assume we have a function called animal_sound(args), whose job is to generate animal sound based on it’s argument. Now, let’s say, we have a “dog” variable of type sDog_t and a “cat” variable of type sCat_t (sDog_t and sCat_t are typedefs). How do we make animal_sound() function to take both types of variables and not complain? void pointer to the rescue.

1
2
3
4
sDog_t dog;
sCat_t cat;

void animal_sound(void *animal);


Implementation

The implementation can be tricky. See, you cannot deference a void pointer. Because, compiler doesn’t know what void pointer is pointing to. First, you will need to cast that pointer with proper variable type. For example, if you want animal_sound() to generate only dog sound you would do:

1
2
3
4
5
void animal_sound(void *animal)
{
    sDog_t animalDog = (sDog_t *)animal;
    printf("%s\r\n", animalDog->sound); 
}

So, the million dollar question is, how will animal_sound() know the type of argument and properly cast it? There aren’t any other ways, unless you tell the function somehow. There are different ways to solve this problem.

sDog_t and sCat_t are struct. They can have different sets of variables. But at the beginning of the list of variables we can have a common variable (e.g. Animal Type) for all the animal typedefs. Let’s declare an enum type variable called AnimalType.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef enum eAnimalType
{
    CAT,
    DOG
}eAnimalType_t;

typedef struct sDog
{
    eAnimalType_t animalType;
    char *sound;
    ... ... ...
    int randomInteger;
    float randomFloat;
}sDog_t;

typedef struct sCat
{
    eAnimalType_t animalType;
    char *sound;
    ... ... ...
    int randomInteger;
    int anotherRandomInteger;
}sCat_t;

The first variable of both structs is eAnimalType_t. So whatever Animal type we pass to the animal_sound() we will know that the first variable will be eAnimalType_t. Now, let’s create instances of sDog_t and sCat_t:

1
2
sDog_t dog = {.animalType = DOG, .sound = "bark!"};
sCat_t cat = {.animalType = CAT, .sound = "meow!"};

And this is how you should implement animal_sound():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void animal_sound (void *animal)
{
    // By casting animal with eAnimalType_t we are 
    // just addressing the first variable - animalType
    switch (*((eAnimalType_t *)animal))
    {
        case CAT:
        {
            sCat_t *catType = (sCat_t *)animal;
	    printf ("%s\r\n", catType->sound);
	    break;
        }
        case DOG:
        {
            sDog_t *dogType = (sDog_t *)animal;
	    printf ("%s\r\n", dogType->sound);
	    break;
        }
        default:
        {
            printf("Here");
            break;
        }
    }
}

First we de-referenced animal by type casting with eAnimalType_t. Once we know the type of the variable animal points to, we properly de-referenced it again inside the switch-case. Now you can simply call animal_sound(&dog) or animal_sound(&cat). The full code can be found here.

REAL world example


Example 1:
If you ever used malloc() you are already using void pointers. malloc() returns a void pointer. You always put return value of malloc() with a type casting. For example:

1
2
3
int *arr;
arr = (int *)malloc(5*sizeof(int));
char *foo = (char *)malloc(sizeof(char));

Example 2:
C library’s qsort() is a good example of generic function. The routine will sort an array of any type when you provide the base pointer, number of elements, the size of the type, and a comparison function which takes pointers to two of the data types.

void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));

Here is a link if you want to check it out.

Example 3:
You can make a generic container for a linked-list by using a void pointer to point to a data:

struct list 
{ 	
    struct list *next; 	
    struct list *prev; 	
    void *data;
};