Professional Documents
Culture Documents
However, with the advent of Object Orientation, things have changed somewhat. For example, the TStringList class allows a list of
strings to be built without the user needing to manage the storage. And hence, removing the need to use pointers to do this.
Additionally, the Pascal language has also evolved to avoid dynamic storage management by coders - for example with the advent of
dynamic arrays. You can now simply code a SetLength call to set or increase the size of such an array as your program runs.
However, there are still some situations where pointers are valid in Delphi.
When calling Windows APIs (Application Programmer Interfaces), we are obliged to use pointers.
Types of pointers
Delphi provides a number of typed pointer types, such as PChar, and PExtended, along with a generic, 'point to anything' type -
the Pointer type.
The nice thing about the typed pointers is that they work sensibly with the Inc and Dec functions. Incrementing an PInt64 pointer
will add SizeOf(Int64) bytes to the pointer address so that it points to the next Int64 variable in memory.
The Pointer type is a dangerous one - it falls foul of Delphi's normally tight type handling. Use it with care, or you will end up
addressing the wrong memory.
begin
// Create a string of Char's
myString := 'Hello World';
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 PInt64^, for example, will give us an Int64 value. This is where the typing comes in.
Record pointers
You can define a pointer to any data type using a different technique:
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 ^.
type
TMyRecord = Record
name : String[20];
age : Integer;
end;
var
myRecord : TMyRecord;
myRecordPtr : ^TMyRecord;
begin
myRecord.name := 'Fred Bloggs';
myRecord.age := 23;
myRecordPtr := @myRecord;
The class uses pointers to help store the numbers in a block of memory, reallocating this block when it is all used up.
var
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 : PInt64; // Points to the next free slot in memory
const
ALLOCATE_SIZE = 20; // How many numbers to store in first memory block
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.
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 :
unit NumberList;
interface
type
TNumberList = class
private
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 : PInt64; // Points to the next free slot in memory
public
property Items[index : Integer] : Int64
read GetValue; default; // Default means we can use the list[i]
published
constructor Create;
destructor Destroy; override;
procedure Add(const number : Int64);
implementation
const
ALLOCATE_SIZE = 20; // How many numbers to store in first memory block
// 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;
end.
And here is how the code could be used :
var
list : TNumberList;
value : Int64;
i : Integer;
begin
// Create a number list object
list := TNumberList.Create;
// Add the first 30 even numbers to the list, each doubled in size
for i := 0 to 29 do
list.Add(i * 2);