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

2024-01-06

看看这个片段:

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.
    }
}

我的问题是:

  1. Is p += 2;布? (IE。,p被感动了two超出自的元素s.x,所以它超出了&s.x+1)
  2. 在这里,我们知道S没有填充,是float t = *p;布?或者它的定义是否明确,即t应包含的值s.z?
  3. 优化器可以优化访问吗p at float z = *p;?我的意思是,是否允许z是 0? (是否允许编译器看不到,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.
    }
}

陈述p += 2其本身就是未定义的行为;p是一个指向浮点对象的指针,它指向单个浮点对象(而不是指向这些对象的数组)。尽管就指针算术而言,单个对象被视为由一个元素组成的数组(例如,5.7 (4) https://timsong-cpp.github.io/cppwp/n3337/expr.add#4在线标准草案的一部分),将指针移到末尾两点。这个指针算术本身已经是 UB (cf.5.7 (5) https://timsong-cpp.github.io/cppwp/n3337/expr.add#5),无论您是否取消引用该指针。

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

那么直接回答你的问题:

(1) 由于指针运算无效而为 UB

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

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

关于union-construct,在 C++ 中(与 C 不同),访问除先前编写的联合体成员之外的联合体成员又是 UB。所以写工会成员s然后访问联盟成员a再次导致 UB(尽管现在由于不同的原因)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否将指针移动到结构成员 UB 的后面?并访问它? 的相关文章

随机推荐