php 使用引用时foreach的奇怪行为:foreach($a as &$v){.}

fcg9iug3  于 2023-09-29  发布在  PHP
关注(0)|答案(3)|浏览(94)
<?php
  $a = array('a', 'b', 'c', 'd');

  foreach ($a as &$v) { }
  foreach ($a as $v) { }

  print_r($a);
?>

我认为这是一个正常的程序,但这是我得到的输出:

Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => c
)

有人能给我解释一下吗?

e5njpo68

e5njpo681#

这是一个有很好文档记录的PHP行为,请参阅php.net foreach页面上的warning

警告

即使在 foreach 循环之后,$value 和最后一个数组元素的引用仍然存在。建议使用unset()销毁它。

$a = array('a', 'b', 'c', 'd');

foreach ($a as &$v) { }
unset($v);
foreach ($a as $v) { }

print_r($a);

编辑

尝试一步一步地指导这里实际发生的事情

$a = array('a', 'b', 'c', 'd');
foreach ($a as &$v) { }   // 1st iteration $v is a reference to $a[0] ('a')
foreach ($a as &$v) { }   // 2nd iteration $v is a reference to $a[1] ('b')
foreach ($a as &$v) { }   // 3rd iteration $v is a reference to $a[2] ('c')
foreach ($a as &$v) { }   // 4th iteration $v is a reference to $a[3] ('d')

                          // At the end of the foreach loop,
                          //    $v is still a reference to $a[3] ('d')

foreach ($a as $v) { }    // 1st iteration $v (still a reference to $a[3]) 
                          //    is set to a value of $a[0] ('a').
                          //    Because it is a reference to $a[3], 
                          //    it sets $a[3] to 'a'.
foreach ($a as $v) { }    // 2nd iteration $v (still a reference to $a[3]) 
                          //    is set to a value of $a[1] ('b').
                          //    Because it is a reference to $a[3], 
                          //    it sets $a[3] to 'b'.
foreach ($a as $v) { }    // 3rd iteration $v (still a reference to $a[3]) 
                          //    is set to a value of $a[2] ('c').
                          //    Because it is a reference to $a[3], 
                          //    it sets $a[3] to 'c'.
foreach ($a as $v) { }    // 4th iteration $v (still a reference to $a[3]) 
                          //    is set to a value of $a[3] ('c' since 
                          //       the last iteration).
                          //    Because it is a reference to $a[3], 
                          //    it sets $a[3] to 'c'.
wnvonmuf

wnvonmuf2#

第一个foreach循环不会对数组进行任何更改,正如我们所期望的那样。但是,它确实会导致$v被分配到$a的每个元素的引用,因此,在第一次循环结束时,$v实际上是对$a[2]的引用。
第二个循环一开始,$v就被分配了每个元素的值。但是,$v已经是$a[2];的引用,因此,任何赋给它的值都会自动复制到数组的最后一个元素中!
因此,在第一次迭代期间,$a[2]将变为0,然后变为1,然后再次变为1,有效地复制到自身上。为了解决这个问题,你应该总是取消你在foreach循环中使用的变量的设置,或者,更好的是,完全避免使用前者。

0s0u357o

0s0u357o3#

引用变量不一定是循环变量。例如(这看起来可能有些做作,但当然是对我的程序的过度简化),

$list = [ 'one', 'two', 'three', 'four' ] ;
$results = [] ;
foreach ($list as $item) {
    $newitem = $item ;
    $results[] = &$newitem ;
#    unset($newitem) ;
}
var_dump($results) ;

指纹

array(4) {
  [0]=>
  &string(4) "four"
  [1]=>
  &string(4) "four"
  [2]=>
  &string(4) "four"
  [3]=>
  &string(4) "four"
}

因为$results的每个元素都指向$newitem,在每次迭代中,$newitem是 * 相同的变量 ,并且 * 在每次迭代中 * 被更改为指向列表的“当前”元素-因此在末尾它指向最后一个。
注解的unset()修复了程序,确保我们在每次迭代中都有一个“新鲜的”$newitem,总结是,
注意引用的范围!*

相关问题