While there is no need to cast a pointer from type to void or void to type (with some limited exceptions not relevant here), the cast becomes of critical importance when you need to derefernce a void pointer. As you know a pointer stores as its value the address of something else. When dealing with pointers of the same type as the value, there are no need for any additional casts as the type has already been fixed. e.g.
int a[] = { 1, 3, 5, 7, 9 };
int *ap = a;
ap++;
printif (" ap points to : %d\n", *ap);
All good, no mystery. Now lets take a function that will iterate over the values in the array taking the array and number of elements as its parameters. But there is still another parameter needed to be able to iterate over the array passed as void *. What is it? The sizeof each element that make sup a.
Otherwise, since the array was passed as a void pointer, what would happen to pointer arithmetic? In the function, what would happen if we attempted ap++?
Therein lies the issue. When handling pointers as void pointers, they are very convenient to use, but when the values they hold must be manipulated, they must be cast so that the compiler knows size of the object pointed to, so it can know how many bytes exist between ap and the next value in your array located at ap++.
There is another caveat in type casting pointers. They must be cast in such a way that rules of strict aliasing are not violated and that type punning does not occur. See C11 N1570 Committee Draft — April 12, 2011 For that your will want to take a look at sections 6.5 (6) and (7) paying close attention to the notes included on the draft as well. Without going into the loopholes and qualifications, you can safely cast a void pointer to its effective type or to char. Any other combination is asking for an aliasing violation resulting in a type punned pointer.
So continuing with our example, if we were to pass our array a to a function as void * instead of int *, what would be required in our function to allow pointer arithmetic to work properly? We know from rule 6.5 (6) that we can cast to char and not run afoul of the strict-aliasing rules, so we may be able to handle it like this, where a is our array, n the number of elements, and sz the size of each element in the array:
void prnarray (void *a, size_t n, size_t sz)
{
char *ap = a;
printf ("\npointers printed from prnarray:\n\n");
while (n--) {
printf (" ap points to : %d\n", *ap);
ap += sz;
}
}
We can then step through the array without problem while adding the appropriate size/offset from each prior element.
What if we know we will be taking an array of int through the void pointer? That allows a cast directly to the effective type of the array and we no longer need to worry about passing the size. Pointer arithmetic will take care of the offset to/from each value within the array, e.g.
void prnarrayint (void *a, size_t n)
{
int *ap = a;
printf ("\npointers printed from prnarrayint:\n\n");
while (n--)
printf (" ap points to : %d\n", *ap++);
}
While not as flexible as the previous function, where we were not limited to the type, there are many circumstances where you will know the type of pointer you are getting passed as void, and in those instances, casting to the effective type will simplify your code.
Now putting all the pieces together you could create a short illustrative example something as follows:
#include <stdio.h>
void prnarray (void *a, size_t n, size_t sz)
{
char *ap = a;
printf ("\npointers printed from prnarray:\n\n");
while (n--) {
printf (" ap points to : %d\n", *ap);
ap += sz;
}
}
void prnarrayint (void *a, size_t n)
{
int *ap = a;
printf ("\npointers printed from prnarrayint:\n\n");
while (n--)
printf (" ap points to : %d\n", *ap++);
}
int main (void) {
int a[] = { 1, 3, 5, 7, 9 };
int *ap = a;
size_t i, n = sizeof a/ sizeof *a;
i = n;
printf ("\npointers printed in main:\n\n");
while (i--)
printf (" ap points to : %d\n", *ap++);
prnarray (a, n, sizeof *a); /* pass/print based on sz with cast to char */
prnarrayint (a, n); /* pass/print based on effective type of array */
return 0;
}
Use/Output
$ ./bin/voidpcast
pointers printed in main:
ap points to : 1
ap points to : 3
ap points to : 5
ap points to : 7
ap points to : 9
pointers printed from prnarray:
ap points to : 1
ap points to : 3
ap points to : 5
ap points to : 7
ap points to : 9
pointers printed from prnarrayint:
ap points to : 1
ap points to : 3
ap points to : 5
ap points to : 7
ap points to : 9
So in summary, the universal pointer void can be assigned to/from any type pointer and back without the need of a cast, however when any operation requires the pointer to be dereferenced to obtain the value pointed to, then an appropriate cast must be made to either its effective type or char in order to comply with the rules of strict-aliasing.
uint16_t?void*from/to other data pointer types.uint16_t * a = p_evt_type->p_instance->p_instance_handler;should work fine.