在PHP開發中,遇到這個問題,將問題簡化下,如下:
$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;}
由於 each() 迴圈不會自動重設的數組指標,那麼上邊代碼執行輸出: 456
但是寫在函數中結果卻不同了,如下:
$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; }}
代碼執行結果為: 123456
其實都是php的變數"寫時複製(copy on write)"惹的禍,為什麼這麼說,且看分析:
什麼是"寫時複製(copy on write)"?
這裡用一個例子說明:
$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 = 3list($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 = 2list($k,$v) = each($arr); // 不存在變數分離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 = 2list($k,$v) = each($arr); // 不存在變數分離echo $v;debug_zval_dump($arr); // refcount = 2
所以在使用each之前,最好是先用reset(如果存在引用會產生變數分離)統一重設數組指標,就可以避免上述問題。