Dynamic Memory Management
Ordinary Pascal variables live on the stack or in static storage with sizes fixed at compile time, but many programs need structures whose size is unknown until runtime — a linked list that grows as items are added, or a tree with an unpredictable number of nodes. Pascal handles this with pointers and the heap: a pointer variable, declared as ^SomeType, holds the memory address of a dynamically allocated block rather than the data itself, and the heap is the runtime memory pool from which such blocks are carved out on demand.
Cricket analogy: A fixed-size stack array is like a pre-printed scorecard with exactly 11 batting slots, while the heap is like extra loose-leaf paper you pull out only when an unusual number of substitutes need recording — a pointer is the note in the margin saying which extra sheet to check.
Pointers and the ^ Operator
A pointer type is declared with a caret before the base type, such as PNode = ^TNode;, and a pointer variable is created with New(p), which allocates enough heap memory for one TNode and sets p to point at it. The caret after a pointer variable, as in p^.Data, dereferences it — meaning 'follow the address and access the data there' — while p alone (without the trailing caret) refers to the address itself, a distinction that trips up many beginners.
Cricket analogy: A pointer is like a locker number written on a team sheet — the number itself (p) is just a reference, but going to that locker and opening it (p^) is where the actual kit lives.
type
PNode = ^TNode;
TNode = record
Data: Integer;
Next: PNode;
end;
var
head, p: PNode;
begin
New(head);
head^.Data := 10;
head^.Next := nil;
New(p);
p^.Data := 20;
p^.Next := nil;
head^.Next := p; { link the two nodes }
p := head;
while p <> nil do
begin
Writeln(p^.Data);
p := p^.Next;
end;
{ free every allocated node }
while head <> nil do
begin
p := head^.Next;
Dispose(head);
head := p;
end;
end.Dynamic Data Structures
Linked lists, trees, and graphs are built by giving each record a pointer field to its neighbors — a Next field for a singly linked list, or Left/Right fields for a binary tree — so the structure can grow one node at a time via repeated New calls, without ever needing to know the final size in advance. Traversal simply follows the pointer chain, checking for nil (the sentinel meaning 'no further node') to know when to stop.
Cricket analogy: A linked list is like a chain of runners in a relay-style baton handoff drill — each runner (node) only knows who's next, and you follow the chain until you reach the last runner, who holds nothing (nil).
Always initialize a new node's pointer fields (like Next := nil) immediately after New(p) — freshly allocated heap memory contains leftover, unpredictable bytes, not automatically zeroed values, so an uninitialized pointer field can look like a valid address and cause a crash or corruption when dereferenced later.
Memory Leaks and Dangling Pointers
Every New(p) call must eventually be matched with a Dispose(p) call once the node is no longer needed; forgetting to do so is a memory leak, where heap memory stays reserved for data nothing can reach anymore, slowly exhausting available memory in a long-running program. The opposite danger is a dangling pointer: if you Dispose(p) but keep using p^ afterward (or keep a second pointer that pointed at the same freed block), you're reading or writing memory that may have already been reused for something else entirely, producing unpredictable corruption.
Cricket analogy: Forgetting to Dispose a node is like a ground crew never removing old, unused practice nets after the season — they keep occupying space nobody uses, until eventually there's no room left to pitch new nets.
After calling Dispose(p), immediately set p := nil unless the variable goes out of scope right away. This turns any accidental later use of p^ into a detectable nil-pointer error instead of silent, hard-to-debug corruption from a dangling pointer that happens to still 'look' valid.
- Pointers hold memory addresses;
^Typedeclares a pointer type andp^dereferences it to reach the actual data. New(p)allocates heap memory for one instance of the pointed-to type;Dispose(p)frees it.- Linked structures (lists, trees) are built from records containing pointer fields to neighboring nodes, terminated by nil.
- Freshly allocated memory is not zeroed — always initialize pointer fields explicitly after New.
- Every New must be matched by a Dispose, or the unreachable memory becomes a leak.
- Using a pointer after Dispose (a dangling pointer) causes undefined, unpredictable behavior.
- Setting a pointer to nil immediately after Dispose makes accidental reuse fail safely and visibly.
Practice what you learned
1. What does the trailing caret in `p^.Data` do?
2. Which procedure allocates heap memory for a pointer variable?
3. What is a memory leak?
4. What is a dangling pointer?
5. Why should a pointer field be explicitly initialized right after New(p)?
Was this page helpful?
You May Also Like
Object Pascal Basics
Get started with Object Pascal's classes, methods, constructors, and inheritance — the object-oriented extension of standard Pascal.
File Handling in Pascal
Master reading and writing text, typed, and untyped files in Pascal, along with safe error handling for I/O operations.
Exception Handling in Pascal
Learn how try..except and try..finally let Object Pascal programs handle errors gracefully and guarantee cleanup.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics