Liste aufräumen

  • Themenstarter Themenstarter KeDaiv
  • Beginndatum Beginndatum
K

KeDaiv

Hi,
ich versuche seit ca einer Woche C zu lernen. Und habe mir dabei mal als Ziel gesetzt eine möglichst dynamische und leicht zu nutzende Liste zu schreiben. Zur Zeit ist dies noch nicht allzuweit vorangeschritten. Jedoch funktionieren erst einmal alle Funktionen wie ich will.
Nun zu meinem Prblem, welches ich erkannt habe, Ich nutze void - Zeiger um auf die gespeicherten Elemente zu zeigen. Den Speicher für die Elemente reserviere ich ebenfals n der Liste. Da es sich nun aber um void Zeiger handelt, kann ich ja alles mögliche darin speichern. Wenn nun eine Struktur in der Liste gespeichert wird, die einen Zeiger auf irgendein anderes Element hat, und ich den Knoten, worin das Element gespeichert ist lösche, dann würde ich auch den Speicherplatz frei räumen. Nun ist es aber dann so, dass ich ein den Speicher, auf den eine Variable in der Struktur zeigte nicht frei geräumt habe. Spontan würden mir da Funktionszeiger einfallen, die ich beim Löschen nutzen könnte. So müsste der Anwender selbst eine Funktion implementieren, die dann den Speicher auf den die Struktur zeigt frei räumen. Jedoch kenne ich Funktionszeiger nur aus C# wo sie als delegates bekannt sind und weiß nicht so genau wie ich einen Funktionszeiger als parameter einer Funktion deklarieren kann. Die andere Frage für mich wäre, ob es andere Möglichkeiten/Strategien gibt um solch ein Problem zu lösen. Denn ich möchte, dass der Anwender möglichst wenig zu tun hat. Von daher möchte ich das gesammte freiräumen in der Liste übernehmen. Ich hoffe dasd das geht und danke euch schon mal für eure Beiträge. Im Anschluss noch mal mein bisheriger Quellcode... ^^

klist.h
Code:
/*
 * klist.h
 *
 *  Created on: 29.09.2008
 *      Author: KeDaiv
 */

#ifndef KLIST_H_
#define KLIST_H_

#ifndef NULL
#define NULL (void *)0
#endif

typedef struct ListNode {
	void *element;
	struct ListNode *next;
} ListNode;

typedef struct List {
	size_t type;
	int count;
	int capacity;
	ListNode *first;
} List;

#endif /* KLIST_H_ */

klist.c
Code:
/*
 * klist.c
 *
 *  Created on: 29.09.2008
 *      Author: KeDaiv
 */
#include <stdlib.h>
#include <string.h>
#include "klist.h"

/**
 * listInit initialisiert eine Liste mit den Standartwerten
 * Es wird ein Zeiger auf die Lieste zurück gegeben
 */
List *listInit( size_t type ) {

	//Alloziiert Speicher für eine neue Liste
	List *newList = ( List * ) malloc( sizeof(List) );

	//Standartwerte werden gesetzt
	newList->capacity = 0;
	newList->count = 0;
	newList->type = type;
	newList->first = NULL;

	//Zeiger der Liste wird gegeben
	return newList;

}

/**
 * Ein ListenKnoten wird initialisiert. Hierin werden später die Werte gespeichert.
 * Ebenfalls wird ein Zeiger zurück gegeben
 */
ListNode *nodeInit( void ) {

	//Für Knoten wird Speicher alloziiert
	ListNode *newNode = ( ListNode * ) malloc( sizeof(ListNode) );

	//Standartwerte werden gesetzt
	newNode->element = NULL;
	newNode->next = NULL;

	//Zeiger wird zurück gegeben
	return newNode;

}

/**
 * Diese Funktion alloziiert Speicher und kopiert das Element auf welches der void-Zeiger zeigt
 * mit der angegebenen Größe in den Speicher
 */
inline void *cpyElement( void *element, size_t size ) {
	//Inhalt auf den der void-Zeiger zeigt wird kopiert.
	return memcpy( malloc( size ), element, size );
}

/**
 * Der Wert auf den der Zeiger element zeigt wird an die Liste angehängt
 */
void add( List *list, void *element ) {

	//Wenn kein erstes Element bereit steht
	if( NULL == list->first ) {
		//Wird ein erster Knoten angelegt
		list->first = nodeInit();
	}

	//Erster Knoten ist der Ausgangspunkt
	ListNode *node = list->first;
	//Solange wie Elemente in den Knoten vorhanden sind
	while( NULL != node->element ) {
		//Wenn es kein Folgeknoten gibt
		if( NULL == node->next ) {
			//wird einer angelegt
			node->next = nodeInit();
		}
		//Es wird weiter durch die Liste iteriert
		node = node->next;
	}
	//Wenn ein leeres Element gefunden wurde, wird es gesetzt
	node->element = cpyElement( element, list->type );

}

/**
 * Fügt ein Element am index an. Alle anderen Elemente werden
 * jedoch um einen Wert nach hinten verschoben
 */
void insert( List *list, int index, void *element ) {

	if( 0 == index ) {
		ListNode *newNode = nodeInit();

		newNode->element = cpyElement( element, list->type );
		newNode->next = list->first;

		list->first = newNode;
		return;
	}

	ListNode *node = list->first;
	while( 1 < index ) {
		index = index - 1;
		if( NULL == node->next ) {
			node->next = nodeInit();
		}
		node = node->next;
	}
	ListNode *newNode = nodeInit();

	newNode->element = cpyElement( element, list->type );
	newNode->next = node->next;

	node->next = newNode;

}

