A simple example using PChar:
The PChar type can be used to scan along a string :
Simba Code:
program new;
var
myString: string;
myCharPtr: PChar;
i: Integer;
begin
myString := 'Hello World';
i := 1;
myCharPtr := @ myString[i];
while i <= Length(myString) do
begin
WriteLn(myCharPtr^);
Inc(i);
Inc(myCharPtr);
end;
end;
There are two things to note here. First the use of @ to get the address of the string. Pointers always work with addresses - the address of a variable here, or a block of acquired memory. Here we point the PChar value to the first character in the string.
Secondly, now that we have a pointer, we use the ^ character at the end of the pointer name to refer to what the pointer points to. In this case, a character.
A PChar^ will always give us a character. A PInteger^, for example, will give us an Integer value. This is where the typing comes in.
Record pointers:
You can define a pointer to any data type using a different technique:
Simba Code:
var
myRecordPtr : ^TMyRecord;
Here, the ^ symbol is used to dereference the type - we are saying that we do not have a TMyRecord type, but a pointer to one. Note that this is a prefix use of ^.
Let us create a full record example :
Simba Code:
program new;
type
TMyRecord = record
name: string [20];
age: Integer;
end;
var
myRecord: TMyRecord;
myRecordPtr: ^ TMyRecord;
begin
myRecord.name := 'John Doe';
myRecord.age := 23;
myRecordPtr := @myRecord;
WriteLn(myRecordptr^.name);
end;
When we simpy refer to the record field name, without a ^, Lape is in fact adding one for us - it recognises what we are doing, and helps us make for more readable code.
A full memory handling example:
In this example, we'll build a new class that is a limited number list equivalent to the TStringList class. This class will allow you to keep adding numbers to a list of numbers.
The class uses pointers to help store the numbers in a block of memory, reallocating this block when it is all used up.
First off, we will look at the constructor :
Simba Code:
const
ALLOCATE_SIZE = 20; // How many numbers to store in first memory block
type
PInt = ^Integer;
type
TNumberList = record
msCount : Integer; // Count of numbers in the list
maxCount : Integer; // Maximum numbers that can fit into current storage
memStart : Pointer; // Start of the memory holding the list
nextSlot : PInt; // Points to the next free slot in memory
end;
procedure TNumberList.Init;
begin
msCount := 0; // No numbers in the list yet
// Allocate space for a limited number of numbers
memStart:=GetMem(ALLOCATE_SIZE * SizeOf(Integer));
// Indicate how many numbers that we can add before acquiring more memory
maxCount := ALLOCATE_SIZE;
// And point to the next free memory slot - the first!
nextSlot := memStart;
end;
The role of the constructor is to initialise the class(record). The key part of this is to allocate a block of memory that can hold 20 numbers.
The GetMem call allocates storage of the desired size, setting the memStart generalised Pointer variable to the starting address of the memory allocated. Note that GetMem insists on a Pointer variable.
We'll add a routine to add a value to the memory :
Simba Code:
// Add a number to the list
procedure TNumberList.Add(number : Integer);
begin
// Store the number at the next slot in our memory block
nextSlot^ := number;
// And update things to suit
Inc(msCount);
Inc(nextSlot);
end;
The passed number is stored in the next Int64 slot in our memory block, and this nextSlot pointer incremented. Note that this adds SizeOf(Int64) bytes to the address value in this pointer, because the Inc call knows the type of this pointer.
And here is a routine for retrieving a value :
Simba Code:
// Get the number at the index position (starting at 0)
function TNumberList.GetValue(index : Integer): Integer;
var
numberPtr : PInt;
begin
// Simply get the value at the given Integer index position
numberPtr := memStart;
Inc(numberPtr, index); // Point to the index'th Integer number in storage
Result := numberPtr^; // And get the Integer number it points to
end;
Here we use Inc to add index Integer size bytes to the start of memory to get to the slot of the required memory.
However, we have not yet covered the situation where the memory we allocate is all used up.
We will extend the Add routine to do just this :
Simba Code:
// Add a number to the list
procedure TNumberList.Add(const number : Int64);
var
newMemoryStart : Pointer;
oldPtr, newPtr : PInt;
i : Integer;
begin
// if we do not have enough space to add the number, then get more space!
if msCount = maxCount then
begin
// First allocate a bigger memory space
newMemoryStart:=GetMem((maxCount + ALLOCATE_SIZE) * SizeOf(Integer));
// Copy the data from the old memory here
oldPtr := memStart;
newPtr := newMemoryStart;
for i := 1 to maxCount do
begin
// Copy one number at a time
newPtr^ := oldPtr^;
Inc(oldPtr);
Inc(newPtr);
end;
// Free the old memory
FreeMem(memStart);
// And now refer to the new memory
memStart := newMemoryStart;
nextSlot := memStart;
Inc(nextSlot, maxCount);
Inc(maxCount, ALLOCATE_SIZE);
end;
// Now we can safely add the number to the list
nextSlot^ := number;
// And update things to suit
Inc(msCount);
Inc(nextSlot);
end;
Here we abandon our old memory block , and create a bigger one. Having allocated it, we must copy the old memory contents to it. Here we see a new concept - assigning the value referred by one pointer to the contents of memory pointed to by another. Delphi knows to copy the whole Integer value rather than just one byte because these are PInt pointers.
Below is the full code of the class :
Simba Code:
program new;
const
ALLOCATE_SIZE = 20;
type
PInt = ^Integer;
type
TNumberList = record
msCount: Integer;
maxCount: Integer;
memStart: Pointer;
nextSlot: PInt;
end;
procedure TNumberList.Init;
begin
msCount := 0;
memStart := GetMem(ALLOCATE_SIZE * SizeOf(Integer));
maxCount := ALLOCATE_SIZE;
nextSlot := memStart;
end;
procedure TNumberList.Destroy;
begin
FreeMem(memStart)
end;
procedure TNumberList.Add(const number: Int64);
var
newMemoryStart: Pointer;
oldPtr, newPtr: PInt;
i: Integer;
begin
if msCount = maxCount then
begin
newMemoryStart := GetMem((maxCount + ALLOCATE_SIZE) * SizeOf(Integer));
oldPtr := memStart;
newPtr := newMemoryStart;
for i := 1 to maxCount do
begin
newPtr ^ := oldPtr ^;
Inc(oldPtr);
Inc(newPtr);
end;
FreeMem(memStart);
memStart := newMemoryStart;
nextSlot := memStart;
Inc(nextSlot, maxCount);
Inc(maxCount, ALLOCATE_SIZE);
end;
nextSlot ^ := number;
Inc(msCount);
Inc(nextSlot);
end;
function TNumberList.GetValue(index: Integer): Integer;
var
numberPtr: PInt;
begin
numberPtr := memStart;
Inc(numberPtr, index);
Result := numberPtr ^;
end;
And here is how the code could be used :
Simba Code:
var
list: TNumberList;
value: Integer;
i: Integer;
begin
list.Init;
try
for i := 0 to 29 do
list.Add(i * 2);
value := list.GetValue(22);
WriteLn('22nd value = ' + IntToStr(value));
finally
List.Destroy;
end;
end;
The end.
Well, this original article I was found in Google a many years ago, and that is very helpful for me. I was reworked that for simba's lape, and all examples is work as it is written in the article.
Happy scripting!
Cheers,
Cynic.