Why is $a + ++$a == 2?


Why is $a + ++$a == 2?

如果我尝试这个:

$a = 0;    
echo $a + ++$a, PHP_EOL;
echo $a;

我得到这个输出:

2
1

演示:http://codepad.org/ncVuJtJu

为什么?

我希望将其作为输出:

1
1

我的理解:

$a = 0;                    // a === 0    
echo $a + ++$a, PHP_EOL;   // (0) + (0+1) === 1
echo $a;                   // a === 1

但为什么这不是输出呢?

所有解释为什么你得到 2 而不是 1 的答案实际上是错误的。根据 PHP 文档,以这种方式混合+++是未定义的行为,因此您可以获得 1 或 2。切换到不同版本的 PHP 可能会改变你得到的结果,它同样有效。

请参阅示例 1,其中说:

// mixing ++ and + produces undefined behavior
$a = 1;
echo ++$a + $a++; // may print 4 or 5

笔记:

  1. 运算符优先级决定计算顺序。运算符优先级仅确定表达式$l + ++$l被解析为$l + (++$l),但不确定是先计算+运算符的左操作数还是右操作数。如果首先计算左操作数,则结果为 0+1,如果先计算右操作数,则结果为 1+1。

  2. 运算符关联性也不决定计算顺序。+运算符具有左关联性仅确定$a+$b+$c被评估为($a+$b)+$c。它不确定以什么顺序计算单个运算符的操作数。

同样相关的是:在这个关于另一个结果未定义的表达式的错误报告中,一位PHP开发人员说:"我们不保证评估的顺序[...],就像C不保证一样。你能指出文档中任何说明首先计算第一个操作数的地方吗?

预递增运算符"++"发生在它所在的表达式的其余部分计算之前。 所以它实际上是:

echo $l + ++$l; // (1) + (0+1) === 2
a + b
a = 1
b = ++a
:= 2

你为什么期待别的东西?

在 PHP 中:

$a = 0;
$c = $a + ++$a;

运算符优先级可视化:

$c = ($a) + (++$a);

评估顺序可视化:

$a = 0; ($a = 0)
$a = 1; (++$a)
$c = $a + $a (1 + 1);

或写出:

执行求和运算的那一刻,$a已经是 1,因为已经计算了++$a++运算符先于+运算符计算。

<小时 />

为了好玩:

$a++ + ++$a

结果也是 2。但是,如果将其作为表达式进行比较,则不相等:

$a++ + ++$a == $a + ++$a

其中作为

$a++ + ++$a == $a-- + --$a 

是"平等的"。

<小时 />

另请参阅:

  • PHP 中的求值顺序 (2013 年 9 月;由 NikiC( (via(

我在 PHP 中的求值顺序博客文章详细解释了这一点,但这是基本思想:

  • 运算符优先级和关联性与计算顺序无关。
  • PHP 不保证求值顺序。顺序可以在PHP版本之间更改,恕不另行通知,也可能根据周围的代码而有所不同。
  • "通常"PHP 将从左到右计算,除了访问"简单"变量(如 $a (。对简单变量的访问将在更复杂的表达式之后执行,而不管表达式实际出现的顺序如何。
  • 在这种特殊情况下,这意味着首先运行++$a,因为它是一个复杂的表达式,然后才获取 $a 的值(此时它已经是 1(。如此有效地求和1 + 1 = 2.
  • 在复杂表达式之后获取简单变量的原因是编译变量 (CV( 优化。如果禁用此优化(例如,通过使用 @ 错误抑制运算符(,则会从左到右计算所有表达式,包括简单变量提取。
  • 在这种特殊情况下,这意味着@($a + ++$a)将导致1,因为第一个$a被获取(当时为 0(并在此之后递增。

>++是优先级较高的运算符,因此首先应用它。

所以现在l = 1.

所以1 + 1 = 2.

当你做你的++$l(前递增(时,它将在你的加法之前完成 ->检查运算符优先级(。

因此,$l的值将在您添加之前1

echo $l + ++$l; // $l => 1  because ++$l is done first

所以你的答案将是2。

但是当你这样做时:

echo $l // you will get your first value which is $l => 1

所以你的答案将是1。

可以通过检查 PHP 如何编译脚本来确认此行为,例如:

$a = 0;
echo $a + ++$a;

编译成以下操作码,然后执行这些操作码:

compiled vars:  !0 = $a
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   1     0  >   ASSIGN                                                   !0, 0
         1      PRE_INC                                          $1      !0
         2      ADD                                              ~2      !0, $1
         3      ECHO                                                     ~2
         4    > RETURN                                                   null

这将转换为以下等效脚本:

$a = 0;              // ASSIGN
$tmp = ++$a;         // PRE_INC
echo $a + $tmp;      // ADD, ECHO

结论

$a被评估为$a + (++$a)的左手表情时,它已经被递增了,因为++$a首先被评估了。

显然,这种行为不应该被依赖;在任何语言中。

查看增量操作手册:

http://www.php.net/manual/en/language.operators.increment.php

或者看这个代码板:http://codepad.org/Y3CnhiLx

<?php
$n = 0;
$m = 0;
echo '++ before:';
echo $n+ ++$n;
echo PHP_EOL;
echo '++ after:';
echo $m+ $m++;
echo PHP_EOL;
echo 'n:'.$n;
echo PHP_EOL;
echo 'm:'.$m;

输出:

++ before:2
++ after:1
n:1
m:1

您可能知道我们有两个增量运算符,一个是前增量,第二个是后增量。前递增在表达式中使用整数之前增加整数的值,另一方面在表达式中使用后递增增加数字的值。

假设您有变量$a和可变$b,如下所示

$a=0;

$b=++$a给出 b=1 的值

$b=$a++ 给出值 b=0

代码的输出因 PHP 版本而异,如下所示

4.3.0 - 5.0.5
的输出
1 1

在上述情况下,首先计算+运算符的左侧 (0, 1, +(。

5.1.0 - 5.5.0alpha4
的输出 阿拉伯数字
1

在上述情况下,首先计算运算符+右侧 (1, 1, +(。

这与interjay的回答一致,即在PHP中无法保证子表达式的计算顺序。输出可以1, 1的假设是正确的,声称输出可以1, 2的答案也是如此。

第一个明显的部分是++的优先级高于+

第二部分是 php 引擎不会将第一个操作数的值存储到另一个匿名变量中。所以$l + ++$l不是

$a = $l;
$b = ++$l;
return $a + $b;
<</div> div class="answers">

如前所述,x++ 和 ++x 是有区别的。你可以这样解释它:

x++;

分号后的增量

++x;

表达式求值时的增量

所以似乎你的表情是从右到左评估的

echo $l + ++$l;
  1. 获取$l:$l = 0
  2. 应用 ++: ++$l = 1
  3. 获取$l:$l = 1
  4. 应用 +: $l + $l = 1
  5. + 1 = 2

所有语句从右到左执行。因此,该值首先递增,变量的值是 = 1,因此 1+1=2