Pointers in mikroPascal.
This article is about pointers to variables in the PRO version of mikroPascal.
Table of content
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.
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 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.
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...
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;
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.
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.
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!
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.
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 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:
- assigning an integer value to a pointer
- assigning one pointer to another (pointers must be of the same type)
- comparing two pointers
- comparing a pointer to zero ("nil")
- adding or subtracting an integer value to or from a pointer
- subtracting (making the difference of) 2 pointers (pointers must be of the same type)
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.
There are a number of warnings given by the compiler due to not correct handling of pointers. The 2 most common are:
- Suspicious pointer conversion
- Implicit typecast of integral value to pointer
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.
-------------------------------------------