背景

  php语言的高度封装和五花八门的库使这门语言很容易上手,而且开发效率比C/C++高出许多。但是也正是由于php封装度很高,一些在c语言中很简单的概念,让php这么一封装,就变得难以琢磨。比如引用,在c语言中的概念很简答, 就是两个变量名指向同一块内存。而且引用必须要你手动操作,哪个变量引用的哪块内存在编写代码的时候心里是一清二楚的。但是在php中,有很多地方使用了隐式的引用,而写代码的时候并不知道这是引用。这就很容易造成问题,而且难以发现。就比如下面的代码我没有使用引用啊?但是name的值确实被改变了。这种类型的错误一旦发生,很可能就应验了一句话,“一个小bug,一查一下午”,还不一定能查出来。

    

  输出:

    

  我没有使用引用啊?但是name的值确实被改变了。这种类型的错误一旦发生,很可能就应验了一句话,“一个小bug,一查一下午”,还不一定能查出来。所以这促使我去仔细了解一下php引用的规则,进而在以后的工作中尽量减少这种没有意义的错误。

  下面在梳理引用规则的时候,先不会管php的写时复制,尽可能从语义的角度来看待php的引用。下面的讨论仅适用于php5.

  

php内置类型的引用

  当使用php内置类型时,php的引用规则和C语言没有区别。例中测试了int, string, array,是预期的结果。只要函数的形参不是引用类型,就不会被莫名其妙的改变。

    

  输出:

    

 php对象的引用

  看下面的例子:

    

  看输出,坑爹的地方来了:

    

  不管函数的形参是引用类型还是非引用类型,obj的name成员都被更改了。

  在某篇博客上看到了这样的解释:“PHP 5 引进了独立于变量容器的『对象存储器』。当一个对象赋值给变量时,变量不再存储整个对象(属性表和其他的『类』信息),而是存储这个对象所在 存储器的引用 —— 当我们复制一个对象变量时,我们复制的是这个『存储器的引用』”。把这句话套到例子中就是,$obj现在是一个引用,引用的就是“new Name()”生成的对象存储器。既然知道了$obj只是个引用,那么解释上面例子为什么会这样输出就很容易了:

  对于fun函数,当实参赋值给形参的时候,相当于 $arg = $obj。当我们复制一个变量对象时,复制的是这个对象存储器的引用。那么现在的情况就是,$arg也引用刚才"new Name()"生成的对象存储器,这个对象存储器的引用计数是2。所以在fun函数中,我们操作$arg->name=“fun”,本质上就是让这个对象存储器中的name属性编程“fun”。当fun函数返回时,$arg被销毁,此对象存储器引用计数减一。但是$obj仍然引用着这个对象存储器,所以我们 echo $obj->name的时候,输出此对象存储器的name属性,即“fun”。

  对于funReference, 当实参赋值给形参时,相当于$arg = &$obj。$arg作为$obj的引用,而$obj作为对象存储器的引用。在解引用的时候,可以简单的认为,$arg->name="funReference"就是在直接操作对象存储器。所以funReference和fun造成了同样额结果。

  但是是不是$arg = $obj 和 $arg = &$obj 就完全等价了?事实证明,不是,举个例子:

    
  输出:

    

  可以看出,$obj2=$obj1,  $obj2复制的是对象存储器的引用,所以后续就算是$obj指向了其他的结构,$obj2仍然引用的是对象存储器。但是$obj3 = &obj1, $obj3是$obj1的引用,按照C语言的引用规则,当$obj1的指向发生改变时,$obj3也应该指向也会发生改变,使之和$obj1指向相同的内存。php也确实这么去实现了这个语义。所以当obj后来指向“abc”时,$obj3也指向了"abc"。php的引用指向在初始化后,还可以随意改变,真实让人脑壳疼。

  另外一个问题,php的对象没法被赋值吗?因为不管怎么弄,我拿到的都只是相当于对象存储器的引用。php开发者也想到这个问题了,我在网上找到了下面的两种办法:

    1. 给类实现一个__clone()方法。然后赋值的时候这样:$obj2 = clone $obj1;

    2. 使用序列化函数。$obj2 = unserialize(serialize($ojb1));

global,$GLOBALS, 引用

  先给个定义:

    $GLOBALS['var_name'], 是全局变量$var本身

    global $val是全局变量的同名引用

  这两句话让人感觉有点云里雾里,举个例子就清楚了:

    

  输出:

    

  可以看到,var1并没有被改变,而var2则被改变了。

  先解释var1, 开头说了,global var 是 全局变量 var 的同名引用。也就是说,在fun()内部,var1只是全局变量var1的引用而已。看看fun()中做了什么?$var1=&$str, 让var1重新引用局部变量str。这个操作只是让fun内部的同名引用var1所引用的对象变了,和全局的var1一点关系都没有,所以var1不会改变。

  $GLOBALS的官方定义是,由所有已定义的全局变量所组成。$GLOBALS["var_name"]就是全局变量var_name本身。套到例子中,$GLOBALS["var2"]也就是全局变量var2本身,让全局变量引用fun中的局部变量str,所以var2的值就发生了改变。

  这两个概念一定要区分清楚,否则在平时编码中,就会发生想改变的全局变量没有被改变,但是不想被改变的全局变量反而被意外改变。而且这种bug还真不好找。

  

  

