前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP中引用的详解(引用计数、写时拷贝)

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

作者头像
黄规速
发布2022-04-14 20:22:08
3.6K0
发布2022-04-14 20:22:08
举报

PHP中引用的详解 huangguisu

《PHP5中文手册》内容中"引用的解释"一文的摘要:

1. PHP中引用的特性

PHP中引用意味着用不同的名字访问同一个变量内容,引用不是C的指针(C语言中的指针里面存储的是变量的内容,在内存中存放的地址),是变量的另外一个别名或者映射。注意在 PHP 中,变量名和变量内容是不一样的,因此同样的内容可以有不同的名字。 最接近的比喻是 Unix 的文件名和文件本身――变量名是目录条目,而变量内容则是文件本身。引用可以被看作是 Unix 文件系统中的紧密连接或者wins的快捷方式。

2.变量的引用

PHP 的引用允许用两个变量来指向同一个内容。

代码语言:javascript
复制
<?php
     $a = " ABC " ;
     $b   =& $a ;
     echo   $a ; // 这里输出:ABC
     echo   $b ; // 这里输出:ABC
     $b = " EFG " ;
     echo   $a ; // 这里$a的值变为EFG 所以输出EFG
     echo   $b ; // 这里输出EFG
?>

这意味着 $a 和 $b 指向了同一个变量。

注: $a 和 $b 在这里是完全相同的,这并不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一个地方。

3 对象的引用(对象的赋值传递)

在PHP5中两者映射到同一对象,不需要使用引用符。

代码语言:javascript
复制
<?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中 对象的赋值是个引用的过程。上列中

代码语言:javascript
复制
$a = new fooclass();
$b = $a;

其实等效于b=new fooclass(); c=&

自 PHP 5 起,new 自动返回引用,因此在此使用 =& 已经过时了并且会产生 E_STRICT 级别的消息。

在php4中,对象的赋值是个拷贝过程,

a = new foo fooclass(), 实际上a和new fooclass()的映射到不同对象实例,其中new fooclass产生的是一个匿名的fooclass对象实例 所以需要显式地使用

所以在php4中,为了节省内存空间,b=new fooclass()一般会改成引用的模式,即 b=& new fooclass()。

4.函数的引用传递(传址调用)

引用传递的定义必须在函数定义中体现,在函数使用中,不要写成foo(&$a)的样子,否则会报"Call-time pass-by-reference过时"的警报。

代码语言:javascript
复制
<?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”中,若要引用传参,就得需要 & 符号,如下代码所示:

代码语言:javascript
复制
function  test( & $b ){
     $b ++ ;
}
$c = 0 ;
call_user_func_array ( 'test' , array ( & $c ));
echo   $c ;

5 函数的引用返回

代码:

代码语言:javascript
复制
<? 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而已, 而a做任何改变 都不会影响到函数中的b。 而通过a=&test()方式调用函数呢, 他的作用是 将return b变量的内存地址与a变量的内存地址 指向了同一个地方 即产生了相当于这样的效果(a=&a的值 也同时改变了b的值 所以在执行了

a=&test();

以后,$b的值变为了5。

注意:

和参数传递不同,这里必须在两个地方都用 & 符号:定义和使用时都需要加&符号。

& test()指出返回的是一个引用,而不是通常的一个拷贝。

这里是为了让大家理解函数的引用返回才使用静态变量的,其实函数的引用返回多用在对象中:

代码语言:javascript
复制
<?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的引用。具体可以写成如下语句:

代码语言:javascript
复制
<?php
     $var   =&   $GLOBALS [ "var" ];
?>  

把 global var; 当成是 var =& GLOBALS['var']; 的简写。从而将其它引用赋给 var 只改变了本地变量的引用。

如果全局变量赋值给另外另外一个变量var_test,删除这个var_test,也不会 unset 全局变量

代码语言:javascript
复制
<?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
 */
代码语言:javascript
复制

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

代码语言:javascript
复制
$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'
代码语言:javascript
复制
$var2 =& $var1; // visible only inside the function

那么实际上就是改变了 $var2 的映射关系,之前与$_GLOBAL['var2']的映射就断开了,再也无法修改函数外部$var2的内容。此特性也同样作用在用引用传递函数参数的例子中。 另外,需要注意在函数中unset($var2)不会真正释放变量内容,可以看做只是断开了函数内部$var2的映射而已。

7 . 取消引用 unset与=null

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

代码语言:javascript
复制
<?php

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

不会 unset $b,只是 $a。

使用unset(a)与a=null的结果是不一样的。如果该块内存只有a一个映射,那么unset(a)与a=null等价,该内存的引用计数变为0,被自动回收;如果该块内存有a和b两个映射,那么unset(a)将导致a=null且b不变的情况,而a=null会导致a=

8 . $this

在一个对象的方法中,$this 永远是调用它的对象的引用。 9. PHP引用使用注意事项

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

就是除非发生写操作,指向同一个地址的变量或者对象是不会被拷贝的,比如下面的代码: a = array('a','c'...'n'); b = a; 如果程序仅执行到这里,b和b是相同的,但是并没有像C那样,a和b占用不同的内存空间,而是指向了同一块内存,这就是php和c的差别,并不需要写成b=&a才表示b指向

代码语言:javascript
复制
    function printArray(&$arr) //引用传递
     {
        print(count($arr));
    }
     printArray($a);

上面的代码中,我们通过引用把a数组传入printArray()函数,zend引擎会认为printArray()可能会导致对a的改变,此时就会自动为b生产一个a的数据拷贝,重新申请一块内存进行存储。这就是前面提到的“引用计数、写时拷贝”概念。

直观的理解:a将使用自己原始的内存空间,而b,则会使用新开辟的内存空间,而这个空间将使用a的原始(a或者

代码语言:javascript
复制
   function printArray($arr)  //值传递
    {
        print(count($arr));
    }
    printArray($a);

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

测试:

代码语言:javascript
复制
	$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
代码语言:javascript
复制
	$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%左右。 所以不正确使用引用,性能反而下降。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2012-04-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. PHP中引用的特性
  • 2.变量的引用
  • 3 对象的引用(对象的赋值传递)
  • 4.函数的引用传递(传址调用)
  • 5 函数的引用返回
  • 6 global 引用
  • 8 . $this
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档