Mercury provides full support for pointers, compatible with Unsafe Code features on the .NET and full direct memory access on the unmanaged platforms.

A pointer variable is declared via the new Ptr type. A pointer can be untyped and reference an arbitrary memory location (a "void pointer"), or represent a specific type. The generic (Of T) syntax is used to declare typed pointers:

Dim v As Ptr = ...
Dim m as Ptr(Of MyStruct) = ...
Dim s as Ptr(Of String) = ...

In the above example, v is an untyped pointer to arbitrary memory, while m is a typed pointer to memory containing a MyStruct, and s is similarly typed and points to a String. (Note that with String being a reference type itself, s does not point to the string itself, but rather to the four or eight bytes holding the reference to the actual string instance, of course.)

Obtaining Pointers

A pointer is, in essence, the address of something, and as such, a pointer can be obtained by using the standard AddressOf keyword.

Dim a As MyStruct

m = AddressOf a
s = AddressOf "Hello Mercury!"

In this case, m now holds the memory address of the struct instance called a, stored on the local heap. And s holds the address of the reference to the String object instance representing the literal "Hello Mercury!".

Using Pointers

Of course, a pointer is only useful if we can work with the data it points to. This is called dereferencing. Every typed pointer exposes an implicit member called Dereference that can be used to access the memory it points to. Dereference can be thought of as a property (or field) of the same type as the pointer:

m.SomeField = 5

The above code sents the SomeField field of the struct to 5, and then prints the length of the string that s refers to (14). Note that because m points to the very same memory that is the local struct stored in a, changing the field via m actually changes the original struct.

Pointer Math

Pointers, by definition, represent a single space in memory (and the data stored within), but it is common to use points to work with a continuous layout of multiple items of the same type in memory – an array. The most common scenario is a Byte array – when operating with data in memory at ist rawest level – but it can be an array of any type.

The + and - operators (and related arithmetic) can be used to increase or decrease a pointer to move it between different elements in memory. An increase or decrease of "1" will move the pointer not by one byte, but by the size of the pointer's type (which can be determined, if necessary, using the sizeof() System Function.

Dim b As Ptr(Of Byte) = AddressOf MyMemoryBlob
b.Dereference = 1
(b+1).Dereference = 2
(b+2).Dereference = 3

b += 3
b.Dereference = 4

The code above writes the values 1, 2, 3, 4 to the first four bytes starting at the address of MyMemoryBlob. Since a Byte is (of course) one byte in size, the each pointer increment changes the pointer by one. Note that you can both change the value of b itself (b += 3), or add (or subtract) from it inline to create a new temporary pointer (b+1).

Dim m As Ptr(Of MyStruct) = AddressOf MyArrayOfStructs
m.Dereference.SomeField = 1
(m+1).Dereference.SomeField = 2
(m+2).Dereference.SomeField = 3

m += 3
m.Dereference.SomeField = 4

Here, m references a struct which (assuming it contains more that a single field of type Byte) has a size of larger than one. As the pointer m gets adjusted, it moves to the next struct in memory, based on the type's size.

Allocating Memory

On the native platforms (Windows, Linux and Cocoa), memory can be allocated as needed, using the malloc() system API.

For example, the line below allocates enough memory on the heat to hold one instance of the MyStruct structure, and returns a pointer to it. Of course, this memory is not managed by the garbage collector or ARC, so care must be taken to release it properly by calling free(), when it is no longer needed.

Dim m As Ptr(Of MyStruct) = rtl.malloc(sizeOf(MyStruct))

See Also