Pointers in mikroPascal.

This article is about pointers to variables in the PRO version of mikroPascal.

Table of content
Introduction A small introduction on Pointers.
Declaration of Pointers How pointers are created.
The "@" operator Construction a pointer to a variable.
Accessing variables pointed to How to use pointers to access what they point at.
Pointers to Records How to use pointers to access records.
Pointers to Arrays How to use pointers to access arrays, in different ways.
Pointer Typecasting Casting from one pointertype to another.
Accessing a variable one byte at a time How to access multiple byte variables byte per byte.
Accessing a variable as being another type How to access multiple byte variables as being from another type.
Nil Nil in mP.
Pointer Arithmetic Exceptions and restrictions using pointer arithmetic.
Pointer related Warnings by the compiler. Warnings given by the compiler due to not correct handling of pointers.



Introduction.

Pointers are in fact variables holding the address of other variables. Pointers point to a certain type of variable, and arithmetic done on pointers take into account the size of the variable they point to (as Delphi does), see examples and section "pointer arithmetic" further.

Declaration of pointers.

A pointer type is declared as follows:
type PByte   = ^byte;             // a pointer to a byte
     PWrd    = ^word;             // a pointer to a word
     PBArr   = ^array[4] of byte; // a pointer to an array of bytes
     PMyType = ^TMytype;          // pointer to something of type "TMyType".
A pointer variable is declared with as follows:
var BytePointer  : ^byte;	  // a pointer to a byte
    WordPointer  : ^word;	  // a pointer to a word
    MyTypePointer: ^TMytype;      // a pointer to a variable of type TMyType, or
    MyTypePointer: PMyType;       //   the same, but with usage of "PMyType"
    // etc...

The "@" operator.

The "@" operator constructs a pointer to its operand. E.g.:
var Bte1: byte;
    BytePointer : ^byte;	// a pointer to a byte
...
  BytePointer  := @Bte1;        // "@Bte" is a pointer to "Bte", and that value is assigned to "BytePointer"
See below for many examples of its usage.

Accessing variables pointed to.

A variable is accessed through its pointer as follows:
// declaration of the variables 
var Bte1, Bte2: byte;
    Wrd1, Wrd2: word;
...
// declaration of pointers to their types
var BytePointer : ^byte;	// a pointer to a byte
    WordPointer : ^word;	// a pointer to a word
...
  BytePointer  := @Bte1;        // Bytepointer does point now to variable Bte1
  BytePointer^ := 10;		// Bte1 is filled with the value 10;
  Bte2         := BytePointer^; // Bte2 gets the value 10 also ("BytePointer" still points to "Bte1")

  WordPointer  := @Wrd1;        // Bytepointer does point now to variable Wrd1
  WordPointer^ := 10000;	// Wrd1 is filled with the value 10000;
  Wrd2         := WordPointer^; // Wrd2 gets the value 10000 also ("WordPointer" still points to "Wrd1")

  // etc...

Pointers to Records.

Using a pointer to a record is also not difficult. Example:
type TMyRec = record
                Bte: byte;
                Wrd: word;
                Str: string[2];
              end;

var MyRec  : TMyRec;     // single TmyRec variable
    PMyRec : ^TMyRec;    // pointer to a single TMyRec variable

    Bte    : byte;
    Wrd    : word
    Str    : String[2];
...
  PMyRec := @MyRec;	  // PMyrec points now to variable MyRec
  PMyRec^.Bte := 25;      // MyRec1 is filled vith values
  PMyRec^.Wrd := 1000;
  PMyrec^.Str := 'ab';

  Bte := PMyRec^.Bte;     // Get the values from MyRec (PMyRec still points to MyRec)
  Wrd := PMyRec^.Wrd;
  Str := PMyRec^.Str;

Pointers to Arrays.

Arrays can be approached via pointers in several ways:

- via a pointer to the whole array

This is the simplest way:
const ARRAY_SIZE = 5;
type  TWrdArr    = array[ARRAY_SIZE] of word;
var   WrdArr     : TWrdArr;   // wordarray
      PWrdArr    : ^TWrdArr;  // pointer to a word array
      I          : byte;
      Wrd        : word;
