Assuming code is compiled with c11 and strict aliasing enabled.
I am not searching for a different approach, I would like to focus on this specific problem and if it works or why not.
(If I unintentionally made some unrelated error let me know and I will fix it)
c11 standard says:
6.2.5.28 All pointers to structure types shall have the same representation and alignment requirements as each other.
6.7.2.1.6 a structure is a type consisting of a sequence of members, whose storage is allocated in an ordered sequence
This means the pointer size and alignment of pointers in struct A and B are the same.
#include <stdio.h>
#include <stdlib.h>
struct S1
{
int i ;
} ;
struct S2
{
float f ;
} ;
struct A
{
struct S1* p ;
} ;
struct B
{
struct S2* p ;
} ;
int main( void )
{
Structs A and B have pointers to structs S1 and S2, and structs A and B are guaranteed to have the same size and alignment.
We have a struct B
whose member pointer is a struct S2 pointer, but is pointing to some struct S1, which achieved with a void* cast.
struct S1 s1 = { 0 } ;
struct B* b = malloc( sizeof( *b ) ) ;
b->p = ( void* ) &s1 ;
That is ok, we can store the pointer, as long as we don't actually use the pointer. But we want to. We could cast the pointer to struct S1.
( ( struct S1* )(b->p) )->i = 123 ; //redundant brackets for emphasis
printf("%d\n" , s1.i ) ;
And use it correctly.
So far I don't see any problems, as the pointer was casted to the correct type.
But can we cast the whole struct B to struct A instead? They are the same regarding size and alignment, though the standard might complain(?), could compilers produce undefined behavior?
( ( struct A* )b)->p->i = 666 ;
printf("%d\n" , s1.i ) ;
I know the solution is to use an union( or use a void and just cast correctly any time), as the standard allows to use the member not last used to store a value.
6.5.2.3.3( 95 ) If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.
But, I would like to avoid this:
struct C
{
union
{
struct S1* p1 ;
struct S2* p2 ;
} ;
} ;
struct C* c = malloc( sizeof( *c ) ) ;
c->p2 = ( void* )&s1 ;
c->p1->i = 444 ;
printf("%d\n" , s1.i ) ;
return 0 ;
}