2

关键是对global的误解,之前以为在函数中global变量,就是把函数外部的变量拿进函数内部使用,但似乎我错了
引用传递+unset+global理解

php的引用(就是在变量、函数、对象等前面加上&符号)
在PHP中引用的意思是:不同的名字访问同一个内存地址

1、变量的引用:PHP的引用允许你用两个变量来指向同一个内存空间
代码:

clipboard.png

结果:

clipboard.png

2、函数的传址调用

clipboard.png

这里$num传递给函数的其实是$num所处的内存地址,通过在函数里改变$a的值,就可以改变$num的值了

3、函数的引用返回 —— 没太明白什么时候用
和参数传递不同,函数的引用返回必须在两个地方都用 & 符号(函数声明时、函数调用时) —— 指出返回的是一个引用,而不是通常的一个拷贝

clipboard.png

结果:

clipboard.png

clipboard.png

结果:

clipboard.png

clipboard.png

结果:

clipboard.png

clipboard.png

结果:

clipboard.png

下面解释下:
尽管函数声明方式是 function &test() 这样,但通过$a = test() 这种方式的函数调用得到的其实不是函数的引用返回,这跟普通的函数调用没有区别
PHP规定通过 $a = &test() 并且在声明函数时也使用了&,得到的才是函数的引用返回
用上面的例子来解释就是,$a = test() 这种方式调用函数,只是将函数的返回值赋给 $a 而已,而$a做任何改变都不会影响到函数中的$b

而通过 $a = &test() 方式调用函数呢(前提是声明函数时也用了&),它的作用是将 return $b 中的 $b 变量的内存地址与 $a 变量的内存地址指向了同一个地方。即产生了相当于这样的效果 ($a=&$b), 所以改变 $a 的值也同时改变了 $b 的值。所以在执行了
$a = &test();
$a = 5;
以后,$b的值也变为了5

再来个例子加深理解:
PHP里的函数的引用在定义及调用都要在函数名前加上 &

clipboard.png

结果:

clipboard.png

这里是为了让大家理解函数的引用返回才使用静态变量的,其实函数的引用返回多用在对象中
很多时候我们会看到这样的代码(出自 CI 框架源码):
$class =& load_class('a','b');

手册里是这么写的:引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时。不要用返回引用来增加性能,引擎足够聪明来自己进行优化。仅在有合理的技术原因时才返回引用!要返回引用,使用此语法:

clipboard.png

和参数传递不同,这里必须在两个地方都用 & 符号——指出返回的是一个引用,而不是通常的一个拷贝,同样也指出 $myValue 是作为引用的绑定,而不是通常的赋值
如果试图这样从函数返回引用:return ($this->value);,这将不会起作用,因为在试图返回一个表达式的结果而不是一个引用的变量。只能从函数返回引用变量——没别的方法。如果代码试图返回一个动态表达式或 new 运算符的结果,自 PHP 4.4.0 和 PHP 5.1.0 起会发出一条 E_NOTICE 错误
似懂非懂?那么我们来改写一下程序吧,让它变成一个常规的函数:

clipboard.png

结果:

clipboard.png

现在能理解“引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时”这句话了吧,函数 &getValue() 把引用绑定在成员变量 $value 上了。正常来说,$obj = new foo; 产生的 $obj 是一个copy,它的成员变量 $value 与函数 getValue() 不存在“别名”(引用)关系(额,不太懂)

4、对象的引用

clipboard.png

结果:

clipboard.png

以上代码是在PHP5中的运行效果,在PHP5中对象的复制是通过引用来实现的。上列中$b=new a; $c=$b; 其实等效于$b=new a; $c=&$b; PHP5中默认就是通过引用来调用对象, 但有时你可能想建立一个对象的副本,并希望原来的对象的改变不影响到副本,为了这样的目的,PHP定义了一个特殊的方法,称为__clone
php5中对于大数组的传递,建议用 "&" 方式,毕竟节省内存空间使用

5、取消引用
重要的是删除引用的变量,当你 unset 一个引用,只是断开了变量名和内存地址之间的绑定。只是unset的变量访问不了,这并不意味着内存地址被销毁了

clipboard.png

不会 unset $b,只是 $a

clipboard.png

地址(引用)传递,只是多个变量指向了同一地址(内存空间)
unset一个,并不能unset掉地址空间

根据这个原理,我们来屡屡$a和$GLOBALS['a']之间的关系
代码1:

clipboard.png

代码2:

clipboard.png

结果:

clipboard.png

代码3:

clipboard.png

结果:

clipboard.png

根据这三段代码的结果,$a和$GLOBALS['a']在内存中的关系肯定不是这样的

clipboard.png

如果是这样的话,unset掉一个,另一个应该还是存在的
所以个人猜测关系应该是这样的:

clipboard.png

其中一个是另一个的别名
关于这个问题,也问了一些人,各有个的说法,况且都牵扯到了PHP底层机制,暂且放一放,按照上面说的来理解吧

6、global

clipboard.png

这里的$num =& $GLOBALS['num']; $num是函数里的$num,函数里的$num指向了$GLOBALS['num']的内存地址

clipboard.png

global 引用
当在函数中用 global $var 声明一个变量时实际上建立了一个到全局变量的引用。也就是说和这样做是相同的:
$var =& $GLOBALS["var"];
这意味着,unset $var 不会 unset 全局变量

$this 在一个对象的方法中,永远是调用它的对象的引用

再来一个例子:

clipboard.png

结果:

clipboard.png

为什么会是0 5呢?
global在函数中产生一个指向函数外部变量的别名变量,而不是真的把函数外的变量拿到函数中使用
一旦改变了别名变量(函数内部的变量)的指向地址
$var2 =& $var1;
其实就是函数中$var2的引用指向了$var1的内存地址
只是函数中$var2的指向发生了变化,函数内部变量的变化只在函数的局部产生效应,在函数外部$var2的指向物理内存地址并没有变化,还是它自己,所以根本就没有改变函数外$var2的值
$GLOBALS[]确确实实调用的是函数外部的变量,函数内外始终保持一致!

clipboard.png

结果:

clipboard.png

7、写时拷贝
php中对于地址的指向(类似指针)功能不是由用户自己来实现的,是由Zend核心实现的,php中引用采用的是写时拷贝的原理,就是除非发生写操作,指向同一个地址的变量或者对象是不会被拷贝的
通俗的讲:
① 如果有下面的代码
$a="ABC"; $b=$a;
其实此时,$a与$b都是指向同一内存地址,而并不是$a与$b占用不同的内存
② 如果在上面的代码基础上再加上如下代码
$a="EFG";
由于$a与$b所指向的内存的数据要重新写一次了,此时Zend核心会自动判断,自动为$b产生一个$a的数据拷贝,重新申请一块内存进行存储

clipboard.png


web360
838 声望28 粉丝

大家都是凡人,难道你确定你是大神?