My goal is something like this:
void alloc(*x)
{
x->ptr = malloc(100);
}
int main()
{
struct { int z; int *ptr; } foo;
struct { int z; double *ptr; } bar;
alloc(&foo);
alloc(&bar);
return 0;
}
The function alloc
shall allocate memory for different kind of structs, which are all basically the same, but use different pointers.
My attempts for a solution would look like this:
struct generic {
int z;
void *ptr;
};
void alloc(void *x)
{
struct generic *tmp = x;
tmp->ptr = malloc(100);
}
or:
union generic {
void *p;
struct {
int z;
void *ptr;
} *g;
};
void alloc(void *x)
{
union generic tmp = {.p = x};
tmp.g->ptr = malloc(100);
}
Are they correct or do they break strict-aliasing as the actual parameters are not compatible with the generic
-struct and dereferencing x
or tmp.g
is not valid?
Further, granted that this was violating strict-aliasing, how would it have an impact?
Strict-aliasing is used for not reloading specific values under the assumption that they could not have been modified when they weren't aliased in a correct manner (char*, void*, union, compatible type).
alloc()
is called with a void-pointer as its parameter which may alias, so the caller can't assume that the underlying data won't change. Inside of alloc()
I would exclusively use the type-punned pointer. So where could something go wrong in this scenario by not reloading correctly?
Neither snippet is portable.
As for the first one,
tmp
's type is not compatible with that of the object pointed to byx
, namelystruct { int z; int *ptr; }
orstruct { int z; double *ptr; }
. Thus dereferencingtmp
is in violation of the strict aliasing rules.The problem with the second snippet is that having a union of pointers with incompatible types would only tell the compiler that the pointers themselves are aliased, not the objects they point to, so it's still breaking strict aliasing (for more information on strict aliasing, see here).
Also what both snippets ignore is that different types of pointers can actually have different internal representations (although rare), that's why type-punning a double pointer to a void pointer and later on using it as a double pointer again results in the pointer's bit pattern getting simply reinterpreted, but not converted, possibly yielding a trap representation and hence an invalid pointer (for more information on internal pointer representations, see here).
In conclusion, the overall approach of both snippets is not portable and needs to be rethought.