I recently encountered a very strange problem at work. After using the each function to traverse an array, the array is then passed to a function as an actual parameter, and each is used again inside the function to traverse the formal parameters. Group. To explain, the purpose of using the each function twice is very simple, which is to convert the key in the array into a variable name, and convert the value corresponding to the key into the value of the variable. In fact, this function can be achieved using the extract function. Next, let’s talk about what problems occurred when using each function inside the function. After traversing, we found that some variables were NULL, which means that some variables were lost. I don’t know if the description is clear, but let’s simplify the problem at work and explain it with the following code.
<?php $arr = array('var1'=>1,'var2'=>2,'var3'=>3,'var4'=>4,'var5'=>5); while( list($key,$value) = each($arr) ) { $$key = $value; } //下面这一步操作很关键,问题就出在这里,遍历完之后,对数组添加一个值。 $arr['var6'] = 6; func($arr); function func($arrtmp) { while(list($key,$value) = each($arrtmp) ) { $$key = $value; } var_dump($var1); var_dump($var2); var_dump($var3); var_dump($var4); var_dump($var5); var_dump($var6); } ?>
Output result: NULL NULL NULL NULL NULL int(6).
According to conventional thinking, at this time, the variables $var1, $var2, $var3, $var4, $var5, $var6 should all be present inside the function func, but this is not the case. Only the variable $var6 has a value. Several other variables are NULL. Why is this?
The problem lies in the array pointer issue we are going to discuss today. This function each will return the element pointed to by the current array pointer in the form of an array, and move the array pointer forward one bit to point to the next array unit. After we use the each function to traverse the array $arr, the internal pointer of the $arr array already points to the next bit of the last unit (without any value). At this time, we performed the operation $arr['var6'] = 6, adding a new unit to the array. We know that arrays must be stored in continuous address units in memory. That is to say, the location of the value of $arr['var6'] in memory should be in the unit pointed by the current array pointer (it was empty before). Moreover, assigning a value to an array will not move the internal pointer of the array. After the assignment is completed, the array pointer of the $arr array changes from pointing to a NULL to pointing to an address unit with an actual value.
There is such a rule when arrays are passed as parameters between functions: we know that when a function is called, the system will copy a copy of the actual parameters to the formal parameters (except for reference calls), but for arrays, not only copy The value of the actual parameter is obtained, and the position of the current internal pointer of the actual parameter group is also copied. If the position of the internal pointer of the actual parameter points to the end of the array, the system will reset the internal pointer of the formal parameter to point to the first unit of the formal parameter group; if the position of the internal pointer of the actual parameter is not at the end of the array, it points to a valid unit, then the system will point the array pointer position of the formal parameter to the array unit with the same value as the array pointer of the actual parameter.
If you do not do $arr['var6'] = 6, all 6 variables ($var1-$var6) will have values, because after each, the array pointer has pointed to the end of the array, then after When the function func() is called, the system resets the array pointer of $arrtmp to point to the first element. But after the operation $arr['var6'] = 6, everything changed. This operation changed the array pointer of $arr from pointing to a NULL to a valid value (explain, before and after the assignment, the array The address unit pointed by the pointer has not changed. It is just that before the assignment, there was nothing in that address unit, but after the assignment, it became 6). This makes the array pointer of $arr point to a valid unit. When calling function func(), the system will not reset the array pointer of $arrtmp. The array pointer of $arrtmp will be the same as the array pointer of $arr. Same, pointing to his own last unit. The each function starts working from the position of the current array pointer. Therefore, the return value of the first result of the each function operation is the last element of the array $arrtmp, which moves the array pointer down one bit. The while loop ends here, so $arrtmp['var1']-$arrtmp ['var5'] has not been traversed, eventually causing $var1-$var6 to be NULL.
In the process of array assignment, the changes in the array pointers of the assigning array and the assigned array. First give a conclusion, and then we use code to prove this conclusion. $arrtmp=$arr; In this assignment expression, I call $arr the assignment array, and $arrtmp the assigned array. When the array is assigned, if the array pointer of the assigned array already points to the end of the array, the array pointer of the assigned array will be reset after the assignment and points to the first element of the array; if during the assignment, the array pointer of the assigned array does not point to the array. At the end, it points to any valid array element. Then the array pointer of the assigned array will not be reset after the assignment, but the element it originally pointed to will be retained. After the assignment, the assigned array not only has the value of the assigned array, but also the array pointer of the assigned array points to that element, and the assigned array will also point to the element with the same value in itself.
demo1:
<?php $arr = array('var1'=>1,'var2'=>2,'var3'=>3,'var4'=>4,'var5'=>5); while( list($key,$value) = each($arr) ) { if($value == 4) break; } var_dump(current($arr)); $arr1 = $arr; var_dump(current($arr)); var_dump(current($arr1)); ?>
demo1 的执行结果是:int(5) int(5) int(5) 。从这个结果可以看出,赋值前后$arr的数组指针位置没有发生任何变化,$arr1不仅值跟$arr相同,而且数组指针所指向的元素值也是相同的。现在 用上述结论来解释这个结果,在while循环中,有一个if判断语句,目的是不让$arr的数组指针指向数组末尾,而是保留在一个有效的位置。 在$value=4时会跳出循环,而each这个函数会将数组指针向前移动一位,这就导致了$arr的数组指针指向了第5个元素,所以在赋值之 前,current($arr)的结果是5,赋值之后,由于在赋值之前$arr的当前指针并没有指向末尾,因此在赋值之后不会将$arr的数组指针进行重 置,而是保留了其原有的位置,因此在赋值之后使用current($arr)的结果仍然是5。赋值时$arr1不仅获得了$arr的值,而且数组指针指向 的元素和$arr的相同,二者都是5。
demo2:
<?php $arr = array('var1'=>1,'var2'=>2,'var3'=>3,'var4'=>4,'var5'=>5); while( list($key,$value) = each($arr) ) { //if($value == 4) break; } var_dump(current($arr)); $arr1 = $arr; var_dump(current($arr)); var_dump(current($arr1)); ?>
demo2中我们将 if($value == 4) break; 这一句注释掉了,目的很简单,就是通过each将$arr的数组指针位置指向数组末尾。
demo2 的执行结果:bool(false) int(1) bool(false) 。如果数组指针对应的元素为0,"",或者不是一个有效的值时,current函数会返回false,$arr的值中没有为0或者""的情况,因此可以断 定是因为数组指针指向了一个无效的元素而导致current返回了一个false。换句话说就是可以确定在while循环完成之后,$arr的数组指针已 经指向了数组的末尾。所以我们看到在赋值之前current($arr)的值是false,而赋值之后current($arr)的值变成了1,说明赋值 之后$arr的数组指针被重置了,指向了数组的第一个元素。current($arr1)的值为false,说明赋值之后$arr1让然保留了赋值之 前$arr的数组指针指向的元素。
通过demo1和demo2就可以证明上述结论了。
因此为了在遍历数组时不受数组指针的影响,最好在使用each()函数之前或者之后调用函数reset()将数组指针重置。这样就可以避免上述问题的发生了。另外还有一个操作数组指针的函数prev(),它的作用是将数组指针当前的位置后退一位,它也需要注意一点,就是如果数组指针已经指向数组末尾,那么使它就得不到想要的结果了。
顺便说一下foreach这个函数,使用foreach函数来遍历数组时,它会重置数组指针,将其指向数组的第一个元素。必须注意的是foreach操作的对象是对你要遍历的数组的copy值,而不是遍历数组本身。