我有一个
union
:
union Obj64 {
uint64_t u64;
uint32_t u32[2];
uint16_t u16[4];
uint8_t u8[8];
};
static_assert(sizeof(union Obj64) == sizeof(uint64_t), "Unexpected trailing padding in union!");
现在我有一个指向
union
的指针:
union Obj64 *union_ptr = SOME_VALUE;
我想要一个指向相同数据的
uint16_t *
指针。由于指针衰减,我可以访问
u16
成员并将其用作 uint16_t *
:
uint16_t *u16_ptr = union_ptr->u16;
但是直接将
union
转换为 uint16_t *
有效吗?
uint16_t *u16_ptr = (uint16_t *)union_ptr;
我知道:
指向联合对象的指针,经过适当转换后,指向其每个成员(或者如果成员是位字段,则指向它所在的单元),反之亦然。
但是
union
不包含任何孤立的 uint16_t
成员,而只包含 uint16_t
数组。 那么上述规则是否适用?
以上换算是否有效?
使用指针时,在(未)定义的行为方面是否有任何差异如果上述转换首先是有效的?
例如,我可能有多个
union Obj64
对象的缓冲区。我可以使用 ((uint16_t *)union_ptr)[4]
访问缓冲区中 下一个
uint16_t
中的 union
,即使使用 union_ptr->u16
这样做是无效的?
将指向
union
的指针转换为指向其任何成员的指针并返回是有效的。
联合的大小足以包含其最大的 成员。至多一个成员的值可以存储在一个 随时联合对象。 指向联合对象的指针,适当地 转换后,指向其每个成员(或者如果一个成员是位- 字段,然后转到它所在的单位),反之亦然。
但这并不会改变链接问题中提到的行为。您本质上要问的是,给定
uint16_t x[4][4]
,访问 x[0][4]
是否有效。正如链接的问题所述,答案是否定的。
所以
union_ptr->u16[4]
和 ((uint16_t *)union_ptr)[4]
都是未定义的行为。
C 标准没有正式定义支持的转换,因此尝试对这些转换进行语言保护在某种程度上是徒劳的。 C 2018 6.7.2.1 16 说“……指向联合对象的指针,经过适当转换,指向其每个成员(或者如果成员是位字段,则指向它所在的单元),反之亦然, ”,但标准中没有定义“适当转换”。
(uint16_t *)union_ptr
生成一个指向 uint16_t
的指针。 Obj64
的所有成员都不是 uint16_t
,因此 this 不指向其任何成员,因此即使定义了“适当转换”,6.7.2.1 16 也不适用。 u16
成员是一个数组,因此 (uint16_t (*)[4]) union_ptr
几乎肯定是指向该成员的“适当转换”的指针。
即使您认为
(uint16_t *)union_ptr
首先是对数组的转换,然后是对指向其第一个成员的指针的转换,C 标准实际上并未定义后一种转换。给定 int x[4];
,(int *) &x
是部分定义的转换(它生成一个指针,可以转换回 int (*)
以生成指向数组的指针),但它不一定定义为指向 x[0]
。 (在许多普通的 C 实现中确实如此,但这是一个语言律师问题。)
您可以通过使用
u16
指定数组并允许发生数组到指针的转换来生成指向 * (uint16_t (*)[4]) union_ptr
成员的第一个元素的指针,或者您可以使用 &((uint16_t (*)[4]) union_ptr)[0]
显式地获取它。或者简单地使用 union_ptr->u16
进行指针到数组的转换。
无论如何,一旦通过任何方法获得了指向
union_ptr->u16[0]
的指针,您就可以使用它来访问 union_ptr->u16[i]
(0 ≤ i
< 4. Using it to access the memory beyond that is not safe without some additional prerequisite.)
...如果
和((uint16_t *)union_ptr)[x]
是指向x > 3
数组中某个元素的指针并且后面有一个元素,那么union_ptr
会安全吗?Obj64
不。为什么会这样?
((uint16_t *)union_ptr)[x]
最多包括对指向数组中元素的指针的算术运算,并且该数组具有从 0 到 3 索引的元素,并且如果该指针引用的索引超过 4,则不会定义对该指针的算术运算。为 4 定义,但取消引用指针则不然。 Obj64
包含 4 个 uint16_t
的数组这一事实并不意味着 2 个 Obj64
的数组包含 8 个 uint16_t
的数组。