It is permissible to assign the result of malloc
to a void *
object and then later assign it to an int **
object. This is because the return value of malloc
has type void *
anyway, and it is guaranteed to be suitable for assignment a pointer to any type of object with a fundamental alignment requirement.
However, this code:
#define NUM_ELEMENTS 1000
void *bar = malloc(sizeof(void *) * NUM_ELEMENTS);
int **fubar = bar;
*fubar = 0;
is not guaranteed by the C standard to work; it may have undefined behavior. The reason for this is not obvious. The C standard does not require different types of pointers to have the same size. A C implementation may set the size of an int *
to one million bytes and the size of a void *
to four bytes. In this case, the space allocated for 1000 void *
would not be enough to hold one int *
, so the assignment to *fubar
has undefined behavior. Generally, one would implement C in such a way only to prove a point. However, similar errors are possible on a smaller scale: There are C implementations in which pointers of different types have different sizes.
A pointer to an object type may be converted to a pointer to another object type provided the pointer has alignment suitable for the destination type. If it does, then converting it back yields a pointer with the original value. Thus, you may convert pointers to void *
to pointers to void
and back, and you may convert pointers to void *
to pointers to int *
and back, provided the alignments are suitable (which they will be if the pointers were returned by malloc
and you are not using custom objects with extended alignments).
In general, you cannot write using a pointer to an object type and then read the same bytes using a pointer to a different object type. This violates aliasing rules. An exception is that if one of the pointers is to a character type. Also, many C implementations do support such aliasing, but it may require setting command-line options to enable such support.
This prohibition on aliasing includes reinterpreting pointers. Consider this code:
int a;
int *b = &a;
void **c = (void **) &b;
void *d = *c;
int *e = (int *) d;
In the fourth line, c
points to the bytes that b
occupies but *c
tries to interpret those bytes as a void *
. This is not guaranteed to work, so the value that d
gets is not necessarily a pointer to a
, even when it is converted to int *
as in the last line.