PHP底层原理之:变量及数据结构(下篇)
PHP底层原理之:变量及数据结构(下篇)
上篇说到写时复制,本篇继续
7.传值引用
思考1:下面代码,两者是否共享一个结构体,如果是,和传值赋值有什么不同?
<?php
$a = 3;
$b = &$a;
echo $a . '---' . $b;
echo "\r\n";
$b = 5;
echo $a . '---' . $b;
不是共享,是共用,两个变量都有对此内存的结构体的操作资格。跟传值赋值的区别是,两个变量直接指向同一块内存,而且修改时不会发生延迟复制。
1.当$b = &$a时,两个变量都指向同一块内存,refcount_gc加1,并且,is_ref_gc = 1,表示是引用关系。
2.当修改$b = 5时,由于is_ref_gc = 1,表明此内存的结构体与对应变量时引用关系,所以直接修改此结构体的值。
传值赋值与引用赋值的区别
需要注意的是,写时复制与引用机制是不同的概念。写时复制在赋值时使用延迟复制,而引用则直接让两个变量指向同一块内存,且不会有延迟复制的操作。
- 写时复制:赋值时内存共享,但修改时才会复制。
- 引用:多个变量指向同一块内存,任何修改都会影响所有引用。
8.强制分裂
<?php
$a = 3;
/**
* $a => {
* value : 3
* refcount_gc: 1
* if_ref_gc:0
* }
*/
$b = $a;
/**
* $a,$b => {
* value : 3
* refcount_gc: 2
* if_ref_gc:0
* }
*/
$c = &$a;
/** 思考:此时是否会改成如下结构?
* {
* value : 3
* refcount_gc: 3
* if_ref_gc:1
* }
*
* 不会,否则$b将会受到影响。
* 如果is_ref_gc 由0->1的过程,refcount_gc > 2,说明此时有多个变量共享此内存,
* 不能直接引用,此时就会发生强制分裂。复制一份$a给$b
*
* $b => {
* value : 3
* refcount_gc: 1
* if_ref_gc:0
* }
*
* 完后再修改原结构体,以及建立引用关系
* $a,$c = {
* value : 3
* refcount_gc: 2
* if_ref_gc:1
* }
*/
$c = 5;
echo $a, $b, $c; // 输出 5 3 5
9.引用数组时的怪现象
回忆之前的代码,数组的值存储的是一个指向哈希表的地址,而不是具体的值。
HashTable *ht;
思考1:下面代码中,$tmp是否会受到影响
<?php
$arr = [0, 1, 2, 3];
$tmp = $arr;
$arr[1] = 11;
echo $tmp[1];
不会,此时会正常写时复制。
但注意当$arr[1]修改时,$tmp会有一个新的结构体,但是除了$tmp[1]会指向新的内存地址,数组中其他的值还是指向原结构体的zval对应地址。
思考2:下面代码,是否会影响$tmp?
<?php
$arr = [0, 1, 2, 3];
$x = &$arr[1];
$tmp = $arr;
$arr[1] = 100;
$arr[2] = 100;
echo $tmp[1];
哈希表内部的$arr[1]已经改为引用关系:is_ref_gc = 1,外部还不知道,此时发生写时复制,复制一份$arr给$tmp,但里面的小单元1是引用关系,所有$arr[1]和$tmp[1]此时都指向同意内存地址。所以会影响到$tmp[1];
10.循环数组时的怪现象
1.数组尽量避免使用引用
2.foreach后,不会重置数组内部指针,使用数组是,不要假想指针正好指向数组头部,可以在foreach后rest一下数组。
11.符号表与作用域
t函数在执行时,根据函数的参数,局部变量等,生成一个执行环境的结构体函数编译后的opcode,称为op_parrray(就是执行逻辑),开始执行--以入栈的环境结构体为环境来执行。并生成此函数的符号表,函数找变量,就在符号表找,即局部变量。
注意:1个函数可能调用多次,栈中可能有某函数的多个执行环境入栈,但这个函数的op_array只有1个。
当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »