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
|