C++标准究竟在哪里说取消引用未初始化的指针是未定义的行为?

sha*_*oth 11 c++ standards undefined-behavior language-lawyer

到目前为止,我无法找到如何推断以下内容:

int* ptr;
*ptr = 0;
Run Code Online (Sandbox Code Playgroud)

是未定义的行为.

首先,有5.3.1/1表示这*意味着转换T*为的间接T.但这对UB没有任何说明.

然后经常引用3.7.3.2/4说在非空指针上使用解除分配函数会使指针无效,之后无效指针的使用是UB.但是在上面的代码中没有关于释放的内容.

如何在上面的代码中推导出UB?

JUS*_*ION 12

第4.1节看起来像一个候选人(强调我的):

可以将非函数非数组类型T的左值(3.10)转换为右值.如果T是不完整类型,则需要进行此转换的程序格式不正确.如果左值引用的对象不是类型T的对象,并且不是从T派生的类型的对象,或者如果对象未初始化,则需要此转换的程序具有未定义的行为.如果T是非类型,则rvalue的类型是T的非限定版本.否则,rvalue的类型是T.

我敢肯定只是在规范中搜索"uninitial"可以找到更多的候选人.

  • 前几天我从Johannes/litb那里了解到这里的规范存在一些缺陷:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#240.因此,您需要阅读该段落并同情一个事实,即当它表示"未初始化"时,真正的标准应该更清楚未初始化/不确定值.在这种情况下,对象肯定是未初始化的,因此您的引用涵盖了它. (2认同)

Sha*_*our 6

我发现这个问题的答案是C++草案标准的一个意想不到的角落,24.2 Iterator要求部分,特别是24.2.1 在第5段和第10段 中分别说(强调我的):

[...] [示例:在声明未初始化的指针 x(与int*x;一样)之后,必须始终假定 x 具有指针的奇异值.-end example] [...]可解除引用的值总是非单数的.

和:

无效的迭代器是一个可能是单数的迭代器.268

和脚注268说:

此定义适用于指针,因为指针是迭代器.取消引用已失效的迭代器的效果未定义.

尽管对于空指针是否是单数指针看起来确实存在一些争议,但看起来像单数值这个术语需要以更一般的方式正确定义.

奇异的意图似乎在缺陷报告278中得到了很好的总结.迭代器的有效性意味着什么?根据理由部分说:

为什么我们说"可能是单数",而不是"单数"?这是因为有效的迭代器是一个已知非常规的迭代器.使迭代器失效意味着以一种不再为非奇异的方式对其进行更改.例如:正确地将元素插入向量的中间以使所有指向向量的迭代器无效.这并不一定意味着它们都变得奇异.

因此,无效和未初始化会 may创建一个单数的值,但由于我们无法证明它们是非奇异的,我们必须假设它们是单数的.

更新

另一种常识性方法是注意标准部分草案5.3.1 一元操作员第1段(强调我的):

一元*运算符执行间接:它所应用的表达式应该是指向对象类型的指针,或指向函数类型的指针,结果是一个左值,引用表达式指向的对象或函数.[. ..]

如果我们再去3.10 Lvalues和rvalues第1段说(强调我的):

左值(历史上所谓的左值,因为左值可能出现在赋值表达式的左侧)指定一个函数或一个对象.[...]

但ptr除了偶然之外,它不会指向有效的对象.


Ytt*_*ill 5

OP的问题是无稽之谈.没有要求标准说某些行为是不确定的,事实上,我认为所有这些措辞都应从标准中删除,因为它会使人感到困惑并使标准更加冗长而不必要.

标准定义了某些行为.问题是,它是否指定了这种情况下的任何行为?如果没有,则无论是否明确说明,行为都是不确定的.

实际上,有些事情未定义的规范在标准中主要作为标准编写者的调试辅助,如果在一个地方存在与另一个地方的未定义行为的明确陈述冲突的想法,则产生矛盾的想法:这是证明标准缺陷的一种方法.如果没有未定义行为的明确陈述,则规定行为的其他条款将是规范性的且不受质疑.


M.M*_*M.M 5

评估未初始化的指针会导致未定义的行为。由于取消引用指针首先需要对其进行评估,这意味着取消引用也会导致未定义的行为。

尽管措辞发生了变化,但 C++11 和 C++14 中都是如此。

在 C++14 中,它被 [dcl.init]/12 完全覆盖:

当获得具有自动或动态存储持续时间的对象的存储时,该对象具有不确定值,并且如果没有对该对象执行初始化,则该对象将保留不确定值,直到该值被替换。

如果计算产生不确定值,则行为未定义,但以下情况除外:

其中“以下情况”是对 的特定操作unsigned char。


在 C++11 中,[conv.lval/2] 在左值到右值转换过程中涵盖了这一点(即从 表示的存储区域检索指针值ptr):

非函数、非数组类型 T 的左值可以转换为纯右值。如果 T 是不完整类型,则需要此转换的程序是格式错误的。如果泛左值引用的对象不是 T 类型的对象,也不是从 T 派生的类型的对象,或者该对象未初始化,则需要此转换的程序将具有未定义的行为。

C++14 中的粗体部分已被删除,并替换为 [dcl.init/12] 中的额外文本。


Mai*_*ori 3

我不会假装我对此了解很多,但有些编译器会将指针初始化为 NULL,并且取消引用指向 NULL 的指针是 UB。

\n\n

另外考虑到未初始化的指针可能指向任何内容(包括 NULL),当您取消引用它时,您可以得出结论,它是 UB。

\n\n

第 8.3.2 节中的注释 [dcl.ref]

\n\n
\n

[注意:特别是,空引用不能存在于明确定义的程序中,因为创建此类引用的唯一方法是将其绑定到\xe2\x80\x9cobject\xe2 \x80\x9d 通过 取消引用空指针而获得,\n 会导致未定义的行为。正如 9.6 中所述,引用不能直接绑定到位域。]

\n
\n\n

\xe2\x80\x94ISO/IEC 14882:1998(E),ISO C++ 标准,第 8.3.2 节 [dcl.ref]

\n\n

我想我应该把这个写成评论,我不太确定。

\n