PHP的变量_数组_对象值复制和引用复制
一.变量
1.变量传值赋值
在PHP两个变量之间进行传值赋值时,不会产生新结构体,而是2个变量共用1个结构体(refcount__gc=2),
只有在1个变量改变时,将会造成结构体的分裂;
$a=10;
/* $a结构体 {
value: {long 10}
type: IS_LONG
recount_gc : 1
is_ref_gc :0
}*/
$b=$a;
/* $a $b结构体 {
value: {long 10}
type: IS_LONG
recount_gc : 2
is_ref_gc :0
}*/
$b=5;
/* $a结构体 {
value: {long 10}
type: IS_LONG
recount_gc : 1
is_ref_gc :0
}
$b结构体 {
value: {long 5}
type: IS_LONG
recount_gc : 1
is_ref_gc :0
}*/
结构体一开始共用, 到某一方要修改值时才分裂. 这种特点,称为cow , copy on write写时复制
2.变量引用赋值
两个变量之间进行引用赋值时,2个变量共用1个结构体(is_ref_gc=1),
$a=10;
/* $a结构体{
value: {long 10}
type: IS_LONG
recount_gc : 1
is_ref_gc :0
}*/
$b=&$a;
/* $a $b结构体{
value: {long 10}
type: IS_LONG
recount_gc : 2
is_ref_gc :1
}*/
$b=5;
/* $a $b结构体{
value: {long 5}
type: IS_LONG
recount_gc : 2
is_ref_gc :1
}*/
在1个变量改变时,结构体不会分裂,而是直接改值,所有指向此结构体的变量值都变化。
二.数组
一个整体数组的传值赋值和引用赋值可以看成是将多个(数组元素)变量一起赋值。
数组单个的传值赋值和引用赋值可以看成是将一个(数组元素)变量赋值。
$arr=array('a','b','c');
$x=&$arr[1];
$tmp=$arr;
$arr[1]='d';
$arr[2]='e';
var_dump($arr,$tmp);//$arr=array('a','d','e') $tmp=array('a','d','c')
1.数组传值赋值
调用函数时通过将PHP数组作为实参赋给形参,在函数中修改,并不会影响到数组本身。
$arr = array(
'name' => 'corn',
'age' => 24
);
test_arr($arr);
function test_arr($arr){
$arr['name'] = 'xiaoming';
}
print_r($arr); //result: Array ( [name] => corn [age] => 24 )
说明此过程中的传递为值传递,数组变量并非是指向此数组本身的引用,
PHP数组本身以值的形式存在,形参只是是对数组元素数据的拷贝。
$arr=array('a','b','c');
/* $arr结构体{
value: *ht-->指向哈希表{'a','b','c'}
type: IS_ARRAY
recount_gc : 1
is_ref_gc :0
}*/
$tmp=$arr;//传值引用 recount_gc : 2
/* $arr $tmp结构体{
value: *ht-->指向哈希表{'a','b','c'}
type: IS_ARRAY
recount_gc : 2
is_ref_gc :0
}*/
$arr[1]='e';//根据cow机制,开始复制数组,结构体分裂
/* $arr结构体{
value: *ht-->指向哈希表{'a','e','c'}
type: IS_ARRAY
recount_gc : 1
is_ref_gc :0
}
$tmp结构体{
value: *ht-->指向哈希表{'a','b','c'}
type: IS_ARRAY
recount_gc : 1
is_ref_gc :0
}*/
2.数组引用赋值
$tmp=&$arr;//复制引用 recount_gc : 2
/* $arr $tmp结构体{
value: *ht-->指向哈希表{'a','b','c'}
type: IS_ARRAY
recount_gc : 2
is_ref_gc :1
}*/
$arr[1]='e';//结构体不会分裂,而是直接改值,所有指向此结构体的变量值都变化
/* $arr结构体{
value: *ht-->指向哈希表{'a','e','c'}
type: IS_ARRAY
recount_gc : 2
is_ref_gc :1
}
3.数组foreach($items as $item)
//在PHP中 next、current、prev、end、reset等函数和数组元素值修改的行为会对数组进行拷贝操作
数组指针会在新复制的数组上移动,原数组指针在拷贝时就停止移动。eg:
$arr=array('a','b','c','d');
$i=0;
foreach ($arr as $v) {
if($i==0){
current($arr);
}
$i++;
}
var_dump(current($arr));//返回b
4.数组foreach($items as &$item)
在使用foreach($items as &$item)时,会直接在原数组上进行操作,整个数组相当于多个变量的都使用引用传值。
下面是针对使用PHP数组问题的两个建议:
a)foreach循环内部禁用next、current、prev、end、reset五个array内部指针操作函数,
使用foreach后数组指针不会重置,每个foreach内第一次会重置,可以在foreach后使用reset重置指针。
b)在foreach内部可能有对$items进行的写操作时,推荐使用foreach($items as &$item),避免大数组拷贝。
三.对象
class myclass{
public $name='lily';
public $age=5;
}
$stu1=new myclass();//$stu1不是对象,只是对象的引用
/* $stu1结构体{
value: {handle:2385}-->handle指向哈希表{name:'lily',age:5}
type: IS_OBJECT
recount_gc : 1
is_ref_gc :0
}*/
1.对象直接赋值
直接赋值只是拷贝对象标识符handle,对象标识符handle的数据指向并没有改变。
$stu2=$stu1;//recount_gc:2
$stu2->name='hale';//通过拷贝对象标识符handle指向哈希表修改name值,不会导致结构体分裂
var_dump($stu1->name,$stu2->name);//hale hale
$stu2=false;//cow导致结构体分裂,$stu1结构体值不变,recount_gc :0
/* $stu2结构体{
value: 0
type: IS_BOOL
recount_gc : 0
is_ref_gc :0
}*/
var_dump($stu1->name);//hale
2.对象引用赋值
引用赋值共用一个对象标识符handle,不复制handle
$stu2=&$stu1;
$stu2="abc";//$stu2改变成字符串,handle则不存在了
/* $stu1 $stu2结构体{
{
char: 'abc'
len:3
}
type: IS_STRING
recount_gc : 2
is_ref_gc :1
}*/
3.对象克隆拷贝
克隆拷贝会形成新的不同对象标识符handle,
这就意味着指向的数据块也是不用的,那么两对象之间的修改操作互不影响了
$stu2=clone $stu1;