将结构指针转换为联合指针
我有一个包含各种兼容struct
成员的union
:
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的字段。 我知道只要type
不变量得到维护,就会定义将union U *
为struct S1 *
,但反过来也是如此吗?
struct S1 *s1 = malloc (sizeof (struct S1)); union U *u = (union U *)s1;
也就是说,仅分配sizeof (struct S1)
字节并将结果转换为union U *
是否安全? 我想避免分配sizeof (union U)
字节,在我的情况下可能会大得多。
演员本身不太可能成为问题。 只要struct和union类型具有相同的对齐要求,就允许它们。 此外,在这种情况下,标准要求如果将值转换回原始类型,则结果将与原始指针进行比较,因此不会丢失任何数据。
问题是当您访问指向对象时会发生什么,就好像它是一个union U
你可以这样做的一些事情可能会很好,但其他事情肯定是不安全的。 具体来说,你问
也就是说,仅分配sizeof(struct S1)字节并将结果转换为union U *是否安全?
不,这不安全。 该标准规定:
当值存储在结构或联合类型的对象中(包括在成员对象中)时,对应于任何填充字节的对象表示的字节采用未指定的值。 […]
当值存储在union类型的对象的成员中时,对象表示的不对应于该成员但对应于其他成员的字节采用未指定的值。
(C2011,6.2.6 / 6-7)
如果触发其中一个条款,特别是后者,并且分配的空间没有union的表示那么大,那么程序会显示未定义的行为。 您可以避免执行此类程序操作,但在程序行为中创建此类额外约束在我的书中不能被称为“安全”。
我想避免分配sizeof(union U)字节,在我的情况下可能会大得多。
因为您显然打算使用union的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); // ie not do_something(&s)
当然,您需要转换contents
指针以取消引用它。 另一方面,这只是在指针周围有点花哨,以识别指向类型。 相反,你可以不进行包装,并明确地传递类型信息。