Accesses

Pointers

Access Types

Ada focus on safety made this referential objects quite complex, but if this is your first encounter with pointers, they work like directions (in a town, or IP in the internet...) sometimes you may just refer to someone as "The neighbor in Baker Street 221b", instead of his given name (Sherlock Holmes).

It allows speakers of a language to give contextual information. It doesn't really matter who lives in a house, same way it may not really matter the data in a memory allocation, but that we can access to that data.

In other languages they are called pointers.

A familiar way to introduce accesses is by explaining its use in arrays. Given an array of houses (also called by some "a street") the number of the door is the position that differentiates that house from all other houses in the street.

type Street is array (doors'range) of Houses;
Baker: Street := London.Baker_St;
Sherlock : House := Baker(221B);

Memory in a computer is just a big array of data. The access is the direction of that space in memory (roughly speaking).

In the previous case 221B would be the direction. We would called it the access to Sherlock.

Semantically speaking, we also call the access to the variable that holds the value 221B

door: doors := 221B;
declare
type Integer_Access is access Integer;
my_pointer : Integer_Access;  --Initialized to null. 
p1, p2 : Integer Access := new Integer'(0); 
  --Initialized to an Object Integer set to value 0
begin
 my_pointer.all := 1; --Allocates
 p1 := my_pointer;
end;

All accesses must point to a null or to an object.

The content of the pointer is referred by the object oriented syntax of ".all" most people read it as "All, as in everything it contains", but to me it's easier to understand it as what it was intended to be : a shortcut for Allocation.

But it's also the case that pointers to records come in handy to think of all as "all content".

type Car is record
  ID, wheels, license, year : Integer;
end record;
type Car_Access is access Car;
C1, C2, C3 : Car_Access;
...
C1.ID := 200;  --Sets the content of the pointed object car, field ID.
C1.all.year := 2019; --Sets the content's field. 
                -- ^ Equivalent notations
C2 := C1;      -- C2 Points to the same object as C1
C3.all := C1.all;  --  A different object, pointed by C3
                   --     copies the content of the object pointed by C1

This feels more natural because you are accessing all fields.

You don't have to call it access in the name by the way.

It's also worth mentioning the syntaxt for acesses mixed with arrays

type vector is array(1..3) of float;
type Point is access Vector; --Just a play on word with pointer
                             --This is NOT how you define a Point in space
p1, p2, p3 : point;
...
p1(1) := 3;    --Passes forward the access
p2 := p1;      -- p2 Points to the same object as p1
p3.all := p1.all;  --A different object, pointed by p3
                     -- copies the content of the object pointed by p1
for i in p3'range loop  -- Forwards the attribute
...

The forward mechanism might look a little of a soft spot in the generally strong typing differentiation of Ada, but it proves to be incredibly useful when extending code. Keeping some syntactic structures in between different

Accesses come with some variety:

Pool access

Obviously I don't mean an All-Year Pass for the local swimming pool.

It refers to the memory pool. A literal direction in the heap. They point to content values on the heap.

It must be named.

declare
  type Pointer_To_Integer is access Integer;
  P_integer: Pointer_To_Integer;
begin
  P_integer := new Integer; 
    -- Create a new Integer by pointer
  P_integer.all := 42; --All the content / Allocation
end;

The .all is used to dereference the pointer. If you're pointing at a record, then it can be omitted, so you can use the same variable.member syntax you would use for a non-pointer.

Aliases

It's a general access type. In-out parameters in a function are, in many ways aliases.

It can refer to the heap, or the stack.

Must be named. That's kind of the definition of an alias. A second name.

declare
  type Pointer_To_Integer is access Integer;
  P_integer: Pointer_To_Integer;
  i: aliased integer; -- Aliased allows for an alias
begin
  P_integer := i'access; 
    -- The Pointer can now be used as alias
  P_integer.all := 42; --Changes the value of i
end;

Only stack variables that have been marked as aliased can have their pointer taken.

Here an actor plays Henry McCarty, who went by the alias William H. Bonney, whose identity also had the alias Billy The Kid.That's the inception of aliases.

Constant access

It's not a constant access, but an access that doesn't modify the content. Just like in parameters, they can only be read.

It can refer to the heap, or the stack. But they don't allow modification of the thing they're pointing at.

declare
  type Reader_To_Integer is access constant Integer;
  R_integer: Reader_To_Integer;
  i: aliased integer; -- Aliased allows for an alias
begin
  R_integer := i'access; 
    -- The Pointer can now be read
  --❌ P_integer.all := 42; --Nope! ❌--
end;

Oh, yeah. I should casually mention that any Unicode character is legal in an Ada file, just that to use it as names of variables is a mess of setting things up. I will make a post when I find out how to make it in any OS.

You may think is useless, but I β™₯️ using emojis in comments.

Also Ο€ comes in handy in math libraries.

Anonymous access

In Ada, when you define a type with the type keyword, you define a new type --- and two different types aren't compatible. (Use subtype instead of you want compatible types.) Therefore these two types cannot be used interchangeably:

type Pointer1 is access integer;
type Pointer2 is access integer;

An anonymous access is when you use the type directly in the variable declaration.

Anonymous judges your Internet History
declare
  p1: access integer;
  p2: access integer;
begin
  p1 := p2;
end;

Anonymous accesses have the same semantics as general accesses. As such, an anonymous accesses can point at a stack object.

This can cause a few issues, as Ada tries really, really hard to make sure that you don't leak pointers to local objects to a surrounding scope.

Here is a function which reverses a list:

type Node is record
  next: access Node;
end record;

function reverselist(list: access Node) return access Node is
  result: access Node := null;
  this: access Node := list;
  next: access Node := null;
begin
  while this /= null loop
    next := this.next;
    this.next := result; -- [*]
    result := this;
    this := next;
  end loop;
  return result; -- [*]
end;

The two lines marked [*] will cause compilation failures on Ada 2005, because Ada 2005's type rules think there's a risk of local pointer leakage. Ada 2012's type rules are more sophisticated and will allow this code. By using a named pool access type instead of the anonymous access, we can assure that compiler that we can never be pointing at a stack object, and it'll be happy.

Preventing local pointer leakage is a big theme in Ada (they are, rightly, terrified of dangling pointers). The way this is done is painfully simple: access types carry with them the scope in which they were defined, and you may not assign a pointer to a local to an access type defined outside the local's scope.

In fact, pointers to local objects appear to be rarely used in Ada --- in and out parameters achieve the same effect in a vastly cleaner way.


Ada also supports true references:

type Vector is array(integer range <>) of float;

procedure Show(values: Vector) is
begin
  for i in values'range loop
    declare
      item: float renames values(i);
    begin
      Put_Line(float'image(item));
    end;
  end loop;
end;

Garbage collector

When dynamically creating an object, the object does not disappear just because the pointer stopped accessing to it. But if no pointer is accessing at it, then there is no way to access to it now. It becomes memory garbage.

To the process of producing memory garbage we call it Memory Leaking.

type Pointer is access integer;
P : Pointer := new Integer'(0);

When all access variables pointing at an object go out of scope the object that was pointed at by those variables can no longer be referenced and the memory it uses should be made available again. In Ada garbage collection is optional.

Implementations are allowed to provide it, but they are not required to do so. This may seem surprisingly wishy-washy for a language that endeavors to support reliable and portable programming. The problem is that Ada also endeavors to support low level embedded systems and real time programming. In such environments garbage collection is widely considered problematic. It is difficult to meet real time objectives if a complex garbage collection algorithm might run at anytime.

This presents an immediate problem for programmers interested in portability. Thus for maximum portability one must assume that garbage collection is not done and take steps accordingly


The Ada library provides a generic procedure named Unchecked_Deallocation that can be used to manually deallocate a dynamically allocated object. Unfortunately the use of Unchecked_Deallocation can violate important safety properties the language otherwise provides. In particular, if you deallocate an object while an access variable still points at it, any further use of that access variable will result in erroneous behavior. As a service Unchecked_Deallocation will set the access variable you give it to null causing future use of that access variable to result in a well defined exception. However, there might be other access variables that point at the same object and Unchecked_Deallocation cannot, in general, know about all of them.

If your program never uses Unchecked_Deallocation then all access variables are either null or point at a real object; dangling pointers are impossible. However, your program might also leak memory if the Ada implementation does not provide garbage collection.Thus in most real programs Unchecked_Deallocation is used.This is an example of where Ada compromises safety for the sake of practical reality.

In fact, there are several other β€œUnchecked” operations in Ada that are used to addresscertain practical concerns and yet introduce the possibility of unsafe programs. Sinceevery such operation starts with the word β€œUnchecked” it is an simple matter to searchan Ada program for occurrences of them.