将结构指针转换为联合指针

时间:2016-10-09 23:19:12

标签: c struct casting undefined-behavior unions

我有一个union包含各种兼容的struct成员:

enum Type { T1, T2, … };

struct S1 { enum Type type; … };
struct S2 { enum Type type; … };
…

union U {
    enum Type type;
    struct S1 as_s1;
    struct S2 as_s2;
    …
};

这些结构的type字段被视为不可变,并且只访问与当前type对应的union的字段。我知道,只要保持union U *不变量,就会定义struct S1 *type,但反之亦然吗?

struct S1 *s1 = malloc (sizeof (struct S1));
union U *u = (union U *)s1;

也就是说,仅分配sizeof (struct S1)字节并将结果转换为union U *是否安全?我想避免分配sizeof (union U)字节,在我的情况下可能会大得多。

1 个答案:

答案 0 :(得分:4)

演员本身不太可能成为问题。只要struct和union类型具有相同的对齐要求,就允许它们。此外,在这种情况下,标准要求如果将值转换回原始类型,则结果应与原始指针进行比较,因此不会丢失任何数据。

问题是当您访问指向对象时会发生什么,就像它是union U一样。你可以这样做的一些事情可能会很好,但其他的肯定是不安全的。具体来说,你问

  

也就是说,只分配sizeof(struct S1)字节并将结果转换为union U *是否安全?

不,这不安全。该标准规定:

  

当值存储在结构或联合类型的对象中(包括在成员对象中)时,对应于任何填充字节的对象表示的字节采用未指定的值。 [...]

     

当值存储在union类型的对象的成员中时,对象表示的不对应于该成员但与其他成员对应的字节采用未指定的值。

(C2011,6.2.6 / 6-7)

如果触发其中一个条款,尤其是后者,并且分配的空间没有union的表示那么大,那么程序会显示未定义的行为。您可以避免执行此操作的程序操作,但是对程序行为创建此类额外约束不能表示为" safe"在我的书中。

  

我想避免分配sizeof(union U)字节,在我的情况下可能会大得多。

由于您显然打算使用联盟的type成员来识别要使用的成员,如何执行此操作:

struct generic_s {
    enum Type type;
    void *contents;
};

struct generic_s s = { T1 };
s.contents = malloc(sizeof (struct S1));

直接传递struct_generic类型的对象以避免额外的分配和解除分配:

do_something(s);  // i.e. not do_something(&s)

当然,您需要强制转换contents指针才能取消引用它。另一方面,这只是一些花哨的包围指针以识别指向类型。相反,你可以不进行包装,并明确地传递类型信息。