转载于:https://www.cnblogs.com/MyOnlyBook/p/9688789.html

php对引用的简单理解相关推荐

  1. C++ 对引用的深入理解

    观看了唐老师讲解的一节<第5课 - 引用的本质分析>感觉非常不错,有深度不废话,我喜欢--- 再此总结下,并且奉上视频下载地址--- 360网盘下载地址: https://yunpan.c ...

  2. 【转载】Deep learning:十九(RBM简单理解)

    Deep learning:十九(RBM简单理解) 这篇博客主要用来简单介绍下RBM网络,因为deep learning中的一个重要网络结构DBN就可以由RBM网络叠加而成,所以对RBM的理解有利于我 ...

  3. 学习:双机热备、集群、负载均衡、SQL故障转移群集简单理解(转)

    双机热备.集群.负载均衡.SQL故障转移群集简单理解平常,大家常提到几个技术名词:双机热备.集群.负载均衡.SQL故障转移群集.这里,就我的理解,和大家简单探讨下,有不足或错误之处还请各位指出! 这些 ...

  4. JS闭包的简单理解。优缺点以及垃圾回收机制

    闭包是什么? ·了解闭包首先了解js的'链式作用域'结构,对象可以一级一级的向上查找父对象的变量,所以父对象的变量对子对象可见,反之不成立:所以都可以访问全局变量 ·为了解决函数外部无法访问函数内局部 ...

  5. php _call call_user_func_array,PHP call_user_func和call_user_func_array函数的简单理解与应用分析...

    本文实例讲述了PHP call_user_func和call_user_func_array函数的简单理解与应用.分享给大家供大家参考,具体如下: call_user_func():调用一个回调函数处 ...

  6. vue函数如何调用其他函数?_编程|如何简单理解函数的传参、返回、调用

    1 函数内部变量的局部性 在一个函数中定义的变量(包括实参),其作用域仅限于定义它的函数中,在其它的函数不能使用,这种变量称为"局部变量". 局部变量的作用域:函数内部声明和定义的 ...

  7. Javascript闭包简单理解

    Javascript闭包简单理解 原文:Javascript闭包简单理解 提到闭包,想必大家都早有耳闻,下面说下我的简单理解. 说实话平时工作中实际手动写闭包的场景并不多,但是项目中用到的第三方框架和 ...

  8. 关于C语言函数的简单理解

    关于C语言函数的简单理解 C语言中的函数 在C语言中,函数是构成程序的基本模块.程序的执行从main()函数的入口开始,到main()函数的出口结束,中间循环.往复.迭代的调用一个有一个函数.每个函数 ...

  9. linux网桥的简单理解和配置

    linux网桥的简单理解和配置 Linux网桥是linux虚拟网络设备之一.网上很多分析linux网桥的文章,例如代码层面的分析.这里不牵扯复杂的分析和配置,主要是面向虚拟机链接一个用途,小白我的备忘 ...

  10. Android:安卓学习笔记之navigation的简单理解和使用

    Android navigation的简单理解和使用 1 .基本概念 1.1.背景 1.2.含义 2.组成 2.1.Navigation graph 2.2.NavHostFragment 2.3.N ...

最新文章

  1. 隐藏在程序旮旯中的“安全问题”
  2. Pytest之pytest.assume用例中断言1失败会继续执行后续代码断言2
  3. hyperworks安装教程linux,HyperWorks安装说明.ppt
  4. php ds扩展,PHP教程:老生常谈PHP中的数据结构:DS扩展
  5. SpringBoot(四):mybatis之通用mapper、分页插件PageHelper
  6. BT.1120协议简介
  7. 锋利的jQuery系列一
  8. ADAMS搭建控制系统教程:偏心连杆机构的转速控制
  9. 服务器运行cad慢,cad启动很慢的三种解决方法
  10. java单继承多实现_单继承,多实现
  11. JAVA-DDD项目结构
  12. 《R语言入门与数据分析》
  13. java+poodle漏洞修复_SSLv3 Poodle攻击漏洞检测工具
  14. php药膳 源码,药膳
  15. mysql5.7出现:ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)问题解决
  16. Rosalind Java|Overlap Graphs
  17. Others_各行业优秀的人
  18. 面试经验-(1)宁波银行
  19. 网络安全工程师学习第二天
  20. 广东各高校的校花大全(多图)

热门文章

  1. 【渝粤教育】电大中专成本会计 (2)_1作业 题库
  2. 【渝粤教育】广东开放大学 服务标准化 形成性考核 (41)
  3. caffe dataset
  4. 约束最优化方法 (一) 最优性条件
  5. ubuntu16.04下ROS操作系统学习笔记(七 )机器语音-语音听写-科大讯飞SDK调用
  6. autotools使用总结
  7. C++写的B+树源码
  8. 5-8 第五天 微信 JS-SDK
  9. Git 分支 - 分支的新建
  10. 错误处理与调试[下]