PHP foreach循环使用&引用的小坑

源起:今天有小🔥伴发了一段 PHP 代码问执行结果和常规逻辑思考🤔结果为啥么不一样。代码如下,当时只是说和 foreach 使用 & 有关。也没深入思考到底是什么关系,午睡过后,心血来潮的想搞搞这个问题,故做如下记录。

小伙伴给的原始代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

$arr = [1, 2, 4, 8];
foreach ($arr as &$val) {
//四次循环迭代后依次输出 1, 2, 4, 8; 没任何毛病
echo $val . PHP_EOL;
}

print_r($arr);
echo "************************" . PHP_EOL;
foreach ($arr as $val) {
print_r($arr);

echo $val . PHP_EOL;
echo "###################" . PHP_EOL;
}

//echo 依次打印的是: 1,2,4,4 [(O_O)]?

代码执行结果如下图

那么问题来了,第一次输出 1,2,4,8 没有任何问题。但是,但是,但是为什么第二次输出的是 1, 2, 4, 4 而不是 1、2 4、8 呢?查询 PHP foreach 文档 有辣么一句特别提醒

Warning 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁。

看此提示,冥冥之中好像知道了答案。

  • 第一次 foreach 由于使用的是 & 引用赋值,在循环完成后如果没有 unset($val) 会保留最后一个元素的 $val引用
  • 第二次 foreach 由于使用的和第一次同样的迭代变量 $val, 数组的最后一个元素 $arr[3] 和当前循环迭代值 $val 指向同一个变量地址,此时 $val 值改变时 $arr[3] 的值也跟着改变了。

用上图说明

  • 原始数组为图中 A区域 数组

  • 0 次循环迭代:最后一个元素 $arr[3]$val 即($arr[0]) 值相同。$arr[3] = $val = 1,如图中 0区域

    1
    2
    3
    4
    5
    6
    7
    8
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 4
    [3] => 1
    )
    第0次循环结果:1
  • 1 次循环迭代:最后一个元素 $arr[3]$val 即($arr[1]) 值相同。$arr[3] = $val = 2,如图中 1区域

    1
    2
    3
    4
    5
    6
    7
    8
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 4
    [3] => 2
    )
    第1次循环结果:2
  • 2 次循环迭代:最后一个元素 $arr[3]$val 即($arr[2]) 值相同。$arr[3] = $val = 4,如图中 2区域

    1
    2
    3
    4
    5
    6
    7
    8
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 4
    [3] => 4
    )
    第2次循环结果:4
  • 3 次循环迭代:最后一个元素 $arr[3]$val 即($arr[3]) 值相同。$arr[3] = $val = 4,如图中 3区域

    1
    2
    3
    4
    5
    6
    7
    8
    Array
    (
    [0] => 1
    [1] => 2
    [2] => 4
    [3] => 4
    )
    第2次循环结果:4
  • 综上,第二次循环 1, 2, 4, 4 就是这么来的 😜