Pointers in FreeBASIC

written by Eclipzer

Introduction to Pointers

One of the newest additions to the BASIC language (FreeBASIC) is pointers. Though powerful, programming with pointers is not a simple task. Pointers require a whole new level of understanding to be used effectively. Regardless of your programming experience, if you've never worked with pointers before, it's going to take some time to wrap your head around them.

What's a Pointer? A Trip Down Memory Lane

So what exactly is a pointer?! For simplicity, a pointer can be thought of as a variable, with the exception that, instead of holding data, it points to the data by holding the memory address of that data. Every piece of memory has a unique number associated with it. These numbers are refered to as memory addresses. Information can be stored at these addresses to be used at a later point in time. The key thing to understand is that, unlike a variable, where you only have access to the value of that variable, with a pointer you actually have access to where a value is stored in memory.

Creating a Pointer

You define a pointer exactly the same way you do a variable. The only difference is that you add POINTER or PTR to the end of your statement. Also a pointer's type must be excplicitly stated in its definition.

  dim myPtr     as integer ptr     'use ptr suffix to define pointer
  dim myPointer as integer pointer 'use pointer suffix to define pointer
It is important to understand that, unlike variables, a pointer's type does not affect the amount of memory it uses. This is because memory addresses are all the same size, and a pointer simply holds a memory address. Let's take a look:
  dim myByte as byte    'define byte
  dim myWord as integer 'define integer

  print len(myByte) 'output size of byte (in bytes)
  print len(myWord) 'output size of integer (in bytes)
In the above code, the size of a byte is in fact one byte, while the size of an integer is four bytes, exactly as we'd expect. Now for the pointers:
  dim bytePtr as byte    ptr 'define byte pointer
  dim wordPtr as integer ptr 'define integer pointer

  print len(bytePtr) 'output size of byte pointer (in bytes)
  print len(wordPtr) 'output size of integer pointer (in bytes)
It might be a little unexpected, but these two pointers are exactly the same size, even though one is a byte pointer and the other an integer pointer. So to reiterate, pointers hold memory addresses, which are all the same size, hence pointers always use the same amount of space, regardless of their type. This naturally begs the question, why must we explicitly state a pointer's type if it doesn't affect the space allocated to it? What exactly does the pointer's type do or mean? We'll tackle this soon, but first we need to understand a few more things.

Pointer Operators

In all honesty, a memory address doesn't do much for us in programming. It's when we can get a hold of the address of something specific in memory that pointers start to make a little more sense. To this end we introduce two new operators: the "address of" operator and the "contents of" operator.

The "address of" operator is denoted with the '@' symbol and is used to obtain a variable's memory address.

The "contents of" operator is denoted with the '*' symbol and is used to obtain the contents of a pointer.

  dim myVar as integer     'define integer
  dim myPtr as integer ptr 'define integer pointer

  myVar = 512    'assign myVar a value
  myPtr = @myVar 'obtain the memory address of myVar

  print myVar  'print myVar 
  print @myVar 'print address of myVar

  print *myPtr 'print contents of myPtr (myVar)
  print myPtr  'print myPtr (address of myVar)
Notice that the output is identical. The first two lines are in terms of myVar, while the next two lines are in terms of myPtr. Let's review the two most important lines in the above code.
  myPtr = @myVar
Remember myPtr is a pointer, meaning it holds a memory address. So, anything we assign myPtr must also be a memory address. We use the '@' symbol to obtain the memory address of myVar and then assign it to myPtr. So looking at the above line of code you should literally read "My pointer equals the address of my variable".
  print *myPtr
Here we're displaying the contents of myPtr. Because we've set myPtr to point to myVar when we display the contents of myPtr we're really displaying myVar. It is important to understand that pointers only point to data, they do not contain it. Therefore if the data changes the contents of the pointer change as well.
  dim myVar as integer
  dim myPtr as integer ptr

  myVar = 512    'assign myVar
  myPtr = @myVar 'point to it

  print " var =";myVar  'output myVar 
  print "*ptr =";*myPtr 'output contents of myPtr (myVar)
  print " ptr = ";myPtr 'output myPtr (address of myVar)
  print

  myVar = 256 'change the value of myVar, indirectly changing the contents of myPtr
      
  print " var =";myVar  'output myVar 
  print "*ptr =";*myPtr 'output contents of myPtr (myVar)
  print " ptr = ";myPtr 'output myPtr (address of myVar)
  print

Here, by simply changing the value of myVar we have indirectly changed the output of *myPtr. It is important to note that the value of myPtr remained unchanged, as it still contains the address of myVar. Well, if we can change the contents of myPtr just by changing the value of myVar, can we do the opposite? Change the value of myVar by simply changing the contents of myPtr? As it turns out, we can do this rather easily. We simply change the following line from the above code:
  myVar = 256  'change the value of myVar, indirectly changing the contents of myPtr
to:
    
  *myPtr = 256 'change the contents of myPtr, indirectly changing the value of myVar
As you can begin to see, this is where pointers derive a lot of their power, in the ability to change data indirectly without having to actually "contain" the data. They only need to "point" to it.

Pointer Arithmetic

The idea behind pointer arithmetic is that we can use a pointer as a starting point and then add or subtract from that pointer to access a new memory location. The syntax for this can be written one of two ways:

  myPtr[offset]

or:

  *(myPtr+offset)
