Pointers in Delphi

Pointers in Delphi

Pointers are a special type of variable. Like a meta-variable. They can point to other variables, or to memory. You might use a record pointer, for example, to point to a block of memory where you have stored lots of record data. You would then use the pointer just as if it were a record variable. The Pointer type provides a general use pointer to any memory based variable. That is, one that is accessed by reference. 

Delphi provides a number of typed pointer types, such as PChar, and PExtended, along with a generic, 'point to anything' type - the Pointer type.

var
  generalPtr : Pointer;  // A pointer to anything
  formPtr    : ^TForm;   // A pointer to a form object

begin
  // The current unit's form is addressable via the self keyword
  generalPtr := Addr(self);

  // We can assign this pointer to the form pointer
  formPtr := generalPtr;

  // And set the form caption to show this
  formPtr.Caption := 'Test program';
end;

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. It is normally always better to use a specific pointer reference to the data type you are using.

Different Pointer types in Delphi

PAnsiChar: A pointer to an AnsiChar value
PAnsiString: Pointer to an AnsiString value
PChar: A pointer to an Char value
PCurrency: Pointer to a Currency value
PDateTime: Pointer to a TDateTime value
PExtended: Pointer to a Extended floating point value
PInt64: Pointer to an Int64 value
PShortString: A pointer to an ShortString value
PString: Pointer to a String value
PVariant: Pointer to a Variant value
PWideChar: Pointer to a WideChar
PWideString: Pointer to a WideString value

A simple example using PChar

The PChar type can be used to scan along a string :
 
 var
   myString  : string;
   myCharPtr : PChar;
   i : Integer;

 begin
   // Create a string of Char's
   myString  := 'Hello World';

   // Point to the first character in the string
   i := 1;
   myCharPtr := Addr(myString[i]);

   // Display all characters in the string
   while i <= Length(myString) do
   begin
     ShowMessage(myCharPtr^);  // Display the string characters one by one
     Inc(i);
     Inc(myCharPtr);
   end;
 end;

There are two things to note here. First the use of Addr function to get the address of the string. You could equally use the @ operator. 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 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 ^.
 
Let us create a full record example :
 
 type
   TMyRecord = Record
     name : String[20];
     age  : Integer;
   end;

 var
   myRecord    : TMyRecord;
   myRecordPtr : ^TMyRecord;

 begin
   myRecord.name := 'Fred Bloggs';
   myRecord.age  := 23;

   myRecordPtr := @myRecord;

   ShowMessage(myRecordptr.name);  // Displays 'Fred Bloggs'
 end;

When we simply refer to the record field name, without a ^, Delphi is in fact adding one for us - it recognises what we are doing, and helps us make for more readable code.