我的目标是这样的:
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;
}
函数 alloc
应为不同类型的结构分配内存,这些结构基本相同,但使用不同的指针 .
我尝试解决方案的方式如下:
struct generic {
int z;
void *ptr;
};
void alloc(void *x)
{
struct generic *tmp = x;
tmp->ptr = malloc(100);
}
要么:
union generic {
void *p;
struct {
int z;
void *ptr;
} *g;
};
void alloc(void *x)
{
union generic tmp = {.p = x};
tmp.g->ptr = malloc(100);
}
它们是正确的还是它们是否会破坏严格别名,因为实际参数与 generic
-struct不兼容且解除引用 x
或 tmp.g
无效?
此外,如果这违反了严格别名,它将如何产生影响?严格别名用于不重新加载特定值,假设它们在没有以正确方式别名(char *,void *,union,兼容类型)时无法被修改 . 使用void-pointer作为参数调用 alloc()
,该参数可能是别名,因此调用者可以更改.1806597_ . 在 alloc()
里面我会专门使用类型惩罚指针 . 那么在这种情况下,如果没有正确重新加载会出现问题?
2 回答
您可以将第二个成员放在结构中(以避免指针兼容性问题) .
然后你可以使用union来进行类型惩罚(以避免严格的别名问题) .
您将不得不访问该结构的成员 . 如果您使用gcc或任何与C11兼容的编译器,则可以使用匿名(未命名)结构成员绕过该问题 .
似乎没有办法不符合一致的实现 .
这些片段都不是便携式的 .
至于第一个,
tmp
的类型与x
指向的对象的类型不兼容,即struct { int z; int *ptr; }
或struct { int z; double *ptr; }
. 因此,取消引用tmp
违反了严格的别名规则 .第二个片段的问题是,指针与不兼容类型的联合只会告诉编译器指针本身是别名,而不是它们指向的对象,因此它仍然会破坏严格的别名(有关严格别名的更多信息,请参阅here) .
两个片段忽略的是,不同类型的指针实际上可以具有不同的内部表示(尽管很少见),'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'的位模式被简单地重新解释,但是没有被转换,可能产生陷阱表示并因此产生无效指针(有关更多信息)内部指针表示,请参阅here) .
总之,两个片段的整体方法都不可移植,需要重新考虑 .