看有人遇到問題,嘗試尋找了下問題的原因。
在寫一個方法的時候,遇到這個問題,將問題簡化下,如下:
- $arr = array(1,2,3,4,5,6);
- $n = 3;
- foreach($arr as $key=>$val){
- if($val == $n) break;
- }
- while(list($k,$v) = each($arr)){
- echo $v;
- }
但是寫在函數中結果卻不同了,如下:
- $arr = array(1,2,3,4,5,6);
- $n = 3;
- test1($arr,$n);
- function test1($arr,$n) {
- foreach($arr as $key=>$val){
- if($val == $n) break;
- }
- while(list($k,$v) = each($arr)){
- echo $v;
- }
- }
其實都是php的變量"寫時複製(copy on write)"惹的禍,爲什麼這麼說,且看分析:
什麼是"寫時複製(copy on write)"?可以參看PHP內核探索:變量的引用與計數規則 與深入理解PHP原理之變量分離/引用(Variables
Separation),這裏用一個例子說明:
- $arr = array(1,2,3,4,5,6);
- $n = 3;
- $arr1 = $arr; // 此時並沒有複製,是引用關係
- debug_zval_dump($arr); // recount = 3,因爲debug_zval_dump有一次
- $arr[0] = 1; // 此時有寫操作,要分離
- debug_zval_dump($arr); // recount = 2
php手冊上說:
因爲將一個數組賦值給另一個數組時會重置原來的數組指針,因此在上邊的例子中如果我們在循環內部將$fruit 賦給了另一個變量的話將會導致無限循環。
php手冊關於each介紹有提到,其實就是說數組變量分離的時候會重置數組的指針,看下面的例子:
- $arr = array(1,2,3,4,5,6);
- $n = 3;
- $arr1 = $arr; // 此時並沒有複製,是引用關係,可以看作是函數複製調用
- foreach($arr as $key=>$val){
- if($val == $n) break;
- }
- debug_zval_dump($arr); // refcount = 3
- list($k,$v) = each($arr); // 因爲each會改變數組的指針,所以還是寫操作,存在分離
- echo $v;
- debug_zval_dump($arr); // refcount = 2
- $arr = array(1,2,3,4,5,6);
- $n = 3;
- $arr1 = &$arr; // 引用,“變量分離”是在&操作的同時完成
- foreach($arr as $key=>$val){
- if($val == $n) break;
- }
- debug_zval_dump($arr); // refcount = 2
- list($k,$v) = each($arr); // 不存在變量分離
- echo $v;
- debug_zval_dump($arr); // refcount = 2
相同的效果還有:
- $arr = array(1,2,3,4,5,6);
- $n = 3;
- $arr1 = $arr; <span style="font-family: Arial, Helvetica, sans-serif;">// 此時並沒有複製,是引用關係,可以看作是函數複製調用</span>
- foreach($arr as $key=> &$val){ // 存在可能寫的情況,變量分離
- if($val == $n) break;
- }
- debug_zval_dump($arr); // refcount = 2
- list($k,$v) = each($arr); // 不存在變量分離
- echo $v;
- debug_zval_dump($arr); // refcount = 2