这是我参与更文挑战的第5天,活动详情查看: 更文挑战
在数组中, 我们同样可以进行引用传值, 引用返回值, 以及取消引用的操作. 只不过是在引用的核心逻辑外面套了一层数组的外衣.
但针对数组的模式, 有2点需要注意的火坑:
先看下面两个例子:
这是引用在数组中的一般应用,和普通赋值没有什么区别:
$arr = array(1, 'test');
$p =& $arr[1]; // $p和$arr[1]都指向了test
$new = 'new';
$arr[1] =& $new;//$new给$arr[1]赋予新的引用,都指向了new
echo $arr[1]; // echo 'new';
echo $p.'<br>'; //'test',$p的指向并没变;
这个是数组引用特殊的地方,不注意的话会出问题:
$array1 = array(1,2);
$x = &$array1[1]; //x和$array1[1]指向同一个变量
$array2 = $array1; // 这里是数组对数组整体的赋值,将其中元素的引用关系,也进行了传递!
$array2[1]=22; // (changing [0] will not affect $array1)
var_dump($array2,$x); //同时修改了$array1[1]
上面第二个例子中, $array2 = $array1, 这一步非常重要, 我们如果不注意, 可能就认为这是一个普通赋值, 不存在引用关系. 但实际上, 元素级的引用关系却进行了传递, 在新数组下得到了保留.
这就牵出了数组型引用的第一个坑:
元素级用 & 数组引用
上面那个例子是元素级别的引用关系, 它可以通过普通数组间赋值来传递引用关系.
我们再来看数组级别的引用:
$brr1=[1,2,3,4];
$brr2=[5,6,7,8];
$brr3=&$brr1; //建立brr3和brr1的引用关系;
$brr2=$brr3; //对brr2进行整体赋值
$brr3[3]=100;
print_r($brr2); //依然是[1,2,3,4]
print_r($brr1); //[1,2,3,100], 被修改;
可以看到,数组级别的引用关系, 无法通过普通赋值来传递. 引用关系不会保留. 只能通过引用赋值来传递.
这就好比, 你(数组$a) 和 你的老婆(数组$b,引用赋值), 或和情人(普通赋值), 她们怀了你的孩子(数组元素), 这个孩子都是你的家人(引用关系), 而且你和你的老婆也是一家人(引用关系), 但和你的情人却不是一家人.
元素级别的引用和数组整体的引用, 是相互独立. 当我们为数组进行整体赋值的时候, 元素级别的应用会进行传递和保留, 而数组级别的引用却不会, 需要单独加&说明.
所以在进行元素级引用的时候要格外小心!!!尽量少用, 可以通过条件判断, 如果发生改变就重新赋值的操作, 更安全一些.
好的,我们再来看一下第二个坑:
foreach()引用的小尾巴
这个函数可以用来遍历数组, 并可与引用组合使用, 实现数组赋值, 修改等操作. 举例:
$a1=[1,2,3,4]
foreach ($a as &$value) {
$value = $value * 2;
print_r($a1); //[2,4,6,8] 通过引用实现了对数组所有元素的修改
现在问题来了, 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。这个就是 foreach引用的小尾巴.
此时数组$a1的结构已经变为了:
$a1=[$a1[0]=>2, $a1[1]=>4, $a1[2]=>6, &$a1[3]=>8]
注意最后一项 a1[3]与函数参数$var之间的引用关系. 可以理解为:
$a1[3]=&$value=8;
如果我们忽视这个小尾巴的话, 就会被它反噬, 比如我们现在再遍历一次数组的话, 你就会发现:
$a1=[1,2,3,4]
foreach ($a as &$value) {
$value = $value * 2;
print_r($a1);
foreach ($a as $key => $value){
echo "$key to $value";
} //0 => 2 1 => 4 2 => 6 3 => 6
此时, $a1[3]=6. Why?
因为第二次foreach的时候, value, 而同时也修改着$a[3].
我把每一步分解一下:
$value= $a1[0]=2, 此时$a1[3] = 2;
$value= $a1[1]=4, 此时$a1[3] = 4;
$value= $a1[2]=6, 此时$a1[3] = 6;
$value= $a1[3]=6, 此时$a1[3] = 6;
看到这里, 大家应该都明白了, foreach (&)会在遍历结束后,保留最后一项的引用关系, 最终导致被引用数组的最后一项发生改变.
那么如何规避这个问题? 2个办法:
unset, 每次foreach遍历后,都对引用变量$value进行unset, 解除它与数组元素的引用关系.
\$a=[1,2,3,4];
foreach ($a as $key => $value){
$a[$key]=$value*2;
print_r($a); //可以通过这样的方式进行赋值,避开引用;
以上就是关于PHP数组中涉及引用的两个坑, 这两个坑都很隐蔽, 各位在写代码时要特别小心!
感谢阅读, 抛砖引玉, 如有不准确和错误之处请留言指正, 我会及时修正, 拜谢!
学习不易,请勿私自转载, 否则别怪老大爷不客气.
参考资料:
www.php.net 官方文档