序列点和部分顺序

几天前, 这里有一个关于这个expression的讨论

我= ++ i + 1

调用UB(未定义的行为)或不。

最后得出的结论是它调用UB,因为'i'的值在两个序列点之间不止一次地改变。

我参与了与Johannes Schaub的讨论。 按照他的说法

i =(i,i ++,i)+1 ——(1)/ *也调用UB * /

我说(1)不会调用UB,因为前面的子expression式的副作用是由逗号操作符','在i和i ++之间以及在i ++和i之间清除的。

然后他给出了以下解释:

“是的,在i ++之前完成所有副作用之后的顺序点,但是没有任何东西阻止了分配副作用与i ++的副作用重叠。 根本的问题是分配的副作用没有被指定为在在对赋值的两个操作数进行评估之前,所以序列点在保护这一点上不能做任何事情:序列点引起一个偏序:只是因为在++之后和之后有一个序列点并不意味着所有的副作用都被序列化关于我

另外,请注意,仅仅一个序列点就意味着什么:评估顺序不是由代码forms决定的。 这是由语义规则决定的。 在这种情况下,没有语义规则说明何时分配副作用发生在评估这些操作数的操作数或子expression式方面。

以“大胆”写的陈述让我感到困惑。 我所知道的:

“在执行顺序中的某些特定点称为顺序点,以前评估的所有副作用应该是完整的,并且不会发生后续评估的副作用。

因为,逗号运算符也指定了执行顺序,当我们到达最后的i时,i ++的副作用已经被取消了。如果没有指定评估顺序,那么他(Johannes)会是正确的(但是在逗号运算符的情况下, )。

所以我只想知道(1)是否调用UB? 有人能给出另一个有效的解释吗

谢谢!

C标准说的关于赋值运算符(C90 6.3.16或C99 6.5.16赋值运算符):

更新左操作数存储值的副作用应发生在前一个序列点和下一个序列点之间。

在我看来,在声明中:

i=(i,i++,i)+1; 

赋值运算符的'previous'之前的序列点将是第二个逗号运算符,'next'序列点将是该expression式的结尾。 所以我会说,expression式不会调用未定义的行为。

但是,这个expression式:

 *(some_ptr + i) = (i,i++,i)+1; 

会有未定义的行为,因为赋值运算符的2个操作数的赋值次序是不确定的,在这种情况下,而不是赋值运算符的副作用发生时的问题,问题是您不知道我用左手柄操作数将在右手边之前或之后进行评估。 评估问题的顺序在第一个例子中不会出现,因为在那个expression式中, i的值并没有在左边实际使用 – 赋值操作符感兴趣的全部就是i的“左值” 。

但我也认为这一切都是粗略的(我对这些细微差别的理解足够粗略),如果有人能够说服我(否则),我不会感到惊讶。

我相信下面的expression式肯定有不确定的行为。

 i + ((i, i++, i) + 1) 

原因在于逗号运算符在括号内的子expression式之间指定了序列点,但是没有指定在该序列中哪里出现+的左侧操作数的评估。 一种可能性是在i++周围的序列点之间,这违反了5/4,因为i在两个序列点之间写入,但是也在相同的序列点之间读取两次,而不仅仅是确定要存储的值,而且还要确定第一个操作数的值给+运算符。

这也有不确定的行为。

 i += (i, i++, i) + 1; 

现在,我不太确定这个说法。

 i = (i, i++, i) + 1; 

尽pipe同样的原则适用, i必须被评估为一个可修改的左值,并且可以在任何时候这样做,但是我不相信它的价值是作为它的一部分被读取的。 (还是有另一个限制,expression违反导致UB?)

子expression式(i, i++, i)作为确定要存储的值的一部分而发生,并且该子expression式包含在将值存储到i之后的序列点。 我没有看到任何方式,这将不需要在确定要存储的值之前完成i++的副作用,因此可能发生分配副作用的最早可能点。

在这个sequnce点之后, i的值最多被读取一次,只是为了确定将存储回i ,所以最后一部分没有问题。

i=(i,i++,i)+1 ------ (1) /* invokes UB as well */

它不会调用未定义的行为。 i++的副作用将在下一个序列点的评估之前进行,该序列点由后面的逗号表示,也在分配之前。

不错的语言数独,但。 🙂

编辑:这里有一个更详细的解释。

我在开始时对约翰内斯的发言感到困惑,但他提到:

 i = (i, ++i, i) +1 

<Johannes>
如果<a>是赋值,并且是一个增量。 :s:是一个序列点,那么副作用可以在序列点之间按如下顺序排列: (i :s: i++< a ><n> :s: i) + 1 。 标量i的值在这里的第一个和第二个序列点之间改变了两次。 赋值和增量发生的次序是未指定的,因为它们之间没有序列点,所以它们之间不是primefaces的。这是这些副作用的非指定顺序所允许的一个允许的sorting。

这与(i++, i++) ,因为两个子expression式的评估顺序是从左到右的,在它们之间的顺序点上,前一个评估的增量应该是完整的,下一个增量还没有发生在。 这强制了两个序列点之间的i值没有变化,这使得(i++, i++)有效
</ Johannes>

这使我认为由litb提到的序列是无效的,因为按照C99:

6.5.16.1(2)在简单赋值(=)中,右操作数的值被转换为赋值expression式的types,并replace左操作数指定的对象中存储的值。

即在赋值副作用之前需要知道右操作数的值(修改存储在对应于左操作数的对象中的值)

6.5.17(2)逗号运算符的左操作数被评估为voidexpression式; 评估后有一个顺序点。 然后评估右操作数; 结果有它的types和价值。

即逗号操作的最右边的操作数需要进行评估,以知道逗号expression式的值和types(以及我的示例中右操作数的值)。

因此,在这种情况下,分配副作用的“上一个顺序点”实际上是最右边的逗号操作。 Johannes提到的可能的序列是无效的。

如果我错了,请纠正我。