...
  PWrdArr := @WrdArr;        // PWrdArr points to the word array now
  for I := 0 to ARRAY_SIZE - 1 do
  begin
    PWrdArr^[I] := I + 100;  // fill one of the array elements a value
  end;

  for I := 0 to ARRAY_SIZE - 1 do
  begin
    Wrd := PWrdArr^[I];      // get the value of one of the array elements
  end;

- via a pointer to an element of the array

This is a little bit more difficult:
const ARRAY_SIZE = 5;
type  TWrdArr = array[ARRAY_SIZE] of word;
var   WrdArr  : TWrdArr;   // wordarray
      PWrd    : ^word;     // a pointer to a word
      I       : byte;
      Wrd     : word;
...
  PWrd := @WrdArr;        // PWrd points to the first element in WrdArr now
  for I := 0 to ARRAY_SIZE - 1 do
  begin
    PWrd^ := I + 200;     // give the element a value
    inc(PWrd); // or PWrd := PWrd + 1; // point to next --> word <-- in the array
  end;

  PWrd := @WrdArr;        // PWrd points to the first element in WrdArr now
  for I := 0 to ARRAY_SIZE - 1 do
  begin
    Wrd := PWrd^;         // get a value from the array element
    inc(PWrd); // or PWrd := PWrd + 1; // point to next --> word <-- in the array
  end;
Important: In above example you can see the pointer PWrd is always incremented by 1 to point to the next element in the array. mP knows that, since the pointer points to a word, it has to move the pointer 2 bytes to point to the next word. The same principle is of course if one want to access the previous element in the array.
So, the compiler takes into account the necessary pointer arithmetic. For the mP user it is a simple increment (or +1) operation.

Pointer typecasting.

Pointers to different types of variables are per difinition incompatible, one can not assign the value of one type of pointer to another type of pointer without getting the "Suspicious pointer conversion" warning from the compiler.
In this case "Typecasting" is needed: the conversion from one pointer type to another. This is done in the usual way, e.g.:
var SomeTypePointer : ^SomeType;
  ...
  SomeTypePointer :=^SomeType(SomeOtherTypePointer); // typecasting from one type of pointer to another
where "Sometype" can be a predefined one (like ^byte) or one that has been defined by you. Example:
type TWordArr: array[2] of word;  // TWordArray is an "array of word" type
var  PWrdArr : ^TWordArr;         // PWordArr is a pointer variable to that type (so, a pointer to an "array of word" variable)
...
  RealVar := 100.05;
  PWrdArr := ^TWordArr(@RealVar); // <--- typecasting from ^real to ^TWordArr, or
  PWrdArr := PWrdArr(@RealVar);   //   the same, only with usage of the pointertype "PWrdArr"
Attention: Above pointer typecasting is available from mP v3.80 onwards.

In older mP versions the following can be done: The type "word" can be used, because it is compatible with any pointer type. E.g:
  SomeTypePointer := word(SomeOtherTypePointer); // typecasting from one type of pointer to another
The only debt to pay for doing that is some kind of message about implicite or suspicious pointer conversion.

Note: there is no generic "Pointer" type in mikroPascal as there is in Delphi.

Accessing a variable one byte at a time.

In some cases there are operators for that (like "highest"), but in case they can not be used (e.g. when getting a variable out of Eeprom, always done byte per byte), you can do it via pointers.
Example: accessing a word (to keep it simple) byte per byte:
var Wrd: word;
    PByte: ^byte;
...
  PByte  := ^byte(@Wrd); // PByte points to the lower byte of Wrd, note the casting from "wordpointer" to "bytepointer"
  PByte^ := $E8;         // give it the value $E8
  inc(PByte);            // point to the next byte (the higher byte of Wrd)
  PByte^ := $3;          // give it the value $3
In above example Wrd gets the value 1000 ($3E8 in hex).
This technique can be used for any type of variable. Always keep into account the number of bytes in that variable though!

Accessing a variable as being another type.

This was already illustrated in previous section, where a word was accessed byte per byte, thus as an "array of byte". This principle can also be used for other combinations, e.g.
type TWordArr: array[2] of word;
var  PWrdArr : ^TWordArr;
     RealVar : real;
     WordArr : TWordArr;
     I       : byte;
...
  RealVar := 100.05;
  PWrdArr := ^TWordArr(@RealVar); // <--- typecasting needed from ^real to ^TWordArr

  // get the values from RealVar as if RealVar was an array of word
  for I := 0 to 1 do
  begin
    WordArr[I] := PWrdArr^[I];
  end; 
Above example shows how to acces a real (which has 4 bytes or 2 words) as an array of words.
mP does not accept 2 different pointer types as being compatible. This is also the case here: mP does not allow PWrdArr (a pointer to a word array) to be filled with the address of a real (RealVar), so "typecasting" is needed here. See above section "Pointer Typecasting".
Always take into account the size of the variable. Make sure you do not access "outside" it.

Nil.

In Pascal a pointer that points to "nothing" (= "unassigned") should have the value "nil", e.g.:
  BytePointer  := nil; // make the pointer point to "nothing"

Pointer Arithmetic.

Pointer arithmetic is something special. It deviates from the normal arithmetic in its limitations and properties.

The most important deviating property: addition/subtraction a integer value to a pointer advances/backups the pointer with the size of the variable it points to. This means that e.g. adding 1 to a pointer makes it point to the next "variable" it points to, not to the next byte.
Also making the difference of two pointers gives the difference in number of "variables", not number of bytes. The following arithmetic operations are allowed:
Examples:

- Assignment:
  PointerVar := nil;                   // set pointer to "nil" (not assigned)
  PointerVar := @RealVar;
  PointerVar := $400;
  PointerVar := OtherPointerVar;       // the two pointers must be of the same type
  PointerVar := OtherPointerVar + 10;  // the two pointers must be of the same type, see below for addition/subtraction
- Addition/subtraction of a value to/from a pointer:
  PointerVar := PointerVar + 5;        // makes the pointer point 5 "variables" further = increment of 5 * sizeOf(the type the pointer points to)
  PointerVar := PointerVar - 1;        // makes the pointer point 1 "variable" back 
  inc(PointerVar);                     // makes the pointer point to the next variable
  dec(PointerVar);                     // makes the pointer point to the previous variable
- Comparison with zero ("nil"):
  if PointerVar =  nil then...;        // test for "nil" (not assigned)
  if PointerVar <> nil then...;        // test for non nil (assigned)
  if PointerVar = 20 then...;          // this is illegal, only comparison to zero is allowed (typecast to word needed)
- Comparison of 2 pointers:
  if PointerVar1 =  PointerVar2 then ...;
  if PointerVar1 >  PointerVar2 then ...;
  if PointerVar1 <> PointerVar2 then ...;
  if PointerVar1 <  PointerVar2 then ...;
  // etc...
Difference of 2 pointers:
  IntVar := PointerVar1 - PointerVar2;  // gives the difference in "variables", not in bytes
Other operations than those above are not allowed.

If two pointers are involved in above operations then they should be of the same type (unless typecasted). They should also (in principle) point to the same entity in memory e.g. the same array of some type.

Additionally one should also remember that "@Variable" is a pointer, e.g.:
var PWrd: ^Word;
    Wrd:  word; 
...  
  PWrd := @Wrd + 5;        // here "@Wrd" is word pointer. So, "pointer" arithmetic is used and the pointer is 
                           //   moved 5 times SizeOf(word), it points 5 "word" variables furher. 


Pointer related Warnings by the compiler.

There are a number of warnings given by the compiler due to not correct handling of pointers. The 2 most common are: The first one signals in fact an implicit typecast from one pointer type into another pointertype, the second one an implicit typecast from an integer (or word) value into a pointertype.
Both can be avoided by explicite typecasting, see here.

-------------------------------------------