为什么 $a + ++$a == 2?

65

如果我尝试这样做:

$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

但为什么不是这个输出结果呢?


23
无论您打算使用"$l + ++$l"表达什么意思,我保证有更简单的方式来表达这个意图。 - Keith Thompson
1
这是PHP中预期的行为,正如其他用户先前所述,因为++ $l在表达式的其余部分之前被评估。顺带一提:这是Andi Gutmans有时在会议中使用的问题示例。 - malko
13个回答

115

所有解释为什么会得到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语言一样。你能指出文档中哪里声明了第一个操作数首先求值吗?"


PHP表达式中的“赋值按从右到左顺序解析”的规则不会对此有所启示吗?它不会消除未定义的行为吗? - hakre
2
@hakre:右侧不一定先被计算。运算符优先级只决定在哪里添加括号,即 $l + ++$l --> $l + (++$l)。但它并不决定 + 运算符的左侧或右侧先被计算。 - interjay
结果似乎并不像这个答案所暗示的那么易变 - 早在4.3.2版本中,这个表达式就已经计算为4了。 - nickb
这个答案并没有说它是易失性行为。同一个PHP实现很可能总是返回相同的结果。但可能是哪个结果并没有指定,因此是未定义的。暗示是行为可以根据实现、版本甚至上下文而改变。你永远不会知道任何保证。 - MatBailie
这个答案对于 PHP 7 仍然有效吗?现在解析器似乎总是先执行增量/减量操作,所以如果 $a = 0; 那么 $a + $a++ 的意思是 0 + 1,$a + ++$a 的意思是 1 + 1。 - Sharak
显示剩余5条评论

66

预增运算符 "++" 在表达式的其余部分评估之前发生。 因此,实际上是这样的:

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

8
另外补充一点:将其与 echo $l + $l++; 进行比较,后者会输出像 OP 预期的那样的 1 - user212218
2
顺便提一下,PHP文档中有关于这个的记录吗?我找不到任何关于它的评估顺序与表达式其余部分的相关信息。 - Joachim Isaksson
1
@JoachimIsaksson 是的,它被记录为未定义行为。 @RyanP 不,有一些语言在这种情况下有定义行为,例如Java - kirilloid
3
操作符优先级并不能真正解释求值顺序,因为后置和前置++具有相同的优先级,但它们的求值顺序不同。 - Joachim Isaksson
8
@RyanP:请查看“未定义行为”(Undefined behaviour)链接的内容。 - ypercubeᵀᴹ
显示剩余14条评论

24
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 

相等性判断。


参见:


3
@hakre,你需要解释一下,因为你的答案不太合理。 - Toby Allen
是的,a = 1,因为当b被评估时,a是1而不是0。这对于每个人来说都不是很清楚,我编辑了答案。最后,为了好玩,我添加了一些额外的表达式。 - hakre
@hakre:我新增了一个非常相似的问题。链接 - ypercubeᵀᴹ
@hakre OT:你的print_r转换出错了。能修复一下你的链接吗? - Martin.
@Martin:它是基于 codepad viper 构建的,但 codepad viper 已发生更改。你可以在这里找到源代码:https://gist.github.com/1102761 - hakre
@hakre 是的,我也注意到了。等等,每个 Codepad Viper 条目都被删除了? - Martin.

7

我的PHP中的求值顺序博客文章对此进行了详细解释,但这里是基本概念:

  • 运算符优先级和结合性与求值顺序无关。
  • PHP不保证求值顺序。该顺序可能会在PHP版本之间不经通知更改,并且也可能因周围代码的不同而不同。
  • "通常"情况下,PHP将从左到右评估,但有例外:对于 "简单 "变量(例如$a)的访问将在执行更复杂的表达式之后才执行,无论表达式实际上发生的顺序如何。
  • 在这种特殊情况下,这意味着首先运行 ++ $a ,因为它是一个复杂的表达式,然后才获取 $a 的值(此时已经是1)。因此,您实际上正在计算 1 + 1 = 2
  • 简单变量被延迟获取的原因是编译变量(CV)优化。如果禁用此优化,例如使用@错误抑制运算符,则所有表达式都将从左到右进行评估,包括简单变量获取。
  • 在这种特殊情况下,@($a + ++$a)将导致 1 的结果,因为首先获取了$a (此时为0),然后才递增。

那不是一篇博客文章,那只是一个要点(gist):( - Madara's Ghost

6

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

所以现在 l = 1.

因此 1 + 1 = 2.


2
实际上,前缀和后缀++的优先级是相同的,因此优先级与评估顺序无关。 - Joachim Isaksson
@JoachimIsaksson 我相信他指的是 ++ 的优先级高于 + - pb149
是的,在上面的代码片段中,我们只有前缀而没有后缀。 - John3136
为什么优先级更高的操作符会先被执行? - CodesInChaos
1
说真的,什么?“优先级”这个词不让你明白吗? - John3136
3
优先级仅决定分组,求值顺序是独立的。 - Daniel Fischer

3
当你执行 ++$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。


2
这种行为可以通过检查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先被评估了。
显然,这种行为不应该被依赖;无论在哪种语言中都是如此。(原文链接)

1

1
你的代码输出会因PHP版本的不同而变化 如此显示

4.3.0-5.0.5的输出
1
1

在上述情况中,+运算符的左侧先进行评估(0、1、+)。

5.1.0-5.5.0alpha4的输出
2
1

在上述情况中,+运算符的右侧先进行评估(1、1、+)。

这符合interjay's answer的说法,在PHP中,子表达式的评估顺序不能保证。假设输出可能是 1, 1,那么声称输出可能是 1, 2的答案也是正确的。


1

正如您所知,我们有两个递增运算符,一个是前缀递增,另一个是后缀递增。前缀递增在表达式中使用变量之前增加整数的值,而后缀递增在使用变量之后增加数字的值。

假设您有变量$a和变量$b如下:

$a=0;

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接