The Pointer Data Type

Written by rdc

The pointer data type is unique among the FreeBASIC numeric data types. 
Instead of containing data, like the other numeric types, a pointer 
contains the memory address of data.

On a 32-bit system, the pointer data type is 4 bytes (8 bytes on a 64-bit 
system). FreeBASIC uses pointers for a number of functions such as 
ImageCreate, and pointers are used heavily in external libraries such as 
the Windows API. Pointers are also quite fast, since the compiler can 
directly access the memory location that a pointer points to. A proper 
understanding of pointers is essential to effective programming in FreeBASIC
.

For many beginning programmers, pointers seem like a strange and mysterious 
beast. However, if you keep one rule in mind, you should not have any 
problems using pointers in your program. The rule is very simple: a pointer 
contains an address, not data. If you keep this simple rule in mind, you 
should have no problems using pointers.

 Pointers and Memory 

You can think of the memory in your computer as a set of post office boxes 
(P.O. Box) at your local post office. When you go in to rent a P.O. Box, 
the clerk will give you a number, such as 100. This is the address of your 
P.O. Box. You decide to write the number down an a slip of paper and put it 
in your wallet. The next day you go to the post office and pull out the 
slip of paper. You locate box 100 and look inside the box and find a nice 
stack of junk mail. Of course, you want to toss the junk mail, but there 
isn't a trash can handy, so you decide to just put the mail back in the box 
and toss it later. Working with pointers in FreeBASIC is very similar to 
using a P.O. Box.

When you declare a pointer, it isn't pointing to anything which is 
analogous to the blank slip of paper. In order to use a pointer, it must be 
initialized to a memory address, which is the same as writing down the 
number 100 on the slip of paper.  Once you have the address, find the right 
P.O. Box, you can dereference the pointer, open the mail box, to add or 
retrieve data from the pointed-to memory location. As you can see there are 
three basic steps to using pointers.

    Declare a pointer variable.
    Initialize the pointer to a memory address.
    Dereference the pointer to manipulate the data at the pointed-to memory 
   location.

This isn't really any different than using a standard variable, and you use 
pointers in much the same way as standard variables. The only real 
difference between the two is that in a standard variable, you can access 
the data directly, and with a pointer you must dereference the pointer to 
interact with the data.

 Typed and Untyped Pointers 

FreeBASIC has two types of pointers, typed and untyped. A typed pointer is 
declared with an asscoaited data type.

   Dim myPointer As Integer Ptr

This tells the compiler that this pointer will be used for integer data. 
Using typed pointers allows the compiler to do type checking to make sure 
that you are not using the wrong type of data with the pointer, and 
simplifies pointer arithmetic.

Untyped pointers are declared using the Any keyword.

   Dim myPointer As Any Ptr

Untyped pointers have no type checking and default to size of byte. Untyped 
pointers are used in the C Runtime Library and many third party libraries, 
such as the Win32 API, to accommodate the void pointer type in C. Unless 
you specifically need an untyped pointer, you should use typed pointers so 
that the compiler can check the pointer assignments.

 Pointer Operators 

FreeBASIC has the following pointer operators.

You will notice that the addressof operator not only returns the memory 
address of a variable, but it can also return the address of a subroutine 
or function. You would use the address of a subroutine or function to 
create a callback function such as used in the CRT QSort function.

 Memory Functions 

FreeBASIC also has a number of memory functions that are used with 
pointers.

These functions are useful for creating a number of dynamic structures such 
as linked lists, ragged or dynamic arrays and buffers used with third party 
libraries.

When using the Allocate function you must specify the storage size based on 
the data type using the equation number_of_elements * Sizeof(datatype). To 
allocate space for 10 integers your code would look like this: myPointer = 
Allocate(10 * Sizeof(Integer)). An integer is 4/8 bytes (on 32/64 bit 
systems) so allocating 10 integers will set aside 40/80 bytes of memory (on 
32/64bit systems). Allocate does not clear the memory segment, so any data 
in the segment will be random, meaningless data until it is initialized.

Callocate works in the same fashion, except that the calculation is done 
internally. To allocate the same 10 integers using Callocate your code 
would look like this: myPointer = Callocate(10, Sizeof(Integer)). Unlike 
Allocate, Callocate will clear the memory segment.

Reallocate will change the size of an existing memory segment, making it 
larger or smaller as needed. If the new segment is larger than the existing 
segment, then the data in the existing segment will be preserved. If the 
new segment is smaller than the existing segment, the data in the existing 
segment will be truncated. Reallocate does not clear the added memory or 
change any existing data.

All of these functions will return a memory address if successful. If the 
functions cannot allocate the memory segment, then a NULL pointer (0) is 
returned. You should check the return value each time you use these 
functions to be sure that the memory segment was successfully created. 
Trying to use a bad pointer will result in undesirable behavior or system 
crashes.

   +--------------------------------------------------------------------------------------------------------------------------+
   | There is no intrinsic method for determining the size of an allocation. You must keep track of this information yourself.|
   +--------------------------------------------------------------------------------------------------------------------------+

   +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
   | Be careful not to use the same pointer variable to allocate two or more memory segments. Reusing a pointer without first deallocating the segment it points to will result in the memory segment being lost causing a memory leak.|
   +-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

 Pointer Arithmetic and Pointer Indexing 

