国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > PHP > 正文

php foreach循環(huán)使用引用注意事項(xiàng)

2024-05-04 21:50:32
字體:
供稿:網(wǎng)友

看過PHP相關(guān)書籍的都會(huì)了解到PHP有個(gè)這樣的特性:寫時(shí)復(fù)制,所以在用foreach時(shí),需要對(duì)數(shù)據(jù)做修改的時(shí)候,都會(huì)復(fù)制數(shù)據(jù),如果數(shù)據(jù)很大,那么就會(huì)帶來一定的內(nèi)存消耗,所以為了避免這種復(fù)制操作,就用到了引用,下面就介紹下引用的坑

問題案例:

  1. <?php 
  2.     $arr = array(4, 5, 6); 
  3.     var_dump($arr); 
  4.  
  5.     foreach ($arr as &$v) { 
  6.         //do something here 
  7.     } 
  8.  
  9.     foreach ($arr as $v) { 
  10.         //do something here 
  11.     } 
  12.     var_dump($arr); 
  13. ?> 

輸出為:

  1. array(3) { 
  2.   [0]=> 
  3.   int(4) 
  4.   [1]=> 
  5.   int(5) 
  6.   [2]=> 
  7.   int(6) 
  8. array(3) { 
  9.   [0]=> 
  10.   int(4) 
  11.   [1]=> 
  12.   int(5) 
  13.   [2]=> 
  14.   &int(5) 

問題分析:

foreach 中不使用引用就沒事, 用 foreach $k => $v 然后 $ar[$k] 來改變?cè)紨?shù)組, 略微損失點(diǎn)效率。

執(zhí)行第一個(gè)使用引用的 foreach 時(shí):

一開始,$v 指向 $arr[0] 的存儲(chǔ)空間,空間內(nèi)存儲(chǔ)著 4,foreach 結(jié)束時(shí),$v 指向 $arr[2] 的存儲(chǔ)空間,空間內(nèi)存儲(chǔ)著 6 。

開始執(zhí)行第二個(gè) foreach 時(shí):

注意和第一個(gè) foreach 不同, 第二個(gè) foreach 沒有使用引用,那么就是賦值方式,即將 $arr 的值依次 賦值 給 $v,進(jìn)行到第一個(gè)元素時(shí),要將 $ar[0] 賦值給 $v,問題就在這里,由于剛剛執(zhí)行完第一個(gè) foreach,$v 不是一個(gè)新變量,而是已經(jīng)存在的、指向 $arr[2] 的那個(gè) 引用 ,如此一來,對(duì) $v 進(jìn)行賦值的時(shí)候,就將 $arr[0] = 4 寫入了 $arr[2] 的實(shí)際存儲(chǔ)空間,相當(dāng)于對(duì) $arr[2] 進(jìn)行賦值,依此類推,第二個(gè) foreach 執(zhí)行的結(jié)果,就是數(shù)組的最后一個(gè)元素變成了倒數(shù)第二個(gè)元素的值。

PHP 的開發(fā)者也認(rèn)為,這種情況屬于語言特性造成的,不是 bug。要修復(fù)這個(gè)問題,一種方法是對(duì) foreach 進(jìn)行特殊處理,另一種就是限制 foreach 中 $v 的作用域, 這兩種方式都與目前 PHP 的語言特性不符,開發(fā)人員不愿改,但還是在 官方文檔 中用 Warning 進(jìn)行了說明。

解決方案:

簡(jiǎn)單的方法,就是在使用了引用的 foreach 之后,unset 掉 $v

修改后的案例:

  1. <?php 
  2.     $arr = array(4, 5, 6); 
  3.     var_dump($arr); 
  4.  
  5.     foreach ($arr as &$v) { 
  6.     //do something here 
  7.     } 
  8.     unset($v); 
  9.  
  10.     foreach ($arr as $v) { 
  11.     //do something here 
  12.     } 
  13.     var_dump($arr); 
  14. ?> 

