为(x ++,y)+(Y +,X)未定义或指定,如果没有指定,那是什么计算?(Is (x++, y)

2019-10-17 12:04发布

的逗号序列操作者引入了一个顺序点的表达式中。 我想知道这是否意味着,下面的程序避免不确定的行为。

int x, y;

int main()
{
  return (x++, y) + (y++, x);
}

如果它避免不确定的行为,它可能仍然是不确定,就是返回几个可能值之一。 我认为,在C99,它只能计算1 ,但实际上,GCC的各种版本编译这个程序将返回一个可执行文件2 。 锵生成返回一个可执行文件1 ,用我的直觉显然同意。

最后,就是这个东西,在C11改变?

Answer 1:

就拿表达式:

(x++, y) + (y++, x)

评估左到右:

x++  // yield 0, schedule increment of x
,    // sequence point: x definitely incremented now
y    // yield 0
y++  // yield 0, schedule increment of y
// explode because you just read from y and wrote to y
// with no intervening sequence point

没有什么在这个禁止该标准的,所以整个事情是未定义行为。

对比这伪代码:

f() { return x++, y; }
g() { return y++, x; }
f() + g()

Acoording至C99(5.1.2.3/2)的调用fg本身算作副作用,和函数调用操作符包含在进入一个函数之前的序列点。 这意味着功能的执行不能交错。

根据“评估并行的东西”的模式:

f()  // arbitrarily start with f: sequence point; enter f
g()  // at the same time, start calling g: sequence point

由于执行f计数作为副作用本身,在该序列点g()暂停执行,直到f已经返回。 因此,没有任何不确定的行为。



Answer 2:

该标准的全篇6.5提及评价的顺序,运营商的基础上。 我能找到评估秩序的最好的总结是标准的(非规范)附件J:

C11附件J

J.1未指定的行为

  • (6.5),和逗号运营商:其中的子表达式的计算顺序,并且其中的副作用发生,除了作为函数调用()指定的,&&,||,订单?

在你的例子,可以不知道是否子表达式(x++, y)(y++, x)首先被评估,因为+运算的操作数的计算顺序是不确定的。

至于不确定的行为,逗号运算解决什么。 如果(x++, y)先计算,然后y可能会立即之前评价y++其他子表达的。 因为y是在之间,没有一个顺序点访问两次用于其他目的,而不是确定的值来存储,该行为是未定义的。 这里更多的信息。

所以,你的程序不定,不确定的行为两者。

(另外,它具有实现定义的行为,因为INT主要(),而不是INT主要(无效)不是主要的托管应用的良好定义的版本之一。)



Answer 3:

我的理解是,任何下面的评测命令是合法的:

  • x++, y++, y, x
  • x++, y, y++, x
  • x++, y++, x, y
  • y++, x, x++, y
  • ...

但不是:

  • y, x++, x, y++
  • ...

在我的理解,这是唯一需要的顺序是(x++)前一定要来(y)(y++)必须出现在(x)

因此,我认为,这仍然是不确定的行为。



Answer 4:

标准中明确声明了运营商引入序列点, +不在其中。 所以,你的表达能很好的方式认定是禁止的子表达式求值“接近”对方,没有它们之间的序列点。

上的语用的基础上,存在用于此遮断一个原因,即这样的操作的操作数的评价可以并行,例如被调度如果处理器具有用于所讨论的子表达式的操作数管线。 您可以轻松地说服自己,评价这样的模式下,任何事情都有可能发生,其结果是不可预测的。

在你的玩具例子,冲突是可以预见的,但不一定是,如果你将在子表达式使用一些间接指针的情况。 那么你就不会知道,如果子表达式中的一个将潜在的别名等。 因此,基本上没有多少,如果它要允许这种类型的优化C能做到。



文章来源: Is (x++, y) + (y++, x) undefined or unspecified, and if unspecified, what can it compute?