In both instances, we are dealing with the value stored at the offset from myPtr. Now, you may be asking how big (in memory) is an offset. If we have the following:
  value = *(myPtr+10)

are we talking 10bits from myPtr or 10bytes or 10 something else all together? Remember how we had to explicitly state a pointer's type when we defined it? This is where that comes into play. A pointer's type affects how many units of memory we move for each offset value.
  dim myVar as integer     'create an integer in memory
  dim myPtr as integer ptr 'define pointer

  myPtr = @myVar 'point to our integer

  print *(myPtr+10) 'output 10th integer from myPtr
  print myPtr[10]   'same as above, using different syntax
Since our pointer is an integer pointer, we're moving 10 integers (40bytes) from myPtr to output whatever value is at that memory location. This brings up a VERY important point. Because we're using an offset from a pointer to access a new memory location, we must be certain to know what our new address is pointing at. If you start writing to memory that you havn't explicity allocated, it's almost guaranteed that you will crash your program. Hence, the preceeding code is poorly written, because even though our pointer points to myVar, the offset points to 10 integers away from myVar. Since we havn't explicitly allocated this memory, there is no way to be certain what it is we are pointing at. A better example would be:
  dim myVar(10) as integer     'create 11 integers (0-10) in memory
  dim myPtr     as integer ptr 'define pointer

  myPtr = @myVar(5) 'point to 5th integer

  myPtr[-1]=10  'set integer 5-1 (4) to 10
  *(myPtr+1)=20 'set integer 5+1 (6) to 20

  for i=0 to 10
    print "integer"+str$(i)+" =";myVar(i)
  next

In this instance, by creating an array of integers, which are laid out consecutively in memory, we can safely use pointer arithmetic to access the other integers in the array. Notice, however, that myPtr doesn't point to the begining of the array, it points to the 5th integer. To address integers 0 to 4, we use a negative offset. A positive offset is used to address integers 6 to 10.

UDT Pointers

Pointers aren't limited to just the standard FreeBASIC data types. You can also define a pointer to point to a UDT (User Defined Type). To do this you simply use the name of the UDT for the type in the pointer definition.

  type myUDT
    var1 as integer
    var2 as integer
    var3 as integer
  end type

  dim myType as myUDT     'create UDT in memory
  dim myPtr  as myUDT ptr 'create a pointer of type myUDT

  myPtr = @myType 'point to our UDT
Remember the pointer type just indicates how much memory the pointer can address at one time. So, we must first create our UDT in memory and then have our pointer point to it, before we can start using the pointer. Now that we have a pointer to our UDT, how do we access it's elements through the pointer? Your first thought might be to use the standard dot notation.
  myPtr.var1 = value 'assign value to var1 using myPtr???
However, this is incorrect. The dot notation is only used for variables, not pointers. With a pointer we need to use the "->" symbol.
  myPtr->var1 = value 'correct pointer notation to assign value to var1
It might seem odd, at first, to have two different symbols ("." and "->") which essentially do the same thing. With two symbols, though, we can quickly determine how we are accessing our information. The "->" symbol tells us immediately that we're working with a pointer. Otherwise we're working directly with a variable.

UDT Array Pointers

What happens if we have an array of a UDT?

  type myUDT
    var1 as integer
    var2 as integer
    var3 as integer
  end type

  dim myType(10) as myUDT     'create 11 UDTs (0-10) in memory
  dim myPtr      as myUDT ptr 'create a pointer of type myUDT

  myPtr = @myType(0) 'point to first UDT
How do we access the UDT elements for a given array element? If we follow what we know, we might assume:
  myPtr[offset]->var1 = value 'assign value to var1 of array element offset through myPtr???
However, in this case we use a "." instead of the standard "->" symbol.
  myPtr[offset].var1 = value 'correct pointer notation to assign value to var1 of array element offset
We can do this because the brackets indicate that we're working with a pointer. If we were working directly with the array we would use parenthesis instead. It's important to note that the brackets aren't needed if we're already pointing to the array element we want to modify.
  myPtr = @myType(0)    'point to element 0
  myPtr[5].var1 = value 'assign value to var1 of 5th element

  myPtr = @myType(5)    'point to element 5
  myPtr->var1 = value   'assign value to var1 of 5th element
Both of these code blocks produce the same result.

Conclusion

As you can see, pointers introduce a whole new way of programming. Though this introduction should give you enough information to start using pointers on your own, there are still many advanced techniques to be learned. But that is for another article. Hopefully, you've enjoyed this article. Until next time!

-Eclipzer

Review

The following is a quick review of the material covered in this article. Think of it as a reference sheet.

Define a pointer using either the POINTER or PTR keyword.


  dim myPtr     as myType ptr
  dim myPointer as myType pointer
Use the '@' and "*" operators to obtain the address of a variable or the contents of a pointer.
  myPtr = @myVar 'obtain address of variable using '@' operator
  myVar = *myPtr 'obtain contents of pointer using '*' operator
Use pointer arithmetic to obtain the contents of a memory location from a pointer.
  value = myPtr[offset]   'obtain contents at offset from myPtr (array style)
  value = *(myPtr+offset) 'obtain contents at offset from myPtr (pointer style)
Use the "->" symbol to access the elements of a UDT (user-defined type) with a pointer.
  value = myPtr->element   'access UDT element using the "->" symbol
Use brackets with dot notation (".") to access UDT elements of an array.
  value = myPtr[offset].element   'access UDT element of an array