【发布时间】:2019-05-25 18:49:43
【问题描述】:
考虑这个联合:
union A{
int a;
struct{
int b;
} c;
};
c 和a 不是layout-compatibles 类型,因此无法通过a 读取b 的值:
A x;
x.c.b=10;
x.a+x.a; //undefined behaviour (UB)
试用 1
对于下面的情况,我认为从 C++17 开始,我也得到了一个未定义的行为:
A x;
x.a=10;
auto p = &x.a; //(1)
x.c.b=12; //(2)
*p+*p; //(3) UB
让我们考虑[basic.type]/3:
指针类型的每个值都是以下之一:
- 指向对象或函数的指针(据说该指针指向对象或函数),或
- 一个指针一个对象([expr.add]),或
- 该类型的 空指针值 ([conv.ptr]),或
- 指针值无效。
我们将这 4 个指针值类别称为指针值类型。
指针的值可能会从上述类型转换为另一种类型,但标准并没有明确说明这一点。 如果我错了,请随时纠正我。所以我假设在 (1) 处,p 的值是一个 指向 值的指针。然后在 (2) a 生命结束,p 的值变成一个无效的指针值。所以在 (3) 中我得到了 UB,因为我尝试在对象 (a) 的生命周期之外访问它的值。
试用 2
现在考虑这个奇怪的代码:
A x;
x.a=10;
auto p = &x.a; //(1)
x.c.b=12; //(2)
p = reinterpret_cast<int*>(p); //(2')
*p+*p; //(3) UB?
reinterpret_cast<int*>(p) 能否将指针值类型从 invalid pointer value 更改为 pointer to 值。
reinterpret_cast<int*>(p)被定义为等价于static_cast<int*>(static_cast<void*>(p)),那么让我们考虑如何定义static_cast从void*到int*,[expr.static.cast]/13:
“指向
cv1 void的指针”类型的纯右值可以转换为“指向cv2 T的指针”类型的纯右值,其中T是一个对象类型,cv2是相同的cv-qualification,或比cv1更高的 cv 资格。如果原始指针值表示内存中一个字节的地址A,并且A不满足T的对齐要求,则结果指针值未指定。否则,如果原始指针值指向对象a,并且存在T(忽略cv-qualification)类型为pointer-interconvertible 的对象b a,结果是一个指向b的指针。否则,指针值不会因转换而改变。
所以在我们的例子中,原始指针ed指向对象a。所以我想reinterpret_cast 将无济于事,因为a 不在其生命周期内。 我的阅读是否严格?这段代码能很好定义吗?
【问题讨论】:
-
结束被指针的生命周期并不会使指针值无效。
-
@Evg RetiredNinja 链接到您无权查看的现已删除的问题。
-
@Oliv 可能我没有完全理解你的要求,但从我的直觉来看,永远不可能通过使用强制转换(任何类型)来修复已经无效的东西。您将进入未定义行为的领域。
-
@Oliv:
int *p = new int; delete p;或int *p; { int a; p = &a; }。在这两个示例中,p最终都具有无效的指针值。但是更改活动联合成员不会使指向先前活动和现在非活动成员的指针无效,因为存储仍然存在。
标签: c++ pointers language-lawyer c++17 undefined-behavior