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)
我的问题是:
p += 2;UB吗?(即,从 fromp移动了两个元素s.x,所以它指向了&s.x+1)S没有填充,是float t = *p;UB?或者它是否定义明确,t应该包含的值s.z?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)
声明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(尽管现在由于不同的原因)。
| 归档时间: |
|
| 查看次数: |
128 次 |
| 最近记录: |