关于PHP foreach引用引起的古怪问题笔记分析。

echo "<pre>";
$arr = array('one' => 1, 'two' => 2, 'three' =>3);
               //此处引用符号注意
foreach($arr as &$val){

}
               //此处没有引用符号
foreach($arr as $val){

}
var_dump($arr);//猜猜输出什么。

这种循环应该会出现在很多程序员的代码中, 然后引发很多古怪的问题,然后百思不得姐。

如果已经知道的就可以走人了。 还不太清楚的继续往下看, 我们把输出改成

echo "<pre>";
$arr = array('one' => 1, 'two' => 2, 'three' =>3);
              //此处引用符号注意
foreach($arr as &$val){
    var_dump($arr, $val);
}
                //此处没有引用符号
foreach($arr as $val){
    var_dump($arr, $val);
}

输出结果为

array(3) {
  ["one"]=>
  &int(1)
  ["two"]=>
  int(2)
  ["three"]=>
  int(3)
}
int(1)
array(3) {
  ["one"]=>
  int(1)
  ["two"]=>
  &int(2)
  ["three"]=>
  int(3)
}
int(2)
array(3) {
  ["one"]=>
  int(1)
  ["two"]=>
  int(2)
  ["three"]=>
  &int(3)
}
int(3)
array(3) {
  ["one"]=>
  int(1)
  ["two"]=>
  int(2)
  ["three"]=>
  &int(1)
}
int(1)
array(3) {
  ["one"]=>
  int(1)
  ["two"]=>
  int(2)
  ["three"]=>
  &int(2)
}
int(2)
array(3) {
  ["one"]=>
  int(1)
  ["two"]=>
  int(2)
  ["three"]=>
  &int(2)
}
int(2)

很明显了结果。

我们一步步来分析, 第一次循环, 分别遍历1,2,3没错了很好理解

因为使用的是&引用符号

foreach遍历 ‘one’ => 1 的时候, 即 $val = &$arr[‘one’], //$arr[‘one’]值是1

遍历 ‘two’ => 2 的时候, 即 $val = &$arr[‘two’],

//这个时候PHP内部写时分离, 就是$val解除了对$arr[‘one’]的引用, 变成引用 $arr[‘two’]的地址, 同时值也就是2,

遍历 ‘three’ => 3 的时候, 即 $val = &$arr[‘three’],

//同上, $val解除了对$arr[‘three’]的引用, 变成引用 $arr[‘three’]的地址, 同时值也就是3

好了, 下面到第二次foreach循环了,但是第二次foreach循环是没有&引用符号的

第二次foreach开始循环, 遍历 ‘one’ => 1 的时候, 即 $val = 1, 同时 $val也是 ‘three’ => 3的引用, 这就同时把 ‘three’ => 3 赋值成 ‘three’ => 1, 这是为什么看到输出结果是&int(1)的原因了。

第二次foreach再遍历 ‘two’ => 2 的时候, 即 $val = 2, 同时 $val也是 ‘three’ => 1的引用, 这就同时把 ‘three’ => 1 赋值成 ‘three’ => 2, 这是为什么看到输出结果是&int(2)的原因。

最后一次遍历 ‘three’ => 2 (前面已经改变成2)的时候,很简单, $val = 2, 引用的是自己, 也就是2, 这次看到最后的输出结果是 &int(2), 所以整个过程是这样的。

为什么输出成那样也就清楚明白啦, 因此, 不推荐在foreach里使用&引用符号, 当然, 如果数组的值是占用很大内存, 使用&引用符号能加快速度(这个普遍是这么理解, 本人没有做个详细测试…) ,   如果使用了&引用符号, 请及时在foreach完成后 unset($val)解除引用, 这样就不会出现后面古怪问题了。

 

 

#######

最近开了个技术群, 有兴趣的朋友进来吹牛逼吧, QQ群号221073018

1 comment

Leave a Reply

Your email address will not be published.