輸出:

  1. array(3) { 
  2.     [0]=> 
  3.     int(4) 
  4.     [1]=> 
  5.     int(5) 
  6.     [2]=> 
  7.     int(6) 
  8. array(3) { 
  9.     [0]=> 
  10.     int(4) 
  11.     [1]=> 
  12.     int(5) 
  13.     [2]=> 
  14.     int(6) 

補(bǔ)充:

foreach雖然簡(jiǎn)單,不過它可能會(huì)出現(xiàn)一些意外的行為,特別是代碼涉及引用的情況下。

下面列舉了幾種case,有助于我們進(jìn)一步認(rèn)清foreach的本質(zhì):

  1. $arr = array(1,2,3); 
  2. foreach($arr as $k => &$v) { 
  3.     $v = $v * 2; 
  4. // now $arr is array(2, 4, 6) 
  5. foreach($arr as $k => $v) { 
  6.     echo "$k"" => ""$v"
先從簡(jiǎn)單的開始,如果我們嘗試運(yùn)行上述代碼,就會(huì)發(fā)現(xiàn)最后輸出為0=>2  1=>4  2=>4 。

為何不是0=>2  1=>4  2=>6 ?

其實(shí),我們可以認(rèn)為 foreach($arr as $k => $v) 結(jié)構(gòu)隱含了如下操作,分別將數(shù)組當(dāng)前的'鍵'和當(dāng)前的'值'賦給變量$k和$v。具體展開形如:

  1. foreach($arr as $k => $v){  
  2.     //在用戶代碼執(zhí)行之前隱含了2個(gè)賦值操作 
  3.     $v = currentVal();  
  4.     $k = currentKey(); 
  5.     //繼續(xù)運(yùn)行用戶代碼 
  6.     …… 

根據(jù)上述理論,現(xiàn)在我們重新來分析下第一個(gè)foreach:

第1遍循環(huán),由于$v是一個(gè)引用,因此$v = &$arr[0],$v=$v*2相當(dāng)于$arr[0]*2,因此$arr變成2,2,3

第2遍循環(huán),$v = &$arr[1],$arr變成2,4,3

第3遍循環(huán),$v = &$arr[2],$arr變成2,4,6

隨后代碼進(jìn)入了第二個(gè)foreach:

第1遍循環(huán),隱含操作$v=$arr[0]被觸發(fā),由于此時(shí)$v仍然是$arr[2]的引用,即相當(dāng)于$arr[2]=$arr[0],$arr變成2,4,2

第2遍循環(huán),$v=$arr[1],即$arr[2]=$arr[1],$arr變成2,4,4

第3遍循環(huán),$v=$arr[2],即$arr[2]=$arr[2],$arr變成2,4,4

OK,分析完畢。

如何解決類似問題呢?php手冊(cè)上有一段提醒:

Warning : 數(shù)組最后一個(gè)元素的 $value 引用在 foreach 循環(huán)之后仍會(huì)保留。建議使用unset()來將其銷毀。

  1. $arr = array(1,2,3); 
  2. foreach($arr as $k => &$v) { 
  3.     $v = $v * 2; 
  4. unset($v); 
  5. foreach($arr as $k => $v) { 
  6.     echo "$k"" => ""$v"
  7. // 輸出 0=>2  1=>4  2=>6 

從這個(gè)問題中我們可以看出,引用很有可能會(huì)伴隨副作用。如果不希望無意識(shí)的修改導(dǎo)致數(shù)組內(nèi)容變更,最好及時(shí)unset掉這些引用。

unset只會(huì)刪除變量,并不會(huì)清空變量值對(duì)應(yīng)的內(nèi)存空間:這是與指針不同的地方,如下:

  1. $a = "str";   
  2. $b = &$a;   
  3. unset($b);   
  4. echo $a

依然輸出   str

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 海城市| 泸州市| 鄱阳县| 荥经县| 当涂县| 临泽县| 石泉县| 柘城县| 潼关县| 镇江市| 塔河县| 保靖县| 九龙城区| 台北市| 闽清县| 苍梧县| 抚宁县| 峨眉山市| 义马市| 武乡县| 平武县| 禹州市| 宁海县| 同仁县| 衡水市| 巫山县| 九台市| 赣州市| 密云县| 大埔区| 柳江县| 金湖县| 梅州市| 江孜县| 赞皇县| 玛曲县| 游戏| 大新县| 城口县| 长春市| 贵州省|