Consider the following example program:
#include <stdio.h>
struct base {
int a, b;
};
struct embedded {
struct base base;
int c, d;
};
struct pointed {
struct base* base;
int c, d;
};
static void base_print(struct base* x) {
printf("a: %d, b: %d\n", x->a, x->b);
}
static void tobase_embedded(void* object) {
base_print(object); // no cast needed, suitably converted into first member.
}
static void tobase_pointed(void* object) {
struct base* x = *(struct base**) object; // need this cast?
base_print(x);
}
int main(void) {
struct embedded em = {{4, 2}};
struct pointed pt = {&em.base};
tobase_embedded(&em);
tobase_pointed(&pt);
return 0;
}
Compiled with:
$ gcc -std=c99 -O2 -Wall -Werror -pedantic -o main main.c
The expected output is:
$ ./main
a: 4, b: 2
a: 4, b: 2
The C99 standard says this about the first member of a structure:
C99 6.7.2.1 (13): A pointer to a structure object, suitably converted, points to its initial member... and vice versa. There may be unnamed padding within as structure object, but not at its beginning.
In the example program a pointer to struct embedded
is converted to a pointer to struct base
(through void*
) without the need for an explicit cast.
What if instead the first member is a pointer to base as in struct pointed
? I'm unsure about the cast within tobase_pointed
. Without the cast garbage is printed, but no compilation warnings/errors. With the cast the correct values for base.a
and base.b
are printed, but that doesn't really mean much if there is undefined behavior.
Is the cast to convert struct pointed
into its first member struct base*
correct?
The code doesn't just casts, it also dereferences the pointer to the pointer to struct base. This is necessary to obtain the pointer to base in the first place.
This is what happens in your code, if the function tobase_pointed
was removed:
struct pointed pt = {&em.base};
void* object = &pt; //pass to the function
struct base** bs = object; //the cast in the function
assert( bs == (struct base**)&pt ) ; //bs points to the struct pointed
assert( bs == &(pt.base) ) ; //bs also points to the initial member struct base* base
struct base* b = *bs ; //the dereference in the function
base_print(x);
bs
is the pointer that was suitably converted to point to the initial member. Your code is correct.
This cast is justified, and you need it because you want to convert a pointer into a pointer to pointer. If you do not cast, dereference will be incorrect.
In other words, your base*
has the same address as pt
object. So you can access it through a pointer to pt. But you have to dereference it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With