In diesem Slashdot-Interview wird Linus Torvalds mit folgenden Worten zitiert:
Ich habe zu viele Leute gesehen, die einen einfach verknüpften Listeneintrag löschen, indem sie den Eintrag "prev" verfolgen und dann den Eintrag löschen, indem sie so etwas tun
if (prev)
prev-> next = entry-> next;
sonst
list_head = entry-> next;und wenn ich solchen Code sehe, gehe ich einfach zu "Diese Person versteht keine Zeiger". Und es ist leider ziemlich häufig.
Personen, die Zeiger verstehen, verwenden einfach einen "Zeiger auf den Eintragszeiger" und initialisieren diesen mit der Adresse des Listenkopfs. Und dann, wenn sie die Liste durchlaufen, können sie den Eintrag entfernen, ohne Bedingungen zu verwenden, indem sie einfach "* pp = entry-> next" ausführen.
Als PHP-Entwickler habe ich seit Einführung in C an der Universität vor einem Jahrzehnt keine Hinweise mehr berührt . Ich bin jedoch der Meinung, dass dies eine Art Situation ist, mit der ich zumindest vertraut sein sollte. Worüber spricht Linus? Um ehrlich zu sein, wenn ich gebeten würde, eine verknüpfte Liste zu implementieren und ein Element zu entfernen, würde ich den oben genannten "falschen" Weg einschlagen. Was muss ich wissen, um zu codieren, wie Linus am besten sagt?
Ich frage hier eher als bei Stack Overflow, da ich im Produktionscode eigentlich kein Problem damit habe.
prev
Knotens speichern müssen, anstatt den gesamten Knoten zu speichern, Sie einfach die Position von speichern könnenprev.next
, da dies das einzige ist, woran Sie interessiert sind. Ein Zeiger auf einen Zeiger. Und wenn Sie das tun, vermeiden Sie das Dummeif
, da Sie jetzt nicht den unangenehmen Fall habenlist_head
, ein Zeiger von außerhalb eines Knotens zu sein. Der Zeiger auf den Kopf der Liste ist dann semantisch der gleiche wie der Zeiger auf den nächsten Knoten.Antworten:
Verwenden meiner L331 MS Paint-Kenntnisse:
Die ursprüngliche Lösung besteht darin, auf Knoten über zu zeigen
curr
. In diesem Fall prüfen Sie, ob der nächste nachfolgende Knotencurr
den Löschwert hat, und setzen in diesem Fall dencurr
Knotenzeiger zurücknext
. Das Problem ist, dass es keinen Knoten gibt, der auf den Kopf der Liste zeigt. Das heißt, es muss einen Sonderfall geben, um dies zu überprüfen.Was Linus (wahrscheinlich) stattdessen vorschlägt, ist nicht, den Zeiger auf den aktuell untersuchten Knoten zu speichern, sondern den Zeiger auf den Zeiger auf den aktuellen Knoten (beschriftet
pp
). Die Operation ist dieselbe - wenn derpp
Zeiger auf einen Knoten mit dem richtigen Wert zeigt, setzen Sie denpp
Zeiger zurück.Der Unterschied steht ganz am Anfang der Liste. Während es keinen Knoten gibt, der auf den Kopf der Liste zeigt, gibt es tatsächlich einen Zeiger auf den Kopf der Liste. Und es ist genauso ein Zeiger auf einen Knoten wie ein Zeiger eines anderen Knotens
next
. Daher ist für den Beginn der Liste keine spezielle Klausel erforderlich.quelle
list_head
auf etwas mit einemnext
Knoten zu zeigen, der auf das erste reale Datenelement zeigt (undprev
auf dasselbe Dummy-Objekt initialisiert hat). Ich mag die Idee nicht,prev
auf etwas anderes zu verweisen, da solche Tricks undefiniertes Verhalten über Aliasing einführen und Code unnötig empfindlich für das Strukturlayout machen können.prev
auf Knoten zu zeigen, zeigt es auf Zeiger. Es zeigt immer auf etwas vom gleichen Typ, nämlich einen Zeiger auf einen Knoten. Ihr Vorschlag ist im Wesentlichen der gleiche -prev
zeigen Sie auf etwas "mit einemnext
Knoten". Wenn Sie die Shell verwerfen, erhalten Sie nur den Anfangszeigerlist_head
. Oder mit anderen Worten - etwas, das nur durch einen Zeiger auf den nächsten Knoten definiert wird, entspricht semantisch einem Zeiger auf einen Knoten.list_head
undnext
die gleiche "Art" von Zeigern enthält. Vielleicht kein Problem in C, aber vielleicht problematisch in C ++.Codebeispiel
Wenn wir eine Logik benötigen, um den Knoten zu zerstören, können wir am Ende einfach eine Codezeile hinzufügen:
quelle