类型 - 正确地在不兼容但等效的结构内部指针

时间:2014-11-18 15:56:13

标签: c struct strict-aliasing type-punning

我的目标是这样的:

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不兼容 - 结构和解除引用xtmp.g无效?

进一步,认为这违反了严格别名,它会产生什么影响? 严格别名用于不重新加载特定值,假设它们在没有以正确方式别名(char *,void *,union,兼容类型)时无法被修改。 使用void-pointer作为参数调用alloc(),该参数可能是别名,因此调用者不能假定基础数据不会更改。在alloc()内部,我将专门使用类型惩罚指针。那么在这种情况下,如果没有正确重新加载会出现问题呢?

2 个答案:

答案 0 :(得分:2)

您可以将第二个成员放在结构中(以避免指针兼容性问题)。

  

C99 6.2.5.26

     

所有指向结构类型的指针都应具有相同的表示和对齐要求。

然后你可以使用union来进行类型惩罚(以避免严格的别名问题)。

struct generic
{
    int z;
    union
    {
        struct {int i;} i;
        struct {double d;} d;
    } *ptr;
};

void alloc(struct generic *x)
{
    x->ptr = malloc(100);
}

int main(void)
{
    struct generic foo;
    struct generic bar;
    alloc(&foo);
    alloc(&bar);
    return 0;
}

您不得不访问结构的成员。如果您使用gcc或任何与C11兼容的编译器,则可以使用匿名(未命名)结构成员绕过该问题。

似乎没有办法在符合要求的实施方式下工作。

答案 1 :(得分:0)

这两个代码段都不是便携式的。

至于第一个,tmp的类型与x指向的对象的类型不兼容,即struct { int z; int *ptr; }struct { int z; double *ptr; }。因此,取消引用tmp违反了严格的别名规则。

第二个片段的问题是,指针与不兼容类型的联合只会告诉编译器指针本身是别名的,而不是它们指向的对象,因此它仍然会破坏严格的别名(有关严格的更多信息)别名,请参阅here)。

两个片段忽略的是,不同类型的指针实际上可以具有不同的内部表示(尽管很少见),这就是为什么对双指针进行类型化处理,然后再将它用作双指针再次导致指针的位模式简单地重新解释,但没有转换,可能产生陷阱表示,因此产生无效指针(有关内部指针表示的更多信息,请参阅here)。

总之,两个片段的整体方法都不可移植,需要重新考虑。