Fehlermeldung: Zugriffsverletzung beim Schreiben an Position [Adresse]



  • Hallo Leute,

    ich hoffe ihr könnt mir bei einem Problemchen helfen. Ich quäle mich gerade im Studium mit Compilerbau herum und während wir grad so dabei sind unsere Aufgabe zu lösen stoßen wir mit unserem bisherigen Compiler auf oben genannte Fehlermeldung.
    Ein großes Problem ist dass wir bei unserer Arbeit viele Datenstrukturen selber bauen müssen und somit das Fehlerpotential krass ansteigt. Der Fehler wird nämlich aufgrund folgendem ausgelöst:

    Während der Initialisierung unseres Automaten (unwichtig) wird folgende Methode unserer eigenen LinkedList Klasse aufgerufen:

    void LinkedLists::addFirst(StateObject* value) {
    	Element eO(value);									
    	Element* e = &eO;
    	if (anzahlElemente == 0 | anzahlElemente == 1) {
    		add2(0, value);
    	} else {
    		startElement->setVorgaenger(e);  // <------Hier
    		e->setNachfolger(startElement);  // startElement ist ein
    		startElement = e;                // Element *
    		anzahlElemente++;
    	}
    }
    

    Sie soll einfach nur ein Objekt an die erste Stelle der Liste hängen. (In unserem Fall ist die Liste vorher leer, deswegen tritt der erste Fall der IF ein)

    und in dieser Methode wiederrum wird die Methode "setVorgaenger(e)" benutzt,
    welche nun den Fehler auslöst:

    void Element::setVorgaenger(Element* e){
    	this->vorgaenger = e;  // Hier scheint der Wurm drin zu stecken?
    }
    

    Bei erwähnter Zuweisung kommt dieser Zugriffserror und es geht nicht weiter. Kann es sein dass ich hier Pointer-Pointer definieren muss? Oder was könnte das Problem sein? (Kann auch bei bedarf gerne die kompletten Klassen zeigen).

    Grüße 🙂


  • Mod

    Der Wurm liegt hoechstwahrscheinlich ganz woanders. Es aeussert sich bloss an dieser Stelle, weil hier ben auf irgendwas zugegriffen wird, was vorher falsch gelaufen ist. Beispielsweise Zeilen 4-6: Warum | statt ||? Warum ueberhaupt eine Sonderbehandlung fuer den Fall, dass nur ein Element vorhanden ist? Und was macht das add2?

    Vorschlaege zu moeglichen Vorgehensweisen:
    -Weiter wild rumraten; betrifft sowohl uns als auch dich
    -Ein Debugger oder Programme wie valgrind koennen immens hilfreich sein, wenn du halbwegs weisst, was du tust.
    -Du versuchst ein vollstaendiges, minimales Beispielprogramm zu erstellen. Wahrscheinlich findest du dabei selber den Fehler (sofern du dir Muehe gibst), ansonsten jemand hier im Forum.



  • dein

    Element eO(value);
    Element* e = &eO;

    dein eO ist eine variable im Kontext von LinkedLists::addFirst
    die ist nicht mehr gültig/vorhanden wenn die Routine ausgeführt wurde
    damit zeigt this->vorgaenger = e auf ungültigen/anderweitig belegten Speicher



  • Hallo ihr Beiden,

    vielen Dank zunächst für eure Posts und entschuldigt dass ich mich erst so
    spät melden kann. Der Fehler hat sich von der Stelle nun ein paar Zeilen weiter im Code verfrachtet nachdem ich paar Kleinigkeiten abgeändert habe. Ich probiere mal analog weiter und wenn ich mich wieder 1-2 Tage an einem Bug festhänge melde ich mich wieder 🙄

    Wie gesagt trotzdem Danke für eure Denkanstöße 🙂 Ich werde mich mal etwas mit Valgrind außereinandersetzen.

    €dit1: Ich sehe gerade dass dieses Valgrind nur für Linux verfügbar ist? Welche alternativen für Windows-Entwicklung sind gut? (MS Visual Studio Ultimate 2013)

    Schönen Abend noch!



  • x0r3 schrieb:

    €dit1: Ich sehe gerade dass dieses Valgrind nur für Linux verfügbar ist? Welche alternativen für Windows-Entwicklung sind gut? (MS Visual Studio Ultimate 2013)

    Ich halte zwar nix von so tools, aber clang tönt recht nett.
    http://www.youtube.com/watch?v=JSjoCisIHcM&index=9&list=PLQR34fJnvoYSsyoa-ZqBSXqDWV6QSDtjb



  • dein Fehler ist maximal schlecht - wenn du den nicht sauber korrigierst läuft dein Programm IMMER irgendwie komisch/nicht wie es soll usw.

    du DARFST einfach keine Zeiger auf lokale Variablen (Stack) aus der Funktion heraustragen

    wenn du den Fehler nicht verstehst werden dir Valgrind(und andere) auch nur bedingt helfen



  • Gast3 schrieb:

    dein Fehler ist maximal schlecht - wenn du den nicht sauber korrigierst läuft dein Programm IMMER irgendwie komisch/nicht wie es soll usw.

    du DARFST einfach keine Zeiger auf lokale Variablen (Stack) aus der Funktion heraustragen

    wenn du den Fehler nicht verstehst werden dir Valgrind(und andere) auch nur bedingt helfen

    Hi, um nochmal auf dein Hinweis zurückzukommen:

    Das heißt also ich darf innerhalb von Methoden nicht Pointer auf Objekte welche
    nur im Scope der Methode liegen verweisen? Welche alternative gibt es dafür? Und wie steht es da mit den Zuweisungen weiter unten, Pointer zu Pointer ist in Ordnung?

    Und die für mich nach wie vor wichtigste Frage: Wie ist das mit der Nullreferenz in C++? In Java kann man ja bei der Implementierung von Datenstrukturen bzw. gerade Collections problemlos die Nullreferenz (NULL) benutzen um z.B. das Ende einer Datenstruktur abzufragen (ähnlich dem \0 - Operator). Kann ich mich drauf verlassen dass dieses DEFINE NULL 0 ausreicht um NULL gleich wie in Java zu benutzen?

    Sorry bin echt eine C++ Pfeife, vor allem sobald Pointer ins Spiel kommen. Die LinkedList haben wir auch in Java geschrieben und ich habe versucht die Referenzen durch Pointer zu realisieren und hat auch halbwegs gut funktioniert wenn man bedenkt welcher Großteil des Programms läuft 😃

    Grüße


  • Mod

    Komisch. Sonst schmeißen ehemalige Java-Programmierer wie verrückt mit new um sich, aber hier, wo man mal wirklich ein new benötigt, verweist du stattdessen auf lokale Variablen. In C++ sind (automatische) Variablen keine Referenzen, sondern sie sind das Objekt selbst. Und es gibt auch keinen Garbage Collector, der sie irgendwann wegräumt, wenn sich nichts mehr auf sie bezieht, sondern sie werden automatisch zerstört (daher der Name), sobald ihr Gültigkeitsbereich endet. Im Fall des eO ist das also das Ende der Funktion addFirst. Ein Zeiger auf diese Variable zeigt nach Ende der Funktion auf ein Objekt, das nicht mehr gültig ist.

    Ein mit new erzeugtes Objekt wird hingegen erst dann und nur dann zerstört, wenn ein passendes delete erfolgt. Es lebt ganz woanders als die automatischen Variablen. Ein Zeiger, der auf ein solches Objekt zeigt und seinen eigenen Gültigkeitsbereich verlässt, zerstört das referenzierte Objekt nicht (außer es handelt sich um einen entsprechenden Smartpointer, die sind genau dazu da, dass man ein delete nicht vergessen (oder noch schlimmer: doppelt ausführen) kann).

    Bevor du aber new (oder meinetwegen einen allgemeinen Allokator) benutzt, stell unbedingt sicher, dass du RAII verstanden hast. Höchstwahrscheinlich ist es Teil der Aufgabe, dies zu lernen. Oder benutz einen passenden Smartpointer um das neue Objekt zu referenzieren (Smartpointer implementieren eine Version von RAII), vorzugsweise mit dem zugehörigen make_XXX anstatt new zur Erzeugung; das geht aber eventuell an der Intention der Aufgabenstellung vorbei.

    PS: Trollen am besten gar nicht antworten. Irgendwann wird sich ein Moderator ihrer annehmen. So wie jetzt 🙂 .


  • Mod

    x0r3 schrieb:

    Und die für mich nach wie vor wichtigste Frage: Wie ist das mit der Nullreferenz in C++? In Java kann man ja bei der Implementierung von Datenstrukturen bzw. gerade Collections problemlos die Nullreferenz (NULL) benutzen um z.B. das Ende einer Datenstruktur abzufragen (ähnlich dem \0 - Operator). Kann ich mich drauf verlassen dass dieses DEFINE NULL 0 ausreicht um NULL gleich wie in Java zu benutzen?

    Nullreferenzen gibt es in C++ nicht (oder nur als ungewollte Sprachvergewaltigung). C++ unterscheidet zwischen Referenzen und Zeigern. Referenzen sind in C++ so eine Art Zeiger (direkt in die Sprache eingebaut, das sind die Dinger mit dem '&'), die aber nie 0 sein können.

    Nullzeiger gehen in C++ wie in Java sonst auch und du kannst und solltest die gleichen Dinge damit tun. Das heißt: Ja, sie sind sehr nützlich beim Aufbau einer Listenstruktur, beispielsweise um das Ende zu markieren. Wenn du mit deinem "DEFINE NULL 0" das entsprechende Makro aus der Standardbibliothek meinst: Das ist C. In C++ gibt's das zwar auch, aber es ist wirklich nur ein define für "0". das heißt n C++ kannst du für einen Nullzeiger direkt 0 benutzen und es ist auch üblich, dies so zu tun. Oder aber, ab C++11, nullptr. Hier sollte es keinen Unterschied machen, welches von beiden du benutzt, aber es kann prinzipiell nicht schaden, nullptr zu benutzen. Der Unterschied zwischen den beiden:
    -0 ist ein Integer, mit der Sondereigenschaft, dass diese eine Integerkonstante ausnahmsweise in jede Art von Zeiger konvertierbar ist (ansonsten sind Integer nicht in Zeiger konvertierbar, ohne einen passenden Cast).
    -nullptr hat hingegen den Typ nullptr_t, der auch in jeden Zeigertyp konvertierbar ist, aber eben nicht als Integer zählt.
    Der Unterschied kann (und wird!) beispielsweise wichtig sein, wenn man mehrere Überladungen einer Funktion hat, einmal für Ganzzahlen und einmal für Pointer.



  • SeppJ schrieb:

    Komisch. Sonst schmeißen ehemalige Java-Programmierer wie verrückt mit new um sich, aber hier, wo man mal wirklich ein new benötigt, verweist du stattdessen auf lokale Variablen. In C++ sind (automatische) Variablen keine Referenzen, sondern sie sind das Objekt selbst. Und es gibt auch keinen Garbage Collector, der sie irgendwann wegräumt, wenn sich nichts mehr auf sie bezieht, sondern sie werden automatisch zerstört (daher der Name), sobald ihr Gültigkeitsbereich endet. Im Fall des eO ist das also das Ende der Funktion addFirst. Ein Zeiger auf diese Variable zeigt nach Ende der Funktion auf ein Objekt, das nicht mehr gültig ist.

    Ein mit new erzeugtes Objekt wird hingegen erst dann und nur dann zerstört, wenn ein passendes delete erfolgt. Es lebt ganz woanders als die automatischen Variablen. Ein Zeiger, der auf ein solches Objekt zeigt und seinen eigenen Gültigkeitsbereich verlässt, zerstört das referenzierte Objekt nicht (außer es handelt sich um einen entsprechenden Smartpointer, die sind genau dazu da, dass man ein delete nicht vergessen (oder noch schlimmer: doppelt ausführen) kann).

    Bevor du aber new (oder meinetwegen einen allgemeinen Allokator) benutzt, stell unbedingt sicher, dass du RAII verstanden hast. Höchstwahrscheinlich ist es Teil der Aufgabe, dies zu lernen. Oder benutz einen passenden Smartpointer um das neue Objekt zu referenzieren (Smartpointer implementieren eine Version von RAII), vorzugsweise mit dem zugehörigen make_XXX anstatt new zur Erzeugung; das geht aber eventuell an der Intention der Aufgabenstellung vorbei.

    PS: Trollen am besten gar nicht antworten. Irgendwann wird sich ein Moderator ihrer annehmen. So wie jetzt 🙂

    Nullreferenzen gibt es in C++ nicht (oder nur als ungewollte Sprachvergewaltigung). C++ unterscheidet zwischen Referenzen und Zeigern. Referenzen sind in C++ so eine Art Zeiger (direkt in die Sprache eingebaut, das sind die Dinger mit dem '&'), die aber nie 0 sein können.

    Nullzeiger gehen in C++ wie in Java sonst auch und du kannst und solltest die gleichen Dinge damit tun. Das heißt: Ja, sie sind sehr nützlich beim Aufbau einer Listenstruktur, beispielsweise um das Ende zu markieren. Wenn du mit deinem "DEFINE NULL 0" das entsprechende Makro aus der Standardbibliothek meinst: Das ist C. In C++ gibt's das zwar auch, aber es ist wirklich nur ein define für "0". das heißt n C++ kannst du für einen Nullzeiger direkt 0 benutzen und es ist auch üblich, dies so zu tun. Oder aber, ab C++11, nullptr. Hier sollte es keinen Unterschied machen, welches von beiden du benutzt, aber es kann prinzipiell nicht schaden, nullptr zu benutzen. Der Unterschied zwischen den beiden:
    -0 ist ein Integer, mit der Sondereigenschaft, dass diese eine Integerkonstante ausnahmsweise in jede Art von Zeiger konvertierbar ist (ansonsten sind Integer nicht in Zeiger konvertierbar, ohne einen passenden Cast).
    -nullptr hat hingegen den Typ nullptr_t, der auch in jeden Zeigertyp konvertierbar ist, aber eben nicht als Integer zählt.
    Der Unterschied kann (und wird!) beispielsweise wichtig sein, wenn man mehrere Überladungen einer Funktion hat, einmal für Ganzzahlen und einmal für Pointer..

    Super, Vielen Dank für diese detaillierte und verständliche Erklärung, hättest echt nicht besser zusammenfassen können 😋 Denke ich bin der C++ Geschichte nun ein Stückchen näher. Gerade wegen dem fehlenden GC habe ich versucht zu vermeiden mit new um mich zu schmeißen weil unser Prof was dynamische Speicherverwaltung angeht seeeeehr penibel ist 😞 Und das schon öfters großen Terz gab. RAII ist bestimmt dieses programmierparadigma für intelligente Speicherverwaltung. Dies hätten wir eig im zweiten Semester lernen sollen als wir String implementieren mussten 😃 Habe es ja eigentlich auch prinzipiell verstanden aber praktisch zu wenig Erfahrung damit gesammelt bisher.

    Wirklich vielen Dank, "Java vs C++ in a Nutshell" taufe ich deine 2 Posts 🙂

    Grüße


  • Mod

    Ja, RAII ist dieses Paradigma für manuelle Speicherverwaltung. Aber nicht nur dafür, sondern für alle Arten von Ressourcen. Seien es Dateien, Sockets, Handler für "Objekte" in irgendwelchen C-Bibliotheken, was auch immer. Ungeheuer wichtig und nützlich. Wobei man aber meistens relativ wenig Kontakt mit der konkreten Umsetzung hat, da es alle wichtigen Arten von Ressourcen bereits irgendwo in einer Bibliothek in einem Container gibt, der RAII umsetzt. Außer man hat eben als Aufgabenstellung, dass man diese Art von Container selbst schreiben soll.
    Viele hier im Forum würden sicher sagen, dass RAII der Grund für C++ gegenüber Java ist, oder mindestens unter den Top 3 Gründen. Wenn man RAII konsequent umsetzt, dann können keine Ressourcenlecks auftreten. Aber im Gegensatz zu einem GC weiß man genau, wann ein Objekt zerstört wird und man kann jede Art von Ressource mit RAII kapseln, wohingegen ein GC nur Speicher verwaltet.

    Bei Google solltest du mit der Abkürzung ganz gut Erfolg haben, sie ist recht eindeutig. Ansonsten eben RAII und C++ zusammen. Oder Resource Acquisition is Initialization. Auch nützlich könnten sein:
    -"Regel der großen Drei" bzw. "rule of three" oder "Dreierregel" oder ähnliches, eventuell in Kombination mit "C++". Eine Regel, die man bei der Umsetzung von RAII beachten sollte.
    -Neuerdings nennt man das auch "Regel der großen Fünf", da in C++11 ein paar Punkte dazu gekommen sind.
    -"Copy-and-Swap Idiom". Auch hilfreich bei der Umsetzung.
    -"Exception safety", "Ausnahmesicherheit" (das ist kein "Schutz" vor Exceptions!). Erklärung, warum man Copy&Swap macht. Führt aber ein bisschen weit vom Thema RAII. Es gibt dazu auch ein paar Artikel hier im Magazin:
    http://www.c-plusplus.net/forum/219864
    http://www.c-plusplus.net/forum/219865 (Hier wird unter anderem die Technik hinter Exceptions und automatischen Variablen erklärt! Also im Prinzip, warum RAII funktioniert.)


Anmelden zum Antworten