To share this page click on the buttons below;
Pointer to void
This is the time to resume the discussion about void type: this type comes with two different meanings. When it is used as argument of a function means that the function takes no argument and when it is returned by a function it means that the function is not returning anything. But when we talk about void pointers, that is a completely different history: you cannot in fact declare a variable as void, but you can however declare and use a pointer to void.
It is frequent in fact to have functions which have a pointer to void argument and functions that returns pointer to void. Declaring a pointer to void is perfectly valid and does not mean a pointer to anything, but it means a pointer to whatever type of pointer. Returning a pointer to void is again valid and means that the returned pointer is "generic" and need to be further "interpreted" (i.e. need to be casted to a particular kind of pointer).
The possibility to have the notion of a "generic" pointer is due to the fixed dimension of each pointer variable: a pointer is always a variable containing an address and an address has always the same size once you fix the processor. When you use a particular kind of pointer you are actually superimposing the "structure" of the chosen type over the memory, you are asking the compiler to interpret that portion of memory as a particular type, but the address has always the same dimension. It is from this consideration that the concept of "generic" pointer draws origin.
The pointer "to a generic type" gives us an additional degree of freedom while we are writing functions: we can indeed write functions able to operate on whatever kind of pointer by declaring one of the argument as a pointer void and we can make function returning whatever type of pointer we want. That might be quite convenient.
Pointer arithmetic and void
That should be quite obvious: pointer arithmetic does not work with a void pointer. The reason is simple: the pointer arithmetic (the ability to increment or decrement a pointer to get the next "element" in a array) is based on the dimension of the type, but a pointer to void can be anything type and so the compiler does not know the amount of space it has to move the address to get the next element.
It is forbidden to increment or decrement a pointer to void, but you can use pointer arithmetic if you cast the void pointer to a proper well defined "type" of pointer.
Functions with pointer to void argument
The reason to have a function with a pointer to void as argument is the possibility to have "generic" functions able to operate on different "type" (or, to be more precise, on different type of pointers).
The most straightforward example is the copy operation: suppose in fact that you want to perform a function able to copy different variables, without the concept of generic pointer (i.e. pointer to void) you will be forced to write a copy functions for each different type. This will include also some "user defined" types like structures.
It turns out that a function able to perform a generic copy is already present is the standard C library: that is memcpy.
The signature of memcpy (the declaration is in the string.h header file) is the following:
void *memcpy(void *destination, const void *source, size_t num);
The use is pretty simple, you put a pointer to the source of the data you want to copy as source argument, the function will then copy size bytes to the destination pointer. The function returns the destination pointer.
There are some particularities we are not yet aware of:
- the source void pointer has the modifier const, this means that the function is promising it will not make any modification to the source pointer (that argument will be covered in a following chapter)
- the size argument is a type of size_t, for the moment we can think that this type is equivalent to int (actually it is an integer, also this topic will be the argument of another chapter).
The following example shows a simple usage of memcpy:
#include <stdio.h>
#include <string.h>
/* a phone contact structure example */
struct tagPhoneContact {
char name[32];
char first_name[32];
char phone[16];
char e_mail[64];
int age;
};
void main(void) {
struct tagPhoneContact contact1 = {"John"", "Smith", "555-123456","j.smith@mail.com", 32};
struct tagPhoneContact contact2;
/* contact 1 is copied to contact 2 */
memcpy(&contact2, &contact1, sizeof(struct tagPhoneContact));
printf("\nThe phone of %s %s is %s (age %i) and its e-mail is (%s)\n",
contact2.name,
contact2.first_name,
contact2.phone,
contact2.age,
contact2.e_mail);
}
The example is pretty simple so that I do not think any further explanation is necessary: just note as memcpy is generic and can be used to copy (almost) any kind of type. The reason to specify "almost" is that this kind of copy might not be suitable for large data that contains pointers to other variables. The memcpy in fact will perform a copy of the pointers but not of the other variables pointed by these pointers and that might be not what you want to happen.
Function returning pointer to void
Functions that return pointer to void are also common: that gives the possibility of having functions returning "anything".
The most straightforward example of that kind of functions are the functions used to dynamically allocate variables. Since this topic will be deeply analyzed in the next chapter I forward you to that for further details.
Use with caution
Pointer to void might seems fairly simple, but they are both more powerful and more dangerous than might appear at a first glance, however I will not talk here specifically about any of the advanced uses of this kind pointers. What I would like to shortly point out is that the powerfulness of the pointer to void resides on the ability to trait different types in the same way. However behind this ability there is also the problem: using a generic type means to lose the type control, that might lead to problems and bugs hard to discover.
To share this page click on the buttons below;