8 Zugriffstypen ( Zeiger ) 1. Zugriffstypen, die auf Daten in einem Storage Pool zeigen Heap. 2. Allgemeine Zugriffstypen, die auf (mehr oder weniger) beliebige Daten zeigen. 3. Zugriffsparameter für Unterprogramme 4. Zugriffstypen, die auf Unterprogramme zeigen. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.1/28
1. Storage Pools Jeder Zugriffstyp (d.h., jede Definition type... is access... ) definiert einen Storage Pool, aus dem bei Bedarf Objekte des ensprechenden Typs alloziert werden. Dies bedeutet nicht, dass bestimmte Teile des Speichers fest für Daten eines bestimmten Typs reserviert werden das wäre arg verschwenderisch. Unterschiedliche Storage Pools können sich unterschiedlich verhalten (z.b. können bestimmte Storage Pools mit, andere ohne Garbage Collection arbeiten). In jedem Fall wird der allozierte Speicherplatz wieder freigegeben, wenn die Lebensdauer des Storage Pools (d.h., des zugehörigen Zugriffstyps) zu Ende ist. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.2/28
Definition einer Linearen Liste type Cell; type Cursor is access Cell; type Cell is record Content: Element; Next: Container; end record; type Container is new Cursor; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.3/28
Auftretende Typen Cell: Datenzelle, bestehend aus Inhalt (vom Type Element) und Container (Zeiger auf nächste Zelle. Container, Cursor: Zeiger auf (irgend) eine Zelle Hinweise: Niemals Zelle verwechseln mit Zeiger auf Zelle!!! Container und Cursor Cursor sind strukturell gleich, in Ada aber verschiedene Typen, inbesondere nicht Zuweisungskompatibel. type Container is new Cursor; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.4/28
Einfügen in Lineare Liste procedure Put (Cont: in out Container; Item: in Element) is begin Cont := new Cell (Item, Cont); exception when Storage_Error => raise SL.Containers.Overflow; end Put; Was tut diese Prozedur genau? Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.5/28
Allokation und Zugriff Fordere neuen Speicherplatz vom Typ Cell aus einem Storage Pool an: Mit Initialwerten: new Cell (Item, Cont); Ohne Initialwerte: new Cell; Zugriff auf den Zeiger Cont: Cont.Content: Element Cont.Next: Zeiger auf nächste Zelle Cont.all: ganzer record vom Typ Cell Cont: Zeiger auf aktuelle Zelle Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.6/28
Entfernen aus Linearer Liste procedure Get (Cont: in out Container; Item : out Element) is begin if Cont = null then raise SL.Containers.Underflow; else Item := Cont.Content; Cont := Cont.Next; -- no De-Allocation implemented! end if; end Get; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.7/28
De-Allokation von Speicher 3 Möglichkeiten: 1. Explizites De-Allozieren mit Hilfe von Ada.Unchecked_Deallocation. 2. Automatische De-Allozieren des Speichers, wenn der entsprechende Zugriffstyp out of scope gerät. 3. Automatische Suche nach unbenutztem Speicher ( Garbage Collection, GC). (Erlaubt, aber nicht vorgeschrieben!) Die meisten Ada-Compiler unterstützen keine GC. Warum eigentlich? Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.8/28
Programmfragment (1) with Ada.Text_IO; use Ada.Text_IO; type Cell; type Cursor is access Cell; type Cell is record X: Integer; Next: Cursor; end record; L, N: Cursor; L := new Cell (10, null); N := new Cell (10, null); Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.9/28
Programmfragment (2) for I in 1.. 2 loop Put_Line("1. " & Boolean Image(L = N)); Put_Line("2. " & Boolean Image(L.X = N.X)); Put_Line("3. " & Boolean Image(L.Next = N.Next)); Put_Line("4. " & Boolean Image(L.all = N.all)); L := N; end loop; Was leistet diese Programmfragment? Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.10/28
Was geschieht hier? with Ada.Text_IO; use Ada.Text_IO; type Cell; type Cursor is access Cell; type Cell is record X: Integer; Next: Cursor; end record; L, N: Cursor; L := new Cell (10, null); N := new Cell (20, L); L.Next := N; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.11/28
Zeiger auf beliebige Typen (1) Zugriffstypen müssen nicht immer nur auf records zeigen: procedure Unbounded_Array is type Feld is array (Positive range <>) of Integer; type Feld_Access is access Feld; package PIO is new Ada.Text_IO.Integer_IO(Positive); P: Positive; A: Feld_Access; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.12/28
Zeiger auf beliebige Typen (2) begin PIO.Get(P); A := new Feld(1.. P); A.all := (1 => 1, 2 => 1, others => 0); for I in 3.. P loop A(I) := A(I-1) + A(I-2); end loop; Ada.Text_IO.Put_Line(Integer Image(A(P))); end Unbounded_Array; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.13/28
Zeiger auf beliebige Typen (3) Wir können nicht A := new Feld; schreiben! Das ganze Feld, auf das A zeigt, erhalten wir mit A.all! Entsprechend auch Zeiger auf andere Typen definieren, z.b. hlkeytype AI is access Integer; X: AI;... X := new Integer; X.all := 13; Aber AI kann nicht auf eine normale Integer-Variable zeigen... Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.14/28
2. Allgemeine Zugriffs-Typen (1) procedure Play_Array is type Feld is array (1.. 2048) of Integer; type Feld_Access is access all Feld; A: Feld_Access; B: aliased Feld; begin A := B Access; A.all := (1 => 1, others => 2); for I in 2.. A.all Last loop A(1) := A(1) + A(I); end loop; Ada.Text_IO.Put_Line(Integer Image(A(1))); end Play_Array; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.15/28
Allgemeine Zugriffs-Typen (2) Der Zugriffs-Typ (Zeiger) muss mit access all definiert werden. Alternative: access all ( nächste Folie) Der Typ, auf den gezeigt werden soll, muss mit aliased definiert werden. Warum ist das sinnvoll? Wird kein Speicherplatz alloziert (new), muss man sich um die De-Allokation nicht kümmern. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.16/28
Allgemeine Zugriffs-Typen (3) type Constant_String_Access is access constant String; type Texts_Type is array (Positive range <>) of Constant_String_Access; Bad_In: aliased constant String := "Too many fingers on keyboard!"; Pre_V: aliased constant String := "Procondition Violated!"; Post_V: aliased constant String := "Postcondition Violated!"; Texts: Texts_Type := (Bad_In Access, Pre_V Access, Post_V Access); Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.17/28
Allgemeine Zugriffs-Typen (4) with Ada.Text_IO; procedure was_mache_ich is type AI is access all Integer; A: AI; begin declare B: AI; I: aliased Integer := 13; begin B := I Access; A := B; end; Ada.Text_IO.Put_Line(Integer Image(A.all)); end was_mache_ich; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.18/28
Allgemeine Zugriffs-Typen (5) Regel zur Vermeidung von dangling Pointers: Ein Zeiger kann niemals auf eine Variable zugreifen, deren Lebenszeit kürzer ist als der Typ des Zeigers! Bei Bedarf kann man das Attribut Unchecked_Access verwenden mit Vorsicht! Was würde was_mache_ich tun, wenn wir B := I Access; durch B := I Unchecked_Access; ersetzen würden? Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.19/28
3. Zugriffs-Parameter (1) with Ada.Text_IO; procedure access_play is procedure P (Ptr: access Integer)is begin Ada.Text_IO.Put_Line (Integer Image(Ptr.all)); end P; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.20/28
Zugriffs-Parameter (2) type AI is access all Integer; A: AI; type BI is access Integer; B: BI; I: aliased Integer := 13; begin A := new Integer (17); B := new Integer (32); P(I Access); P(A); P(B); end access_play; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.21/28
Zugriffs-Parameter (3) Zugriffsparameter sind spezielle in-parameter, wobei das Schlüsselwort access an die Stelle von in tritt. Insbesonder dürfen auch Funktionen Zugriffsparameter definieren (während Durchgangs- oder Ausgangsparameter bekanntlich verboten sind). Beispiel: Nächste Folie. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.22/28
Zugriffs-Parameter (4) Ein Typ T, der durch type T is access Some; definiert wurde, kann wie gewohnt als beliebiger Parameter an eine Prozedur übergeben werden: procedure P( A: in T; B: out T; C: in out T; D: access T; E: access Some ); Was ist der Unterschied zwischen access T und access Some? Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.23/28
4. Zugriff auf Unterprogramme (1) type Do_Element is access procedure(e: Element); procedure Iterate(The_Cont: Container; Do_It: Do_Element); S_All: Integer; procedure Do_All(I: Integer) is begin S_All := S_All + I; end Do_All; Iterate(Cont, Do_All access); Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.24/28
Zugriff auf Unterprogramme (2) procedure Iterate(The_Cont: Container; Do_It: Do_Element) is Cur: Cursor; E: Element; begin Cur := First (The_Cont); loop E := Pick (The_Cont, Cur); Do_It(E); Cur := Next (The_Cont, Cur); end loop; exception when SL.Containers.Iteration_End => null; end Iterate; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.25/28
Zugriff auf Unterprogramme (3) Es gelten die gleichen Regel zur Vermeidung von dangling Pointers: Ein Zeiger kann niemals auf eine Prozedur zeigen, deren Lebenszeit kürzer ist als der Typ des Zeigers! Dies bedeutet, dass wir eine lokale Prozedur nicht als Parameter einer mehr global definierten Prozedur übergeben können... Diese Restriktion von Ada95 soll Ada2005 lockern. Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.26/28
Zugriff auf Unterprogramme (4) type Button; type Response is access procedure (B in out Button); type Button is record Do_This: Response;... end record; Procedure Push(B: in out Button) is begin B.Response(B); end Push; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.27/28
Zugriff auf Unterprogramme (5) procedure Emergency(B: in out Button) is begin Engines(Zero_Power); Breaks(Maximum_Power); Call_Pilot(B.Name); end Emergency; procedure Emergency_Call(B: in out Button) is begin Call_Pilot(B.Name, urgent => true); end Emergency; Red_Button.Do_This: Button := Emergency access; Red_Button.Do_This := Emergency_Call access; Stefan Lucks, Software-Entwicklung für Sichere Systeme SS 04, Kapitel 8 p.28/28