2017-05-08 5 views
0

Ich habe ein Lehrbuch gelesen und stieß auf die folgende Funktion, um ein Element aus einer verknüpften Liste zu entfernen. Der CELL Typ ist wie folgt definiert;C Implementierung einer verknüpften Liste Element entfernen Funktion

typedef struct CELL CELL; 
struct CELL { 
    int element; 
    CELL* next; 
}; 

void delete(int x, CELL** pL) { 
    if ((*pL) != NULL) { 
     if (x == (*pL)->element) 
      (*pL) = (*pL)->next; 
     else 
      delete(x, &((*pL)->next)); 
    } 
} 

Angenommen, ich habe eine verknüpfte Liste mit den folgenden Elementen beginnend bei 5;

5->10->3->21->15->7

Jetzt nehme ich löschen Element 10. Meine Frage ist, was mit dem CELL geschieht 10 enthält, wenn die delete Funktion kehrt nun, dass diese CELL unreferenced ist? Nimmt es nicht noch Speicherplatz in Erinnerung?

+0

Rufen Sie einen Debugger-Schritt durch den Code und untersuchen Sie die Variablen. – OldProgrammer

+1

Ohne Bezug, diese Funktion ist beispielhaft für das Prinzip: "Nur weil du kannst, bedeutet nicht, dass du es solltest." Dieser Algorithmus * lässt sich leicht zu einer iterativen Lösung portieren. Wahrscheinlich als Teil einer rekursiven Lösung für Datenstrukturen Kurs/Lektion verwendet, kann es dort seinen Zweck erfüllen, aber vermeiden es in der Praxis. – WhozCraig

+0

Ja, es beansprucht immer noch Platz, obwohl nicht klar ist, wie das Element zugewiesen wurde. Es kann besser sein, die "delete" -Funktion bei der Speicherverwaltung nicht zu beachten und einen Zeiger auf die gelöschte "CELL" zurückzugeben (oder "NULL", wenn keine Zelle gelöscht wurde) und den Anrufer freizulassen. Sie könnten eine Wrapper-Funktion haben, die das Element ebenfalls freigibt. Die andere seltsame Sache über diese Funktion ist die Verwendung von Tail-Rekursion anstelle von Iteration. Wenn der Compiler das nicht optimiert, könnten Sie einen Stapelüberlauf bekommen, wenn die Liste lang ist. –

Antwort

2

Da Sie keinen Speicher in Ihrem Code freigeben, sollte er immer noch da sein. Sie heben die Verknüpfung dieses Elements von der verknüpften Liste auf.

Je nachdem, wie Sie den Speicher für diese Elemente verwalten, ist dies möglicherweise kein Problem (Speicherleck). Wenn beispielsweise jedes Element unter Verwendung von malloc zugeordnet wird und die verknüpfte Liste den einzigen Zeiger auf dieses Element hat, liegt wahrscheinlich ein Speicherverlust vor (und die Verwendung von free ist erforderlich). Wenn die Knoten jedoch auf dem Stapel zugeordnet sind (z. B. unter Verwendung von alloca) oder in einem Speicherpool, der später freigegeben wird, ist möglicherweise kein Leck vorhanden. Es kommt also wirklich darauf an, wie Sie mit dem Speicher umgehen und wann Sie ihn freigeben.

+0

Ah ja, ich bin so sehr an die Variablen von benutzerdefinierten Typen gewöhnt, die Heap-dynamisch von Java sind, dass ich vergessen habe, dass sie auch in C stapeln könnten. Danke. – BlaqICE

2

was passiert mit dem CELL 10 enthält, wenn die Löschfunktion kehrt nun, dass diese CELL unreferenced ist? Nimmt es nicht noch Speicherplatz in Erinnerung?

Absolut! Sobald die Funktion zurückkehrt, wird die CELL, die 10 enthält, zu einem Speicherverlust, es sei denn, es gibt einen anderen Zeiger, der darauf verweist, oder die CELL ist nicht dynamisch zugewiesen.

Wenn andere Funktionen für die Liste zu manipulieren (Erstellen, Einfügen, etc.) sind so ausgebildet, dass sie die Liste auf das Eigentum ihrer Zellen übernehmen, fügen Sie einen Aufruf an free in der Branche, die wieder Punkte *pl-(*pL)->next. Erstellen Sie eine temporäre Variable, speichern Sie die Adresse *pl darin, führen Sie die Neuausrichtung durch, und rufen Sie free auf dem temporären.

+0

Danke. Bevor du gepostet hast, habe ich darüber nachgedacht, die Aussage zwischen dem inneren "if" und "else" zu löschen und die folgenden drei Aussagen der Reihe nach hinzuzufügen; 'int * temp = (* pL) -> nächstes;', 'free (* pL);', und '* pL = temp;' Jetzt frage ich mich, würde dies dasselbe erreichen, oder wäre es besser, es zu tun Mach es so, wie du es gesagt hast? – BlaqICE

+0

@BlaqICE Ja, es würde dasselbe bewirken: Es ist egal, ob Sie 'temp' für den zu löschenden Knoten oder für den Knoten verwenden, den Sie behalten möchten, so dass beide Ansätze das gleiche Ergebnis liefern würden. – dasblinkenlight

1

Für den Anfang einige Bemerkungen über die Funktion selbst. Es wäre besser, die Funktion folgendermaßen zu deklarieren:

int delete(CELL **pL, int x); 

Tatsächlich macht die Funktion zwei Dinge. Es durchsucht einen Knoten mit dem Wert x. Und wenn ein solcher Knoten gefunden wird, wird die Verknüpfung aufgehoben.

Es ist also wünschenswert, dass die Funktion meldet, ob ein solcher Knoten gefunden wird.

Eine weitere Möglichkeit, die Funktion für den Fall zu erklären, wenn die Funktion nicht der entfernte Knoten frei von @IanAbbott in einem Kommentar darauf ist

CELL * delete(CELL **pL, int x); 

, dass die Funktion an den entfernten Knoten zurückgibt Zeiger ist oder NULL, wenn ein solcher Knoten mit dem Wert x nicht gefunden wird.

Zweitens ist es besser, den ersten Parameter als Referenz auf den Knoten und den zweiten Parameter als gesuchten Wert zu deklarieren. Das ist zuerst, was Sie zu tun haben und wie Sie mit der Liste umgehen.

Wie für Ihre Frage, dann die Funktion, wie ich schon sagte, die Verknüpfung eines Knotens. Der Knoten selbst wird nicht aus dem Speicher entfernt und nicht geändert. Wenn es dynamisch zugewiesen wurde, müssen Sie eine Kopie des Zeigers behalten, der auf den Knoten zeigt. Andernfalls tritt ein Speicherverlust auf, weil Sie ihn nicht ohne einen Zeiger auf den Knoten freigeben können.

+1

Sie könnten anstelle eines 'int' einen Zeiger auf den gelöschten Knoten zurückgeben. Gib 'NULL' zurück, wenn nicht gefunden. Dann kann der Anrufer tun, was er tun muss, um den Knoten freizugeben. –

+0

@IanAbbott Ja, es ist ein alternativer Ansatz. –

Verwandte Themen