代码之家  ›  专栏  ›  技术社区  ›  mlvljr

“读取”POD预增量结果不会产生未定义的行为。为什么呢?

  •  7
  • mlvljr  · 技术社区  · 14 年前

    这是个愚蠢的问题。 :)

    [编辑:愚蠢或不,这原来是一个C++特殊问题,见UpDATE2]

    假设我们有:

    int a = 0; // line 1
    int b = ++a; // line 2
    

    第二行发生的是( 注意,数字只是标记,不能指定确切的顺序。 ):

                          = [1: write result of (3) to result of (2)]
                         /\
    [2: take "b" l-value] [3: convert result of (4) to an r-value ]
                          |
                          [4: take "a" l-value, "increment" and return it]
    

    (4)中的“写入”在(3)中的“读取”之前是“有序的”,并且由于在(3)之间没有序列点,因此不保证副作用在(3)之前发生(在(4)本身中也有“读取”,但在(3)之前是有序的 之前 “写”,这样就不会产生ub)。

    那么,上面的错误在哪里呢?

    [更新,针对没有经验的序列点律师:)

    换句话说,问题是:

    1. 无论是L值到R值转换(“读取”)还是增量(“写入”)副作用首先发生,似乎都存在“竞争”。

    2. 在C语言中, 会给你一个UB ,根据 JTC1/SC22/WG14 N926 "Sequence Point Analysis" *(例如,参见示例5: int x,y; (x=y) + x; // UB )

    3. 注:如果使用后增量,则不会出现这种情况,因为(3)和(4)将构成一个 [(3):取“a”l值,转换为r值并返回r值] 由于“写入”副作用延迟到下一个序列点之前的某个位置

    γ

    (*)这似乎是C99标准委员会成员给出的主题最清晰的系统基础。

    [更新\2]

    1. 经验教训:永远不要用C规则判断C++)。我确实在想,为什么N926(它清楚地描述了C99的工作方式)在产生L值的预增量问题上“不够清楚”。

    2. 问题是,如何建立一个类似的理由C++,因为没有一个,因为即使在C的情况下,只是解释标准是相当困难的,和C++语言标准更复杂和晦涩难懂。

    [更新\3]

    有一个讨论涉及一些相关主题(至少在较新的部分),在 "The undefinedness of a common expression." ,再加上这件事由委员会成员讨论。 here (见“222.序列点和左值返回运算符”)。

    2 回复  |  直到 13 年前
        1
  •  2
  •   Johannes Schaub - litb    14 年前

    我认为解决办法可能是用“++I”这个词。它表示“该值是操作数的新值;它是左值”。行为在5/4中被定义为“此外,只有确定要存储的值才能访问先前的值”。

    所以,我们不是访问先前的,而是访问新的值。然后我们会好起来的。不过,这似乎是未定义行为和已定义行为之间的一条很细的线。

    实际上,“先前值”在我看来就像“对象在先前序列点的值”。如果这样解释,那么这个结构看起来是未定义的。但是,如果我们直接比较5.3/2到5/4中“++i”的措词,我们会遇到“新值”和“先验值”,事情会“弯曲”到定义的行为(“++i”会在下一个序列点偷看“i”的值,并产生该值作为“++i”的结果左值的内容)。

        2
  •  1
  •   John Marshall    14 年前

    C++ 5/4中的主要句子是

    在上一个序列点和下一个序列点之间,标量对象的存储值应通过表达式的计算最多修改一次。

    (约翰内斯引用的句子是下面的从属句子。)

    您怀疑哪个标量对象的存储值在此处被多次修改? a b 在第2行中每修改一次,所以没有问题。

    (C标准中6.5/2中有类似的语言。)


    编辑:

    (4)中的“写入”在(3)中的“读取”之前是“有序的”,并且由于在(3)之前没有序列点,因此副作用不能保证发生。

    在重新阅读你的问题时,我认为困惑来自于一种困惑的思考方式 ++a :表达式没有真正窥视 . 相反,这可能有助于我们思考 +a 作为“返回” a+1 作为副作用增量 ,在您空闲时(在下一个序列点之前)”。

    那么,谁在乎这种副作用是发生在(3)之前还是之后呢?表达式的值(即输入到(3)的值)已经确定。