将标准布局对象数组转换为元素数组

Casting an array of standard-layout objects to array of elements

提问人:Arek' Fu 提问时间:10/18/2023 更新时间:10/18/2023 访问量:85

问:

在 C++ 中,指向标准布局类型的指针指向指向 的任何成员的指针是合法的,可能使用 offsetof。如果只有一个成员,则没有必要,这是合法的:reinterpret_castSSSoffsetof

struct S {
    int x;
};

static_assert(std::is_standard_layout_v<S>);

void f(S s) {
    // this is legal for standard-layout classes, offsetof the first member is zero
    S *s0 = &s;
    int *x0 = reinterpret_cast<int *>(s0);
}

但现在假设我有一个数组:S

S arr_s[10];

我可以合法地让数组衰减为指针

S *s0 = arr_s;

我可以合法地将指针投射到:int *

int *x0 = reinterpret_cast<int *>(s0);

这是否意味着我可以通过以下方式访问整个阵列?索引超过 0 是否合法?我很确定答案是否定的,因为可能有不止一个成员。但是,如果我们将自己限制在只有一个成员的情况下,我是否可以以某种方式合法地将数组视为数组?x0x0SSSint

C++ 数组转换 标准布局

评论

3赞 bitmask 10/18/2023
如果结构的大小和对齐方式与其成员相同,则可能是。你可以在结构内部有一个对齐的结构,即使只有一个成员,它也不会飞行。shortlong
0赞 BoP 10/18/2023
该问题询问如何强制转换为元素数组。但 an 不是整数数组。那里有一个问题。int*

答:

6赞 user17732522 10/18/2023 #1

我可以合法地将指针转换为 int *:

是的。

这是否意味着我可以通过 x0 访问整个阵列?索引 x0 超过 0 是否合法?

不。您可以通过 形成、取消引用和访问。也可以形成 ,但它将是 one-past-the-object 指针,并且不能被取消引用。不能形成指向任何其他索引的指针。从强制转换接收到的指向不属于数组的单个对象。此类情况被视为属于大小数组,并且指针算术仅在该数组中定义。x0+0x0+1int*intint1

我很确定答案是否定的,因为 S 可能有多个成员。但是,如果我们将自己限制在 S 只有一个成员的情况下,我是否可以以某种方式合法地将 S 数组视为 int 数组?

不,类的布局完全没有影响,除了对于非标准布局类,它本身将无法按预期工作,甚至在索引处访问或任何指针算术(包括)都将是 UB,因为在这种情况下甚至不会导致指向对象的指针。然后,您将尝试访问与(指针删除的)表达式类型不相似的对象或对对象执行指针算术运算。reinterpret_cast0+0reinterpret_castint

目前没有办法实现你想要的。

在 C++ 中,将指向标准布局类型 S 的指针reinterpret_cast指向指向 S 的任何成员的指针是合法的,可能使用 offsetof 宏。

从技术上讲,目前除了第一个元素之外,这是不可能的。但这是标准的缺陷。它目前没有正确指定应该生成指向对象表示的指针(这是唯一可以使用的方法)。此规范的细节将取决于哪些结构具有 UB,哪些没有。这并非直截了当。reinterpret_cast<unsigned char*>offsetoffsetof


所有这些都是在标准保证(C++ 或更高版本)方面。这在实践中是否会给编译器带来问题是一个不同的问题。在实践中(我认为),只要您确保满足所有对齐要求并且大小匹配,您就可以使用当前的编译器。请注意,类中只有一个成员并不能保证后者。最后可能仍然有填充。

-3赞 Ahmed Elias 10/18/2023 #2

是的,因为通过 x0 访问数组元素的合法性取决于 S 类型的属性。如果 S 只有一个成员,如提供的代码所示,则通过 x0 访问数组元素是合法的。这是因为 reinterpret_cast 有效地将 S 数组视为 int 数组。

但是,正如您提到的,此方法仅在 S 只有一个成员时才有效。