PHP中each與數組變量分離的那些事情

看有人遇到問題,嘗試尋找了下問題的原因。


在寫一個方法的時候,遇到這個問題,將問題簡化下,如下:

  1. $arr = array(1,2,3,4,5,6);  
  2. $n = 3;  
  3.   
  4. foreach($arr as $key=>$val){  
  5.     if($val == $nbreak;  
  6. }  
  7. while(list($k,$v) = each($arr)){  
  8.     echo $v;  
  9. }  
由於 each() 循環不會自動重置的數組指針,那麼上邊代碼執行輸出: 456

但是寫在函數中結果卻不同了,如下:
  1. $arr = array(1,2,3,4,5,6);  
  2. $n = 3;  
  3. test1($arr,$n);  
  4. function test1($arr,$n) {  
  5.   
  6.   
  7.     foreach($arr as $key=>$val){  
  8.         if($val == $nbreak;  
  9.     }  
  10.     while(list($k,$v) = each($arr)){  
  11.         echo $v;  
  12.     }  
  13. }  
代碼執行結果爲: 123456


其實都是php的變量"寫時複製(copy on write)"惹的禍,爲什麼這麼說,且看分析:

什麼是"寫時複製(copy on write)"?可以參看PHP內核探索:變量的引用與計數規則 與深入理解PHP原理之變量分離/引用(Variables Separation),這裏用一個例子說明:

  1. $arr = array(1,2,3,4,5,6);  
  2. $n = 3;  
  3.   
  4. $arr1 = $arr// 此時並沒有複製,是引用關係  
  5. debug_zval_dump($arr); // recount = 3,因爲debug_zval_dump有一次  
  6. $arr[0] = 1; // 此時有寫操作,要分離  
  7. debug_zval_dump($arr); // recount = 2  

php手冊上說:
因爲將一個數組賦值給另一個數組時會重置原來的數組指針,因此在上邊的例子中如果我們在循環內部將$fruit 賦給了另一個變量的話將會導致無限循環。
php手冊關於each介紹有提到,其實就是說數組變量分離的時候會重置數組的指針,看下面的例子:

  1. $arr = array(1,2,3,4,5,6);  
  2. $n = 3;  
  3.   
  4. $arr1 = $arr// 此時並沒有複製,是引用關係,可以看作是函數複製調用  
  5.   
  6. foreach($arr as $key=>$val){  
  7.     if($val == $nbreak;  
  8. }  
  9.   
  10. debug_zval_dump($arr); // refcount = 3  
  11. list($k,$v) = each($arr); // 因爲each會改變數組的指針,所以還是寫操作,存在分離  
  12. echo $v;  
  13. debug_zval_dump($arr); // refcount = 2  


至於&操作的話,不存在“變量分離”,或者說“變量分離”是在&操作的同時完成的。

  1. $arr = array(1,2,3,4,5,6);  
  2. $n = 3;  
  3.   
  4. $arr1 = &$arr// 引用,“變量分離”是在&操作的同時完成  
  5.   
  6. foreach($arr as $key=>$val){  
  7.     if($val == $nbreak;  
  8. }  
  9.   
  10. debug_zval_dump($arr); // refcount = 2  
  11. list($k,$v) = each($arr); // 不存在變量分離  
  12. echo $v;  
  13. debug_zval_dump($arr); // refcount = 2  

相同的效果還有:

  1. $arr = array(1,2,3,4,5,6);  
  2. $n = 3;  
  3.   
  4. $arr1 = $arr; <span style="font-family: Arial, Helvetica, sans-serif;">// 此時並沒有複製,是引用關係,可以看作是函數複製調用</span>  
  5.   
  6. foreach($arr as $key=> &$val){ // 存在可能寫的情況,變量分離  
  7.     if($val == $nbreak;  
  8. }  
  9.   
  10. debug_zval_dump($arr); // refcount = 2  
  11. list($k,$v) = each($arr); // 不存在變量分離  
  12. echo $v;  
  13. debug_zval_dump($arr); // refcount = 2  
所以在使用each之前,最好是先用reset(如果存在引用會產生變量分離)統一重置數組指針,就可以避免上述問題。
發佈了20 篇原創文章 · 獲贊 9 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章