x = y = z = 1;
z = ++x||++y&&++z;
运算符优先级如下——
(pre-increment) > && > ||
所以答案应该是——
1. 2||2 && 2
2. 2||1
3. 1
print x,y,z should be 2,2,1
但是,答案是 2,1,1。
x = y = z = 1;
z = ++x||++y&&++z;
运算符优先级如下——
(pre-increment) > && > ||
所以答案应该是——
1. 2||2 && 2
2. 2||1
3. 1
print x,y,z should be 2,2,1
但是,答案是 2,1,1。
优先级与评估顺序不同。优先级只是确定哪些操作数和运算符属于一起。评估的确切顺序是未指定的,除了逻辑运算符,它们以严格的从左到右的顺序进行评估以启用短路。
所以因为&&
具有更高的优先级,所以首先将变量分组如下:
(++x) || (++y && ++z)
然后,按照从左到右的顺序++x
进行评估。鉴于这++x
是真的,就知道整个表达式是真的。所以表达式求值是短路的。 (++y && ++z)
永远不会被评估。因此 y 和 z 永远不会增加。
带有逻辑运算符&&
并||
从左到右求值的表达式:
C99,第 6.5.14-4 节与按位运算
|
符不同,运算||
符保证从左到右的求值;在计算第一个操作数之后有一个序列点。如果第一个操作数比较不等于0
,则不计算第二个操作数。
由于x++
不为零,因此表达式会短路对 右侧的所有内容的评估||
,包括它们的副作用。这就是为什么 onlyx++
被评估,所以x
成为2
。其余变量1
应保持在 。
表达式中没有序列点:
z = ++x || ++y && ++z;
在 的预增量z
和分配到之间z
。
因此,如果++z
实际评估了 ,那会使您立即进入未定义的行为领域,并且任何事情都可能发生。在没有中间序列点的情况下,不允许您两次修改同一个对象。附件 C(来自 C99)列出了所有的序列点,这里的控制点遵循一个完整的表达式(整个计算和赋值)。
6.5 Expressions /2
状态:
在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的评估修改一次。此外,应仅读取先验值以确定要存储的值。
但是,给定您的初始值为x
1,++z
在这种特殊情况下,不会评估表达式的一部分。这并没有降低表达式本身的危险性,因为它会在起点为x == -1
and的情况下调用 UB y != -1
。
在这种情况下,标准的控制部分是6.5.14 Logical OR operator /4
:
不像按位 | 运算符,|| 运算符保证从左到右的评估;在计算第一个操作数之后有一个序列点。如果第一个操作数比较不等于 0,则不计算第二个操作数。
因此,++x
首先评估的是,因为它评估为非零,++y && ++z
所以永远不会评估。x
递增2
并z
设置为该值的“真实”值,或者1
,y
在 处保持不变1
。