转载: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. saltstack使用

    saltstack中salt-key的用法 介绍: saltstack中master和minion是依靠证书来进行加密通信的.在saltstack中salt-key命令是用来管理证书的 用法: sal ...

  2. java rgb转hsv

    public static double[] toHSV(int r, int g, int b) { Color color = new Color(r, g, b); float[] hsv = ...

  3. mp4封装格式与MPEG4Extractor

    首先来看mp4的封装格式,mp4数据都被放在一个个的箱子当中,也就是box,box的字节序为网络字节序,也就是大端存储,box由header和body组成,header指明box的大小和类型,body ...

  4. .Net6 winform 程序使用依赖注入

    .net Blazor webassembly 和 webAPI 内建支持依赖注入, Winform 和 Console 应用虽然不带有依赖注入功能, 但增加依赖注入也很简单. 本文将示例如何为 Wi ...

  5. AppFlow上新——智谱ChatGLM轻松接入聊天

    智谱AI 开放平台提供一系列具有不同功能和定价的大模型,包括通用大模型.超拟人大模型.图像大模型.向量大模型等,并且支持使用您的私有数据对模型进行微调.其中ChatGLM系列模型在国内也享有盛名,现在 ...

  6. 008. gitlab代码克隆与推送

    推送配置 gitlab需要推送的客户端sshkey添加到gitlab服务器中 node1 推送配置 centos node1: [root@node1 ~]# cat ~/.ssh/id_rsa.pu ...

  7. C#笔记 窗体练习:海康相机SDK二次开发

    第一次写窗体应用程序,太闲了,给自己找点事情做... 1. 最基本的打开关闭 代码:https://gitee.com/yurj0403/hik-camera 强行练习一下用git 2. 加了状态栏 ...

  8. QEMU EDU设备模拟PCI设备驱动编写

    环境安装 buildroot编译 buildroot下载,编译: 下载地址:Index of /downloads (buildroot.org) 下载版本:https://www.buildroot ...

  9. 利用QEMU模拟大端序机器

    简介 当前我们安装虚拟机,一般小端机器比较多,有时候想模拟大端机器测试程序,这时就有模拟大端机器的需求. 参考:利用 QEMU USER 模式运行 mips 程序 - sinpo828 - 博客园 ( ...

  10. LeetCode 146. LRU CacheLRU缓存机制 (C++/Java)

    题目: Design and implement a data structure for Least Recently Used (LRU) cache. It should support the ...