einf. verkettete Liste u. Listhandler
-
Haa,
das hilft mir in diesem Fall leider auch nichts, obwohl Du prinzipiell vollkommen recht hast.
Mein Problem ist es halt zu verstehen, wie die Funktion zum erzeugen der Liste (die ja wohl auch so noch keinen Sinn macht, da die anderen Funktionen zum Anhängen, Löschen etc.. fehlen) hauptsächlich mit dem Listhandler zusammen arbeiten. Der Listhandler tritt doch immer in Aktion, wenn sich etwas in der Liste an einer Stelle verändert, oder ? Warum steht da z.b. struct element *thisElement; /*Zeiger auf den Vorgänger*/. Warum sollte man den Zeiger auf ein Element davor zeigen lassen ?
-
hmmja ... sowas wie diese listhandler struktur hab ich noch nicht gesehen.
aber ich denke mal die erspart einige hilfszeiger wenn du etwa ein element einfügst.da brauchst du halt zeiger auf die elemente zwischen denen eingefügt werden soll.poste doch mal was mehr code.
bei www.pronix.de ist die sache mit den verk. listen schön erklärt ...
aber ich bin ja immernoch für zeichen, dann wird auch klar für was du die zeiger brauchst.
-
wie die verkette Liste für alle einzelnen Schritte arbeitet, habe ich mir bereits aufgezeichnet. Auf dieser Ebene (und das hab ich zuerst gemacht) habe ich die Listen soweit verstanden. Bei mir liegst eher an der Umsetzung (speziell bei der Pointer Manipulation) ... und am Listhandler s.u.
hier mal der gesamte quelltext, jetzt aber in seiner ursprünglichen form:
/* Einfach verkettete Liste */ /*-------------------------------------------------------------------------*/ #include <stdio.h> // definiert einen Datentyp, mit dem die Daten // der Liste gespeichert werden koennen // key gibt den Schluesselwert des Elements // (damit die Elemente auch sortiert werden koennen) // name sind die eigentlichen Daten typedef struct { int key; char name[10]; } userdata_t; /*-------------------------------------------------------------------------*/ // definiert einen Struktur, welche die Elemente // der Liste bilden // zunaechst werden die eigentlichen Daten benoetigt // (zuvor definierter Datentyp) // ausserdem muss das naechste Element der Liste gemerkt // werden: // Rekursive Datentypen: Der Zeiger // next verweist auf einen Eintragen desselben Typs // list_element_t struct list_element_t { //eigentliche Daten userdata_t data; // Zeiger auf naechstes Listenelement (oder NULL) struct list_element_t *next; }; /*-------------------------------------------------------------------------*/ // ein Datentyp fuer die Verwaltung von Listen // genannt Listhandler typedef struct { // Zeiger auf Listenanfang struct list_element_t *head; // Zeiger auf DEN VORGAENGER des aktuell zu //bearbeitenden Listenelements struct list_element_t *thisElement; // Zeiger auf das letzte Listenelement struct list_element_t *lastElement; } list_handle_t; /*-------------------------------------------------------------------------*/ /* Es folgen die Funktionen zur Verwaltung von Listen */ /*-------------------------------------------------------------------------*/ // Funktion zum Erzeugen einer Liste // Rueckgabewert: Zeiger auf Listhandler // keine Parameter list_handle_t *createList(void) { // einen Zeiger auf einen Listhandler erzeugen list_handle_t *h; // malloc() erzeugt dynamisch Speicher // und gibt einen Pointer auf den Speicheranfang zurueck h =(list_handle_t *)malloc(sizeof(list_handle_t)); // jetzt ist Platz im Speicher reserviert // markiere, dass Liste leer ist // Anfang, Ende und Vorgaenger des aktuelles Element sind NULL, // d.h. vorerst nicht belegt h->head =NULL; h->thisElement = NULL; h->lastElement = NULL; // gib den neuen Handler zurueck return h; } /*-------------------------------------------------------------------------*/ //Funktion, die Elemente an die Liste anhaengt //kein Rueckgabewert //Parameter: Listhandler (fuer die Liste, an die angehaengt werden soll //Datensatz, der im neuen Element der Liste stehen soll void appendList(list_handle_t *h, userdata_t d) { //1. Schritt: Speicher fuer neues Listenelement // allokieren struct list_element_t *l; l =(struct list_element_t *)malloc(sizeof(struct list_element_t)); // 2. Schritt: Nutzdaten d in neues Listenelement kopieren l->data = d; // 3. Schritt: Verkettung - bisher letztes // Listenelement muss neuen Eintrag l als // Nachfolger bekommen. l hat keinen Nachfolger l->next = NULL; // Unterscheide zwischen leerer und gefuellter Liste // 1. Fall: leere Liste if ( h->head == NULL ) { h->head = l; h->thisElement = NULL; // zu head existiert // kein Vorgaenger } // 2. Fall: es existiert ein Element else { h->lastElement->next = l; } // nach dem Anhaengen ist das neue Element auch das letze h->lastElement = l; } /*-------------------------------------------------------------------------*/ // Funktion zum Einfuegen in die Liste // kein Rueckgabewert // Parameter: Listhandler fuer die Liste, in die eingefuegt werden soll // Datensatz fuer das neue Element der Liste void insertList(list_handle_t *h, userdata_t d) { //1. Schritt: Speicher fuer neues Listenelement allokieren struct list_element_t *l; struct list_element_t *temp; l = (struct list_element_t *)malloc(sizeof(struct list_element_t)); // 2. Schritt: Nutzdaten d in neues Listenelement kopieren l->data = d; // 3. Schritt: Verketten des neuen Elements // 1. Fall: die Liste ist leer, das neue Element wird das erste // jetzt ist das neue Element sowohl das letzte als auch das erste // thisElement zeigt auf den Vorgaenger des aktuellen Elements, // beim ersten Element gibt es aber keinen Vorgaenger, deshalb NULL if ( h->head == NULL ) { h->head = l; h->thisElement = NULL; h->lastElement = l; l->next = NULL; } // 2. Fall: es gibt genau ein Element in der Liste // in diesem Fall sind head und lastElement gleich diesem Element // und thisElement ist NULL (siehe auch 1. Fall) // das neue Element wird also vor dem Element in der Liste eingehaengt // es ist damit der neue Kopf der Liste // ausserdem ist der Nachfolger des neuen Elements das alte Element in // der Liste else if ( h->thisElement == NULL ) { l->next = h->head; h->head = l; } // 3. Fall: es gibt mehr als ein Element in der Liste // in diesem Fall wird der Nachfolger des neuen Elements das bisher // aktuelle (thisElement zeigt auf dessen Vorgaenger!) // der Nachfolger des Vorgaengers des aktuellen Elements muss jetzt auf // das neu eingefuegte Element zeigen else { l->next = h->thisElement->next; h->thisElement->next = l; } // zu guter Letzt wird der Vorgaenger des aktuellen Elements // auf das neu eingefuegte Element gesetzt h->thisElement = l; // falls hinten an die Liste angehaengt wurde: if ( h->lastElement->next ) h->lastElement = l; } /*-------------------------------------------------------------------------*/ // eine Funktion, um ein Lesezeichen in der Liste zu setzen (Merker) // Rueckgabewert: Zeiger auf das Listenelement, das gemerkt werden soll // Parameter: Listhandler, fuer die entsprechende Liste struct list_element_t* bookmarkList(list_handle_t *h) { // wenn der Listhandler ungueltig ist, gibt es keine Liste, // also Abbruch if ( !h ) return NULL; // wenn es nur ein Listenelement gibt, muss dies head sein // gib also head zurueck if ( !(h->thisElement) ) return h->head; // wenn das Vorgaenger des aktuellen Elements keinen Nachfolger hat // liegt ein Fehler vor, gib NULL zurueck if ( !(h->thisElement->next) ) return NULL; // in allen anderen Faellen, gib das aktuelle Element zurueck return h->thisElement->next; } /*-------------------------------------------------------------------------*/ // eine Funktion, um zur gemerketen Bookmark zurueckzugehen // kein Rueckgabewert // Parameter: Listhandler fuer die zu bearbeitende Liste, // Bookmark, d.h. Zeiger auf ein Element der Liste void gotoBookmarkList(list_handle_t *h, struct list_element_t* b) { // temp Variable zum Merken eines Zeigers auf ein Element struct list_element_t *temp; // wenn der Listhandler ungueltig ist, verlasse die Funktion if ( !h ) return; // wenn das Bookmark der Anfang der Liste ist, muss das // Vorgaenger des aktuellen Elements der Liste auf NULL gesetzt werden if ( b == h->head ) { h->thisElement = NULL; } else { // ansonsten merke den aktuellen Head temp = h->head; // solange das aktuelle Element nicht NULL und nicht das // Bookmark ist, gehe ein Element weiter while ( temp != NULL && temp->next != b ) temp = temp->next; // wenn ich das Bookmark gefunden habe // setze den Vorgaenger des aktuellen Element als Bookmark if ( temp ) h->thisElement = temp; } } /*-------------------------------------------------------------------------*/ // Funktion, um Elemente aus der Liste zu lesen // Rueckgabewert: gefundenes Nutzdaten // Parameter: Listhandler der zu bearbeitenden Liste userdata_t *readElementList(list_handle_t *h) { // wenn der Listhandler nicht gueltig ist // oder die Liste leer ist // oder der Vorgaenger des aktuellen Elements keinen Nachfolger hat // gib NULL zurueck, da ungueltige Werte if ( !h || !(h->head) || !(h->thisElement->next) ) return NULL; // wenn es keinen Vorgaenger vom aktuellen Element gibt, existiet nur ein // Element, das Head, also gib dessen Daten zurueck if ( !(h->thisElement) ) return &(h->head->data); // in allen anderen Faellen // gib die Daten des aktuellen Elements zurueck return &(h->thisElement->next->data); } /*-------------------------------------------------------------------------*/ // Funktion zum Zuruecksetzen des Vorgaengers des aktuellen Elements // kein Rueckgabewert // Parameter: Listhandler der zu bearbeitenden Liste void rewindList(list_handle_t *h) { // wenn der Handler gueltig ist, setze den Vorgaenger des aktuellen // Elements auf NULL if ( h ) h->thisElement = NULL; } /*-------------------------------------------------------------------------*/ // Funktion, um schrittweise durch die Liste zu gehen // Rueckgabewert: Zeiger auf die Daten des aktuellen Listenelements // Parameter: Listhandeler der zu bearbeitenden Liste userdata_t *traverseList(list_handle_t *h) { // Zeiger fuer die Daten des Listenelements userdata_t *dPtr; // wenn der Listenhandler ungueltig ist // oder der Vorgaenger des aktuellen Elements gleich dem letzten // Element ist, ist die Liste ungueltig, Rueckgabewert NULL if ( (!h) || h->thisElement == h->lastElement ) { return NULL; } // wenn die Liste nur ein Element hat, ist der Rueckgabewert // der Zeiger auf die Daten des Heads // thisElement wird fuer den naechsten Schritt hochgesetzt if ( h->thisElement == NULL ) { h->thisElement = h->head; return &(h->head->data); } // in allen anderen Faellen ist der Rueckgabewert ein Zeiger // auf die Daten des letzten Elements dPtr = &(h->thisElement->next->data); // das aktuelle Element wird auf das naechste Element gesetzt // (fuer den naechsten Schritt) h->thisElement = h->thisElement->next; // Rueckgabe der Daten return dPtr; } /*-------------------------------------------------------------------------*/ // Funktion zum Ausgeben der Liste // kein Rueckgabewert // Parameter: Listhandler der zu bearbeitenden Liste void printList(list_handle_t *h) { // Zeiger auf die Daten userdata_t *dPtr; // Liste zuruecksetzen rewindList(h); // Daten holen dPtr = traverseList(h); // wenn die Daten gueltig sind, ausgeben bis Ende while(dPtr != NULL) { printf("key = %d name = %s\n", dPtr->key, dPtr->name); dPtr = traverseList(h); } } /*-------------------------------------------------------------------------*/ /* Hauptprogramm: Beispiel zum Arbeiten mit Listen */ /*-------------------------------------------------------------------------*/ int main() { // zwei Listhandler list_handle_t *list1Handle; list_handle_t *list2Handle; // Variable fuer Daten userdata_t d; // Zeiger auf Daten userdata_t *dPtr; // Listenelement struct list_element_t* bm; // zwei Listen erzeugen list1Handle = createList(); list2Handle = createList(); // Daten erzeugen d.key = 10; // Kopieren von String in char-array: // der Ziel char-array muss lang genug sein, // um den Quellstring + Null-Character // abzuspeichern strcpy(d.name,"name1"); // Daten an die Liste 1 anhaengen appendList(list1Handle,d); // noch mehr Daten erzeugen und anhaengen d.key = 5; strcpy(d.name,"name2"); appendList(list1Handle,d); d.key = 7; strcpy(d.name,"name3"); appendList(list1Handle,d); // Liste 1 ausgeben printf("1. Lesevorgang:\n"); printList(list1Handle); // Liste 1 zuruecksetzen rewindList(list1Handle); // aktuelles Element ausgeben printf("lies aktuelles Element:\n"); dPtr = traverseList(list1Handle); printf("key = %d name = %s\n",dPtr->key, dPtr->name); // Daten veraendern printf("Veraendere die Daten des aktuellen Elementes in der Liste\n"); dPtr->key = 200; strcpy(dPtr->name,"name200"); // Liste zum zweiten Mal ausgeben printf("2. Lesevorgang:\n"); printList(list1Handle); printf("key = %d name = %s\n",dPtr->key, dPtr->name); // Liste zuruecksetzen rewindList(list1Handle); // zwei Elemente weitergehen dPtr = traverseList(list1Handle); dPtr = traverseList(list1Handle); // Element lesen dPtr = readElementList(list1Handle); // Bookmark setzen printf("Setze Bookmark bei: key = %d name = %s\n", dPtr->key, dPtr->name); bm = bookmarkList(list1Handle); // neue Daten erzeugen und einfuegen d.key = 99; strcpy(d.name,"name99"); insertList(list1Handle,d); // Liste zum dritten Mal ausgeben printf("3. Lesevorgang:\n"); printList(list1Handle); // zur Bookmark gehen gotoBookmarkList(list1Handle,bm); dPtr = readElementList(list1Handle); printf("Gelesen vom Bookmark: key = %d name = %s\n", dPtr->key, dPtr->name); // Liste zuruecksetzen rewindList(list1Handle); // Daten erzeugen und einfuegen d.key = 100; strcpy(d.name,"name100"); insertList(list1Handle,d); // Liste zum vierten Mal ausgeben printf("4. Lesevorgang:\n"); printList(list1Handle); // Daten erzeugen und einfuegen d.key = 101; strcpy(d.name,"name101"); insertList(list1Handle,d); // Liste zum fuenften Mal ausgeben printf("5. Lesevorgang:\n"); printList(list1Handle); }
-
was ist denn jetzt eigentlich genau die Frage?
-
ja, wo genau klemmts? ist dir nicht klar _worauf_ die ptr jeweils zeigen, oder warum man jetzt grade _diesen_ ptr für jenes problem benutzt ...
-
habe mir jetzt nochmal alles in ruhe angeguckt. Jetzt tun sich mir zwei wichtige Fragen auf.
1.) Der Listhandler dient in diesem Beispiel also dazu, um sich ein paar Zeiger zu ersparen, oder ?
2.)die Funktion void insertList... kann man das mal etwas anschaulicher machen:// 3. Fall: es gibt mehr als ein Element in der Liste // in diesem Fall wird der Nachfolger des neuen Elements das bisher // aktuelle (thisElement zeigt auf dessen Vorgaenger!) // der Nachfolger des Vorgaengers des aktuellen Elements muss jetzt auf // das neu eingefuegte Element zeigen
Was hat hier konkret "thisElement zeigt auf dessen Vorgaenger" für eine Funktion, die ja auch im Listhandler verankert ist.?
Jede Hilfe kommt mir gelegen.
-
struct element *thisElement; /*Zeiger auf den Vorgänger*/
die sache mit dem "vorgänger" ist verwirrend. das ist im kontext mit der verwendung in den funktionen gemeint. eigentlich zeigt er nur auf irgendein element, nach dem einfügen etwa auf das grade eingefügte element.
und für das nächste element das eingefügt wird ist es dann eben der vorgänger, da das neue hinter h->thiselement eingefügt wird.else { /*noch hängt l in der luft, wurde zwar gemalloced aber hat kein vorgänger/nachfolger*/ l->next = h->thisElement->next; /*jetzt hat l ein nachfolger, es zeigt auf den ehem nachfolger von h->thisElement*/ h->thisElement->next = l; /*jetzt wird "zeigt auf dessen vorgänger" klar h->thisElement->next zeigt auf l, also ist es jetzt der vorgänger*/ } // zu guter Letzt wird der Vorgaenger des aktuellen Elements // auf das neu eingefuegte Element gesetzt h->thisElement = l; /*und ist dann wieder slbst ein vorgänger für neue elemente*/
hmm weiss nicht obs nur mir so geht, finde die kommentierung (total konfus und verwirrend) und die datenstruktur (langweilig: elemente werden willkürlich eingesetzt) sehr seltsam.
kann dir nur nochmal www.pronix.de ans herz legen.
-
Original erstellt von andro:
**Was hat hier konkret "thisElement zeigt auf dessen Vorgaenger" für eine Funktion, die ja auch im Listhandler verankert ist.?**
ist die funktion klargeworden?
man braucht halt nen anker, an den man das neue ele anhängen kann.
und das ist hier halt h->thisElement. gleichzeitig kann es auch noch den nachfolger für das neue ele liefern nämnlich h->thisElement->nextnach dem eifügen sieht das dann so aus:
h->thisElement->l->h->thiselement->(natürlich jetzt EX-)next
-
hi,
deine comments gefallen mir auch besser, aber ich habe das thema insgesamt noch nicht verstanden, vielleicht liegts wirklich noch an den basics und damit die klar sind, frage ich lieber nochmal nach.geg:
h->thisElement->next = l;
/*jetzt wird "zeigt auf dessen vorgänger" klar h->thisElement->next zeigt auf l, also ist es jetzt der vorgänger*/meine verständnis:
h ist der kopfzeiger, der auf das erste element (thisElement) zeigt, dessen zeiger next wieder auf irgendwas zeigt stimmt doch, oder ?aber warum: h->thisElement->next zeigt auf l ??? Ist das nicht wie a=b, so dass a der wert von b zugewiesen wird und kein Zeiger... Habe Probleme mit der Syntax
grüsse und danke für die geduld.
[ Dieser Beitrag wurde am 04.05.2003 um 18:31 Uhr von andro editiert. ]
-
should i rtfm ?
-
Original erstellt von andro:
should i rtfm ?RTFM: www.pronix.de --> std C --> Einführung in Pointer
-
du solltest dir vor allem mal klarmachen was der -> operator macht