是否定义了非数组组成成员之间字节指针偏移量的计算?

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

我理解expr.add#4.2背后的推理,将

+
-
限制为针对同一数组元素的指针,这在我关于偏移指针类的问题的几条评论中进行了解释。然而,通过指针和指针加偏移目标位于同一(嵌套)组合(不一定是数组)内也可以实现同样的效果。如果这些差异可能是任何东西,那么 offsetof 宏将毫无意义。

演示 offsetof 类比的示例:

#include <iostream>
#include <cstddef>

struct Inner
{
    int i1;
    float f;
    bool b;
    int i2;
};

struct Outer
{
    int i1;
    char c;
    int i2;
    Inner inner;
    double d;
};

int main(int argc, char* argv[])
{
    Outer outer;
    std::cout << "reinterpret_cast<std::byte*>(&outer.i2) - reinterpret_cast<std::byte*>(&outer.i1): "
              << reinterpret_cast<std::byte*>(&outer.i2) - reinterpret_cast<std::byte*>(&outer.i1) << std::endl;
    std::cout << "reinterpret_cast<std::byte*>(&outer.inner.i2) - reinterpret_cast<std::byte*>(&outer.i1): "
              << reinterpret_cast<std::byte*>(&outer.inner.i2) - reinterpret_cast<std::byte*>(&outer.i1) << std::endl;
    std::cout << "offsetof(Outer, i2) - offsetof(Outer, i1): " << offsetof(Outer, i2) - offsetof(Outer, i1) << std::endl;
    std::cout << "offsetof(Outer, inner) + offsetof(Inner, i2) - offsetof(Outer, i1): "
              << offsetof(Outer, inner) + offsetof(Inner, i2) - offsetof(Outer, i1) << std::endl;

    return 0;
}

退货

reinterpret_cast<std::byte*>(&outer.i2) - reinterpret_cast<std::byte*>(&outer.i1): 8
reinterpret_cast<std::byte*>(&outer.inner.i2) - reinterpret_cast<std::byte*>(&outer.i1): 24
offsetof(Outer, i2) - offsetof(Outer, i1): 8
offsetof(Outer, inner) + offsetof(Inner, i2) - offsetof(Outer, i1): 24

所有涉及的指针目标都在对象内

outer
,但不是同一个对象。未定义的行为?

(在评论后编辑,非字节指针会因混合类型组合而中断,并且 expr.add#4.2 中的“假设”不满足我的需求。谢谢!)

支持定义行为的论据可以是:

  • 指向 byte、char 和 unsigned char 类型的指针的转换是在严格别名的限制下定义的。
  • 指向外部组合本身的字节指针指向字节数组A的第一个元素,代表整个组合。
  • offsetof
    的语义要求所有组合成员在组合内都有固定的偏移量。
  • 因此,组合成员的所有字节转换指针也都指向A的元素。
c++ language-lawyer undefined-behavior
1个回答
0
投票

是的,这段代码是未定义的行为。 我们来看看:

reinterpret_cast<std::byte*>(&outer.i2) - reinterpret_cast<std::byte*>(&outer.i1)

i1
outer
的第一个成员,因此可以与其进行指针互换。 这意味着通过
reinterpret_cast
,您可以获得指向
outer
的指针,以及指向表示
outer
对象的字节数组的指针。

但是,

i2
不是第一个成员,并且无法通过它访问该字节数组。 您还可以说
outer
中的所有存储字节都可以通过 i1
 访问,但不能通过 
i2
 访问。
因此,根据
[expr.add] p4,这个指针减法就是UB。 “可能假设的”部分也不相关,因为它仅适用于与任何数组的(假设的)后一位元素进行指针算术。

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