Reputation: 102
I often see void pointers being cast back and forth with other types of pointers and wonder why, I see that malloc() returns a void pointer that needs to be cast before used also.
I am very new to C and come from Python so pointers are a very new concept to wrap my head around.
Is the purpose of the void * pointer to offer some "dynamic" typing?
Take this code as example, is it valid code?
struct GenericStruct{
void *ptr;
};
// use void pointer to store an arbitrary type?
void store(struct GenericStruct *strct, int *myarr){
strct->ptr = (void *) myarr;
}
// use void pointer to load an arbitrary type as int*?
int *load(struct GenericStruct *strct){
return (int *) strct->ptr;
}
Upvotes: 2
Views: 1971
Reputation: 180201
Is the purpose of the
void *
pointer to offer some "dynamic" typing?
No, because C has no dynamic typing. But your title is about right, on the other hand -- void *
serves roughly as a generic pointer type, in the sense that a pointer of that type can point to an object of any type. But there is no dynamism as that term is usually applied in this sort of context, because values of type void *
do not contain any information about the type of object to which they point.
The use, then, is for pointers where the type of the object pointed to does not matter (for a given purpose). In some cases the size of the pointed-to object does matter, however, so that is sometimes provided alongside such pointers. The standard qsort()
function is a canonical example. In other cases, such as generic linked lists, it is up to the user to know, somehow, what type of object is pointed to.
As @Vlad already observed, however, casting between void *
and other object pointer types is not required in C. Such conversions are performed automatically upon assignment, and in other contexts that use the same rules as assignment.
Upvotes: 1
Reputation: 123468
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.
char
.
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.
Upvotes: 2
Reputation: 310980
C suffers from the absence of function overloading. So most C "generic" functions as for example qsort
or bsearch
use pointers to void *
that to be able to deal with objects of different types.
In C you need not to cast a pointer of any type to a pointer of the type void *
. And a pointer of any type can be assigned with a pointer of the type void *
without casting.
So in C the functions from your code snippet can be rewritten like
void store(struct GenericStruct *strct, int *myarr){
strct->ptr = myarr;
}
int *load(struct GenericStruct *strct){
return strct->ptr;
}
Upvotes: 2