转载内容:
foreach指针问题
foreach指针引用销毁问题
代码1:
<?php
$arr = array(1,2,3,4,5);
foreach($arr as $key => &$row) {
echo key($arr), '=>', current($arr), "\r\n";
}
代码2:
<?php
$arr = array(1,2,3,4,5);
foreach($arr as $key => $row) {
echo key($arr), '=>', current($arr), "\r\n";
}
结论:
1、为什么foreach循环体中执行key或current会显示第二个元素(非引用情况)?
以key函数为例,我们执行函数调用时,会执行中间代码SEND_REF,此中间代码会将没有设置引用的变量复制一份并设置为引用。当进入循环体时,PHP内核已经经过了一次fetch操作,相当于执行了一次next操作,当前元素指向第二个元素。因此我们在foreach的循环体中执行key函数时,key中调用的数组变量为PHP执行了一次fetch操作的数组拷贝,此时foreach的内部指针指向第二个元素。
2、为什么在foreach中执行end等操作,其循环过程不变?
在遍历的代码中通过end,next等操作数组的指针,数组的指针不会变化,这是因为在PHP内核进行FETCH操作时,会通过中间变量存储当前操作数组的内部指针,每遍历一个元素,会先获取之前存储的指针位置,获取下一个元素后,再恢复指针位置。
3、为什么$row的引用和非引用情况下输出结果不同?
如果是引用,PHP内核在reset数组时,会直接分裂数组,生成一个数组的拷贝,并将其设置为引用。
如果是非引用,PHP内核在reset数组时,当数组的引用计数大于1,并且不存在引用时,会拷贝数组供foreach使用,其它情况使用原数组,将其引用计数加1。
代码:
$a = array('abe','ben','cam');
foreach ($a as $k=>&$n){
$n = strtoupper($n);
}
print_r($a);
foreach ($a as $k=>$n){ // notice NO reference here!
echo "$n\n";
}
print_r($a);
打印结果:
Array
(
[0] => ABE
[1] => BEN
[2] => CAM
)
ABE
BEN
BEN
Array
(
[0] => ABE
[1] => BEN
[2] => BEN
)
可以看到第一次foreach()循环以后是正常的,第二次循环以后$a[2]从变成了'BEN',和$a[1]一样。
初看很纳闷,但是如果在第二个foreach()里print_r($a),就很容易明白了,结果如下:
Array
(
[0] => ABE
[1] => BEN
[2] => ABE
)
Array
(
[0] => ABE
[1] => BEN
[2] => BEN
)
Array
(
[0] => ABE
[1] => BEN
[2] => BEN
)
由于第一个foreach()是用指针指向数组元素,所以循环结束后变量$n依然指向数组$a的最后一个元素,也就是$a[2]。
第二个foreach()里,第一次循环就将$a[0]的值'ABE'赋给$n,第二次又将$a[1]的值'BEN'赋给$n,其实这两次修改的都是$a[2]这个元素,第三次等于没有变化,所以$a[2]无辜地被修改了三次,导致最后的结果。
由此可见,如果foreach()中用指针指向数组元素,循环结束后最好销毁指针,以免后面再次用到重名的变量导致数组的值被无辜修改。
附加一只小的算法题:
$arr个人围成一个圈,按照$num计算,数到第$num的时候踢出该人,直到最后一个,糗最后一个人。
<?php
$num = 3;
$arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];
$i=1;
$node = 0;
while(true){
if($i<3){
$i++;
if(next($arr)===false){
reset($arr);
}
}else{
$i=1;
$node = key($arr);
$val = $arr[$node];
unset($arr[$node]);
if(count($arr)===0){
echo $node;
break;
}
}
}