When you create a memory segment using the allocation functions, you will 
need a way to access the data contained within the segment. In FreeBASIC 
there are two methods for accessing data in the segment; using the 
indirection operator with pointer arithmetic, and pointer indexing.

Pointer arithmetic, as the name suggests, adds and subtracts values to a 
pointer to access individual elements within a memory segment. When you 
create a typed pointer such as Dim myPointer as Integer ptr, the compiler 
knows that the data being used with this pointer is of size Integer or 4 
bytes. The pointer, when initialized, points to the first element of the 
segment. You can express this as *(myPtr + 0). To access the second 
element, you need to add 1 to the pointer, which can be expressed as 
*(myPtr + 1).

Since the compiler knows that the pointer is an Integer pointer, adding 1 
to the pointer reference will actually increment the address contained in 
myPtr by 4/8 (on 32/64bit systems), the size of an Integer. This is why 
using typed pointers is preferable over untyped pointers. The compiler does 
much of the work for you in accessing the data in the memory segment.

Notice that the construct is *(myPtr + 1) and not *myPtr + 1. The * 
operator has higher precedence than +, so *myPtr + 1 will actually 
increment the contents myPtr points to, and not the pointer address.

   myPtr will be evaluated first, which returns the contents of the memory 
   location and then +1 will be evaluated, adding 1 to the memory location. 
   By wrapping myPtr + 1 within parenthesis, you force the compiler to 
   evaluate myPtr + 1 first, which increments the pointer address, and then 
   the * is applied to return the contents of the new address.

Pointer indexing works the same way as pointer arithmetic, but the details 
are handled by the compiler. *(myPtr + 1) is equivalent to myPtr[1]. Again, 
since the compiler knows that myPtr is an integer pointer, it can calculate 
the correct memory offsets to return the proper values using the index. 
Which format you use if up to you, but the index method resembles the 
standard array access method and visually is easier to understand than the 
indirection operator.

 Pointer Functions 

Freebasic has a set of pointer functions to complement the pointer 
operators.

    CPtr Converts expression to a data_type pointer. Expression can be 
   another pointer or an integer.
    Peek Returns the contents of memory location pointed to by pointer. 
   Data_type specifies the type of expected data.
    Poke Puts the value of expression into the memory location pointed to 
   by pointer. The data_type specifies the type of data being placed into 
   the memory location.
    SAdd Returns the location in memory where the string data in a dynamic 
   string is located.
    StrPtr The same as Sadd.
    ProcPtr Returns the address of a function. This works the same way as 
   the addressof operator @.
    VarPtr This function works the same way as the addressof operator @.

The Sadd and Strptr functions work with the string data types to return the 
address of the string data. The Peek and Poke functions have been added for 
the purposes of supporting legacy code. Procptr and Varptr both work just 
like the address of operator @, but Proptr only works on subroutines and 
functions and Varptr only works on variables. Cptr is useful for casting an 
untyped pointer to a typed pointer, such as a return value from a third 
party library.

 Subroutine and Function Pointers 

Subroutines and functions, like variables, reside in memory and have an 
address associated with their entry point. You can use these addresses to 
create events in your programs, to create pseudo-objects and are used in 
callback functions. You create a sub or function pointer just like any 
other pointer except you declare your variable as a pointer to a subroutine 
or function rather than as a pointer to a data type.

Before using a function pointer, it must be initialized to the address of a 
subroutine or function using Procptr or @. Once initialized, you use the 
pointer in the same manner as calling the original subroutine or function.

You declare a function pointer using the anonymous declaration syntax.

   Dim FuncPtr As Function(x As Integer, y As Integer) As Integer

You then need to asscoate this function pointer with an actual subroutine 
or function within your code.

   Function Power(number As Integer, pwr As Integer) As Integer
      Return number^pwr
   End Function

   FuncPtr = @Power

You can then call the function pointer much like you would call the real 
function.

   FuncPtr(2, 4)

While this may not be useful at first glance, you can use this technique to 
implement polymorphic functions where a single variable instance can point 
to one of several different subroutine or functions.

For example, suppose you have a dog and cat object. Both objects need a 
Speak method. By defining Speak as a function pointer and associate Speak 
with a Bark subroutine for a dog and a Meow subroutine for a cat, you can 
make Speak either issue a "Woof!" or "Meow!" depending on the object type.

 Creating a Callback Function 

One of the primary uses for function pointers is to create callback 
functions. A callback function is a function that you have created in your 
program that is called by another function or subroutine either in your own 
code space or in an external library. Windows uses callback functions to 
enumerate through Window objects like fonts, printers and forms.

