转载:https://blog.csdn.net/ljguo212/article/details/8972865

1. PHP中引用的特性

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

2.变量的引用

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

  1. <?php
  2. $a = " ABC " ;
  3. $b   =& $a ;
  4. echo   $a ; // 这里输出:ABC
  5. echo   $b ; // 这里输出:ABC
  6. $b = " EFG " ;
  7. echo   $a ; // 这里$a的值变为EFG 所以输出EFG
  8. echo   $b ; // 这里输出EFG
  9. ?>

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

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

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=new fooclass(); $c=&$b;

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

$a = new foo fooclass(), 实际上$a和new fooclass()的映射到不同对象实例,其中new fooclass产生的是一个匿名的fooclass对象实例 所以需要显式地使用$a = & new fooclass()来进行引用赋值传递。

所以在php4中,为了节省内存空间,$b=new fooclass()一般会改成引用的模式,即 $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而已, 而$a做任何改变 都不会影响到函数中的$b。
而通过$a=&test()方式调用函数呢, 他的作用是 将return $b中的 $b变量的内存地址与$a变量的内存地址 指向了同一个地方
即产生了相当于这样的效果($a=&$b;) 所以改变$a的值 也同时改变了$b的值 所以在执行了

$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 只改变了本地变量的引用。

如果全局变量赋值给另外另外一个变量$var_test,删除这个$var_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']的映射就断开了,再也无法修改函数外部$var2的内容。此特性也同样作用在用引用传递函数参数的例子中。
另外,需要注意在函数中unset($var2)不会真正释放变量内容,可以看做只是断开了函数内部$var2的映射而已。

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

<?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=$b=null的情况。
原因:某变量赋值为null,将导致该变量对应的内存块的引用计数直接置为0,被自动回收。

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指向$a的内存,zend就已经帮你实现了引用,并且zend会非常智能的帮你去判断什么时候该这样处理,什么时候不该这样处理。

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

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

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

直观的理解:$a将使用自己原始的内存空间,而$b,则会使用新开辟的内存空间,而这个空间将使用$a的原始($a或者$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%左右。
所以不正确使用引用,性能反而下降。

 
 

PHP中引用的详解(引用计数、写时拷贝)的更多相关文章

  1. 深拷贝&浅拷贝&引用计数&写时拷贝

    (1).浅拷贝: class String { public: String(const char* str="") :_str(]) { strcpy(_str,str); } ...

  2. PHP引用(&)使用详解

    初学php关于&引用 官方文档: 1.引用是什么:http://www.php.net/manual/zh/language.references.whatare.php 2.引用做什么:ht ...

  3. String 类的实现(2)引用计数与写时拷贝

    1.引用计数 我们知道在C++中动态开辟空间时是用字符new和delete的.其中使用new test[N]方式开辟空间时实际上是开辟了(N*sizeof(test)+4)字节的空间.如图示其中保存N ...

  4. 写时拷贝(Copy On Write)方案详解

    本文旨在通过对 写时拷贝 的四个方案(Copy On Write)分析,让大家明白写时拷贝的实现及原理. 关于浅拷贝与深拷贝,我在之前的博客中已经阐述过了  浅拷贝容易出现指针悬挂的问题,深拷贝效率低 ...

  5. C++ 值传递、指针传递、引用传递详解

    C++ 值传递.指针传递.引用传递详解 最近写了几篇深层次讨论数组和指针的文章,其中提到了“C语言中,所有非数组的形式参数传递均以值传递形式” 数组和指针背后——内存角度 语义"陷阱&quo ...

  6. C++之值传递&指针传递&引用传递详解

    C++之值传递&指针传递&引用传递详解 目录 C++之值传递&指针传递&引用传递详解 1.函数基础 2.值传递 3.指针传递 4.引用传递 1.函数基础 一个函数由以下 ...

  7. php引用&符号详解——————给变量起小名

    学习了这篇博客[http://blog.csdn.net/jiedushi/article/details/6428585] php中引用采用的是“写时拷贝”的原理,就是除非发生写操作,指向同一个地址 ...

  8. php引用传值详解

    php的引用(就是在变量或者函数 .对象等前面加上&符号) 在PHP 中引用的意思是:不同的名字访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的指针里面存储的是变量的内容在内存中存 ...

  9. 引用&符号详解

    变量的引用 PHP 的引用允许你用两个变量来指向同一个内容. 例一: <?php $a="2010"; $b =&$a; echo $a;//这里输出:2010 ec ...

  10. Objective-C中的@Property详解

    Objective-C中的@Property详解 @Property (属性) class vairs 这个属性有nonatomic, strong, weak, retain, copy等等 我把它 ...

随机推荐

  1. AIRIOT智慧变电站管理解决方案

    随着社会电气化进程的加速,电力需求与日俱增,变电站作为电网的关键节点,其稳定性和智能化管理水平直接关系到整个电力系统的高效运作.传统变电站管理平台难以适应现代电力系统复杂管理需求,存在如下痛点: 数据 ...

  2. navicat premium 15 下载和激活

    Navicat Premium 15 下载地址 链接:https://pan.baidu.com/s/1bL-M3-hkEa4M-547giVjYQ?pwd=1107 推荐安装参考地址:https:/ ...

  3. HTML——结构和标签格式

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. Android 13 - Media框架(12)- MediaCodec(二)

    关注公众号免费阅读全文,进入音视频开发技术分享群! 前面一节我们学习了 MediaCodec 的创建以及配置过程,了解部分设计机制以及功能,这一节我们将继续学习其他方法. 1.start start ...

  5. ASP.NET Core SignalR 概述

    什么是 SignalR? ASP.NET Core SignalR 是一个开放源代码库,可用于简化向应用添加实时 Web 功能. 实时 Web 功能使服务器端代码能够将内容推送到客户端. 适合 Sig ...

  6. k8s——搭建集群环境

    服务器要求(三台都要操作) 一台master两台node 能连外网 关闭防火墙 关闭selinux 设置主机名,域名解析 关闭swap(记得关完之后重启) swapoff -a //临时关闭 vim ...

  7. Java中将jsonArray导出为Excel

        java中使用jxl导出excel时,需指定WritableSheet对象中对应于每个单元格的数据.List类型是一种常用的数据类型,它里面的元素是实体对象,当将它创建为WritableShe ...

  8. react移动端组件antd-mobile

    使用react移动端组件antd-mobile完成底部导航功能实现. 官网:https://mobile.ant.design/docs/react/introduce-cn antd-mobile ...

  9. 三维API sheder 基础

    这个shader 是靠三维数学 影响 二维像素 导致像素颜色改变 它是每个像素走一遍脚本算法 写的时候注意 语言格式 写错了 shader脚本是不能用的,根本就不好使这个 可以用区域 用xyz y为0 ...

  10. 别想宰我,怎么查看云厂商是否超卖?详解 cpu steal time

    据说有些云厂商会超卖,宿主有 96 个核心,结果卖出去 100 多个 vCPU,如果这些虚机负载都不高,大家相安无事,如果这些虚机同时运行一些高负载的任务,相互之间就会抢占 CPU,对应用程序有较大影 ...