Access types (and the objects they access) are used to create dynamic data structures. It should be noted that access types are not necessarily implemented as pointers are in other languages (e.g. the address of the object in question), although in many cases they are. They can be used to create dynamic data structures such as linked lists, trees, graphs etc.
Access types allow for the dynamic referencing and allocation of objects in Ada.
To specify an access type consider the following:
type person is record name :string(1..4); age :0.. 150; end record; fred :person type person_ptr is access person; someone :person_ptr;
To dynamically create an object that someone will point at:
someone:= new person;
An object referred to by someone can be referenced in exactly the same way as an explicity declared object.
E.g.
fred.name := "fred"; someone.name:= "jim ";
A special value called null is used to indicate that the access object does not reference any valid objects. An access object is always initialised to null.
Given the following declarations
declare x :person_ptr := new person; y :person_ptr := new person; begin x := ("fred",27); y := ("anna",20); x := y; -- * y.all := ("sue ",34); put(x.name); end;
This will output the string "sue ". When the line highlighted with a star is executed, the value of the access object y is assigned to x, not the object that y was accesses. Both x and y both refer to the same object, so a change to the object that y refers to implicity changes the object x refers to.
If we wish to change a field of the object refered to by x
x.name := y.name;
Access objects x and y still point to distinct objects. Both access objects are
implicitly dereferenced in this example. Because of the implicit derefence, we
need a special syntax to denote the entire object the access object accesses.
Ada has the reserved word all for this purpose.
x.all := y.all; -- all field in object referred to by y now
-- in the object referred to by y. They are
-- still distinct objects.
A comparison of Pascal, C and Ada pointer/access syntax.
Pascal C Ada Access a^.fieldname *a.fieldname a.fieldname a->fieldname Copying pointer b := a; b = a; b := a; Copying accessed b^ := a^; *b = *a; b.all := a.all object
Ada95. Typically all access objects are allocated from a storage pool (heap) (see below). However it is possible to create an access type to other objects, so long as they are declared aliased, and the access type is declared to point at 'all' objects.
procedure demo is
type ptr is access all integer;
a :aliased integer; x, y :ptr;
begin
x := a'access;
y := new integer;
end;
Scope rules ensure that there cannot be any dangling references to objects. The attribute Unchecked_Access can be used to create access values in an unsafe manner.
A feature not available in Ada83, access types to subprograms allow for functional parameterisation such as used in Fortran mathematical libraries, as well as ad-hoc late binding. As for the rest of Ada, this is type safe.
An example from the LRM (3.10).
type Message_Procedure is access procedure (M:in String := "Error!");
procedure Default_Message_Procedure (M :in String);
Give_Message : Message_Procedure := Default_Message_Procedure'Access;
...
procedure Other_Procedure (M :in String);
...
Give_Message := Other_Procedure'Access;
...
Give_Message.("File not found");
Give_Message.all;
To define a structure that refers to itself requires normally requires making references to data structures that don't as yet exist. Ada circumvents this problem by allowing for incomplete type declarations. These simply specify the name of a type that is yet to be defined. The compiler expects that the full definition will be given before the end of the source file.
type element; -- incomplete type definition.
type ptr is access element;
type element is -- full definition of the type.
record
value:integer;
next:ptr;
end record;
When an object is dynamically allocated its value can be set in the same statement as its creation. This simply uses a qualified (type name specified) aggregate.
E.g.
head := new element'(40,head); -- insertion at the head -- of a linked list.
The same thing can be done using named aggregates.
head := new element'(value => 40; next => head);
There is no language requirement for deallocated memory to be put back into use for later allocation. You should consult your compiler reference manual for details.
If you want to deallocate memory (similar to free system call in Unix), then you can instantiate the generic procedure Unchecked_Deallocation (child of package Ada in Ada95). It is called unchecked because it does no live object analysis before deallocation.
generic
type Object(<>) is limited private;
type Name is access Object;
procedure Ada.Unchecked_Deallocation(X: in out Name);
pragma Convention(Intrinsic, Ada.Unchecked_Deallocatoin);
This would be instantiated as follows...
procedure Free is new Ada.Unchecked_Deallocation(object => node, name => ptr);
Free could then be called as follows...
head := new node;
...
free(head);
Ada95 allows a storage pool to be specified from which allocated memory comes from. Different access types can share a pool, typically most user defined access types share the one program wide pool. By extending the abstract type Root_Storage_Pool, defined in packge System.Storage_Pools, users can write their own storage pool type, and then associate it with an access type via the Storage_Pool attribute.
For example (from LRM, 13.11)
Pool_Object : Some_Storage_Pool_Type;
type T is access <something or other>;
for T'Storage_Pool use Pool_Object;
Storage pools allow for the possibility access types that are smaller than for a default access type that accesses a default pool.