Ada's Dirty Pointer Secrets: to Access, or to Address?

May 6th, 2021

~This seems to turn into a mini-series of sorts, see the previous example of passing dynamically allocated arrays from Ada to C~

In true ladylike fashion, Ada deals with the unmentionable -but useful, darn it- dirtiness of pointers by calling them something entirely else: if p is for pointers and a is for Ada, then at least be civilised enough to use a-words when working directly with memory! So the type for pointers in Ada is not denoted by * (does that stand for some expletive in C, I wonder?) but by the magic word "access" that is used to denote a pointer type and wards off segfaults as well as more general memory read/write mishaps. And like any other magic, if it didn't work for you, it's because you used it wrongly!

The core of the access type idea in Ada is to leverage the overall strongly typed approach to provide at least some amount of automated checks and safeguards even when using pointers. So there is never a "pointer" variable but only a "pointer to this exact type" variable, at most (aka "access" is a generic type that needs specific instantiation before any use). As a result and in the otherwise overall Ada-style, there is no implicit conversion between pointer types, even if or when they might happen to point to the same type of thing otherwise!

Each access type can also contain further constraints such as not accepting null values (basically enforcing an automated check for that very common null value bane of pointer use) or allowing access only to some (as opposed to any) memory spaces (storage pools and even subpools, in Ada terminology). The significant advantage of this is that one can at all times be quite specific and therefore know exactly what sort of thing they expect to find the pointer pointing to, so that it's at least harder to mess it all up. The disadvantage is at times quite the same: having to be very specific about details when not needing the details or having to be very specific about things that are not yet known or even possible to know upfront can create its own sort of mess (although it's indeed one way more subtle than a segfault, granted).

For those cases when the neat access types are just too much overhead or too much of a straightjacket or simply not needed, one can still ask in principle directly for the memory address of anything at all: simply add 'Address after the name of the thing and there you go, what you get as a result is what you expect (and if you expected something else than what you got, it's because you had the wrong expectations, obviously). Note however that from Ada's point of view, access and the result of 'Address are two different sort of things and as such don't easily mix1.

The 'Address attribute2 is very useful indeed to do basically memory overlays (mappings) that are just about the fastest and most convenient way of converting between two types no matter how complex, with the only real requirement that they are of exactly the same size. This is absolutely invaluable when needing to get the raw representation of complex types for whatever purpose (mine tend to include at the moment encrypting/decrypting and/or fast storage). All that is required is to simply declare one variable of each type, make sure that they do indeed have addresses (by declaring them as "aliased" since otherwise Ada might optimize them into registers) and then request that they use the very same memory address. For instance, to convert a whole array, no matter how big, from the C-compatible char_array type to the one underlying raw type used everywhere in the Ada code around Eulora's client, all it takes is one function wrapping the few lines of code to map the memory and return the contents read with the expected type:

-- NB: result has length exactly A'Length
function Char_Array_To_Octets(A: in Interfaces.C.char_array)
return Raw_Types.Octets is
ca: aliased Interfaces.C.char_array(1..A'Length) := A;
o : aliased Raw_Types.Octets(1..A'Length);
for o'Address use ca'Address;
begin
return o;
end Char_Array_To_Octets;

Of course, direct memory mapping via the 'Address attribute as above is as dangerous as it gets and as such entirely not in keeping with Ada's style. Except, of course, that Ada fully allows it in the first place and provides the means to do it too but probably averts her eyes whenever you do it, then moves on and pretends it never happened. So it still works but it's also all clean, neat and tidy, unlike that ugly, dirty C. And if anyone asks about it, it's all *your* fault, too, you made her do it!


  1. There's a whole package to convert from one to the other (System.Address_To_Access_Conversions) and it even uses the unmentionable word "pointer" as in having a function named To_Pointer returning an Object_Pointer!  

  2. Ada has this concept of attributes: things one can get and/or set about any entity, simply using the syntax entity'attribute_name. For example, one can get the size of the Integer type simply by writing Integer'Size, one can get the size of a variable x by writing x'Size and one can set the size of some MyType by writing for MyType'Size use 133525.