In Object Pascal, most memory management is handled by the system (the Stack) or by the object model (the Heap). However, Pointers allow you to work directly with memory addresses, giving you low-level control when needed.
1. What is a Pointer?
A Pointer is a variable that stores a memory address. Instead of holding data (like the number 42), it holds the address of the location where the data is stored (like “address 0x7FFC00A8”).
2. Pointer Declaration and Syntax
A. The Caret (^) Operator
In Pascal, the caret operator (^) is used for two main purposes:
- Declaration: Declaring a variable as a pointer to a specific type.
- Dereferencing: Accessing the data stored at the memory address the pointer holds.
program PointerDemo;
type
PInteger = ^Integer; // PInteger is a pointer type that points to an Integer
PChar = ^Char; // PChar is a pointer type that points to a Char
var
IPtr: PInteger; // IPtr is a pointer variable of type PInteger
I: Integer; // A standard Integer variable
B. The Address Operator (@)
The commercial at symbol (@) is used to get the memory address of any variable or routine.
begin
I := 42; // I holds the value 42 (on the Stack)
// 1. Get the Address: Assign the memory address of I to the pointer IPtr
IPtr := @I;
// 2. Dereference: Access the value stored AT the address held by IPtr
Writeln('Value via pointer: ', IPtr^); // Output: 42
// 3. Modify via Pointer: Change the value at the memory address
IPtr^ := 100;
Writeln('Original I after change: ', I); // Output: 100
end.
3. Untyped Pointers (Pointer)
The standard type Pointer (with a capital P) is a special untyped pointer.
- Usage: It can hold the address of any data type.
- Limitation: You cannot dereference an untyped pointer (
Pointer^) directly, as the compiler doesn’t know the size or type of data it holds. You must first cast it to a typed pointer (e.g.,PInteger) before dereferencing.
var
VoidPtr: Pointer; // Untyped
begin
VoidPtr := @I; // OK, can hold the address of I
// To use it, cast it:
Writeln('Value via cast: ', PInteger(VoidPtr)^);
end.
4. Dynamic Allocation and Deallocation
Pointers are the primary tool for manually allocating and freeing memory on the Heap. This is necessary when working outside of the Object Model (classes).
| Procedure | Purpose | Location |
New(Ptr) | Allocates memory on the Heap sufficient for the pointer’s type and assigns the address to Ptr. | Heap |
Dispose(Ptr) | Frees the memory block on the Heap pointed to by Ptr. | Heap |
var
HPtr: PInteger; // Pointer to an Integer
begin
// 1. Allocate Heap Memory
New(HPtr); // Allocates 4 bytes on the Heap and sets HPtr to point to it
// 2. Access and use the Heap memory via dereferencing
HPtr^ := 500;
Writeln('Heap value: ', HPtr^);
// 3. Deallocate Memory (Crucial for avoiding memory leaks!)
Dispose(HPtr);
// HPtr is now a dangling pointer (still holds an address, but the memory
// is no longer allocated to us). It should be set to nil for safety.
HPtr := nil;
end.
5. Pointers in Modern Object Pascal
While manual pointer usage is rare for day-to-day coding in modern Delphi/Lazarus, the concepts are vital because:
- Objects are Pointers: An object variable (
TClient) is internally a pointer to a block of memory on the heap. When you writeClient.Name := 'X', the system is actually performing a dereference. - Interoperability: Pointers are essential when interacting with external libraries (like DLLs or the Windows API) written in languages like C/C++, which heavily rely on passing memory addresses.
