PHP 引用的概念

什么是引用?

在 PHP 中引用意味着用不同的名字访问同一个变量内容。它不是C的指针,保存的并不是内存地址,无法进行指针运算。引用只是符号表的别名。就像 Unix 系统中的硬链接, Windows 系统中的快捷方式。

上面是官方手册中的原文,怎么说呢,引用其实和我们印象中的C里面的指针并不是相同的概念。指针是针对真实内存的操作,引用是针对指向这个内存的符号表的操作。还是从操作系统的快捷方式来说,快捷方式是可以删的,这就是PHP的引用。而C不仅删了快捷方式,还把原文件也给删了,这就是C的指针操作。

// 引用不是指针
$a = 1;
$b = &$a;
echo $a, '===', $b, PHP_EOL;
unset($b);
echo $a, '===', $b, PHP_EOL;

上面的代码是在PHP中,我们把b变量指向b变量指向a,作为a的引用变量。然后删除a的引用变量。然后删除b,对$a没有任何影响。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // C 中的指针和引用
    int a = 1;
    int* b = &a;
    printf("%i\n", a); // 1
    free(b); // free b
    printf("%i\n", a); //get error: *** error for object 0x7fff6350da08: pointer being freed was not allocated
    return 0;
}

而C中的引用指针就不行了,我们把b变量删掉后,再打印a变量就直接报错了。

虽然说PHP的底层也是C写得,但我们都知道C中的指针是出了名的变态,没有一定的功底非常容易出错。所以PHP的开发者没有暴露C的原始指针能力,而是采用了和Java之类的类似的引用能力。这也是现代语言的特性,不需要我们过多的关注过于底层的能力,而将更多的时间放在业务实现上。

引用在数组和对象中的使用

如果具有引用的数组被拷贝,其值不会解除引用。对于数组传值给函数也是如此。

$arr1 = ["a", "b"];
$t1 = &$arr1[1];
$arr2 = $arr1;
$arr2[1] = "c";
var_dump($arr1);

// array(2) {
//     [0]=>
//     string(1) "a"
//     [1]=>
//     &string(1) "c"
// }

$arr1 = ["a", "b"];
$t1 = &$arr1[1];
unset($t1); // unset 掉引用
$arr2 = $arr1;
$arr2[1] = "c";
var_dump($arr1);

// array(2) {
//     [0]=>
//     string(1) "a"
//     [1]=>
//     string(1) "b"
// }

这个其实挺有意思的,我们对比这两个例子可以看出一个问题,t变量指向t变量指向arr[1]的引用。arr2直接=这个arr2直接=这个arr1,没有使用引用,然后arr2修改了arr2修改了arr2[1]的内容,arr1相应的内容也发生了改变,如果unset掉arr1相应的内容也发生了改变,如果unset掉t变量,则$arr1相应的内容就不会发生改变。对此,我在文档中找到了下面的解释:

由于PHP内部工作的特殊性,如果对数组的单个元素进行引用,然后复制数组,无论是通过赋值还是通过函数调用中的值传递,都会将引用复制为数组的一部分。这意味着对任一数组中任何此类元素的更改都将在另一个数组(和其他引用中)中重复,即使数组具有不同的作用域(例如,一个是函数内部的参数,另一个是全局的)!在复制时没有引用的元素,以及在复制数组后分配给其他元素的引用,将正常工作(即独立于其他数组)。

不仅仅是数组,对象的引用也会有一些好玩的问题。

$o1 = new stdClass();
$o1->a = 'a';
var_dump($o1);
// object(stdClass)#1 (1) {
//   ["a"]=>
//   string(1) "a"
// }

$o2 = &$o1;
$o3 = $o1;

$o2->a = 'aa';

var_dump($o1);
// object(stdClass)#1 (1) {
//   ["a"]=>
//   string(2) "aa"
// }

var_dump($o3); // $o2修改了$a为'aa',$o3也变成了'aa'
// object(stdClass)#1 (1) {
//   ["a"]=>
//   string(2) "aa"
// }

$o1->a = 'aaa';
$o1 = null;
var_dump($o2); // $o2引用变成了null
// NULL

var_dump($o3); // $o3不仅引用还存在,并且$a变成了'aaa'
// object(stdClass)#1 (1) {
//   ["a"]=>
//   string(3) "aaa"
// }

上面例子中有三个对象,o1、o1、o2、o3,其中,o3,其中,o2是对o1的引用,o1的引用,o3是直接赋值为o1。对o1。对o2属性的操作不仅会反映在o1中,也会反映到o1中,也会反映到o3中。其实我们之前专门有一篇文章就讲的这个问题,首先对象默认赋值就是引用,其次这个例子很好地证明了引用就是一个符号表的绑定。删除了快捷方式对原始对象和其他快捷方式没有任何影响。

