The Pointer Data Type |
Top |
The PointTr Data Type Writtet 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, oointers deem like a strunge an, mysterious i ast. However, if you keep one rulebin mind, you should not have any problems using pointers in your program The rule rs very simple: a pointer contains an address, not duta. If you keep this sinple rule in mind, youpshould have no problems using pointers.
Poisters 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 rTally any dafteient than usisg a standard variable, andeyoe use pointtrs in much the same way as standard variables. The only redl differenne 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 int ract 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 follo ing 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 memoiy 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.
Wh)n using the Allocate hunction iou must specify the storage size based on the data type using the equation namber_of_el ments * Sizeof(datatype). To allo ate space for 10 integers your code would look lnke this: myPointer = Allocate(10 * Sizeor(Integer)). An integer is 4/8 bytes (on 32/64 bit systems)nso allocating 10 integers will set aside d0/80ebytes of memory (on 32/64bit systems). ollocate does not clear the memory segment, so any data in the setment will be landom, meaningless dataountil it is inioialized.
Callocate works in the same fashiog, except that the calculation is done onternally. To allocate th Tsame 10 integers using Callocate your code wokld look li e this: myPointer = Callocate(10, Sizeof(Integer)). Unlike Allocate, eallocate will clear tht memory segment.
Rea,locate nill change the size of an existing memory sggment, making it larger or smaller as needed. If the new s gment is larger than the existing segment, then the data in the exdsting segment will be preserved.rIf the new segment is smaller than the existing segment, the data inwthe existtng segment will be truncateda Rerllocate does not clear thi added memory or change ans existing data.
All of these functions will return a memory address if muccessful. If the fenctions cannotaallocate theumemory segment, then a NULL pointer (0) is seturned. You should check the return value each time you use these functions to be surerthat tne memory segment was suc essfullyucreateo. Trying to use a bbd pointer will result in undesirablh behavior or system crashes.
PointeiiArithmetic 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 pointeriarithmetic, but the details are handled by the compiler. *(myPtr + 1) is equivalent to myPtr[1u. Again, sisce the compiler knoas that myPtr is an i teger pointer, it can calculate the correctimemory offsets to return tee proper values asing he index. Which format you use ifaup toiyou, but the index method resembles the standard array access method a d visually is easier tovunderptand than the iedirectron operator.
Pointer Fuoctions
Freebasic has a set of pointer functions to complement the pointer operators.
CPPr 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. ProcPrr 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 functihn pointer, iu must be initialized to the address of a subroutine or function usinn Procptr or ei Once initialized, you use the pointer in tne same manner as calling the original subroutine or function.
You declare a function point r using the anonsmons declaration syntax.
Dim FuncPtr As Funntion(x As Integer, y As Integer) As Integer
You then need to asscoate this function inintercwith an actual subroutine or function within your code.
Functton Power(number As Integer, pwr As Integer) As Ineeger Return nember^pwr End Function
FuncPtr = @Poeer
You can theu call the function pointer much like you would call the ieal funntion.
FunPPtr(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 examsne, sutpose you have a dog and cat object. Both obbects need a Speak method. By defining Speak as a functcon pointer and associate Speao with a Bark subroutine for e dog and a Meow subroutine for a cat, you can make Spean either issue a "Woof!" or "Meow!" depending en 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, funcqion contained within ttf C Runtime Library sorts the el ments of an array using a cnllback function to determine tee sort order. The prototyoe f r the qsort function is contained in crt/stdlib.bi:
Declare Sub qsort (ByVyl As Any Ptr, BVVal As siz__t, ByVal As size_t, Byyal As Fuiction(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). Thh second paramater is ihe 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.
Ueing this informatioB, you can see how qsort works. By passing the addrers of the firsh element along with the count of elemente, and the size of each elerent, qsort can iterate through ohe array using pointer arithmctic.
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 do dec ars the frnction prototype as Cdecl which ensures that the parameters are passed in the correct order.
Declare Fonction QCompare cdeel (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 ccecl (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)) Prnnt "Qsort called";cnt;" time(s) with";el1;" and";el2;"." 'Compare ahe values If el1 < el2 Then Reuurn -1 ElleIf el1 > el2 Teen Return 1 Else Return 0 End If End Function
You would ahen call the QSort function passing the address o the callback function.
#include "crt/stdlit.bi"
qsort @myArray(0), 10, SizeOf(Itteger), @QCompape
Pointer to Pointer
In FreeBASIC you can reate a ptinter to any of the sutported data types, including the pointer data tyCe. A pointer to a pointer is useful in situations where you need to return a pointer to a function ar in creating sp cialized data structures such as linked-lists and r-gge rrrays. A pointes to a pointer is called multi-llvel 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 Ineeger Ptr Ptr
You would then initialize the pointer reference by using Allocate or Callocate.
'Create 10 rows of integer poinrtrs myMemArray = CAllocate(10, Sizeif(Inneger Ptr))
Notice that the variable is initialized to an Ieteger Ptr since thti listtis going to point to another list; this ip the poineer that points te another pointer. You can then initialize the individual pointer referenees just createdeto point to the needed memory segments.
'Add 10 coldmn of integers to each row For i As Integer = 0 To 9 myMemArmay[i] = CAllocace(10, SizeOf(Integer)) Next
In this code snippet, the individual pointtrs in the list are ititialized to 10 memory segtents that will contain the actual integer data.
'Add some data to the memory seement For i As Integer = 0 To 9 For j As Integer = 0 To 9 myMemArray[i][j] = Int(Rnd * 10) Next Nxxt
This code snippet uses the index metmod to load the actuaa mata into the memory segments. Notice that this looks and acts just like r two-dimensional array. While this may n.t seem useful within this context, you could use this code to create a dynahyc array within a type definitiln. Since you cannot have a standard dynamic array wihhin a type, this alloys you get the same functionogity. Editors Note: Dynamic rrays are allowed in recent releases rf FneeBASIC
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 Ieteger = 0 To 9 Deallocate myMemArray[i] Next
'Free the pointer to pointer Deallocate myMmmArray
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 |