int main( void ) {

	List *meineListe = listInit( sizeof(char) );

	char *vieleElemente = malloc( sizeof(char) );

	*vieleElemente = 'A';
	add( meineListe, vieleElemente );

	*vieleElemente = 'B';
	add( meineListe, vieleElemente );

	*vieleElemente = 'C';
	add( meineListe, vieleElemente );

	*vieleElemente = 'X';
	insert( meineListe, 1, vieleElemente );

	*vieleElemente = 'Y';
	insert( meineListe, 0, vieleElemente );

	*vieleElemente = 'Z';
	insert( meineListe, 10, vieleElemente );

	return EXIT_SUCCESS;
}
 
Hallo,

den Ansatz find ich persönlich nicht übel. Realisieren könntest du es folgendermaßen:

klist.h:
C:
#ifndef KLIST_H_
#define KLIST_H_

#ifndef NULL
#define NULL (void *)0
#endif

typedef struct ListNode {
  void *element;
  struct ListNode *next;
} ListNode;

typedef struct List {
  int type;
  int count;
  int capacity;
  ListNode *first;
} List;

typedef void (*DestroyListNodeElementUserFunc)(void *element, void *user_data);

void destroyList(List *list, DestroyListNodeElementUserFunc dest_func, void *user_data);
#endif /* KLIST_H_ */

klist.c
C:
#include <klist.h>

void destroyList(List *list, DestroyListNodeElementUserFunc dest_func, void *user_data)
{
  ListNode *next = list->first;
  ListNode *last;

  while (next != NULL) {
      dest_func(next->element, user_data);
      last = next;
      next = next->next;
      last->next = NULL;
      free(last);
  }

Daraufhin könnte der Benutzer deiner Liste dann folgendes machen:
C:
#include <klist.h>

void destroyListNodeElement(void *element, void *user_data)
{
  free(element);
}

int main()
{
  List *l;
  destroyList(l, destroyListNodeElement, NULL);
}

Must es nat. noch austesten ob das alles so korrekt ist, der Ansatz sollte aber passen.

HTH,
RedWing
 
Danke. So was in der Art meinte ich. Ich werde mal schaun, ob ich bei der Zugfahrt moren nach hause genügend Zeit habe, das auszuprobieren. Ich werd dann mal am Wochenende den Erfolg/Misserfolg melden! ^^

Aber gibt es da noch irgendwelche anderen herangehensweisen, auch wie man das machen kann, ohne das der User sich dafür erst eine Fkt zu schreiben? Also, dass ich bei der Remove methode irgendwie die Struktur untersuchen kann, die in der Liste gespeichert ist? Per Reflection, würde ich fast sagen ^^ Aber das gibt es ja hier nicht... ^^
 
Sorry, wenn ich Blödsinn schreibe
hab nicht den ganzen Beitrag durchgelesen...
aber das klingt irgendwie, als bräuchtest du nur einen Destruktor
 
Ich sage mal so, ich komme aus Java und C#. Und da kenne ich es nicht so, dass man sich um den Speicher kümmern muss. Da gibt es ja den Garbage Collector. Den hab ich hier ja nicht.
Aber ich will mir halt mal ein bisschen C anschaun und da halt so viel wie möglich lernen. Und von daher, dachte ich ne Liste wäre nen schickes Bespiel... ^^
Aber Destruktoren gobt es ja nur in der OOP. und da bin ich ja gerade net! ^^
 
Naja. Unter anderem soll es möglich sein darin Strukturen zu speichern. Aber auch andere Typen sollen gespeichert werden können. Also, die Liste soll so dynamisch wie möglich werden.
Für den Fall, dass dort eine einfache Struktur gespeichert ist, oder ein eifacher Datentyp, der keine Verweise hat, habe ich mir überlegt ebenfalls eine Remove Funktion bereit zu stellen, damit der Anwender halt so wenig zu tun hat, wie es geht... ^^
Aber nun noch ne kleine Frage, wie kann ich denn prüfen, dass ein Speicherplatz ordungsgemäß freigegeben wurde? Gibt free() dort einen Wert zurück, der mir sagt, dass alles geklappt hat?
 
Um noch einmal auf den Destruktor zurückzukommen:
wenn du
delete(irgendeinpointer);
schreibst, gibts zwei Fälle:
1: Das irgendetwas ist eine Klasse/Struktur MIT Destruktor, dann wurd der ordentlich
aufgerufen und dann das Objekt freigegeben
2: Das irgendetwas hat KEINEN Destruktor: Es wird nur entfernt

Du kannst also immer delete aufrufen, egal was freigegeben wird.
Also, wenn es um Strukturen/Klassen geht, verwende lieber new/delete satt malloc/free

Zu deiner Frage: Mir fällt jetzt kein realer Fehlergrund für free/delete ein;
einmal angenommen dass du das, was du übergibst, auch wirklich zuerst mit new/malloc angelegt hast.
Wenn doch ein Fehler ist, hat das OS vermutlich ein weiter reichendes Problem :-)

Gruß
 
Zuletzt bearbeitet:
Hmm, naja. Ich befinde mich in C und da habe ich doch keine Destruktoren. Da muss ich doch mit free/malloc arbeiten. Zumindest habe ich mich auf der Zugfahrt mal hin gesetzt und das mit dem Funktionszeiger programmiert. Es scheint auch alles gut zu klappen. hier nun mal meine beiden Dateien. http://phpfi.com/360770?lang=c
http://phpfi.com/360771?lang=c
Dann werde ich mal weiter machen. Mal schaun, wie es denn noch wird.
Aber ich danke euch für eure hilfe... ^^
 
Zurück