前循环和 &$value 的引用

2022-08-30 16:35:35

为什么空的 foreach 循环可以改变结果。

我有以下代码:

$variable = [1,2,3,4];
foreach ($variable  as $key => &$value) 
  $value ++;

var_dump($variable);

我得到的结果是:

array (size=4)
  0 => int 2
  1 => int 3
  2 => int 4
  3 => &int 5

现在,当我添加一个空的 foreach 循环时,如下所示:

$variable  = [1,2,3,4];
foreach ($variable  as $key => &$value) 
  $value ++;

foreach ($variable  as $key => $value);

var_dump($variable);

我得到这个 :

array (size=4)
  0 => int 2
  1 => int 3
  2 => int 4
  3 => &int 4

有人可以解释为什么当我添加第二个空循环时,最后一个元素没有变化,以及为什么最后一个元素前面有一个&?


答案 1

在第一个循环的末尾,指向与(它们指向内存中的相同位置)相同的位置$value$variable[3]

$variable  = [1,2,3,4];
foreach ($variable  as $key => &$value) 
    $value ++;

即使此循环已完成,它仍然是指向 内存中与 相同的位置的引用,因此每次在 中存储值时,这也会覆盖为 存储的值:$value$variable[3]$value$variable[3]

foreach ($variable as $key => $value);
var_dump($variable);

随着对这个前额的每次评估,两者都变得等于可迭代项的价值,$variable。$value$variable[3]

因此,在第二个循环的第三次迭代中,并通过引用等于4,然后在第二个循环的第4次也是最后一次迭代期间,没有任何变化,因为您将(仍然是)的值传递给(仍然是)。$value$variable[3]$variable[3]&$value$value&$value

这是非常令人困惑的,但它甚至没有一点点特殊性;它是完全按照预期执行的代码。

更多信息在这里: PHP: 通过引用传递


若要防止此行为,只需在使用它的每个循环之后添加一个语句就足够了。的替代方法可能是将循环包含在自调用闭包中,以强制为本地,但执行此操作所需的额外字符量大于仅取消设置它:unset($value);unsetforeach$value

(function($variable){
   foreach ($variable  as $key => &$value) $value++;
})($variable);


答案 2

这是一个名称冲突:在第一个循环中引入的名称$value存在于它之后,并在第二个循环中使用。因此,对它的所有赋值实际上都是对原始数组的赋值。在此代码中,您执行的操作更容易观察到:

  $variable = [1,2,3,4];
  foreach ($variable  as $key => &$value) 
    $value ++;
  $value = 123; // <= here you alter the array!
  var_dump($variable);

您将看到 .$variable[3]123

正如其他人所说,避免这种情况的一种方法是在循环之后,这应该是手册建议的良好做法。另一种方法是在第二个循环中使用另一个变量:unset ($value)

  $variable  = [1,2,3,4];
  foreach ($variable  as $key => &$value) 
    $value ++;
  foreach ($variable  as $key => $val);
  var_dump($variable);

这不会改变你的数组。