是否将指针移动到结构成员 UB?并访问它?

gez*_*eza 2 c++ language-lawyer

看看这个片段:

struct S {
    float x, y, z;
};

void fn() {
    S s = { 0, 0, 0 };
    float *p = &s.x;
    p += 2;           // 1.
    if constexpr(sizeof(S)==sizeof(float)*3) { // if S has no padding
        float t = *p; // 2.
        s.z = 1;
        float z = *p; // 3.
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. p += 2;UB吗?(即,从 fromp移动了两个元素s.x,所以它指向了&s.x+1
  2. 在这里,我们知道S没有填充,是float t = *p;UB?或者它是否定义明确,t应该包含的值s.z
  3. 优化器可以优化对pat 的访问float z = *p;吗?我的意思是,是否允许为z0?(允许编译器看不到p==&s.z吗?)

2. 和 3. 的答案是否不同,如果if constexpr不存在,但我们知道(可能来自编译器文档或以前的经验),没有填充S


如果 1. 是 UB(所以 2./3. 毫无意义),那么 2./3. 的答案是什么,如果这样p设置(p在数组的帮助下移动,但除此之外,代码片段是相同的) ?

union U {
    S s;
    float a[3];
};

void fn() {
    U u;
    u.s.x = 0; u.s.y = 0; u.s.z = 0;
    float *p = u.a;  // here, p==&u.s.x as well
    if constexpr(sizeof(S)==sizeof(float)*3) { // if S has no padding
        p += 2;
        float t = *p; // 2.
        u.s.z = 1;
        float z = *p; // 3.
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ner 5

声明p += 2本身就是未定义的行为;p是一个指向浮点对象的指针,它指向单个浮点对象(而不是那些对象的数组)。尽管单个对象 - 在指针算术方面 - 被视为由一个元素组成的数组(例如,请参阅此在线标准草案的5.7 (4)),但您将指针移到末尾两处。这个指针算术本身已经是 UB(参见5.7 (5)),无论您当时是否取消引用指针。

请注意 - 即使您声明 type 的三个连续成员float,并且即使编译器没有在它们之间引入填充,第一个成员和完整的 struct-object 都不会成为标准的数组。即使我们想到的内存布局可能与我们喜欢访问它的情况“兼容”,编译器也不会强制允许/翻译包含我们想到的任何意义上的 UB 的语句。

所以直接回答你的问题:

(1) 是 UB 由于无效的指针运算

(2) 由于访问了无效指针而成为 UB

(3) 是 UB 由于访问无效指针,因此任何关于编译器是否可以优化的问题都无法回答/没有意义。

关于union-construct,在 C++ 中(与在 C 中不同),访问联合的成员而不是先前编写的成员又是 UB。因此,编写 union members然后a再次访问 union member会导致 UB(尽管现在由于不同的原因)。