PHP中引用的详解(引用计数、写时拷贝)

  1. PHP中引用的特性
    PHP中引用意味着用不同的名字访问同一个变量内容,引用不是C的指针(C语言中的指针里面存储的是变量的内容,在内存中存放的地址),是变量的另外一个别名或者映射。注意在 PHP 中,变量名和变量内容是不一样的,因此同样的内容可以有不同的名字。最接近的比喻是 Unix 的文件名和文件本身――变量名是目录条目,而变量内容则是文件本身。引用可以被看作是 Unix 文件系统中的紧密连接或者wins的快捷方式。
    2.变量的引用
    PHP 的引用允许用两个变量来指向同一个内容。
<?php  
     $a = " ABC " ;  
     $b   =& $a ;  
     echo   $a ; // 这里输出:ABC  
     echo   $b ; // 这里输出:ABC  
     $b = " EFG " ;  
     echo   $a ; // 这里$a的值变为EFG 所以输出EFG  
     echo   $b ; // 这里输出EFG  
?>  

这意味着 ab 指向了同一个变量。
注: ab 在这里是完全相同的,这并不是ab 或者相反,而是ab 指向了同一个地方。
3 对象的引用(对象的赋值传递)
在PHP5中两者映射到同一对象,不需要使用引用符。

<?php  
class fooclass{  
    public $att ;   
}  
$a = new fooclass();  
$b = $a;   
$a->att = 1;  
$b->att = 2;  
echo 'a obj:',($a->att),'<br>';  
echo 'b obj:',($b->att);  
/** 
 * 结果: 
 * a obj:2 
   b obj:2 
 */  

在PHP5中 对象的赋值是个引用的过程。上列中

$a = new fooclass();  
$b = $a;  

其实等效于b=newfooclass();c=&b;  
PHP5中默认就是通过引用来调用对象, 但有时你可能想建立一个对象的副本,并希望原来的对象的改变不影响到副本 . 为了这样的目的,PHP5定义了一个特殊的方法,称为__clone。  
自 PHP 5 起,new 自动返回引用,因此在此使用 =& 已经过时了并且会产生 E_STRICT 级别的消息。  
在php4中,对象的赋值是个拷贝过程,
a = new foo fooclass(), 实际上anewfooclass()newfooclassfooclass使a = & new fooclass()来进行引用赋值传递。
所以在php4中,为了节省内存空间,b=newfooclass()b=& new fooclass()。
4.函数的引用传递(传址调用)
引用传递的定义必须在函数定义中体现,在函数使用中,不要写成foo(&$a)的样子,否则会报”Call-time pass-by-reference过时”的警报。

<?php  
    function  foo( & $a ){  
         $a = $a + 100 ;  
    }  
    $b = 1 ;  
    echo   $b ; // 输出1  
    foo( $b );// 这里$b传递给函数的其实是$b的变量内容所处的内存地址,通过在函数里改变$a的值 就可以改变$b的值了  
    echo   " <br> " ;  
    echo   $b ; // 输出101  

但是在函数“call_user_func_array”中,若要引用传参,就得需要 & 符号,如下代码所示:

function  test( & $b ){  
     $b ++ ;  
}  
$c = 0 ;  
call_user_func_array ( 'test' , array ( & $c ));  
echo   $c ;  

5 函数的引用返回
代码:

<? php  
function   & test()  
{  
     static   $b = 0 ; // 申明一个静态变量  
     $b = $b + 1 ;  
     echo   $b ;  
     return   $b ;  
}  
$a = test(); // 这条语句会输出 $b的值 为1  
$a = 5 ;  
$a = test(); // 这条语句会输出 $b的值 为2  
$a =& test(); // 这条语句会输出 $b的值 为3  
$a = 5 ;  
$a = test(); // 这条语句会输出 $b的值 为6  
?>  

通过这种方式a=test();a而已, 而ab。
而通过a=&test()方式调用函数呢, 他的作用是 将returnb中的 ba变量的内存地址 指向了同一个地方
即产生了相当于这样的效果(a=&b;) 所以改变ab的值 所以在执行了
a=&test();a=5;
以后,b的值变为了5。  
注意:  
和参数传递不同,这里必须在两个地方都用 & 符号:定义和使用时都需要加&符号。  
& test()指出返回的是一个引用,而不是通常的一个拷贝。
a =& test()同样也指出 $a 是作为引用的绑定,而不是通常的赋值。
这里是为了让大家理解函数的引用返回才使用静态变量的,其实函数的引用返回多用在对象中:

<?php  
/** 
 * This is the way how we  use  pointer to access variable inside the  class . 
 */  
class  talker{  
     private   $data   =   ' Hi ' ;  
     public   function   &  get(){  
         return   $this -> data;  
    }  
    public   function  out(){  
         echo   $this -> data;  
    }    
}  
$aa   =   new  talker();  
$d   =   & $aa -> get();  
$aa -> out();  
$d   =   ' How ' ;  
$aa -> out();  
$d   =   ' Are ' ;  
$aa -> out();  
$d   =   ' You ' ;  
$aa -> out();  
//the output is  " Hi How Are You "  

6 global 引用
当用 global var_GLOBAL的引用。具体可以写成如下语句:

<?php  
     $var   =&   $GLOBALS [ "var" ];  
?>  

把 global var;var =& GLOBALS[var];var 只改变了本地变量的引用。
如果全局变量赋值给另外另外一个变量vartestvar_test,也不会 unset 全局变量

<?php  
global $var;  
$var = "Example variable";  
$var_test =  &  $GLOBALS [ 'var' ];  
echo '$var_test is set to ',$var_test,'<br>'; //  
$var_test = 11;  
unset($var_test);  
echo '$GLOBALS[var] is set to ',$GLOBALS ['var'],'<br>';  
/** 
 * 输出结果: 
$var_test is set to Example variable 
$GLOBALS[var] is set to 11 
 */  

如果在一个函数内部给一个声明为 global 的变量赋于一个引用,该引用只在函数内部可见。可以通过使用 $GLOBALS 数组避免这一点。

$var1 = "Example variable";  
$var2 = "";  
function global_references($use_globals) {  
    global $var1, $var2;  
    if (!$use_globals) {  
        $var2 =& $var1; // visible only inside the function  
    } else {  
        $GLOBALS["var2"] =& $var1; // visible also in global context  
    }  
}  
global_references(false);  
echo "var2 is set to '$var2'", '<br>'; // var2 is set to ''  
global_references(true);  
echo "var2 is set to '$var2'"; // var2 is set to 'Example variable'  
$var2 =& $var1; // visible only inside the function  

那么实际上就是改变了var2_GLOBAL[‘var2’]的映射就断开了,再也无法修改函数外部var2unset(var2)不会真正释放变量内容,可以看做只是断开了函数内部$var2的映射而已。

7 . 取消引用 unset与=null
当你 unset 一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了。例如:

<?php  

     $a   =   1 ;  
     $b   =&   $a ;  
     unset  ( $a );  
     echo $b; //输出:1:  

不会 unset ba。
使用unset(a)a=null的结果是不一样的。如果该块内存只有aunset(a)与a=null0a和bunset(a)将导致a=nullb不变的情况,而a=nulla=b=nullnull08.this
在一个对象的方法中,$this 永远是调用它的对象的引用。

  1. PHP引用使用注意事项

很多人误解Php中的引用跟C当中的指针一样,事实上并非如此,而且很大差别。C语言中的指针除了在数组传递过程中不用显式申明外,其他都需要使用*进行定义,而php中对于地址的指向(类似指针)功能不是由用户自己来实现的,是由Zend核心实现的,php中引用采用的是“引用计数、写时拷贝”的原理,(写时复制(Copy-on-Write,也缩写为COW),顾名思义,就是在写入时才真正复制一份内存进行修改。)

就是除非发生写操作,指向同一个地址的变量或者对象是不会被拷贝的,比如下面的代码:
a=array(a,cn);b = a;b和bCa和bphpcb=&ab指向$a的内存,zend就已经帮你实现了引用,并且zend会非常智能的帮你去判断什么时候该这样处理,什么时候不该这样处理。

如果在后面继续写如下代码,增加一个函数,通过引用的方式传递参数,并打印输出数组大小。

function printArray(&$arr) //引用传递  
 {  
    print(count($arr));  
}  
 printArray($a);  

上面的代码中,我们通过引用把aprintArray()zendprintArray()a的改变,此时就会自动为ba的数据拷贝,重新申请一块内存进行存储。这就是前面提到的“引用计数、写时拷贝”概念。

直观的理解:a使b,则会使用新开辟的内存空间,而这个空间将使用aa或者$b改变之前)内容空间的内容的拷贝,然后做对应的改变。

如果我们把上面的代码改成下面这样:

function printArray($arr)  //值传递  
 {  
     print(count($arr));  
 }  
 printArray($a);  

上面的代码直接传递$a值到printArray()中,此时并不存在引用传递,所以没有出现写时拷贝。

测试:

$a = array(1,2,3);  
$b =$a;  
function printArray($arr)  //值传递  
{  
    print(count($arr));  
}  
$s = microtime(true);   
for($i=1;$i<10000;$i++){  
    printArray($a);  
 }  
$e = microtime(true);   
echo '=----------------------------';  
echo $e-$s;//0.031868934631348 s  
$a = array(1,2,3);  
$b = $a;  
function printArray(&$arr)  //引用传递  
{  
    print(count($arr));  
}  
$s = microtime(true);   
for($i=1;$i<10000;$i++){  
    printArray($a);  
 }  
$e = microtime(true);   
echo '=----------------------------';  
echo $e-$s;//0.047234058380127  

测试结果

值传递:0.031868934631348 s
引用传递: 0.047234058380127

结果引用传递性能下降:50%左右。
所以不正确使用引用,性能反而下降。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值