Pointers and memory access

Pointers

Using pointers is possible by placing one '*' (asterix) in front of the name of a variable, array, list or map. A pointer is a placeholder for a memory address which is usually associated to a structure.

Example

  *MyScreen.Screen = OpenScreen(0, 320, 200, 8, 0)
  mouseX = *MyScreen\MouseX  ; Assuming the Screen structure contains a MouseX field
There are only three valid methods to set the value of a pointer:
- Get the result from a function (as shown in the above example)
- Copy the value from another pointer
- Find the address of a variable, procedure or label (as shown below)

Note: unlike C/C++, in PureBasic the '*' is always part of the item name. Therefore '*ptr' and 'ptr' are two different variables. 'ptr' is a variable (regular one) storing a value, '*ptr' is another variable of pointer type storing an address.

Pointers and memory size

Because pointers receive only addresses as values, the memory size of a pointer is the space allowing to store an absolute address of the processor:
- On 32-bit processors the address space is limited to 32-bit, so a pointer takes 32-bit (4 bytes, like a 'long') in memory
- On 64-bit processors it takes 64-bit (8 bytes, like a 'quad') in memory, because the absolute address is on a 64-bit range.

As a consequence the type of a pointer depends of the CPU address mode, (‘long' on 32-bit CPU and ‘quad' on 64-bit one for example), so a pointer is a variable of type pointer.
It results from this that assigning a native type to a pointer (*Pointer.l, *Pointer.b ...) makes no sense.

Note:
- Every time a memory address needs to be stored in a variable, it should be done through a pointer. This guaranteed address integrity at the compilation time whatever the CPU address mode is.
- PureBasic x86 does not generate 64-bit executables. For PureBasic programs compiled with this, the system grants them only an addressing with 32-bit pointers.

Pointers and structures

By assigning a structure to a pointer (for example *MyPointer.Point) it allows to access any memory address in a structured way (with the operator ‘\').

Example: Pointers and variables

  Define Point1.Point, Point2.Point
  *CurrentPoint.Point = @Point1  ; Pointer declaration, associated to a structure and initialized with Point1's address
  *CurrentPoint \x = 10          ; Assign value 10 to Point1\x
  *CurrentPoint.Point = @Point2  ; move to Point2's address
  *CurrentPoint \x = 20          ; Assign value 20 to Point2\x
  Debug Point1\x
  Debug Point2\x

Example: Pointers and array

  Define Point1.Point, Point2.Point
  Dim *Points.Point(1) ; 2 slots array
  *Points(0) = @Point1 ; Assign the first point variable to the first array slot
  *Points(1) = @Point2 ; Same for second

  *Points(0)\x = 10 ; Modify the variables trough the pointers
  *Points(1)\x = 20 ;
  
  Debug Point1\x
  Debug Point2\x
Pointers allow to move, to read and to write easily in memory. Furthermore they allow programmers to reach big quantities of data without supplementary cost further to data duplication. Copying a pointer is much faster.

Pointers are also available in structures, for more information see the structures chapter.

Pointers and character strings

All variables have a permanent size in memory (2 bytes for Word, 4 bytes for a Long, etc.) except for strings variables with lengths that can change. So string variables are managed by a different way of other variables.
Thus a structure field, that makes reference to a string, store only the memory address of the string instead of the string itself: a such structure field is a pointer towards a string.

Example

  Text$ = "Hello"
  *Text = @Text$            ; *Text store the address of the string in memory
  *Pointer.String = @*Text  ; *Pointer points on *Text
  Debug *Pointer\s          ; Display the string living at the address stored in *Pointer (i.e. @Text$)
Pointers Arithmetic

Arithmetic operations on the pointers are possible and practical by using SizeOf().

Example

  Dim Array.Point(1)         ; Array of points 
 
  *Pointer.Point = @Array()  ; Store the array address
  *Pointer\x = 10            ; Change the first array element values
  *Pointer\y = 15   

  *Pointer + SizeOf(Point)   ; Move to the next array element 

  *Pointer\x = 7             ; Change the second array element values
  *Pointer\y = 9
 
  ; Display results
  For i = 0 To 1
    Debug Array(i)\x
    Debug Array(i)\y
  Next i

Addresses of variables

To get the address of a variable in your code, you use the at symbol (@). A common reason for using this is when you want to pass a structured type variable to a procedure. You must pass a pointer to this variable as you cannot pass structured variables directly.

Example

  Structure astruct
    a.w
    b.l
    c.w
  EndStructure
  
  Procedure SetB(*myptr.astruct)
    *myptr\b = 69
  EndProcedure
  
  Define.astruct myvar
  
  SetB(@myvar)
  
  Debug myvar\b

Addresses of literal strings

To get the address of literal string, you can use the at symbol (@) in front of it. String constants are also supported.

Example

  *String = @"Test"
  Debug PeekC(*String) ; Will display 84, which is the value of 'T'

Addresses of procedures

For advanced programmers. The most common reason to get the address of a procedure is when dealing with the OS at a low-level. Some OSes allow you to specify callback or hook functions (for some operations) which get called by the OS and allows the programmer to extend the ability of the OS routine. The address of a procedure is found in a similar way to variables.

Example

  Procedure WindowCB(WindowID.i, Message.l, wParam.l, lParam.l)
    ; This is where the processing of your callback procedure would be performed
  EndProcedure
  
  ; A special callback for the Windows OS allowing you to process window events
  SetWindowCallback( @WindowCB() )

Addresses of labels

It can also be useful to find the address of labels in your code. This can be because you want to access the code or data stored at that label, or any other good reason you can think of. To find the address of a label, you put a question mark (?) in front of the label name.

Example

  Debug "Size of data file = " + Str(?endofmydata - ?mydata)
  
  DataSection
    mydata:
      IncludeBinary "somefile.bin"
    endofmydata:
  EndDataSection