Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » verkettete Liste

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
06.01.2008, 23:18 Uhr
banshee



hallöchen,

ich bin gerade blind oder so, ich komme jedenfalls irgendwie beim Löschen nicht weiter:


C++:
struct Elt
{
    ...
    struct Elt *next;
};

struct Elt* head;
struct Elt* tail;

// neu anlegen
tail = (struct Elt*)malloc(sizeof(struct Elt));
head = tail;

//einfügen
tail->next = (struct Elt*)malloc(sizeof(struct Elt));
tail = tail->next;

// löschen
current = current->next;


Und da ist guter Rat teuer. Ich lese per sscanf() die Adresse des pointers nach current ein, nur wenn ich die da ändere, wird das ganze nicht in der von head und tail beschriebenen Liste geändert.
Ich bin da jetzt irgendwie durch java durcheinander, weil da ja alles Referenzen aufeinander sind und man das so machen könnte.
Hat jemand eine Idee, wie ich das jetzt machen kann?

Dieser Post wurde am 06.01.2008 um 23:18 Uhr von banshee editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
07.01.2008, 00:46 Uhr
0xdeadbeef
Gott
(Operator)


Bei einer einfach verketteten Liste musst du neben dem Element, das du löschen willst, noch das Element davor wissen, weil dessen next-Zeiger geändert werden muss. Außerdem musst du den Speicher wieder freigeben, und ein paar Spezialfälle beachten, für das Löschen am Ende bzw. Anfang, bzw. in einer fast leeren Liste, also:

C++:
if(tail == head) {
  free(tail); // Hier wird ausgenutzt, dass free(NULL); nichts macht, im Fall einer leeren Liste.
  tail = head = NULL;
} else if(current == head) {
  head = current->next;
  free(current);
} else if(current == tail) {
  tail = current_prev;
  current_prev->next = NULL; // wobei current_prev das vorherige Element ist
  free(current);
} else {
  current_prev->next = current->next;
  free(current);
}


Außerdem musst du bei der Initialisierung noch den next-Zeiger des erstellten Elements auf NULL setzen. Vereinfachen lässt sich das mit einer doppelt verketteten Dummy-Liste, bei der head und tail unbenutzte Elemente sind, dann sieht das aus wie folgt:

C++:
struct list_node {
  struct list_node *prev, *next;
};

struct list {
  struct list_node *head, *tail;
  size_t size;
};

void list_init(struct list *ls) {
  ls->head = malloc(sizeof(struct list_node));
  ls->tail = malloc(sizeof(struct list_node));

  ls->head->prev = ls->head;
  ls->head->next = ls->tail;
  ls->tail->prev = ls->head;
  ls->tail->next = ls->tail;

  ls->size = 0;
}

void list_push(struct list *ls) {
  struct list_node *new_node = malloc(sizeof(struct list_node));

  new_node->next = ls->tail;
  new_node->prev = ls->tail->prev;

  ls->tail->prev->next = new_node;
  ls->tail->prev = new_node;

  ++ls->size;
}

void list_remove(struct list *ls, struct list_node *node) {
  assert(node != ls->head && node != ls->tail); // Die Dummies werden erst am Ende gelöscht

  node->prev->next = node->next;
  node->next->prev = node->prev;
  free(node);
  --ls->size;
}

struct list_node *list_get(struct list *ls, size_t index) {
  size_t i;
  struct list_node *result;

  if(index > ls->size) {
    return NULL;
  }

  result = head->next;
  for(i = 0; i < index; ++i) {
    result = result->next;
  }

  return result;
}

/* Die Liste zerstören */
void list_destroy(struct list *ls) {
  /* Alle Elemente  löschen */
  while(ls->size > 0) {
    list_remove(ls->head->next);
  }

  /* Dummies freigeben */
  free(ls->head);
  free(ls->tail);
}


Der Hintergrund ist, dass die Dummies dir so ziemlich alle Sonderfälle ersparen, weil für jedes Element sichergestellt ist, dass es ein nächstes und vorheriges gibt, so dass node->prev->next und node->next->prev immer da sind, und außerdem, dass head und tail nie geändert werden.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra

Dieser Post wurde am 07.01.2008 um 00:52 Uhr von 0xdeadbeef editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
07.01.2008, 10:06 Uhr
banshee



okay das wäre wahrscheinlich die schönere Lösung, aber ich frage mich ob das mit meiner nicht auch geht. Es ist auch eine Sache, bei der mich grundsätzlich interessiert, ob ich das machen kann.

Also ich versuche es nochmal besser zu beschreiben.

Die Liste kommt im Zusammenhang mit einer Client/Server-Anwendung zum einsatz, bei der eine variable Anzahl an Objekten verwaltet werden müssen und Nachrichten ausgetauscht werden. Jedes Objekt hat eine ID, was in dem Fall einfach die Adresse des pointers auf das Objekt ist.
Das einfügen und alles klappt schonmal wunderbar, nur beim Löschen klappt es aus folgendem Grund nicht so ganz:

Der Server schickt mir also eine ID (= Pointeradresse) eines Objekts und die lese ich dann per sscanf in die Adresse vom temporären Objekt current ein (sscanf(buf, "%p", &current))
Jetzt habe ich in current zwar einen Zeiger auf das richtige Element, allerdings, wenn ich diesen ändere, dann bleibt die Liste, die von den Elementen head und tail beschrieben wird, davon unberührt. Anscheinend legt er dann davon eine Kopie an, ich bräuchte aber jetzt eine Referenz, womit das Objekt auf das ich zeige auch im Original mitgeändert wird.
Geht das nicht irgendwie? Ansonsten muss ich wohl auf die doppelt verkettete Liste zurückgreifen
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
07.01.2008, 15:16 Uhr
0xdeadbeef
Gott
(Operator)


