问题描述:遍历数组时,改变了数组的值,数组遍历完成后,数组的指针指向了第二个元素,当发生这种情况时,会对后续通过指针遍历数组产生影响。
<?
$arrD = array('0'=>'a','1'=>'b','2'=>'c');
echo key($arrD)."</br>";
foreach($arrD as $intK => $val)
{
$arrD[] = $val."2";
}
echo(key($arrD));
?>
输出结果如下:
0
1
因此再次通过指针遍历数组的时候会发现$arrD的第一个值就循环不出来了。
其实问题很好解决,在foreach之后,直接用reset()方法将$arrD的指针重置一下就可以了,但导致问题的原因才是值得关注的,在做了一些搜索和阅读的工作之后,总结问题原因如下,首先,先知道几个php处理赋值遍历等动作的原则:
1,php在变量赋值时候的内存使用策略:写时复制(copy on write, COW),当用赋值方法把一个变量值赋给另一个变量时,由于这两个变量值相同,因此公用同一内存,当其中一个变量值发生变化时候,才会重新为值变化的变量申请内存,已达到节省内存的目的;
2,foreach遍历数组时,实际上是遍历的数组的一个拷贝,并且在开始遍历之前会把指针指向拷贝的开始;
3,在发生写时复制时候,指针的位置也会一并被复制;
针对规则1 的验证
<?
echo "初始内存情况:".memory_get_usage()."</br>";
$foo = str_repeat('aaa', 10000);
echo "使用变量\$foo之后的内存:".memory_get_usage()."</br>";
$bar = $foo;
echo "将变量\$foo拷贝给\$bar后的内存:".memory_get_usage()."</br>";
$bar = str_repeat('aaa', 10000);
echo "对\$bar值修改后使用的内存:".memory_get_usage()."</br>";
?>
输出结果如下:
初始内存情况:118912
使用变量$foo之后的内存:149008
将变量$foo拷贝给$bar后的内存:149056
对$bar值修改后使用的内存:179104
针对规则2的验证
<?
$a = array('a','b','c');
next($a);
foreach($a as $v)
{
$a[] = 'd';//$a值发生了变化,开辟了新的空间存储
echo $v."</br>";
}
?>
输出结果如下
a
b
c
针对规则3的验证
<?
$a = array (1,2,3);
next($a);
$b = $a;//同一内存
$b[] = 4;//$b被开辟了新的存储空间,指针与之前存储空间的一致
echo current($b);
?>
输出结果如下
2
然后来看产生最初问题的原因:
1. foreach 循环遍历 $arrD 时,php创建了一个 $arrD 的拷贝 A(A 是不可见的,只是为了描述方便 ),但是由于此时A 和 $arrD 值一样,所以,共用同一个内存;
2.执行 foreach时候,先把A中第一个指针所在的 key 和 value 分别赋值给 $a 和 $b,同时指针后移一位,由于此时A 和 $arrD 共用同一内存,此时 $arrD 的指针也指向了第二个值;
3.当执行到 $a[] = 'd'; 时,由于 $arrD 的值发生改变,此时系统给 $arrD分配了其他内存,而同时,其指针指向也被拷贝到新内存中;而之后的遍历过程中,A 和$arrD 相当于两个完全不同的变量,不同值,不同存储地址,A 指针的移动已经无法影响 $arrD 指针的变化,因此当 A 被遍历完成后,$arrD的指针仍然被留在第二个值的位置。