But I don't understand how malloc can be made to work with these rules.
Doesn't malloc just return a void pointer which in normal use you
assign to a variable you want. Isn't the following *normal* code?
struct mystruct *myptr;
myptr = malloc(sizeof(myptr));
Yes it is normal code, apart from the wrong size as pete explained.
The conversion is implicit in the assignment from a "pointer to void" to
a "pointer to struct mystruct". The conversion is still performed,
behind the scenes. If the void* had some extra data to represent the
offset within a word, it will be stripped off when the pointer is converted.
Since malloc must return a pointer that is correctly aligned for any
data type, any offset part must logically be zero in any void* that is
returned from malloc.
Or is what Simon Biber said true, that is, the C runtime does some
pointer conversion in the background in this case. If so what's the
difference with:
struct mystruct *myptr;
void *tmp;
tmp = malloc(sizeof(myptr));
myptr = tmp;
There is no difference! In the line above, exactly the same conversion
is performed as in the previous block of code.
I don't see how malloc can be useful if you can't assign a void pointer
to a struct pointer.
I think you misunderstood somewhere. Nobody ever said you can't assign a
void pointer to a struct pointer. It's just that the two pointers may
have a different representation. When you do the assignment, the
pointers will automatically be converted as necessary.
Essentially, you don't need to worry about this unless you start doing
something dodgy, such as accessing a pointer *value* through an
expression of the wrong type.
For example,
/* Define foo as pointer to char, initialise with NULL */
char *foo = NULL;
/* Define bar as int, initialise with 42 */
int bar = 42;
/* Assign pointer to string "hello" to bar */
foo = "hello"; /* No problem */
/* Now define quux as pointer to pointer to int, and
initialise with a suitably converted pointer to foo. */
int **quux = (int **) &foo; /* Dodgy */
/* Find whatever quux points at, and assign it a pointer to bar */
*quux = &bar; /* Undefined Behaviour, but will succeed in
changing foo's value on most systems. */
Here, even accessing *quux was undefined behaviour, since it was
attempting to access a value of type (char *) through an expression of
type (int *).