Mes*_*Mes 5 c++ undefined-behavior language-lawyer c++20
&((T*)NULL)->member我从研究“ UB 是 C 语言吗?”这个问题开始。这是我教科书中的一个例子,它介绍了offsetof.
我知道offsetof现在无法在 C++ 中实现(通过 cppreference 页面)。
但在阅读了一些 C++ CWS 问题后,我的问题有点变成“取消引用空指针 UB 吗?”。
另外,我认为他们不会无缘无故地改变C 中offsetoffrom的实现&((T*)NULL)->member,但我不知道为什么,也许是因为它是 UB?但我在C中没有找到一个术语说&((T*)NULL)->member是UB。对于C++,如果不是标准布局类型,我认为它是UB。
一开始,我以为会有一个明确指定的术语,比如“取消引用 NULL 指针是 UB”
,但是,随着我深入研究,我发现它比我想象的要复杂。
看了很多stackoverflow的文章回复,发现答案并不统一。
有些帖子说它是明确定义的,有些帖子说它是UB,有些帖子说它未指定。
对于那些说它定义明确的帖子,他们引用“ CWG issues #232 ”和“ CWG issues #315 ”作为原因,就像c++ access static Members using null pointer中的答案一样 。
对于那些说未指定的帖子,他们说标准中没有明确指定。
对于那些说这是UB的帖子,他们说这个问题还没有包含在标准中,所以它仍然是UB。此外,他们还给出了“如果为指针分配了无效值,则一元运算符的行为*未定义”的术语。
上面 stackoverflow 中的例子是:
#include <iostream>
class demo {
public:
static void fun()
{
std::cout << "fun() is called\n";
}
static int a;
};
int demo::a = 9;
int main()
{
demo *d = nullptr;
d->fun();
std::cout << d->a;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
他们说它定义明确的大致原因是:
E1->E2相当于(*(E1)).E2*d;合法,则d->fun()合法。p = 0; *p;本质上并不是一个错误。左值到右值的转换会给它带来未定义的行为。*d当 为 null 时,不是错误d,除非将左值转换为右值 (7.3.2 [conv.lval]),但此处不存在这种情况。*d;是合法的,然后d->fun()是合法的。这个问题在 2005 年左右就被讨论过,当时仍在 C++03 规范中。
然而,在 C++20 中, for ->,标准明确指定E1inE1->E2应该是纯右值:
n4861(expr.ref#2):对于第二个选项(箭头),第一个表达式应是具有指针类型的纯右值。
所以我认为这里可能存在左值到右值的转换,因为E1应该是纯右值?
顺便说一句,标准之前使用“取消引用空指针”作为未定义行为的示例
n1146(intro.execution#4):某些其他操作在本国际标准中被描述为未定义(例如,取消引用空指针的效果)。
但该示例在CWG 问题 #1102中进行了更改。他们说的理由是
围绕取消引用空指针的未定义行为存在一些核心问题。其意图似乎是取消引用已明确定义,但使用取消引用的结果将产生未定义的行为。这个主题太混乱了,不能作为未定义行为的参考示例,或者如果要保留它,应该更准确地说明。
这个问题是在2010年讨论的,距今已经有13年了,所以我认为这个问题已经存在很长时间了,但遗憾的是,我现在仍然找不到答案。
总而言之,语言律师能给我关于这个问题的结论吗?C++20 中取消引用空指针 UB 吗?例如,&((T*)NULL)->member还有d->fun()上面的。或者这是IB或未指定的行为?
希望能够提供历史和标准中的术语。
编辑:
我的总结是,这仍然是一个未解决的问题,目前,它始终是由于expr.unary#op-1.sentence-3中的省略而导致的 UB ,它仅定义存在指针指向的对象时的行为。但这可能不是预期的规格。
顺便说一句,最近对该主题进行了一次讨论,结果相同: https: //github.com/cplusplus/CWG/issues/198
请检查@user17732522的评论和@Brian Bi的答案
到目前为止,取消引用空指针是否是UB的问题仍然没有解决。并且尚不清楚CWG 232中指示的方向,即仅当尝试通过解引用的结果访问该值时才应为UB,是否仍然是CWG的共识(尽管至少存在一种情况)它是明确合法的,即当生成的左值是多态类型并且是typeid) 的操作数时。如果 CWG 就某个方向达成一致,则尚不清楚 EWG 是否会接受该方向。所以,实际上,没有人知道答案。
至少有一个充分的理由说明为什么&((T*)NULL)->member应该成为 UB。实现大概是&E->m通过将固定偏移量添加到 的值来进行计算的E。如果E是空指针,则该算术将生成一个可能被硬件识别为无效的地址值,从而导致某些实现上的陷阱,在这些实现上将无效指针值加载到寄存器中会导致陷阱。我认为,如果 CWG 232 的最终决议确实发生,将澄清这种情况属于 UB。
| 归档时间: |
|
| 查看次数: |
415 次 |
| 最近记录: |