Linked Liste, Speicherverwaltungsfehler



  • Hallo Zusammen,

    was macht man eigentlich gerne falsch, wenn man eine dynamische Liste mittels malloc und free verwaltet.

    Ich habe derzeit noch das Problem, das ich zwei Einträge am Anfang der Liste haben, die den Name (null) und Leerstring haben. Jedoch wird durch das Hauptprogramm sichergestellt, das ein Leerstring nicht vorkommt.

    Wenn ich die Liste nicht fülle, bekomme ich trotzdem ein Element gemeldet ? 😞 Ich kann derzeit nicht erkennen woher dieser Eintrag kommt.

    Derzeigt habe ich den Effekt, das wenn ich mein Programm zweimal starte, des bereits Fehler in der Liste. Wenn ich es dann nochmals starte. stürzt es sogar ab. 😮

    Mit der folgenden Funktion füge ich Elemente ein

    void	PitbullyIOBuffer::AddItem(char* VariablenName)
    {
    IOElement   *Pointer;								// Listen Pointer
    
     Pointer=MainListe;								// Anfang der Liste holen
     while (Pointer->NextElement != NULL)
      	{
     	if ( strncmp(Pointer->Name,VariablenName,strlen(VariablenName)) == 0 )  // Var gefunden
         	{
          return;										// raus hier, die gibt es schon
          }
       else
        	{
          Pointer=Pointer->NextElement;			// Auf zum naechsten Objekt
          }
       };
    
      ActualElement = (IOElement *) malloc(sizeof(IOElement));
    
      Pointer->NextElement=ActualElement;
      ActualElement->LastElement=Pointer;
      ActualElement->NextElement=NULL;
    
      strcpy( ActualElement->Name, VariablenName );
      ActualElement->UpdateRequest=0;
      ActualElement->ValueChanged=false;
      ActualElement->Locked=false;
      ActualElement->Enabled=true;
      ActualElement->LastScan=0;
      ActualElement->NextWriteValue=0;
    }
    

    Und mit dieser Funktion soll der Speicher wieder freigegeben werden.

    PitbullyIOBuffer::~PitbullyIOBuffer()
    {
    IOElement   *Pointer;								// Listen Pointer
    
    	AnzahlDerInstanzen--;							// Anzahl der geoeffneten Objekte
       														// verringern
       if (AnzahlDerInstanzen <= 0 )					// Das letzte Objekt wird beendet
       	{
          printf("speicher befreien start\n");
          Pointer=MainListe;							// Anfang der Liste holen
          while ( Pointer->NextElement != NULL)		// Suche den letzten Eintrag
    			Pointer=Pointer->NextElement;			// Weiter bis zum Ende
    
          printf("Speicher befreien letzten Element gefunden %s\n", Pointer->Name);
    
          while (Pointer->LastElement != NULL)	// Jetzt Rueckwaerts
          	{
             printf("Entferne Element %s\n", Pointer->Name );
             Pointer=Pointer->LastElement;			// Pointer auf vorheriges Objekt
             free(Pointer->NextElement);			// letztes Objekt entfernen
             }
    		free(Pointer);								  	// Root Element entfernen
          printf("Speicher befreien fertig");
          }
    }
    

    Hat jemand eine Idee ?

    Gruss



  • so sehe ich erstmal keinen fehler, aber 2 dinge:
    1.

    was macht man eigentlich gerne falsch, wenn man eine dynamische Liste mittels malloc und free verwaltet.

    man benutzt kein malloc und free, man nimmt new.

    die einrückung ist hoffentlich im programm nicht genauso.



  • Warum immer mindestens ein Element da ist, kann man aus den beiden Funktionen nicht sehen. Sehe daran auch nichts, was zum Absturz führen sollte, aber:
    Was spricht gegen std::list?
    Der Code ist eigentlich reines C, mit der Ausnahme, daß du alles in eine Klasse gepackt hast. Freispeicher fordert man in C++ mit new an und gibt ihn mit delete wieder frei. C++ hat eigene Casts, den C-Cast sollte man vermeiden.

    if ( strncmp(Pointer->Name,VariablenName,strlen(VariablenName)) == 0 )
    

    Ist nicht sehr effizient, weil in der Schleife strlen ausgeführt wird. Wenn du VariablenName zudem als const std::string & übergibst, kannst du auch lesbarer schreiben:

    if (VariablenName == Pointer->Name)
    

    Naja, nur so'n paar Anmerkungen. Für deine Fehler bräuchte man wohl den gesamten Code der Klasse, aber vielleicht sieht ja auch jemand mehr als ich. 🙂



  • Huch, da fällt mir doch was in der addItem-Funktion auf:

    strcpy( ActualElement->Name, VariablenName );
    

    Du mußt für ActualElement->Name natürlich Speicher reservieren, bevor du was dorthin kopieren kannst. Benutz also am besten die STL-Container, dann bleibt dir sowas erspart.



  • ne die std::list hat keine namen/wert abhängigkeiten, wie sie hier verlangt werden.

    @Ritchie dann benutz lieber std::string anstatt char* damit ist der code gleich viel schöner und weniger fehleranfällig. oder benutz gleich eine std::map, die hat eine schlüssel/wert speicherung der daten



  • otze schrieb:

    ne die std::list hat keine namen/wert abhängigkeiten, wie sie hier verlangt werden.

    Du kannst aber eine Liste von Paaren nutzen, beides ist in der STL.



  • Hallo Leute,

    erstmal danke für die zahlreichen antworten.

    Bei einigen Antworten muss ich mir jedoch erst das entsprechende Wissen nochmals reinziehen.
    Man merkt wohl, das ich führ mehr C Programm geschrieben habe und ausser die üblichen C++ Lernprogramme, dies mein erstes wirkliches C++ Programm wird.

    Ich nehme daher die Kritik gerne an. Insbesondere das mit dem New und delete stimmt.

    Hier noch das Interface und die Struktur von der Liste

    typedef struct tag_IOElement IOElement;
    
    struct  	tag_IOElement {
             unsigned char	I2CAdresse;				// I2C Adresse 0=Memory Var
    			unsigned char	Enabled;    			// Ist diese Variable aktiv
             unsigned char	Locked;              // Ist eine Sperre auf diese Var.
             char	Name[10];							// Name der Variable
             long	InitValue;							// Startwert der Variable
             unsigned char	ScanRate;            // Abtastrate in 100 milli Sek.
             long	Value;                        // Aktueller Wert
             float	LastScan;                     // mSek. seit dem letzten Scan
             unsigned char	Type;                // Datentyp 0=Byte 1=Word 2=Long
             unsigned char	UpdateRequest;       // Wert neu einlesen
             unsigned char	IOAddress;           // I/O Adresse des I2C Slave
             unsigned char	ValueChanged;        // Mitteilung, das Wert geändert hat
             long	NextWriteValue;               // Schreibwert beim naechsten Zyk.
    		  	IOElement	*LastElement;				// Vorheriges Element in der Liste
             IOElement   *NextElement;				// Zeiger auf das naechste Element
    };
    
    // ----------------------------------------------------------------------------
    // 					Interface des IO Buffer Objektes
    // ----------------------------------------------------------------------------
    
    class PitbullyIOBuffer
    {
    private:													// Siehe jede Instanz nur
         	IOElement	*ActualElement;				// Actives Element der Instanz
    
    protected:                                	// Sehen alle Instanzen gleich
    
       	int			AnzahlDerInstanzen;			// Anzahl der Objekte
         	IOElement	*MainListe;						// HauptListe
    
    public:
    
       	PitbullyIOBuffer(void);                // Konstruktor
          ~PitbullyIOBuffer(void);               // Destruktor
    
          void	SetVariableName(char*);				// Setze den Namen der Var.
          char*	GetVariableName(void);				// Ermittle den Variblen Name
    
          // Setze/frage nach Eigenschaften der Verbindung
    
          void	SetEnableStatus(unsigned char);	// Freigabe der Var. steuern
          int	SetLockStatus(unsigned char);		// Sperrung der Var. setzen
    
          int	IsEabled(void);						// Ist die Variable freigegeben
          int   IsLocked(void);                  // Ist die Variable gesperrt
          int	IsNew(void);							// Hat sich der Wert geaendert
    
          void	MoveFirst(void);      				// Browse Funktionen
          void	MoveNext(void);
          void	MoveLast(void);
          int	EndOfFile( void );
          int	BottomOfFile( void );
    
          //  Informationsfelder fuer die Erstellung einer Verbindung
    
          void	SetScanRate(unsigned char);		// Scanrate festlegen
    		void	SetIOSize( unsigned char );		// Groesse der Variable
          void  SetI2CAdress(unsigned char);		// Setze die aktive I2C Adresse
          void	SetIOAdress(unsigned char);      // Zieladresse des Slave
          void	SetInitValue(long);					// Basis Wert, bis eingelesen
    
       	void	AddItem(char*);                   // Fuege eine neues Element hinzu
          void	RemoveItem(void);                // Entfernt einen Block
    
    		// Memberroutinen, welche Übertragungsfunktionen ausführen
    
          void	DoEvent(void);							// Ausstehende Events durchführen.
          long	GetValue(void);						// Wert ermitteln
          long	SetValue(void);                   // Wert setzen
    };
    

    Reserviert man unter C++ noch Speicher in der Art String[10] ? 😕

    Ach ja, der Quellcode sieht bei mir auf dem Bildschirm etwas besser eingerückt aus.

    Gruss



  • Ach ja,

    nur zur Info. Der Compiler ist schon etwas älter, da ich unbeding 80186 Code benötigt.

    So, ich habe den Fehler gefunden. In der InitRoutine des Klasse habe ich vergessen im Ursprungszeiger das Link-Element zu setzen.

    Danach noch bei dem ersten Additem das Root Element vermerken. (zeiger anpassen).

    Gruss



  • otze schrieb:

    ne die std::list hat keine namen/wert abhängigkeiten, wie sie hier verlangt werden.

    Hö? Ich seh nur, daß er beim Einfügen schaut, ob bereits ein Element dieses Namens existiert. Das kann er doch genauso mit std::list und find() aus <algorithm> machen...



  • Hi Cocaine,

    wie geht das den mit std::list und find() ?

    Diese Funktion kenne ich noch nicht.

    Gruss



  • @cocaine schau dir mal an wie das datenobjekt ausschaut welches er benutzt, und dann sag mir, ob ein find funktionieren würd 🙄.

    wenn dann std::find_if.

    std::find_if durchsucht die liste mithilfe einer funktion die man bestimmen kann.
    mehr dazu gibts hier:
    http://www.sgi.com/tech/stl/find_if.html

    und am besten immernoch die gute alte map, dann braucht man nämlich keinen algorithmus zu benutzen und hat weniger schreibarbeit.(und in der geschwindigkeit nimmt sich das auch nicht viel)



  • otze schrieb:

    @cocaine schau dir mal an wie das datenobjekt ausschaut welches er benutzt, und dann sag mir, ob ein find funktionieren würd 🙄.

    wenn dann std::find_if.

    Hast recht. Dachte an nen operator==, aber mit dem geht's ja nur, wenn man die Elemente nicht als Pointer in die Liste legt.

    Ich schreib hier trotzdem mal ein Beispiel, damit Ritchie sieht, wie man std::find benutzen kann (ich geb auch gerne zu, daß ich erst ausprobiert hab, bevor ich dir Recht geben konnte 😉 ):

    #include <iostream>
    #include <list>
    #include <string>
    #include <algorithm>
    #include <cassert>
    
    struct MyElement
    {
    	MyElement(const char *new_name)
    		: name_(new_name)		{ }
    	MyElement(const MyElement &other)
    		: name_(other.name_)	{ }
    
    	bool operator==(const char *other)
    	{
    		return name_ == other;
    	}
    
    	std::string	name_;
    };
    
    int main()
    {
    	std::list<MyElement> lst;
    
    	lst.push_back(MyElement("Das"));
    	lst.push_back(MyElement("geht"));
    	lst.push_back(MyElement("doch"));
    	lst.push_back(MyElement("oder?"));
    
    	std::list<MyElement>::iterator it = std::find(lst.begin(), lst.end(), "geht");
    	std::cout << it->name_ << std::endl;
    
    	it = std::find(lst.begin(), lst.end(), "nioht?");
    	assert(it == lst.end());
    
    	return 0;
    }
    

    otze schrieb:

    und am besten immernoch die gute alte map, dann braucht man nämlich keinen algorithmus zu benutzen und hat weniger schreibarbeit.(und in der geschwindigkeit nimmt sich das auch nicht viel)

    Dazu müßte man aber den Namen aus dem Objekt ziehen (also als Index der Map benutzen), oder?



  • ja, würde aber keinen unterschied mehr machen 🙂



  • Hallo Leute,

    ich für zwei Tage Offline und konnte daher nicht antworten. Ist sonst nicht meiner Art. Ich werde mir gleich mal diese Funktion ansehen.

    Scheint ja auf den ersten Blick übersichtlicher zu sein.

    Muss ich aber erst mal verdauen. 😉

    Gruss



  • Tja,

    in meinem C++ gibt es keine cassert. 😮

    Was nun ?

    Gruss



  • Das von mir da oben ist nur ein Anwendungsbeispiel für std::find - nicht so nachbauen, sonst wird, sobald in der std::list ein Copy-Ctor aufgerufen wird, jedesmal der String umkopiert (und das passiert nicht selten).



  • Hi Cocaine,

    der Compiler hat mit der Zeile

    #include <cassert>

    ein Problem.

    Ist es kein lauffähiges Beispiel?

    Ich schreib hier trotzdem mal ein Beispiel, damit ... sieht, ...

    Gruss





  • Hab dich doch nicht paepstlicher als der Papst. Anscheinend ist es ein veralteter Compiler, der cassert nicht kennt. In dem Fall einfach wie gehabt assert.h verwenden.



  • Hallo bashar,

    danke für den Tip.

    Ich kenne leider nicht die ganze Ersetzungen und wusste nicht, das man hier assert.h verwenden muss.

    Gruss



  • Mein Beitrag war eigentlich an Lars gerichtet, aber der hat sich inzwischen wegeditiert.


Anmelden zum Antworten