引用的传递

关于引用在方法参数上的传递,最重要的是记住两点:一是方法内部修改了变量外部也会变,这是引用的特性嘛;二是只能传递变量、New 语句、从函数中返回的引用三种类型。

error_reporting(E_ALL);
function foo(&$var)
{
    $var++;
    echo 'foo:', $var;
}
function bar() // Note the missing &
{
    $a = 5;
    return $a;
}
foo(bar()); // 自 PHP 5.0.5 起导致致命错误,自 PHP 5.1.1 起导致严格模式错误
            // 自 PHP 7.0 起导致 notice 信息,Notice: Only variables should be passed by reference
foo($a = 5); // 表达式,不是变量, Notice: Only variables should be passed by reference
// foo(5); // 导致致命错误 !5是个常量!

///////////////////////////////
// 正确的传递类型
$a = 5;
foo($a); // 变量

function &baz()
{
    $a = 5;
    return $a;
}
foo(baz()); // 从函数中返回的引用

function foo1(&$var)
{
    print_r($var);
}
foo1(new stdClass()); // new 表达式

引用的返回

引用的返回并不是经常使用的一个能力。文档中的原文是:不要用返回引用来增加性能,引擎足够聪明来自己进行优化。仅在有合理的技术原因时才返回引用!

$a = 1;
function &test(){
    global $a;
    return $a;
}

$b = &test($a);
$b = 2;
echo $a, PHP_EOL;

当你想要返回一个引用变量的时候,一定要给方法定义和方法调用的时候都使用&符号。这个是需要注意的点。当其他地方修改原本的变量值或者返回的变量值经过修改后,都会影响到所有调用这个值的地方。所以说,引用的返回是比较危险的,因为你不清楚什么时候在什么地方这个值可能发生了修改,对于bug的排查会非常困难。

引用的取消

取消引用其实就是直接unset掉变量就可以了。但是一定要记住,PHP中的引用是指向的符号表,对原始真实的值是不起作用的,所以即使unset掉了最原始的那个变量,对其它引用赋值的变量也不会有影响!!

$a = 1;
$b = &$a;
$c = &$b;
$b = 2;
echo '定义引用后:', $a, '===', $b, '===', $c, PHP_EOL;

unset($b);
$b = 3;
echo '取消$b的引用,不影响$a、$c:', $a, '===', $b, '===', $c, PHP_EOL;

$b = &$a;
unset($a);
echo '取消$a,不影响$b、$c:', $a, '===', $b, '===', $c, PHP_EOL;

// 定义引用后:2===2===2
// 取消$b的引用:2===3===2
// 取消$a,不影响$c:===3===2

$a = 1;
$b = & $a;
$c = & $b; // $a, $b, $c reference the same content '1'

$a = NULL; // All variables $a, $b or $c are unset
echo '所有引用成空:', $a, '===', $b, '===', $c, PHP_EOL;

总结

这一次算是比较彻底的把引用说了个透。关于PHP的引用只要记住了它的定义就非常好理解了,最直观的就是当成是操作系统的快捷方式就好了,并没有我们想象中的那么难,和C的指针相比真的只是娃娃级别,多多练习多多复习自然就能很好地掌握使用啦!

测试代码: github.com/zhangyue050…

以上就是PHP的中引用的概念的详细内容,更多关于PHP 引用的资料请关注我们其它相关文章!

(0)

