欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

php引用传参 - php函数引用传参数组:function set(array &$array, $path, $value)

程序员文章站 2022-06-14 10:52:55
...
function set(array &$array, $path, $value)
{
    print_r($array);
    echo('

'); $segments = explode('.', $path); while(count($segments) > 1) { $segment = array_shift($segments); if(!isset($array[$segment]) || !is_array($array[$segment])) { $array[$segment] = []; } $array =& $array[$segment]; print_r($array); echo('

'); } $array[array_shift($segments)] = $value; print_r($array); echo('

'); } $arr = ['a' => 1, 'b' => 2, 'c' => 3]; set($arr, 'a.b.d', 4); print_r($arr);

结果:

Array ( [a] => 1 [b] => 2 [c] => 3 )

Array ( )

Array ( )

Array ( [d] => 4 )

Array ( [a] => Array ( [b] => Array ( [d] => 4 ) ) [b] => 2 [c] => 3 )

为什么最终结果不是Array ( [d] => 4 ),而是 Array ( [a] => Array ( [b] => Array ( [d] => 4 ) ) [b] => 2 [c] => 3 ) ?
这个怎么解: $array =& $array[$segment];

回复内容:

function set(array &$array, $path, $value)
{
    print_r($array);
    echo('

'); $segments = explode('.', $path); while(count($segments) > 1) { $segment = array_shift($segments); if(!isset($array[$segment]) || !is_array($array[$segment])) { $array[$segment] = []; } $array =& $array[$segment]; print_r($array); echo('

'); } $array[array_shift($segments)] = $value; print_r($array); echo('

'); } $arr = ['a' => 1, 'b' => 2, 'c' => 3]; set($arr, 'a.b.d', 4); print_r($arr);

结果:

Array ( [a] => 1 [b] => 2 [c] => 3 )

Array ( )

Array ( )

Array ( [d] => 4 )

Array ( [a] => Array ( [b] => Array ( [d] => 4 ) ) [b] => 2 [c] => 3 )

为什么最终结果不是Array ( [d] => 4 ),而是 Array ( [a] => Array ( [b] => Array ( [d] => 4 ) ) [b] => 2 [c] => 3 ) ?
这个怎么解: $array =& $array[$segment];

首先,如果语句$array =& $array[$segment]中的&符号去掉,结果就如楼主所期待的最后输出Array([d] => 4)。显然问题的关键就在那一句的&上面。
我们知道php在存储变量和变量的值时分别用了两种数据结构zvalzval_value。在zval中包含四个字段:
refcount_gc 表示引用计数
is_ref_gc 表示是否为引用
value 存储变量标识符
type 变量的具体类型

变量的实际值存储在另一个联合体中,具体结构省略。现在我们来模拟一下过程:
首先$arr = ['a' => 1, 'b' => 2,'c' => 3]创建:
arr{'refcount_gc':1, 'is_ref_gc':0,'value':'arr','type':array}(后面将会省略value和type的书写),同时创建array类型的zval_value。
['a']{'refcount_gc':1, 'is_ref_gc':0, ...}
['b']{'refcount_gc':1, 'is_ref_gc':0, ...}
['c']{'refcount_gc':1, 'is_ref_gc':0, ...}
在调用set方法时,传递的是$arr的一个引用:&arr{'refcount_gc':2, 'is_ref_gc':1, ...}
接着进入函数体,while循环内,第一次由于!is_array($arr['a'])成立,为$arr['a']赋值空数组。
['a']{'refcount_gc':1, 'is_ref_gc':0, ...},并创建类型为array的zval_value。
$arr =& $arr['a'];这一句又将原数组的['a']转为引用['a']:{'refcount_gc':2, 'is_ref_gc':1, ...},它和arr都同时是这个空数组的引用①。
接着第二次循环,由于此时arr是一个空数组的引用,!isset($arr['b'])成立,为$arr['b']赋值空数组。
arr['b']{'refcount_gc':2, 'is_ref_gc':1, ...},并创建类型为array的zval_value,同时由于①,原来数组的['a']也是这个空数组的引用, 也就是在原数组['a']基础上有了['a']['b']②。
$arr =& $arr['b'];这一句又将arr['b']转为引用arr['b']:{'refcount_gc':3, 'is_ref_gc':1, ...},由于②,①中的空数组的['b'],原数组的['a']['b']和arr都同时是这个空数组的引用③。
然后退出循环。
$arr['d'] = 4;为②中的空数组创建索引['d'],且由于③,原数组的['a']['b']、①中数组的['b']也是这个数组的引用,他们都指向了这个(['d'] => 4)的数组。
所以最后打印原数组出来的就是Array ( [a] => Array ( [b] => Array ( [d] => 4 ) ) [b] => 2 [c] => 3 )

简化的图示为:
php引用传参 - php函数引用传参数组:function set(array &$array, $path, $value)

画的比较随意。。大致按数字步骤体现其过程。。。

问题主要出现在

 while(count($segments) > 1)
    {
        $segment = array_shift($segments);

        if(!isset($array[$segment]) || !is_array($array[$segment]))
        {
            $array[$segment] = [];
        }
        //这里传引用赋值,将$array[$segment]的引用传给了$array
        //所以此后的$array已经不再指向传参近来的那个$array(但传参进来的那个$array并未消失)
        //最简单的方法是将这个&去掉,在输出一遍,你应该就明白了
        $array =& $array[$segment];

        print_r($array);
        echo('

'); }

运行一下,如果不明白有问题可以留言

相关标签: php php引用传参