应用间接时,标准是不是要求指针变量的左值到右值转换?

Posted

技术标签:

【中文标题】应用间接时,标准是不是要求指针变量的左值到右值转换?【英文标题】:Does the standard mandate an lvalue-to-rvalue conversion of the pointer variable when applying indirection?应用间接时,标准是否要求指针变量的左值到右值转换? 【发布时间】:2014-01-29 22:55:00 【问题描述】:

TL;DR

给定以下代码:

int* ptr;
*ptr = 0;

*ptr 是否需要在应用间接之前对ptr 进行左值到右值转换?

该标准在很多地方都涵盖了 lvalue-to-rvalue 的主题,但似乎没有指定足够的信息来确定 * 运算符 是否需要这样的转换。

详情

左值到右值的转换在N3485部分4.1中介绍左值到右值转换段落1并说(强调我的未来):

可以转换非函数、非数组类型 T 的泛左值 (3.10) 到一个prvalue.53如果T是一个不完整的类型,一个程序 需要这种转换是不正确的。如果对象 左值引用不是 T 类型的对象,也不是 a 的对象 从 T 派生的类型,或者如果对象未初始化,则为程序 必须进行此转换的行为未定义。[...]

*ptr = 0;需要这种转换吗?

如果我们转到 4 段落 1 部分,它会说:

[...]标准转换序列将应用于表达式 如有必要将其转换为所需的目标类型。

那么什么时候需要?如果我们查看5 Expressions 部分,9 段中提到了 lvalue-to-rvalue 转换:

当一个泛左值表达式作为一个运算符的操作数出现时 期望该操作数的纯右值,左值到右值(4.1), 数组到指针 (4.2) 或函数到指针 (4.3) 标准 应用转换将表达式转换为纯右值。 [...]

和第11段说:

在某些情况下,表达式仅出于其副作用而出现。 这样的表达式称为丢弃值表达式。[...] 当且仅当 表达式是 volatile 限定类型的左值,它是 以下 [...]

这两个段落似乎都不适用于此代码示例,5.3.1 一元运算符段落 1 它说:

一元 * 运算符执行间接:它所指向的表达式 应用的应该是指向对象类型的指针,或指向 函数类型,结果是引用对象的左值或 表达式指向的函数。如果表达式的类型 是“指向 T 的指针”,结果的类型是“T”。 [注:间接 通过指向不完整类型(除了 cv void)的指针是有效的。 这样获得的左值可以以有限的方式使用(初始化一个 参考,例如);此左值不得转换为 prvalue,见 4.1。 ——尾注]

它似乎不需要指针的 ,而且我没有看到任何指针转换的要求,我错过了什么吗?

我们为什么要关心?

我在其他问题中看到了一个答案和 cmets,声称使用未初始化的指针是未定义的行为,因为在应用间接之前需要对 ptr 进行 lvalue-to-rvalue 转换。例如:Where exactly does C++ standard say dereferencing an uninitialized pointer is undefined behavior? 提出了这个论点,我无法将该论点与该标准的任何最新草案版本中的内容相一致。由于我已经多次看到这一点,我想得到澄清。

未定义行为的实际证明并不重要,因为正如我在上面的链接问题中指出的那样,我们还有其他方法可以解决未定义行为。

【问题讨论】:

“它似乎没有” 明确 “需要指针的值”。曾经可能会争辩说该值是隐式(按“常识”)要求的。 明确地说,您的目标是弄清楚标准的哪一部分使int *p; *p=0; 行为未定义?如果做不到这一点,发现标准中的错误? @Yakk 换个说法,答案影响你是否可以使用这个参数来显示使用未初始化的指针是 UB,但这不是唯一的参数,我们可以显示它是 UB b/c we must assume it is singular。我看到三个结果1) 有缺陷,标准应该明确说明存在左到右转换2) 没有强制转换,这不是 UB 3) 左到右转换的证明是隐含的,它确实证明了 UB。 @dyp 但如果它是隐含的,这是否意味着对变量的任何读取都需要从左到右的转换?如果是这样,那么为什么 5 部分中的所有特定语言? @ShafikYaghmour 我不确定是否每次读取都需要从左到右的转换。 Jerry Coffin 的解释通过违反要求给出了 UB w/o l-to-r。类似问题:***.com/q/14991219/420683***.com/q/14935722/420683 【参考方案1】:

我认为你是从一个相当倾斜的角度来处理这个问题,可以这么说。根据§5.3.1/1:

一元* 运算符执行间接:应用它的表达式应该是指向 对象类型,或指向函数类型的指针,结果是引用对象或函数的左值 表达式所指向的。如果表达式的类型是“指向 T 的指针”,则结果的类型是“T”。

虽然这里没有讨论左值到右值的转换,但它要求表达式是指向对象或函数的指针。一个未初始化的指针不会(除了,可能是偶然的)是任何这样的东西,所以取消引用的尝试会产生未定义的行为。

【讨论】:

那么 shall 适用于 type,而不适用于 value。 @dyp:不是这样。它指定“表达式”,而不是“表达式的类型”,因此值 都是表达式的类型。句子的其余部分:“指代表达式所指向的对象或功能。”显然取决于这一点——如果表达式不指向对象或函数,它将变得毫无意义。 我的意思是,它说“应该是指向对象类型的指针”,而不是应该是指向对象的指针。然后有部分说“结果是一个左值,指代表达式指向的对象[...]”,其中隐含地,该值是必需的,并且隐式地,要求指针指向某个东西。 我认为我们同意,但我希望它更明确一点;) @dyp:我不反对它更明确,但我没有看到任何合理的方式来解释它不需要表达式引用对象或函数。【参考方案2】:

我已将我的问题中的更新部分转换为答案,因为此时它似乎是答案,尽管我的问题无法回答是不令人满意的:

dyp 向我指出了两个相关主题,它们涵盖了非常相似的领域:

What is the value category of the operands of C++ operators when unspecified? Does initialization entail lvalue-to-rvalue conversion? Is int x = x; UB?

共识似乎是标准是错误指定的,因此无法提供我正在寻找的答案,Joseph Mansfieldposted a defect report on this lack of specification,看起来它仍然是@987654326 @而且不清楚什么时候可以澄清。

对于标准的意图,有一些常识性的论据。可以争论Logicially, an operand is a prvalue if the operation requires using the value of that operand。另一个论点是,如果我们回顾 C99 draft standard 说的 an lvalue to rvalue conversion is done by default 并注意到异常。 C99 标准草案中的相关部分是 6.3.2.1 左值、数组和函数指示符 段落 2,其中说:

除非它是 sizeof 运算符、一元 & 运算符、++ 运算符、-- 运算符或 . 的左操作数的操作数。运算符或赋值运算符, 不具有数组类型的左值将转换为存储在指定对象中的值(并且不再是左值)。 […]

基本上说除了一些例外情况,操作数 被转换为存储的值,并且由于 间接 不是例外,如果在 C++ 那么它确实可以回答我的问题是的

当我试图澄清未定义行为的证明时,不如澄清左值到右值的转换是否是强制性的重要。如果我们想证明未定义的行为,我们有替代方法。 Jerry 的方法是一种常识,其中 indirection 要求表达式是指向对象或函数的指针,并且不确定的值只会偶然指向有效对象。 一般来说,C++ 标准草案没有明确声明使用不确定值是未定义的,这与 C99 草案标准不同在 C++11 和后面的标准中没有明确声明说使用不确定的值是未定义的。除了迭代器和扩展指针之外,我们确实有奇异值的概念,我们在24.2.1部分被告知:

[…][ 示例:在声明未初始化的指针 x 之后(与 int* x; 一样),必须始终假定 x 具有指针的奇异值。 —结束示例] […] 可取消引用的值始终是非单数的。

和:

无效的迭代器是可能是单数的迭代器。268

脚注 268 说:

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

在 C++1y 中 the language changes and we do have an explicit statement making the use of an intermediate value undefined with some narrow exceptions.

【讨论】:

感谢您的更新! “一般来说,C++ 标准草案没有明确声明使用不确定值是未定义的,这与 C99 草案标准不同” 我认为 CWG 1494 解决了这个问题。 sftrabbit 最近将他的 SO 名称更改为 Joseph Mansfield。除此之外,我想我没有太多要补充的。 +1 @dyp 你的意思是616?

以上是关于应用间接时,标准是不是要求指针变量的左值到右值转换?的主要内容,如果未能解决你的问题,请参考以下文章

C语言 啥叫做左值?右值?

左值的详解

C++ 传递向量(左值到右值)

C++:神一样的左值

右值到左值转换 Visual Studio

C++11 中的左值引用和右值引用的区别