相关推荐

  • PHP函数按引用传递参数及函数可选参数用法示例

    本文实例讲述了PHP函数按引用传递参数及函数可选参数用法.分享给大家供大家参考,具体如下: 一.函数按引用传递参数 1. 代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999

  • php中对象引用和复制实例分析

    本文实例讲述了php中对象引用和复制.分享给大家供大家参考,具体如下: 引用 $tv2 = $tv1; 或者 $tv2 = &$tv1; 以上两种方式,效果是一样的.可以理解为linux里面的硬链接. 克隆(浅复制) $tv2 = clone $tv1; "浅复制":被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象.也就是说,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. 深复制 $tv4 = unserialize(seria

  • php传值和传引用的区别点总结

    php传值:在函数范围内,改变变量值得大小,都不会影响到函数外边的变量值. PHP传引用:在函数范围内,对值的任何改变,在函数外部也有所体现,因为传引用传的是内存地址. 传值:和copy是一样的.[打个比方,我有一橦房子,我给你建筑材料,你建了一个根我的房子一模一样的房子,你在你的房子做什么事都不会影响到我,我在我的房子里做什么事也不会影响到你,彼此独立.] <?php $testa=1; //定义变量a $testb=2; //定义变量b $testb = $testa; //变量a赋值给变量

  • 详解PHP变量传值赋值和引用赋值变量销毁

    本文实例为大家分享了PHP变量传值赋值和引用赋值变量销毁的具体代码,供大家参考,具体内容如下 <?php $a = 100; $b = 200; var_dump($a,$b); //int(100) int(200) ?> php中,上面的代码,变量是怎么存放的呢? 上面的代码变动下,将变量b赋值给变量a,会发生什么? <?php $a = 100; $b = 200; $a = $b;/*多了这个*/ var_dump($a,$b); //int(200) int(200) ?>

  • php的对象传值与引用传值代码实例讲解

    变量赋值与对象赋值对比 <?php // 声明一个变量并赋值 $a = 1; // 将数据类型的值 赋值 给一个变量 $b = $a; // 修改$a的值 $a = 2; // $a和$b是两个独立的内存空间修改其中一个另一个不受影响 echo $b; // 1 class Person{ public $name; public $age; } // 将对象类型的数据 赋值 给一个变量 $p = new Person; // 通过对属性修改值,来确定面向对象中 对象的传值方式 $p->nam

  • 详解PHP的引用计数

    什么是引用计数 在PHP的数据结构中,引用计数就是指每一个变量,除了保存了它们的类型和值之外,还额外保存了两个内容,一个是当前这个变量是否被引用,另一个是引用的次数.为什么要多保存这样两个内容呢?当然是为了垃圾回收(GC).也就是说,当引用次数为0的时候,这个变量就没有再被使用了,就可以通过 GC 来进行回收,释放占用的内存资源.任何程序都不能无限制的一直占用着内存资源,过大的内存占用往往会带来一个严重的问题,那就是内存泄露,而 GC 就是PHP底层自动帮我们完成了内存的销毁,而不用像 C 一样

  • php引用和拷贝的区别知识点总结

    对于值传递和引用传递,书本上的解释比较繁琐,而php面试中总会出现,下面我会通过一个生活的例子带大家理解它们之间区别. 第一步 假设我们去酒店订房间,我们把酒店的门牌号比作变量名,我们把房间里住人数当做给这个变量名赋值的过程 <?php $k1=2;//$k1号房间住了2个人 $k2=$k1;//恰巧今天$k1号房间维修,把$k1号房间的人放到$k2房间里面 $k1=10://假设$k1维修好了,又住进10个人,那么这就是值传递. echo "$k1号房间:".$k1."

  • php 多个变量指向同一个引用($b = &$a)用法分析

    本文实例讲述了php 多个变量指向同一个引用($b = &$a)用法.分享给大家供大家参考,具体如下: 引用是什么? 引用就是多个变量指向同一个内存区域地址.如我们经常用的实例一个类,就是内存中开辟了一个区域存储实例的类,实例赋值给变量就是让这个变量指向这个内存区域. 多个变量指向同一个引用有什么好处? 节约了内存空间,多个变量指向同一个内存地址,在调用的时候多个变量都是指向的同一个内存地址. 多个变量指向同一个引用的缺点 要注意使用安全,即是由于多个变量都是指向的同一个内存地址,其中一个变量更

  • PHP实现无限极分类的两种方式示例【递归和引用方式】

    本文实例讲述了PHP实现无限极分类的两种方式.分享给大家供大家参考,具体如下: 面试的时候被问到无限极分类的设计和实现,比较常见的做法是在建表的时候,增加一个PID字段用来区别自己所属的分类 $array = array( array('id' => 1, 'pid' => 0, 'name' => '河北省'), array('id' => 2, 'pid' => 0, 'name' => '北京市'), array('id' => 3, 'pid' =>

  • Python引用传值概念与用法实例小结

    本文实例讲述了Python引用传值概念与用法.分享给大家供大家参考,具体如下: Python函数的参数传值使用的是引用传值,也就是说传的是参数的内存地址值,因此在函数中改变参数的值,函数外也会改变. 这里需要注意的是如果传的参数类型是不可改变的,如String类型.元组类型,函数内如需改变参数的值,则相当于重新新建了一个对象. # 添加了一个string类型的元素添加到末尾 def ChangeList(lis): lis.append('hello i am the addone') prin

  • PHP 引用的概念

    什么是引用? 在 PHP 中引用意味着用不同的名字访问同一个变量内容.它不是C的指针,保存的并不是内存地址,无法进行指针运算.引用只是符号表的别名.就像 Unix 系统中的硬链接, Windows 系统中的快捷方式. 上面是官方手册中的原文,怎么说呢,引用其实和我们印象中的C里面的指针并不是相同的概念.指针是针对真实内存的操作,引用是针对指向这个内存的符号表的操作.还是从操作系统的快捷方式来说,快捷方式是可以删的,这就是PHP的引用.而C不仅删了快捷方式,还把原文件也给删了,这就是C的指针操作.

  • C++变量引用的概念介绍

    本篇介绍的变量引用与之前介绍的指针不是同一概念,它们有本质的区分: 1)不存在空引用.引用必须连接到一块合法的内存. 2)一旦引用被初始化为一个对象,就不能被指向到另一个对象.指针可以在任何时候指向到另一个对象. 3)引用必须在创建时被初始化.指针可以在任何时间被初始化. 下面代码介绍其应用场景,供参考 /* * Author:W: * 变量引用:是已定义声明的变量的"别名" * 引用与指针的区别: * 1)不存在空引用.引用必须连接到一块合法的内存. * 2)一旦引用被初始化为一个对

  • C++详细分析讲解引用的概念与使用

    目录 1.引用的概念 2.引用的格式 3.引用的特性 4.取别名原则 5.引用的使用场景 做参数 做返回值 int&Count()的讲解 传值传引用效率比较 6.引用和指针的不同点 1.引用的概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 2.引用的格式 类型 & 引用变量名 ( 对象名 ) = 引用实体: 举例如下: 注意:引用类型必须和引用实体是同种类型的 3.引用的特性 (1). 引用在 定义时必须初

  • PHP数组传递是值传递而非引用传递概念纠正

    在调用函数时通过将PHP数组作为实参赋给形参,在函数中修改,并不会影响到数组本身. 说明此过程中的传递为值传递,数组变量并非是指向此数组本身的引用,PHP数组本身以值的形式存在,同时形参是对数组的拷贝. 这点与其他一些语言不同(如c.Js等)很不相同,值得注意! 复制代码 代码如下: $arr = array( 'name' => 'corn', 'age' => '24', ); test_arr($arr); function test_arr($arr){ $arr['name'] =

  • C++ 中引用与指针的区别实例详解

    C++ 中引用与指针的区别实例详解 引用是从C++才引入的,在C中不存在.为了搞清楚引用的概念,得先搞明白变量的定义及引用与变量的区别,变量的要素一共有两个:名称与空间. 引用不是变量,它仅仅是变量的别名,没有自己独立的空间,它只符合变量的"名称"这个要素,而"空间"这个要素并不满足.换句话说,引用需要与它所引用的变量共享同一个内存空间,对引用所做的改变实际上是对所引用的变量做出修改.并且引用在定义的时候就必须被初始化.     参数传递的类型及相关要点: 1 按值

  • 浅谈Java中的四种引用方式的区别

    强引用.软引用.弱引用.虚引用的概念 强引用(StrongReference) 强引用就是指在程序代码之中普遍存在的,比如下面这段代码中的object和str都是强引用: Object object = new Object(); String str = "hello"; 只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象. 比如下面这段代码: public class Main { publi

  • Android利用软引用和弱引用避免OOM的方法

    想必很多朋友对OOM(OutOfMemory)这个错误不会陌生,而当遇到这种错误如何有效地解决这个问题呢?今天我们就来说一下如何利用软引用和弱引用来有效地解决程序中出现的OOM问题. 一.了解 强引用.软引用.弱引用.虚引用的概念 在Java中,虽然不需要程序员手动去管理对象的生命周期,但是如果希望某些对象具备一定的生命周期的话(比如内存不足时JVM就会自动回收某些对象从而避免OutOfMemory的错误)就需要用到软引用和弱引用了. 从Java SE2开始,就提供了四种类型的引用:强引用.软引

  • Java中弱引用和软引用的区别以及虚引用和强引用介绍

    知道弱引用和软引用的概念与如何使用它们是两码事,引用类在垃圾回收工作的过程中有重要作用.我们都知道垃圾回收器会回收符合回收条件的对象的内存,但并不是所有的程序员都知道回收条件取决于指向该对象的引用类型.这正是Java中弱引用和软引用的主要区别.如果一个对象只有弱引用指向它,垃圾回收器会立即回收该对象,这是一种急切回收方式.相对的,如果有软引用指向这些对象,则只有在JVM需要内存时才回收这些对象.弱引用和软引用的特殊行为使得它们在某些情况下非常有用.例如:软引用可以很好的用来实现缓存,当JVM需要

  • C++中引用(&)的用法与应用实例分析

    对于习惯使用C进行开发的朋友们,在看到c++中出现的&符号,可能会犯迷糊,因为在C语言中这个符号表示了取地址符,但是在C++中它却有着不同的用途,掌握C++的&符号,是提高代码执行效率和增强代码质量的一个很好的办法. 引用是C++引入的新语言特性,是C++常用的一个重要内容之一,正确.灵活地使用引用,可以使程序简洁.高效.我在工作中发现,许多人使用它仅仅是想当然,在某些微妙的场合,很容易出错,究其原由,大多因为没有搞清本源.故在本篇中我将对引用进行详细讨论,希望对大家更好地理解和使用引用起

随机推荐