引用

Updated: 2019-01-06

入门

坑!

先创建一个简单的数组:

php > $a = array(1, 2, 3);

然后用 foreach 逐个打印出来,先试试用引用

php > foreach ($a as &$v) { print $v."\n"; }
1
2
3

好像没问题,再试试值传递:

php > foreach ($a as $v) { print $v."\n"; }
1
2
2

为什么最后一个是 2 而不是 3?!

进阶

下面我们看看分解动作

第一个 foreach

相当于先让$v指向 a 的第一个值,$a[0]就变成了引用,注意下面打印结果中的[0]=>&int(1)

php > foreach ($a as &$v) { var_dump($a); break; }
array(3) {
  [0]=>
  &int(1)
  [1]=>
  int(2)
  [2]=>
  int(2)
}

下一步 v 指向 a[1], a[1]就变成了引用,

php > foreach ($a as &$v) { if ($v === 2) {var_dump($a); break; } }
array(3) {
  [0]=>
  int(1)
  [1]=>
  &int(2)
  [2]=>
  int(2)
}

而由于没有任何变量指向 a[0]a[0] 恢复正常,为了演示这一点,可以另创建一个变量指向 a[0],那即使 v 不再指向 a[0]a[0] 依然是引用变量:

php > $t = &$a[0];
php > foreach ($a as &$v) { if ($v === 2) {var_dump($a); break; } }
array(3) {
  [0]=>
  &int(1)
  [1]=>
  &int(2)
  [2]=>
  int(2)
}

如果你运行了上面的语句记得先把 t 扔掉:

php > unset($t);

当三个循环都结束的时候,v 并没有消失,a[2]仍然是引用变量

php > foreach ($a as &$v) {}
php > var_dump($a);
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  &int(2)
}
php > var_dump($v);
int(2)

第二个 foreach

第一步$v仍然指向$a[2],这时foreach相当于运行$v = $a[0],结果$a[2]被赋上了$a[0]的值:

php > foreach ($a as $v) { var_dump($a[2]); break; }
int(1)

$a已经不是原来的[1, 2, 3]了,变成了[1, 2, 1]。第二步类似的,foreach$v = $a[1], 也就是$a[2] = $a[1], $a[2]的值再次改变,成了 2

php > foreach ($a as $v) { if ($v === 2) { var_dump($a[2]); break; } }
int(2)

$a变为[1, 2, 2]

第三步,$v = $a[2],也就是$a[2] = $a[2],所以$a和上一步相同,还是[1, 2, 2].

解决方法

每次使用引用后记得unset,否则会出现这样很隐蔽的 bug。

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

更好的方法是,能不用引用就不要用引用...