将指向联合的指针转换为指向联合数组成员的元素类型的指针

问题描述 投票:0回答:2

我有一个

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
数组。 那么上述规则是否适用?

  1. 以上换算是否有效?

  2. 使用指针时,在(未)定义的行为方面是否有任何差异如果上述转换首先是有效的?

    例如,我可能有多个

    union Obj64
    对象的缓冲区。我可以使用
    ((uint16_t *)union_ptr)[4]
    访问缓冲区中
    下一个
    uint16_t 中的
    union
    ,即使使用
    union_ptr->u16
    这样做是无效的?

arrays c language-lawyer union undefined-behavior
2个回答
2
投票

将指向

union
的指针转换为指向其任何成员的指针并返回是有效的。

这在 C 标准第 6.7.2.1p16 节中有详细说明

联合的大小足以包含其最大的 成员。至多一个成员的值可以存储在一个 随时联合对象。 指向联合对象的指针,适当地 转换后,指向其每个成员(或者如果一个成员是位- 字段,然后转到它所在的单位),反之亦然。

但这并不会改变链接问题中提到的行为。您本质上要问的是,给定

uint16_t x[4][4]
,访问
x[0][4]
是否有效。正如链接的问题所述,答案是否定的。

所以

union_ptr->u16[4]
((uint16_t *)union_ptr)[4]
都是未定义的行为。


2
投票

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
的数组。

© www.soinside.com 2019 - 2024. All rights reserved.