变量引用对比赋值操作
赋值操作:
// 定义一个变量
$a = range(0, 1000); // 在内存中申请空间来存储变量a对应的值
var_dump(memory_get_usage());
// 定义变量b,将a变量的值赋值给b
$b = $a;
// 由于PHP的COW,(Copy On Write,只有在变量发生“写”操作之后,系统才会申请新的内存空间)所以,此时,变量a与变量b会指向同一个内存地址。
var_dump(memory_get_usage());
// 对a进行修改
$a = range(0, 1000);
// 变量a发生了“写”操作,系统将在内存中申请新的空间,保存新的值,同时变量a指向原内存地址的指针将会移除,并指向新的内存地址。
var_dump(memory_get_usage());
引用变量
PHP的引用变量定义符为
&
该操作将会将原有变量的指向的内存地址传递给新的变量。
如:
$a = range(0, 1000); //在内存中开辟空间用于保存 $a 对应的值
$b = &$a; //将 $a 对应的值在内存中的地址空间传递给 $b , 此时 $a 与 $b 共用同一块内存空间。此时,如果对 $a 或者 $b 进行修改,则 $a 与 $b 的值将同时修改。
两者区别
引用变量传递了内存地址,如果有一个变量的值发生修改,那么两个变量的值都会发生变化;
变量赋值变量修改只会修改发生变化的那个变量的值;在未发生修改之前,与引用变量一样,两者都会指向同一个内存地址
zval变量容器变化
赋值操作
// zval变量容器
$a = range(0, 3);
xdebug_debug_zval('a');
/***
* 输出结果
* a: (refcount=1, is_ref=0)=array(4) { // refcount=1:引用计数为1,只有变量a;is_ref=0:是否引用变量:false
* [0] =>(refcount=0, is_ref=0)=int(0)
* [1] =>(refcount=0, is_ref=0)=int(1)
* [2] =>(refcount=0, is_ref=0)=int(2)
* [3] =>(refcount=0, is_ref=0)=int(3)
* }
***/
// 定义变量b,把a的值赋值给b
$b = $a;
xdebug_debug_zval('a');
/***
* 输出结果
* a: (refcount=2, is_ref=0)=array(4) { // refcount=2:引用次数为2,变量a与变量b同时指向a对应的内存空间;is_ref=0:是否引用变量:false
* [0] =>(refcount=0, is_ref=0)=int(0)
* [1] =>(refcount=0, is_ref=0)=int(1)
* [2] =>(refcount=0, is_ref=0)=int(2)
* [3] =>(refcount=0, is_ref=0)=int(3)
* }
***/
// 修改a
$a = range(0, 3);
xdebug_debug_zval('a');
/***
* 输出结果
* a: (refcount=1, is_ref=0)=array(4) { // refcount=1:引用计数为1,由于COW原则,变量a指向了新的内存地址,所以只有变量a;is_ref=0:是否引用变量:false
* [0] =>(refcount=0, is_ref=0)=int(0)
* [1] =>(refcount=0, is_ref=0)=int(1)
* [2] =>(refcount=0, is_ref=0)=int(2)
* [3] =>(refcount=0, is_ref=0)=int(3)
* }
***/
引用变量
$a = range(0, 3);
xdebug_debug_zval('a');
/***
* 输出结果
* a: (refcount=1, is_ref=0)=array(4) { // refcount=1:引用计数为1,只有变量a;is_ref=0:是否引用变量:false
* [0] =>(refcount=0, is_ref=0)=int(0)
* [1] =>(refcount=0, is_ref=0)=int(1)
* [2] =>(refcount=0, is_ref=0)=int(2)
* [3] =>(refcount=0, is_ref=0)=int(3)
* }
***/
// 引用变量
$b = &$a;
xdebug_debug_zval('a');
/***
* 输出结果
* a: (refcount=2, is_ref=1)=array(4) { // refcount=2:引用计数为2,变量a与变量b同时指向a对应的内存空间;is_ref=1:是否引用变量:true
* [0] =>(refcount=0, is_ref=0)=int(0)
* [1] =>(refcount=0, is_ref=0)=int(1)
* [2] =>(refcount=0, is_ref=0)=int(2)
* [3] =>(refcount=0, is_ref=0)=int(3)
* }
***/
// 修改变量a
$a = range(0, 3);
xdebug_debug_zval('a');
/***
* 输出结果
* a: (refcount=2, is_ref=1)=array(4) { // refcount=2:引用计数为2,变量a与变量b同时指向a对应的内存空间;is_ref=1:是否引用变量:true;并未受到修改操作的影响
* [0] =>(refcount=0, is_ref=0)=int(0)
* [1] =>(refcount=0, is_ref=0)=int(1)
* [2] =>(refcount=0, is_ref=0)=int(2)
* [3] =>(refcount=0, is_ref=0)=int(3)
* }
***/
unset操作
unset操作仅会取消引用,而不会销毁内存空间.
$a = 1;
$b = &$a;
unset($b); // 此时只取消了变量b的引用,而不会销毁内存空间,变量a不受影响
echo $a;
对象操作
对象本身就是引用传递!!!!如果需要复制,参考 clone
class Person
{
public $name = "zhangsan";
}
$p1 = new Person;
xdebug_debug_zval('p1');
/***
* p1: (refcount=1, is_ref=0)=class Person#1 (1) {
* public $name =>(refcount=2, is_ref=0)=string(8) "zhangsan"
* }
***/
$p2 = $p1;
xdebug_debug_zval('p1');
/***
* p1: (refcount=2, is_ref=0)=class Person#1 (1) {
* public $name =>(refcount=2, is_ref=0)=string(8) "zhangsan"
* }
***/
$p2->name = "lisi";
xdebug_debug_zval('p1');
/***
* p1: (refcount=2, is_ref=0)=class Person#1 (1) {
* public $name =>(refcount=2, is_ref=0)=string(8) "lisi"
* }
***/
Example
$data = ['a', 'b', 'c'];
foreach ($data as $key=>$val)
{
$val = &$data[$key];
}
var_dump($data); // 输出['b', 'c', 'c']
/***
* 分析:
* foreach循环中,
* 第一次循环:
* $data as $key => $val: $val = $data[0] (即'a'), $key = 0, 进入循环体;
* 由于是引用变量操作,所以 $val 与 $data[0] 均会指向 ‘a’,第一次循环结束;
* 第二次循环:
* $data as $key => $val: $val = $data[1] (即'b'),由于上一步循环中,$val 与 $data[0]是指向的同一个内存地址,且为引用变量,所以,此时$data[0]值则变为了‘b’,进入循环体
* 由于是引用变量操作,所以 $val 与 $data[1] 均会指向 ‘b’,第二次循环结束;
* 第三次循环:
* $data as $key => $val: $val = $data[2] (即'c'),由于上一步循环中,$val 与 $data[1]是指向的同一个内存地址,且为引用变量,所以,此时$data[1]值则变为了‘c’,进入循环体
* 由于是引用变量操作,所以 $val 与 $data[1] 均会指向 ‘c’,第三次循环结束;
* 循环结束后,$data[0] = 'b',$data[1] = 'C',$data[2] = 'c',
* 所以最后输出结果为['b', 'c', 'c']
***/