php 指針

最近在工作中遇到一個很奇怪的問題,在使用each這個函數對一個數組遍歷之後,再把該數組作爲實參傳遞給一個函數,在函數內部再次使用each遍歷形參數組。說明一下,兩次使用each這個函數的目的很簡單,就是將數組中的key轉換成變量名,而key對應的value值轉換成變量的值。其實這個功使用函數extract就能實現。在來接着說說在函數內部使用each函數時出現了什麼問題,在遍歷完之後發現部分變量爲NULL,也就是說丟失了部分變量。不知道描述清楚沒有,是將工作中問題簡化一下,用下面代碼說明一下吧。
01 <?php
02     $arr array('var1'=>1,'var2'=>2,'var3'=>3,'var4'=>4,'var5'=>5);
03     while( list($key,$value) = each($arr) )
04     {
05         $$key $value;
06     }
07     //下面這一步操作很關鍵,問題就出在這裏,遍歷完之後,對數組添加一個值。
08     $arr['var6'] = 6;
09              
10     func($arr);
11          
12     function func($arrtmp)
13     {
14         while(list($key,$value) = each($arrtmp) )
15         {
16             $$key $value;
17         }
18         var_dump($var1);
19         var_dump($var2);
20         var_dump($var3);
21         var_dump($var4);
22         var_dump($var5);
23         var_dump($var6);
24     }
25 ?>

輸出結果:NULL NULL NULL NULL NULL int(6).

按照常規的想法,此時在函數func內部變量 $var1,$var2,$var3,$var4,$var5,$var6 都應該有了,但是事實並非於此,只有變量$var6有值,而其他幾個變量都是NULL。這是爲什麼呢?

問題就出在我們今天要討論的數組指針的問題上。each這個函數會將當前數組指針指向的元素以數組的形式返回,並將數組指針向前移動一位,指向下一個數組 單元。在我們使用each函數將數組 $arr 遍歷完之後,$arr數組的內部指針已經指向了最後一個單元的下一位(沒有任何值)。就在此時我們又 執行了 $arr['var6'] = 6 這個操作,爲數組添加了一個新的單元,我們知道,數組在內存中存儲肯定都是連續的地址單元。也就是說$arr['var6']的值在內存中的位置就應該在 當前數組指針指向的那個單元(之前爲空)。而且給數組賦值是不會移動數組內部指針的,在賦值完成之後,$arr 數組的數組指針就由原來指向一個NULL變成指向一個有實際值存在的地址單元了。

對於數組作爲參數在函數間傳遞時又存在這樣一個規律:我們知道,函數調用時,系統會將實參copy一份賦值給形參(引用調用除外),而對於數組,不僅僅 copy了實參的值,而且還copy了實參數組當前內部指針的位置。如果實參內部指針的位置指向了數組末尾,那麼系統會將形參的內部指針重置,指向形參數 組的第一個單元;如果實參內部指針的位置不在數組末尾,即指向了有效的單元,那麼系統會將形參的數組指針位置與實參的數組指針指向值相同的數組單元。

如果不做 $arr['var6'] = 6 這一步操作,6個變量($var1-$var6)都將有值,因爲在each之後,數組指針已經指向了數組的末尾,那麼在調用函數 func()時,系統會重置 $arrtmp的數組指針,將其指向第一個元素。但是在進行 $arr['var6'] = 6這步操作之後,一切就改變了,這一個操作讓$arr的數組指針由原來指向一個NULL變成了一個有效值(說明一下,賦值前後,數組指針指向的地址單元一 直是沒有變化的,只不過是賦值前,那個地址單元什麼也沒有,而賦值之後變成了6)。這就使得$arr的數組指針指向了一個有效的單元,那麼在調用函數 func()時,系統是不會重置$arrtmp的數組指針的,$arrtmp的數組指針將會跟$arr的數組指針一樣,指向他自己的最後一個單元。而 each函數又是從當前數組指針的位置開始工作的。因此each函數操作的第一個結果的返回值就是數組$arrtmp的最後一個元素了,它將數組指針再向下移動一位,while循環到此結束,因此$arrtmp['var1']-$arrtmp['var5']都沒有被遍歷到,最終導致$var1-$var6爲NULL。

數組在賦值的過程中,賦值數組和被賦值數組各自數組指針的變化情況。 先給出一條結論,然後我們在用代碼來證明這個結論吧。$arrtmp=$arr;在這個賦值表達式中我把$arr叫做賦值數組,把$arrtmp叫做被賦值數組。數 組在賦值時,如果賦值數組的數組指針已經指向了數組末尾,則賦值之後賦值數組的數組指針會被重置,指向數組第一個元素;如果在賦值時,賦值數組的數組指針 沒有指向數組末尾,而是指向了任何一個有效的數組元素,那麼在賦值之後賦值數組的數組指針是不會被重置的,而是保留其原來指向的元素。在賦值之後,被賦值 數組不僅有了賦值數組的值,而且賦值數組的數組指針指向了那個元素,被賦值的數組也會指向自己中值相同的那個元素。

demo1:

01 <?php
02     $arr array('var1'=>1,'var2'=>2,'var3'=>3,'var4'=>4,'var5'=>5);
03     while( list($key,$value) = each($arr) )
04     {
05         if($value == 4) break;
06     }
07     var_dump(current($arr));
08      
09     $arr1 $arr;
10      
11     var_dump(current($arr));
12     var_dump(current($arr1));
13 ?>

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:

01 <?php
02 $arr array('var1'=>1,'var2'=>2,'var3'=>3,'var4'=>4,'var5'=>5);
03 while( list($key,$value) = each($arr) )
04 {
05     //if($value == 4) break;
06 }
07 var_dump(current($arr));
08  
09 $arr1 $arr;
10  
11 var_dump(current($arr));
12 var_dump(current($arr1));
13 ?>

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值,而不是遍歷數組本身。

發佈了15 篇原創文章 · 獲贊 4 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章