The qsort, function contained within the C Runtime Library sorts the 
elements of an array using a callback function to determine the sort order. 
The prototype for the qsort function is contained in crt/stdlib.bi:

   Declare Sub qsort (ByVal As Any Ptr, ByVal As size_t, ByVal As size_t, ByVal As Function(ByVal As Any Ptr, ByVal As Any Ptr) As Long)

The following lists the parameter information for the qsort subroutine.

    The first parameter is the address to the first element of the array. 
   The easiest way to pass this information to qsort is to append the 
   address of operator to the first element index: @myArray(0).
    The second parameter is the number of elements in the array, that is 
   the array count.
    The third parameter is the size of each element in bytes. For an array 
   of integers, the element size would be 4 bytes.
    The fourth parameter is a function pointer to the user created compare 
   function. The function must be declared using the Cdecl passing model, 
   as shown in this parameter.

Using this information, you can see how qsort works. By passing the address 
of the first element along with the count of elements, and the size of each 
element, qsort can iterate through the array using pointer arithmetic.

Qsort will take two array elements, pass them to your user defined compare 
function and use the compare function's return value to sort the array 
elements. It does this repeatedly until each array element is in sorted 
order.

You need to declare the function prototype as Cdecl which ensures that the 
parameters are passed in the correct order.

   Declare Function QCompare cdecl (ByVal e1 As Any Ptr, ByVal e2 As Any Ptr) As Long

You would then define the function like the following.

   'The qsort function expects three numbers
   'from the compare function:
   '-1: if e1 is less than e2
   '0: if e1 is equal to e2
   '1: if e1 is greater than e2
   Function QCompare cdecl (ByVal e1 As Any Ptr, ByVal e2 As Any Ptr) As Long
      Dim As Integer el1, el2
      Static cnt As Integer

      'Get the call count and items passed
      cnt += 1
      'Get the values, must cast to integer ptr
      el1 = *(CPtr(Integer Ptr, e1))
      el2 = *(CPtr(Integer Ptr, e2))
      Print "Qsort called";cnt;" time(s) with";el1;" and";el2;"."
      'Compare the values
      If el1 < el2 Then
         Return -1
      ElseIf el1 > el2 Then
         Return 1
      Else
         Return 0
      End If
   End Function

You would then call the QSort function passing the address of the callback 
function.

   #include "crt/stdlib.bi"

   qsort @myArray(0), 10, SizeOf(Integer), @QCompare

 Pointer to Pointer 

In FreeBASIC you can create a pointer to any of the supported data types, 
including the pointer data type. A pointer to a pointer is useful in 
situations where you need to return a pointer to a function or in creating 
specialized data structures such as linked-lists and ragged arrays. A 
pointer to a pointer is called multi-level indirection.

One application of a pointer to pointer is the creation of a memory segment 
that behaves just like an array. For example, suppose you want to create a 
memory segment to hold an unknown number of integers. You can create a 
dynamic memory segment that you can resize as needed during runtime to 
handle as many integers as you need. You would start by creating a 
pointer-to-pointer variable.

   Dim myMemArray As Integer Ptr Ptr

You would then initialize the pointer reference by using Allocate or 
Callocate.

   'Create 10 rows of integer pointers
   myMemArray = CAllocate(10, SizeOf(Integer Ptr))

Notice that the variable is initialized to an Integer Ptr since this list 
is going to point to another list; this is the pointer that points to 
another pointer. You can then initialize the individual pointer references 
just created to point to the needed memory segments.

   'Add 10 columns of integers to each row
   For i As Integer = 0 To 9
      myMemArray[i] = CAllocate(10, SizeOf(Integer))
   Next

In this code snippet, the individual pointers in the list are initialized 
to 10 memory segments that will contain the actual integer data.

   'Add some data to the memory segment
   For i As Integer = 0 To 9
      For j As Integer = 0 To 9
         myMemArray[i][j] = Int(Rnd * 10)
      Next
   Next

This code snippet uses the index method to load the actual data into the 
memory segments. Notice that this looks and acts just like a 
two-dimensional array. While this may not seem useful within this context, 
you could use this code to create a dynamic array within a type definition. 
Since you cannot have a standard dynamic array within a type, this allows 
you get the same functionality.
Editors Note: Dynamic arrays are allowed in recent releases of FreeBASIC

One thing you need to be aware of is how to deallocate a structure such as 
this. The rule is to just do the reverse of the allocation operations. 
Since the last allocate operation initialized the data memory segments, you 
deallocate these memory segments first and then you can deallocate the base 
pointer.

   'Free memory segment
   For i As Integer = 0 To 9
      Deallocate myMemArray[i]
   Next

   'Free the pointer to pointer
   Deallocate myMemArray

You need to be sure that you deallocate in the right order, otherwise you 
will end up with memory segments that are not freed, but inaccessible. 
These are memory leaks and can cause a whole host of problems in your 
program.

Last reviewed by sancho3 on February 07, 2018 
