Access types (pointers)
Pointers are a potentially dangerous construct, which conflicts with Ada's underlying philosophy.
There are two ways in which Ada helps shield programmers from the dangers of pointers:
One approach, which we have already seen, is to provide alternative features so that the programmer does not need to use pointers. Parameter modes, arrays, and varying size types are all constructs that can replace typical pointer usages in C.
Second, Ada has made pointers as safe and restricted as possible, but allows "escape hatches" when the programmer explicitly requests them and presumably will be exercising such features with appropriate care.
Here is how you declare a simple pointer type, or access type, in Ada:
This illustrates how to:
Declare an access type whose values point to ("designate") objects from a specific type
Declare a variable (access value) from this access type
Give it a value of
In line with Ada's strong typing philosophy, if you declare a second access type whose designated type is Date, the two access types will be incompatible with each other:
In other languages
In most other languages, pointer types are structurally, not nominally typed, like they are in Ada, which means that two pointer types will be the same as long as they share the same target type and accessibility rules.
Not so in Ada, which takes some time getting used to. A seemingly simple problem is, if you want to have a canonical access to a type, where should it be declared? A commonly used pattern is that if you need an access type to a specific type you "own", you will declare it along with the type:
package Access_Types is type Point is record X, Y : Natural; end record; type Point_Access is access Point; end Access_Types;
Allocation (by type)
Once we have declared an access type, we need a way to give variables of the
types a meaningful value! You can allocate a value of an access type
new keyword in Ada.
If the type you want to allocate needs constraints, you can put them in the subtype indication, just as you would do in a variable declaration:
In some cases, though, allocating just by specifying the type is not ideal, so Ada also allows you to initialize along with the allocation. This is done via the qualified expression syntax:
The last important piece of Ada's access type facility is how to get from an
access value to the object that is pointed to, that is, how to dereference the
pointer. Dereferencing a pointer uses the
.all syntax in Ada, but is
often not needed — in many cases, the access value will be implicitly
dereferenced for you:
As you might know if you have used pointers in C or C++, we are still missing features that are considered fundamental to the use of pointers, such as:
Pointer arithmetic (being able to increment or decrement a pointer in order to point to the next or previous object)
Manual deallocation - what is called
deletein C. This is a potentially unsafe operation. To keep within the realm of safe Ada, you need to never deallocate manually.
Those features exist in Ada, but are only available through specific standard library APIs.
The guideline in Ada is that most of the time you can avoid manual allocation, and you should.
There are many ways to avoid manual allocation, some of which have been covered (such as parameter modes). The language also provides library abstractions to avoid pointers:
One is the use of containers. Containers help users avoid pointers, because container memory is automatically managed.
A container to note in this context is the Indefinite holder. This container allows you to store a value of an indefinite type such as String.
GNATCOLL has a library for smart pointers, called Refcount Those pointers' memory is automatically managed, so that when an allocated object has no more references to it, the memory is automatically deallocated.
Mutually recursive types
The linked list is a common idiom in data structures; in Ada this would be most naturally defined through two types, a record type and an access type, that are mutually dependent. To declare mutually dependent types, you can use an incomplete type declaration: