よく質問されますが、foreach を使用して PHP 配列にアクセスする場合、走査の順序は固定されていますか?
例:
コードをコピーします。 🎜> コードは次のとおりです。
$arr['laruence'] = 'huixinchen'
$arr['yahoo'] = 2007;
$arr['baidu'] = 2008;
foreach ($arr as $key => $val) {
//結果は何ですか? 🎜>
別の例:
コードは次のとおりです: $arr[2] = 'huixinchen';
$arr[1] = 2007;$arr[0] = 2008;
foreach ($arr as $key => $val) 🎜>//結果はまた何ですか?
}
この問題を完全に理解するには、まず PHP 配列の内部実装構造を理解する必要があると思います...
PHP 配列
PHP では、配列は HASH 構造 (HashTable) を使用して実装され、O(1) 時間の計算量で配列の追加と削除を可能にし、線形トラバーサルとランダムの両方をサポートします。
typedef struct _hashtable { uint nTableSize; / * ハッシュ テーブルのサイズ、ハッシュ値の範囲*/ uint nTableMask; /* nTableSize -1 に等しい、*/
uint nNumOfElements; HashTable 内の実際の要素数*/ ulong nNextFreeElement; /* 次に使用可能な空き位置の数値インデックス*/
Bucket *pInternalPointer; /* リセットされる現在の位置ポインター。関数は */
Bucket *pListHead; /* 線形トラバーサルに使用されるヘッド要素 */
Bucket *pListTail; /* 線形トラバーサルに使用される */
Bucket **arBuckets;実際のストレージコンテナ*/
dtor_func_t pDestructor;/* 要素デストラクター (ポインター) */
zend_boolpersistent; /* ループトラバーサル保護*/
zend_bool bApplyProtection; if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;
nApplyCount の意味については、次の例で理解できます。 >
コードをコピー
コードは次のとおりです:
var_export($arr); //致命的なエラー: ネスト レベルが深すぎます - このフィールドは
上記の構造を見ると、HashTable のキー要素は実際のストレージ コンテナーである arBuckets であることがわかります。構造体定義:
コードをコピーします
コードは次のとおりです。
void *pData; /* data*/ void *pDataPtr; /* データ ポインタ */ struct Bucket *pListNext; /* 次の要素、線形走査用*/ struct Bucket *pListLast; /* 前の要素、線形走査に使用*/ struct Bucket *pNext; /* 同じジッパー内の次の要素/
struct Bucket *pLast; /* 同じジッパー内の前の要素*/char arKey[1] /* メモリを節約し、初期化を容易にするためのヒント*/
} Bucket;
最後の要素は、メモリを節約し、初期化を容易にする柔軟な配列手法であることに気付きました。興味のある方は、柔軟な配列を検索してください。
h は、数値インデックス付きの要素のハッシュ値です。要素、h は直接のインデックス値 (nKeyLength=0 で表される数値インデックス) です。文字列インデックスの場合、インデックス値は arKey に格納され、インデックスの長さはバケット内の
に格納されます。データは、pData ポインタが指すメモリ ブロックに格納されます。通常、このメモリ ブロックはシステムによって個別に割り当てられます。ただし、例外があります。つまり、バケットによって保存されたデータがポインターの場合、HashTable はシステムにポインターを保存するためのスペースの割り当てを要求せず、ポインターを pDataPtr に直接保存し、pData をメンバーにポイントします。この構造の。これにより効率が向上し、メモリの断片化が軽減されます。このことから、PHP HashTable の設計の微妙な点がわかります。 Bucket 内のデータがポインタでない場合、pDataPtr は NULL になります (この段落は Altair の「Zend HashTable 詳細説明」から引用しています)
上記の HashTable 構造と組み合わせて、HashTable の全体的な構造図を示します。
HashTable pListthHead は線形リスト形式の最初の要素 (上の図では要素 1) を指し、pListTail は最後の要素 0 を指し、各要素の pListNext は赤い線で描かれた線形構造の次の要素です
pInternalPointer は、配列を順次走査する場合、現在の要素を示します。
このとき、pListHead から開始し、Bucket 内の pListNext/pListLast をたどり、pInternalPointer の動きに従って pInternalPointer を移動して、すべての要素の線形走査を実現します。
たとえば、foreach の場合。生成されるオペコード シーケンスを見てください。foreach の前に、まず配列の内部ポインタである pInternalPointer をリセットする FE_RESET があることがわかります (foreach については、PHP 原理の詳細な理解である Foreach を参照してください)。
同様に、各 FE_FETCH で pInternalPointer をインクリメントし、逐次探索を実行します。
同様に、関数の each/next を使用して、配列の内部ポインタを移動することで逐次探索も実現します。 コードをコピー
コードは次のとおりです:
$arr = array(1,2,3,4,5);
foreach ($arr as $v) {
//
}
while (list($ key, $v) = each($arr)) {
/ /Cannot get
}
コードをコピー コード
huixinchen
2007
2008
コードをコピー コードは次のとおりです:
for($i=0,$l= count($arr); $i<$l; $i ) {
//現時点では、逐次走査 (linear Traverse) とはみなされません
}