Generelle Operationen Die VDM-Basistypen Vorbemerkungen

Speicherverwaltung

 

Pointer Die Verwaltung dynamischer Datenstrukturen geschieht mit Hilfe von Pointern, das sind ,,Zeiger`` auf die Objekte in Form von Adressen. In C haben Sie die Möglichkeit, viel mit Pointern zu arbeiten; aber Pointer-Operationen sind sehr fehlerträchtig und erfordern einen sorgfältigen Test. Das VDM-System erleichtert Ihnen die Programmierung ungemein, da die Verwaltungsfunktionen für dynamische Datenstrukturen in der VDM-Bibliothek enthalten sind. Sie vereinbaren eine Variable des gewünschten Typs und brauchen sich nicht mit den Pointern zu beschäftigen -- VDM macht das für Sie. Die Funktionen sind sorgfältig getestet, so daß Sie sich auf deren Korrektheit verlassen können.

Problem! Pointer haben jedoch einen schwer zu umgehenden Nachteil: eine Operation kann, wenn Sie einen Pointer auf ein Objekt als Argument erhält, dieses Objekt verändern. Das ist in vielen Fällen aber nicht erwünscht; meist soll die Operation ein neues, verändertes Objekt liefern, ohne daß das alte Objekt dauerhaft geändert wird.

Lösung!? Ein möglicher Ausweg: in jeder Operation der VDM-Bibliothek wird eine vollständige Kopie des Objekts erzeugt und bearbeitet, während das ,,Original`` unberührt bleibt. Am Ende der Bearbeitung wird diese Kopie, wenn nötig, wieder gelöscht. Das ist einfach, hat aber einen großen Nachteil: der Zeitaufwand jeder kleinen Operation, die schreibend auf ein Objekt zugreift, wird unverhältnismäßig hoch, die Performance des gesamten Programms wird unnötig schlecht; denn manchmal darf das Objekt ruhig verändert werden, weil Sie das Original später nie wieder benötigen -- in diesem Falle sollte das Objekt vollständig gelöscht werden, um keinen kostbaren Speicher zu verschwenden.

Lösung!! Die Operationen, die ihr Argument verändern, sorgen dafür, daß das Original dabei ,,zerstört`` wird, d.h. daß es danach keinen definierten Inhalt hat und auch keinen Speicherplatz mehr belegt. Auch diese Lösung hat einen Nachteil: wenn Sie das Objekt nach dem Aufruf einer solchen Operation noch weiter benötigen, sind Sie gezwungen, vorher eine Kopie davon anzufertigen. VDM stellt dafür zwei Operationen zur Verfügung:

Die Abb. 5.1 stellt die Wirkung der beiden Kopierbefehle grafisch dar.

  figure1255
Abbildung 5.1: Wirkung der Operationen copy1 und copy 

Zerstörung? Wenn eine Operation ein Objekt ,,zerstört`` (in der Befehlsreferenz im Anhang B ist das jeweils angegeben), stellt sie sicher, daß dessen Speicherplatz vorher freigegeben wird. Ein Zugriff nach der ,,Zerstörung`` führt allerdings zu unvorhersagbaren Ergebnissen -- meist erzeugt er einen Laufzeitfehler.

Löschen Nachdem Sie eine Kopie eines Objekts angelegt und eine solche ,,zerstörende`` Operation (vorzugsweise natürlich mit der Kopie!) verwendet haben, brauchen Sie sich danach nicht mehr um diese Kopie zu kümmern -- sie belegt dann ja keinen Speicher mehr. Schreiben Sie aber selbst eine solche zerstörende Funktion, müssen Sie auch dafür sorgen, daß das Objekt seinen Speicherplatz wirklich komplett wieder freigibt (z.B. häufig erforderlich bei rekursiven Funktionen, wie im Beispiel auf S. 5.4.4 im Abschnitt über Sets zu sehen). Dafür gibt es komplementäre Operationen:

Versuchen Sie nie, ein durch eine Operation zerstörtes Objekt ,,sicherheitshalber`` noch mit del oder del1 zu löschen!

Operationen mit impliziten Löschaktionen

 

Es gibt Operationen für strukturierte Objekte, bei denen Elemente aus den Objekten entfernt werden, z.B. die Tuple-Operation tl, die das Ende eines Tuple ohne das erste Element liefert. Alle diese Operationen zerstören das Objekt, das sie manipulieren. Ein zusätzliches Problem ist dabei aber, daß Element-Objekte (Sub-Strukturen) danach eventuell keine Zuordnung mehr zu einem strukturierten Objekt haben, z.B. das erste Tuple-Element nach einer tl-Operation. Häufig wird vor einer solchen Operation das von der Löschung betroffene Element referenziert, d.h. es wird ein Objekt vom Elementtyp eingerichtet, das dieses Element bezeichnet; z.B. wird durch die Tuple-Operation hd ein Verweis auf das erste Element eingerichtet. Wenn ein solches Objekt allerdings nicht mehr anderweitig referenziert ist, sollte es zerstört werden, um keinen Speicherplatz zu verschwenden.

Daher gibt es zu allen diesen Operationen ein Pendant, das die ausgegliederten Elemente implizit löscht; ihr Name entspricht der ,,normalen`` Variante, nur der Buchstabe ,,d`` wird angefügt -- z.B. wird aus tl dann tld.

Beispiel: Sie möchten in einem Tuple der Reihe nach das jeweils erste Element-Tuple löschen, bis es weniger als vier Elemente (deren Typ hier egal ist) enthält. Da Sie die gelöschten Elemente nicht mehr benötigen, verwenden Sie die Operation tld:
* Tup = Elem tex2html_wrap_inline6338
* tex2html_wrap_inline6814
* Tup t;
* tex2html_wrap_inline6814
* while (len_tup(t) > 3)
* t = tld_tup(t);
*[2ex]

Auch Operationen, die auf den ersten Blick keine Elemente aus strukturierten Objekten entfernen, können von dieser Regelung betroffen sein: bei der Vereinigung von Sets z.B. wird ein neues strukturiertes Objekt geschaffen -- da aber Elemente, die in beiden Sets vorkommen, nur einmal in die Vereinigung aufgenommen werden, wird jeweils eines dabei überflüssig und kann implizit gelöscht werden.

Ausnahmen Wann dürfen Sie das implizite Löschen nicht verwenden ? Wenn Sie selbst eine Funktion schreiben, die ihr Argument zerstört, und Sie die (strukturierten) Objekte, auf die Sie diese Funktion anwenden wollen, mit copy1 kopiert haben, aber nicht dessen Sub-Strukturen, würden Sie mit implizitem Löschen die Sub-Strukturen des Originals vernichten -- das Original wäre inkonsistent.

Faustregel Gibt es in Ihrem Programm irgendwo noch ein Objekt, das auf von einem impliziten Löschen betroffene Elemente verweist, sollten Sie die ,,Standardversion`` ohne implizites Löschen verwenden.

Diese Operationen sind betroffen: