php的數組都有一個內部指標,指向數組的元素,初始化的時候是第一個,我要便利數組,讓內部指標逐個移動
$arr = array ('a', 'b', 'c', 'd', 'e');foreach ($arr as $k => $v) { $curr = current($arr); echo "{$k} => {$v} -- {$curr}\n";}
得到結果是
0 => a -- b1 => b -- b2 => c -- b3 => d -- b4 => e -- b
內部指標向後移動了一位就再也沒動過了。。。
foreach對這個數組做了什麼呢?為什麼呢?
我要讓指標遍曆數組,得到如下結果改怎麼做呢?
0 => a -- a1 => b -- b2 => c -- c3 => d -- d4 => e -- e
回複內容:
php的數組都有一個內部指標,指向數組的元素,初始化的時候是第一個,我要便利數組,讓內部指標逐個移動
$arr = array ('a', 'b', 'c', 'd', 'e');foreach ($arr as $k => $v) { $curr = current($arr); echo "{$k} => {$v} -- {$curr}\n";}
得到結果是
0 => a -- b1 => b -- b2 => c -- b3 => d -- b4 => e -- b
內部指標向後移動了一位就再也沒動過了。。。
foreach對這個數組做了什麼呢?為什麼呢?
我要讓指標遍曆數組,得到如下結果改怎麼做呢?
0 => a -- a1 => b -- b2 => c -- c3 => d -- d4 => e -- e
這個問題我在bugs.php.net上回答過: https://bugs.php.net/bug.php?id=63752
1. at the beginning of foreach: ZEND_FE_RESET which increase the refoucnt of $a
2. then FE_FETCH, reset internal pointer of $a
3. then current, current declared to accept a reference, but $a is not a ref and refcount > 1 , then -> separation
PHP 的數組指標操作函數:
pos() http://cn2.php.net/manual/en/function...
end() http://cn2.php.net/manual/en/function...
prev() http://cn2.php.net/manual/en/function...
next() http://cn2.php.net/manual/en/function...
each() http://cn2.php.net/manual/en/function...
reset() http://cn2.php.net/manual/en/function...
current() http://cn2.php.net/manual/en/function...
"Also note that foreach operates on a copy of the specified array, not the array itself, therefore the array pointer is not modified as with the each() construct and changes to the array element returned are not reflected in the original array."
http://cn2.php.net/manual/en/control-...
foreach() 操作原始數組的一個拷貝,如果需要移動指標,使用 while 結構加上 each() 來實現。
$arr = array ('a', 'b', 'c', 'd', 'e');reset($arr);while (list($k, $v) = each($arr)) { # 當前指標已經被指向了下一位 $curr = current($arr); echo "{$k} => {$v} -- {$curr}\n";}
參見 http://cn2.php.net/manual/en/function... 的 example #2。
php的所有變數實際上是用一個struct zval來表示的。
/* Zend/zend.h */typedef struct _zval_struct zval;typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj;} zvalue_value;struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount; zend_uchar type; /* active type */ zend_uchar is_ref;};
而數組就是其中的"HashTable *ht",實際上就是一個雜湊表(Hash Table),表中的所有元素同時又組成一個雙向鏈表,它的定義為:
/* Zend/zend_hash.h */typedef struct _hashtable { uint nTableSize; uint nTableMask; uint nNumOfElements; ulong nNextFreeElement; Bucket *pInternalPointer; /* Used for element traversal */ Bucket *pListHead; Bucket *pListTail; Bucket **arBuckets; dtor_func_t pDestructor; zend_bool persistent; unsigned char nApplyCount; zend_bool bApplyProtection;#if ZEND_DEBUG int inconsistent;#endif} HashTable;
這裡有一個 Bucket *pInternalPointer ,就是被reset/current/next等函數用來遍曆數組儲存位置狀態的。Bucket的實現如下,可以看到這就是個赤裸裸的鏈表節點。
typedef struct bucket { ulong h; /* Used for numeric indexing */ uint nKeyLength; void *pData; void *pDataPtr; struct bucket *pListNext; struct bucket *pListLast; struct bucket *pNext; struct bucket *pLast; char arKey[1]; /* Must be last element */} Bucket;
而foreach的實現,則位於 ./Zend/zend_compile.h ,在解釋期被flex翻譯成由 zend_do_foreach_begin zend_do_foreach_cont zend_do_foreach_end 這三個函數(以及相關代碼)組合起來。由於看起來比較晦澀,我就不貼出來了(實際上我也沒看太懂),詳情可以參考雪候鳥的這篇:深入理解PHP原理之foreach
最後附一段php代碼的opcode
number of ops: 12compiled vars: !0 = $arr, !1 = $xline # * op return operands-----------------------------------------------------2 0 > INIT_ARRAY ~0 1 用1初始化數組 1 ADD_ARRAY_ELEMENT ~0 2 添加個2 2 ADD_ARRAY_ELEMENT ~0 3 添加個3 3 ASSIGN !0, ~0 存入$arr3 4 > FE_RESET $2 !0, ->10 $2 = FE_RESET($arr), 失敗則跳到#10 5 > > FE_FETCH $3 $2, ->10 $3 = FE_FETCH($2), 失敗則跳到#10 6 > ZEND_OP_DATA 7 ASSIGN !1, $3 $x = $34 8 ECHO !1 echo $x 9 > JMP ->5 跳到#5 10 > SWITCH_FREE $2 釋放$25 11 > RETURN 1 返回