100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Dynamic Memory Management

Understand pointers, the heap, and how to allocate, use, and free dynamic memory safely in Pascal.

Advanced PascalAdvanced11 min readJul 10, 2026
Analogies

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.

pascal
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; ^Type declares a pointer type and p^ 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

Was this page helpful?

Topics covered

#Programming#PascalStudyNotes#DynamicMemoryManagement#Dynamic#Memory#Management#Pointers#StudyNotes#SkillVeris#ExamPrep