Uh...zum einen reicht nur das momentane Element einfach nicht aus, zum anderen - wenn die Liste in einem anderen Prozess existiert (und wenn sie auf einem anderen Rechner läuft, ist das mit Sicherheit der Fall), zeigt der Zeiger in deinem Prozess, in dem die Liste nicht existiert, ins Nirvana, und seine Benutzung sollte dir eigentlich dauernd segmentation faults liefern. An der Stelle musst du schon über eine message queue oder vergleichbares dem Server, der die Liste hat, die Nachricht schicken, dass er das Element löschen soll, weil nur der sich darum kümmern kann - er ist ja der einzige, der die Liste hat.

Oder missverstehe ich da jetzt etwas?
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
07.01.2008, 15:48 Uhr
banshee



Nicht unbedingt, allerdings wird die Liste schon vom Client verwaltet, also mir. Damit man sich das ganze etwas bildlicher vorstellen kann: Die Objekte sollen sowas wie kleine Lebewesen symbolisieren, die auf einem Spielfeld (dem Server) Nahrung sammeln, sich vermehren und gegen andere Lebewesen kämpfen können.
Der Server verwaltet jetzt natürlich alle Lebewesen in einer eigenen Liste, allerdings muss ich als Client meine eigenen Lebewesen auch in einer Liste verwalten.
Der Server schickt mir dann in jeder Runde Spielinformationen über jedes Lebewesen (dazu die ID) und ich muss dann darauf antworten (auch wieder mit der ID)
Dass man für eine einfach verkettete Liste auch das vorherige Element braucht, habe ich mir schon gedacht. Allerdings bin ich dann auf die Idee gekommen, einfach die Zeigeradresse, des jeweiligen Elements als ID zu benutzen, sodass ich diese einfach aus der Serverantwort rauslesen kann und dann einen Zeiger auf das zu löschende Element habe.
Das ist auch so nicht unbedingt falsch, nur wird das, was ich mit diesem Zeiger (bzw. der Struktur, auf die er zeigt) anstelle, nicht in der Liste aktualisiert, die ich anlege.

Also nochmal ein Beispiel:

Ich erstelle mir 5 Objekte, dann sieht meine Liste so aus:

head: 1234 -> 1238 -> 1242 -> 1244 -> tail: 1248 hinter den IDs verbirgt sich dann jeweils eine Objektstuktur.

Dann bekomme ich vom Server die Nachricht, dass ID 1242 gelöscht werden soll. Und ich hatte jetzt gehofft ich kann mir dann einfach diese pointer Adresse in einen anderen Zeiger laden und wenn ich diesen dann ändere, wird es auch in der obigen Liste aktualisiert.

Also ich würde dann machen:
sscanf("%p", &current);
current = current -> next;

und danach sollte die Liste so aussehen:
head: 1234 -> 1238 -> 1244 -> tail: 1248

Ich müsste wie gesagt sowas wie eine Referenz auf diesen Zeiger haben, aber ich weiß eben nicht, ob das geht.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
07.01.2008, 16:21 Uhr
0xdeadbeef
Gott
(Operator)


Ja nun, malloc gibt ja nicht immer die gleiche Adresse zurück; das heißt, das Element, das die betroffene Figur darstellt, hat auf dem Server höchstwahrscheinlich eine ganz andere Speicheradresse, als das auf dem Client der Fall ist.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
07.01.2008, 18:01 Uhr
banshee



okay ich war zu ungenau :>

Also die ID dient einzig und alleine dazu, meine Lebewesen zu identifizieren.
Ich könnte mir zb. auch ein Lebewesen mit der ID "hurz" definieren, dann schickt mir der Server eine Nachricht, in der zur ID "hurz" verschiedene Daten stehen, also zb.
PLAY Runde:1 ID:hurz POSX:1 POSY:1

Wie der Server die ganzen Lebewesen verwaltet ist mir also als Client völlig egal.

Die Methode oben funktioniert ja auch, ich bekomme die Adresse eines Zeigers zurück, den ich auch selber angelegt habe, nur ich weiß eben nicht, wie ich das echte Objekt, auf das der Zeiger zeigt, bearbeiten kann, und nicht nur seine Kopie.

Aber wenn das jetzt immernoch wirr ist, dann benutz ich einfach die doppelt verkettete Liste ^^
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
07.01.2008, 18:54 Uhr
0xdeadbeef
Gott
(Operator)


Naja,

C++:
current = current->next;


verändert nur current, lässt also deinen Zeiger auf das nächste Element zeigen. Die Liste wird dadurch nicht verändert. Du könntest aber zum Beispiel das Element nach current löschen, indem du

C++:
struct Elt *tmp = current->next;
current->next = tmp->next; /* tmp->next == current->next->next */
free(tmp); // Speicherfreigabe nicht vergessen, C hat keinen garbage collector


schreibst. Vorher aber sicherstellen, dass current->next auch existiert, sonst dereferenzierst du nachher einen NULL-Zeiger.

Bist du auf C festgelegt, oder könntest du das auch C++ schreiben? Da wär das nämlich deutlich einfacher, mit std::map.
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: