• 在 foreach 里使用引用要注意的陷阱

    PHP的引用计数,写时复制
    服务器君一共花费 18.913 ms 进行了 4 次数据库查询,努力地为您提供了这个页面。
    广告很萌的

    从一道面试题开始

    在开始本节内容前,我们先来看看一道还算比较常见的PHP面试题:

    $arr = array('1','2','3');
    
    foreach($arr as &$v){
    }
    
    foreach($arr as $v){
    }
    
    var_dump($arr);
    

    猜一下,运行的结果会是什么呢?熟悉PHP的同学可能已经知道结果了:

    array
      0 => string '1' (length=1)
      1 => string '2' (length=1)
      2 => &string '2' (length=1)
    
    • 奇怪了,为什么不是输出 1, 2, 3 呢?遍历操作也会改变原数组的值啊,我第一次听说呢。

    是引用在捣鬼

    那么为什么是1,2,2呢。让我们一步步来看:

    我们知道对数组执行foreach循环时,是通过移动数组内部指针来实现的。对于上面的例子:当foreach循环结束的时候,由于$v为引用变量,所以$v 与 $arr[ 2 ] 指向了同一个地址空间(共享变量值),之后对$v的任何修改都会直接反映到数组$a中。

    要不我们在程序中加入调试代码,看看运行过程的情况吧。

    $arr = array('1','2','3');
    
    foreach($arr as &$v){
    }
    
    foreach($arr as $v){
    	var_dump($arr);  
        echo "<br/>"; 
    }
    

    程序运行的结果是:

    array
      0 => string '1' (length=1)
      1 => string '2' (length=1)
      2 => &string '1' (length=1)
    
    array
      0 => string '1' (length=1)
      1 => string '2' (length=1)
      2 => &string '2' (length=1)
    
    array
      0 => string '1' (length=1)
      1 => string '2' (length=1)
      2 => &string '2' (length=1)
    

    简单解释一下:

    1. 在第一次 foreach 循环里面,$v 是个引用。在循环结束之后,$v 与 $arr[2] = 3,指向了同一个内存块;
    2. 紧接着第二次循环,$v 引用并没有改变,还是$arr[2]的引用,这时 $v 值是 $arr[0] 的值,所以导致 $arr[2] = 1;
    3. 同上面,$v 已被 $arr[1] 影响,其值为 2,导致 $arr[2] = 2;
    4. 所以最终结果是 1, 2, 2.

    关于引用,你需要了解的

    1. 引用类似于指针,但是不同于指针。

    下面的操作让 $a 和 $b 指向了同一个内存块,值为 “str”

    $a = "str";
    $b = &$a;
    
    echo $a;
    echo '<br />';
    echo $b;
    

    试下更改$a和$b中任何一个元素的值。另外一个值都为随之改变:

    $a = "str";
    $b = &$a;
    
    $b = "www.nowamagic.net";
    
    echo $a;
    echo '<br />';
    echo $b;
    

    程序运行结果为:

    www.nowamagic.net
    www.nowamagic.net
    

    2. 引用作为函数参数传递时,是可以被函数内部更改的:

    $a = "str";
    $b = &$a;
    
    function change(&$a){  
    	$a = "www.nowamagic.net";
    }  
    
    change($a);
    
    echo $a;
    echo '<br />';
    echo $b;
    

    程序运行结果为:

    www.nowamagic.net
    www.nowamagic.net
    

    3. unset只会删除变量。并不会清空变量值对应的内存空间:

    $a = "str";
    $b = &$a;
    
    unset($b); 
    
    echo $a;
    echo '<br />';
    echo $b;
    

    程序运行结果为:

    str
    Notice: Undefined variable: b
    
    • PHP的引用有上面的特点,在编码的过程中,要小心使用引用。防止陷入莫名其妙的尴尬。PHP采用的复制机制是“引用计数,写时复制”,也就是说,即便在PHP里复制一个变量,最初的形式从根本上说其实仍然是引用的形式,只有当变量的内容发生变化时,才会出现真正的复制。所以才会出现上面的问题。之所以这么做是出于节省内存消耗得目的,同时也提升了复制的效率。
更多 推荐条目

Welcome to NowaMagic Academy!

现代魔法 推荐于 2013-02-27 10:23   

本章最新发布
随机专题
  1. [Python程序设计] Tornado源码解析 23 个条目
  2. [数据库技术] MySQL常用自带函数 3 个条目
  3. [数据库技术] MySQL中英文混合排序 4 个条目
  4. [PHP程序设计] htaccess 设置技巧 6 个条目
  5. [移动开发] Android加载器Loaders 5 个条目
  6. [PHP程序设计] 对输入文件类型的检测 1 个条目
  7. [PHP程序设计] PHP数组探索 4 个条目
  8. [PHP程序设计] PHP扩展模块安装 1 个条目
  9. [软件工程与项目管理] 了解一点WebKit 9 个条目
  10. [PHP程序设计] PHP数组的遍历 7 个条目
  11. [移动开发] Android抽屉导航NavigationDrawer 5 个条目
  12. [Python程序设计] Tornado背景知识介绍 4 个条目
窗口 -- [博客]