在C++中,法律的是reinterpret_cast
一个指向standard-layout typeS
的指针指向S
的任何成员,可能使用the offsetof
macro。如果S
只有一个成员,那么offsetof
是不必要的,这是法律的:
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);
这是否意味着我可以通过x0
访问整个数组?索引x0
大于0是否法律的?我很确定答案是否定的,因为S
可能有多个成员。但是,如果我们将自己限制在S
只有一个成员的情况下,我可以合法地将S
数组视为int
数组吗?
2条答案
按热度按时间qzwqbdag1#
我可以合法地将指针转换为int *:
是的
这是否意味着我可以通过x0访问整个数组?将x0的索引设置为大于0是否法律的?
不可以。您可以通过
x0+0
进行构造、解引用和访问。你也可以构造x0+1
,但它将是一个对象指针,不能被解引用。不能形成指向任何其他索引的指针。从强制转换接收到的int*
指向一个不属于int
数组的int
对象。这样的情况被视为它们属于大小为1
的数组,并且指针算法仅在该数组内部定义。我很确定答案是否定的,因为S可能有不止一个成员。但是如果我们把自己限制在S只有一个成员的情况下,我可以合法地把S的数组当作int的数组吗?
不,类的布局没有任何影响,除了对于非标准布局类,
reinterpret_cast
本身将无法按预期工作,甚至在索引0
或任何指针算术(包括+0
)处访问都将是UB,因为在这种情况下,reinterpret_cast
甚至不会导致指向int
对象的指针。然后,您将尝试访问一个类型与(指针删除)表达式类型不相似的对象或对其执行指针算术。目前没有办法实现你想要的。
在C中,可以法律的将指向标准布局类型S的指针重新解释_转换为指向S的任何成员的指针,可能使用offsetof宏。
这在技术上是不可能的,目前也超出了第一个要素。但这是标准中的一个缺陷。它目前没有正确地指定
reinterpret_cast<unsigned char*>
应该产生一个指向对象表示的指针(这是offset
可以使用的唯一方式)。本规范的细节将取决于哪些offsetof
结构具有UB,哪些没有。这并不简单。所有这些都是标准保证(C17或更高版本)。这是否会在实践中给编译器带来问题是另一个问题。在实践中(我认为),只要确保满足所有对齐要求并且大小匹配,您可能就可以使用当前的编译器。请注意,后者并不能通过类中只有一个成员来保证。最后可能还有填充物。
e0bqpujr2#
是的,因为通过x0访问数组元素的合法性取决于类型S的属性。如果S只有一个成员,如提供的代码所示,则通过x0访问数组元素是法律的。这是因为reinterpret_cast有效地将S的数组视为int的数组。
然而,正如你提到的,这种方法只在S只有一个成员时有效。