Heim  >  Artikel  >  php教程  >  Vertieftes Verständnis von PHP-Arrays (Traversal Order)

Vertieftes Verständnis von PHP-Arrays (Traversal Order)

黄舟
黄舟Original
2016-12-15 09:55:261168Durchsuche

Wenn auf ein PHP-Array mit foreach zugegriffen wird, wird die Reihenfolge festgelegt.
Zum Beispiel:

lautet wie folgt:


$arr['laruence'] = 'huixinchen';
$arr['yahoo'] = 2007; ['baidu '] = 2008;
foreach ($arr as $key => $val) {
//Was ist das Ergebnis? >Ein weiteres Beispiel:


Der Code lautet wie folgt:


$arr[2] = 'huixinchen'; arr[1] = 2007;

$arr[0] = 2008;

foreach ($arr as $key => $val) {

//Was ist jetzt das Ergebnis?




Um dieses Problem vollständig zu verstehen, sollten Sie meiner Meinung nach zunächst die interne Implementierungsstruktur von PHP-Arrays verstehen ...

PHP-Arrays
Arrays werden in PHP verwendet Eine HASH-Struktur (HashTable) ist implementiert. PHP verwendet einige Mechanismen, um das Hinzufügen und Löschen von Arrays mit O(1)-Zeitkomplexität zu ermöglichen, und unterstützt gleichzeitig lineares Durchlaufen und Direktzugriff.

In früheren Artikeln Wir haben auch den HASH-Algorithmus von PHP besprochen.

Bevor wir uns mit HashTable vertraut machen, werfen wir zunächst einen Blick auf die Strukturdefinition von HashTable verstehen:


Der Code lautet wie folgt:


typedef struct _hashtable {
uint nTableSize; /* Hash-Tabellengröße, Bereich der Hash-Werte*/
uint nTableMask; /* gleich nTableSize -1, für schnelle Positionierung*/
uint nNumOfElements; /* Die Anzahl der tatsächlichen Elemente in der HashTable*/

ulong nNextFreeElement; /* Der numerische Index des nächsten freie verfügbare Position*/

Bucket *pInternalPointer ; /* Interner Positionszeiger, wird zurückgesetzt, aktuell von diesen Durchlauffunktionen verwendet */

Bucket *pListHead; /* Kopfelement, wird für lineare Durchquerung verwendet */

Bucket *pListTail; /* Tail-Element, verwendet für lineare Durchquerung*/
Bucket **arBuckets; /* Tatsächlicher Speichercontainer*/
dtor_func_t pDestructor;/* Destruktor (Zeiger) des Elements */
zend_bool persistent;
unsigned char nApplyCount ; /* Loop Traversal Protection*/
#if ZEND_DEBUG
int inconsistent;
} HashTable; 🎜>

Über Wir können die Bedeutung von nApplyCount anhand eines Beispiels verstehen:


Der Code lautet wie folgt:


$arr = array(1,2, 3,4,5,);
$arr[] = &$arr;

var_export($arr); //Schwerwiegender Fehler: Verschachtelungsebene zu tief – rekursive Abhängigkeit?



Dieses Feld ist so eingerichtet, dass Endlosschleifen durch Zirkelverweise verhindert werden.

Wenn Sie sich die obige Struktur ansehen, können Sie das für HashTable erkennen Das Schlüsselelement ist arBuckets, der eigentliche Speichercontainer. Schauen wir uns seine Strukturdefinition an:



Der Code lautet wie folgt:


typedef struct Bucket {
ulong h; /* Numerischer Index/ Hashwert*/

uint nKeyLength; /* Länge des Zeichenindex*/

void *pData; /* data*/
void *pDataPtr ; /* Datenzeiger*/
struct Bucket *pListNext ; /* Das nächste Element, das für die lineare Durchquerung verwendet wird */
struct Bucket *pListLast; /* Das vorherige Element, das für die lineare Durchquerung verwendet wird*/
struct Bucket *pNext; /* Das nächste Element im selben Zipper Ein Element*/

struct Bucket *pLast; /* Das vorherige Element im selben Zipper*/

char arKey[1]; Speicher sparen und Initialisierung erleichtern*/

} Bucket;



Uns ist aufgefallen, dass es sich beim letzten Element um eine flexible Array-Technik handelt, die Speicher sparen und die Initialisierung erleichtern kann.
h ist der Element-Hash-Wert. Für numerisch indizierte Elemente ist h der direkte Indexwert (der numerische Index wird durch nKeyLength=0 dargestellt. Bei String-Indizes wird der Indexwert in arKey und die Länge des Index in
In Bucket gespeichert Die tatsächlichen Daten werden in dem Speicherblock gespeichert, auf den der pData-Zeiger zeigt. Normalerweise wird dieser Speicherblock vom System separat zugewiesen. Es gibt jedoch eine Ausnahme: Wenn die vom Bucket gespeicherten Daten ein Zeiger sind, fordert HashTable das System nicht auf, Speicherplatz zum Speichern des Zeigers zuzuweisen, sondern speichert den Zeiger direkt in pDataPtr und zeigt dann pData auf das Mitglied dieser Struktur. Dies verbessert die Effizienz und reduziert die Speicherfragmentierung. Daraus können wir die Feinheiten des PHP HashTable-Designs erkennen. Wenn die Daten im Bucket kein Zeiger sind, ist pDataPtr NULL (dieser Absatz stammt aus Altairs „Zend HashTable Detaillierte Erläuterung“)
pListhHead von HashTable zeigt auf das erste Element in der linearen Listenform, im obigen Bild ist es das Element 1, und pListTail zeigt auf das letzte Element 0, und für jedes Element ist pListNext das nächste Element der durch die rote Linie gezeichneten linearen Struktur, und pListLast ist das vorherige Element

pInternalPointer zeigt auf die Position des aktuellen internen Zeigers, wenn das Array sequentiell durchlaufen wird, zeigt dieser Zeiger das aktuelle Element an

Wenn eine lineare (sequentielle) Durchquerung durchgeführt wird, beginnt sie bei pListHead und folgt pListNext/pListLast im Bucket. und verschieben Sie pInternalPointer entsprechend Um eine lineare Durchquerung aller Elemente zu erreichen. Wenn wir uns beispielsweise für foreach die von ihm generierte Opcode-Sequenz ansehen, können wir feststellen, dass vor foreach zunächst ein FE_RESET zum Zurücksetzen erfolgt der interne Zeiger des Arrays, also pInternalPointer (für ein detailliertes Verständnis der PHP-Prinzipien lesen Sie bitte foreach), und erhöhen Sie dann pInternalPointer durch jeden FE_FETCH, um eine sequentielle Durchquerung zu erreichen

In ähnlicher Weise. Wenn wir die Funktion „each/next“ zum Durchlaufen verwenden, wird das sequentielle Durchlaufen auch durch Verschieben des internen Zeigers des Arrays implementiert. Hier liegt ein Problem vor, z. B.:


Der Code ist wie folgt:

$arr = array(1,2,3,4,5);
foreach ($arr as $v) {
//Kann
}

while (list ($key, $v) = every($arr)) {
//Kann
}
nicht erhalten?> 🎜>


Verstehen Sie, was ich gerade eingeführt habe, dann wird dieses Problem sehr klar sein, da foreach automatisch zurückgesetzt wird, der while-Block jedoch nicht zurückgesetzt wird, sodass pInternalPointer nach dem Ende von foreach auf das Ende zeigt des Arrays und natürlich kann nicht auf den while-Anweisungsblock zugegriffen werden. Die Lösung besteht darin, den internen Zeiger des Arrays zurückzusetzen.

Während des Zufallszugriffs wird die Position des Kopfzeigers im Hash-Array bestimmt durch den Hash-Wert, und dann wird das charakteristische Element über pNext/pLast gefunden. Beim Hinzufügen von Elementen werden die Elemente am Anfang derselben Hash-Elementkette und am Ende der linearen Liste eingefügt. Mit anderen Worten: Bei der linearen Durchquerung werden die Elemente gemäß der Reihenfolge der Einfügung durchlaufen. Das Design ist so, dass bei der Verwendung der numerischen Indizierung in PHP die Reihenfolge der Elemente durch die Reihenfolge der Addition und nicht durch die Indexreihenfolge bestimmt wird 🎜>
Mit anderen Worten, die Reihenfolge beim Durchlaufen des Arrays in PHP ist dieselbe wie die Reihenfolge der zugehörigen Elemente. Jetzt wissen wir eindeutig, dass die Ausgabe der Frage am Anfang des Artikels erfolgt lautet:


Der Code lautet wie folgt:


huixinchen
2007

2008


Also, Wenn Sie entsprechend der Indexgröße in einem numerisch indizierten Array iterieren möchten, sollten Sie for und nicht foreach


verwenden. Der Code lautet wie folgt:


for ($i=0,$l=count($arr); $i<$l; $i++) {
//Zu diesem Zeitpunkt kann es nicht berücksichtigt werden. Es handelt sich um eine sequentielle Durchquerung (lineare Durchquerung)

}

Das Obige ist ein detailliertes Verständnis von PHP-Arrays (Traversal-Reihenfolge). Weitere verwandte Artikel finden Sie auf der chinesischen PHP-Website (www.php.cn)!




Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn