PHP在使用foreach遇到的坑,引申出foreach原理詳解,你遇到過嗎

今天在調試某laravel框架下的某模塊時遇到了foreach引用地址的問題,下面總結一下

$a = array('a','b','c');

foreach($a as &$v){}

foreach($a as $v){

}

var_dump($a);

首先猜測一下。輸出的結果是什麼? 。正確答案是: array(3) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> &string(1) "b"} 也就是a,b,b. 如果你猜測的是a,b,c的話。 那麼關於引用,你還要查閱一下相關的資料:http://www.php.net/manual/zh/language.references.php

那麼為什麼是a,b,b呢。讓我們一步步來看:

我們知道對數組執行foreach循環時,是通過移動數組內部指針來實現的。因而對於本文中的例子:當foreach循環結束的時候,由於$v為

引用變量,因而$v 與 $a[ 2 ] 指向了同一個地址空間(共享變量值),因而之後對$v的任何修改都會直接反映到數組$a中。我們可以對例子加上調試代碼,便會一清二楚,例如我們在第二次循環內部,加上var_dump($a),測試每次循環時a的值的變化:

$a = array('a','b','c');foreach($a as &$v){} foreach($a as $v){var_dump($a);echo "
";}var_dump($a);運行代碼。結果為: array(3) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> &string(1) "a" }array(3) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> &string(1) "b" }array(3) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> &string(1) "b" }array(3) { [0]=> string(1) "a" [1]=> string(1) "b" [2]=> &string(1) "b" }

畫個圖:可以更加清晰看出來:(圖中"$v指向了$a[2]"並不準確。應該是:$v與$a[2]指向了同一個地方)

PHP在使用foreach遇到的坑,引申出foreach原理詳解,你遇到過嗎

關於引用的幾點簡單解釋:

1.引用類似於指針,但是不同於指針。

例如對於引用:

$a = "str";$b = &$a;// $a$b 指向了同一個地方

一個簡單的示意圖如下:

PHP在使用foreach遇到的坑,引申出foreach原理詳解,你遇到過嗎

那麼此時更改$a和$b中任何一個元素的值。另外一個值都為隨之改變:

$a = "str";$b = &$a;$b = "sssss";echo $a;2.unset只會刪除變量。並不會清空變量值對應的內存空間:(這是與指針不同的地方)$a = "str";$b = &$a;unset($b);echo $a;3.引用作為函數參數傳遞時,是可以被函數內部更改的:function change(&$a){if(is_array($a)){$a = array();}}$test = range(1,10);change($test);print_r($test);

基於以上幾點,在編碼的過程中,要小心使用引用。接下來就是對foreach的個人總結

PHP foreach原理詳解

一、foreach簡介

1.foreach的遍歷順序

如果是索引數組,你會發現遍歷出來的順序並不是按索引大小遍歷,而是按添加的順序,如果按照索引大小遍歷,應該使用for,而不是foreach

$arr[2]='中';$arr[1]='國';foreach($arr as $value){ echo $value;}結果:中國

所以foreach遍歷數組的順序是由元素的添加順序決定的,不管是索引數組還是關聯數組

2.

當 foreach 開始執行時,數組內部的指針會自動指向第一個單元。這意味著不需要在 foreach 循環之前調用 reset()怎麼來理解這個呢?

$arr = array(1,2,3);foreach($arr as $k=>$v){}var_dump(current($arr));foreach($arr as $key=>$value){ echo $value." ";}var_dump(current($arr));結果:boolean false 1 2 3 boolean false

第一個foreach已經把指針移到尾部去了,並且試圖努力的往後移動指針,直到移出界(current($arr)返回false),foreach結束foreach結束後,並沒有幫我們把指針初始化,不然current應該返回數組的第一個單元,第二個foreach並沒有受第一個foreach的影響,當foreach開始執行時,數組內部的指針會自動指向第一個單元。

$key = currentKey($arrCopy); //將獲取到的值分配給$k;$val = currentVal($arrCopy); //將獲取到的值分配給$v;next($arrCopy);//移動副本數組的指針$arr = $arrCopy;//將副本賦值回給$arr((主要是將指針同步移動))

技術細節:當本次賦值給key和val之後,按照流程指針已經向下移動了一位,所以當執行var_dump(current($arr));時打印false。如果移動指針的結果超出了數組單元的末端,則 next() 返回 FALSE。但foreach循環的次數不是副本數組的長度

二、加深foreach理解

$arr = array('a'=>1,'b'=>2,'c'=>3);foreach($arr as $k=>$v){ $v*=2; echo $v."
";}var_dump($arr);foreach($arr as $key=>$value){ $arr[$key]=$value*2;}var_dump($arr);//傳入&foreach($arr as &$v){ $v=$v*2;}

var_dump($arr);

結果:

array (size=3) 'a' => int 1 'b' => int 2 'c' => int 3array (size=3) 'a' => int 2 'b' => int 4 'c' => int 6array (size=3) 'a' => int 4 'b' => int 8 'c' => &int 12

原因分析:

$k和$v都是臨時變量,foreach的時候,把每個數組單元的鍵分別賦值給$k,把每個數組單元的值分別賦給$v,相等於$v=$arr[$k],$v*2僅僅是改變了$v的值(非&傳遞),並不會影響到$arr[$k],自然也就不會影響到$arr

而用第二種方法(引用)的時候,相等於$v=&$arr[$k],$arr[$k]和$v指向同一內存地址,$v*2自然就改變了$arr[$k]的值,也就改變了$arr的值

PHP在使用foreach遇到的坑,引申出foreach原理詳解,你遇到過嗎


分享到:


相關文章: