Skip to content
Part IA Michaelmas Term

Pointers versus References

What Is a Pointer?

In languages like C and C++, a pointer is a variable that holds a raw memory address — a numeric index into the process’s virtual address space. Pointers give the programmer direct, unmediated control over memory:

int x = 42;
int* p = &x;       // p holds the address of x
*p = 99;           // dereference: write through the pointer
p++;               // pointer arithmetic: now p points elsewhere
int* q = (int*)0;  // null pointer — raw zero

Because a pointer is essentially an integer, you can:

  • Increment and decrement it (move to the next/previous object in memory)
  • Subtract two pointers to find the distance between addresses
  • Cast a pointer to a different type (telling the compiler to reinterpret the memory)
  • Cast an integer to a pointer (or vice versa)
  • Point into the middle of an object or array
  • Free the memory and keep the pointer (a dangling pointer)

These operations are powerful but dangerous. They enable buffer overruns, use-after-free bugs, double-free bugs, type confusion, and arbitrary memory reads and writes — the root causes of most critical security vulnerabilities in C/C++ software.

What Is a Java Reference?

A Java reference is a managed, restricted handle to a heap object. It is implemented as a memory address under the hood, but the language and JVM enforce strict rules that prevent unsafe operations:

  • No arithmetic: you cannot add to or subtract from a reference. It points at the start of an object and only at the start.
  • No integer casting: you cannot cast a reference to int or long to obtain the underlying address.
  • No arbitrary dereferencing: you cannot “follow” a reference to an arbitrary address. The only operation is field access and method invocation on a known type.
  • No pointer into middle of object: a reference always designates an entire object, never a byte offset within it.
  • No manual deallocation: you cannot free an object. The JVM’s garbage collector determines when an object is truly unreachable and reclaims it automatically.

The Box-and-Arrow Model

When reasoning about Java references, use the box-and-arrow model:

  • A box is a variable (the space in a stack frame or inside an object on the heap).
  • An arrow points from the box to an object on the heap.
    box                   heap object
  ┌───────┐            ┌──────────────┐
  │   s   │──────────→│   "hello"    │
  └───────┘            └──────────────┘

The variable s does not contain the string; it contains an arrow (reference) to the string object. Assignment, parameter passing, and comparison all operate on the arrow, not on the object.

This model is essential for understanding:

  • Why s1 == s2 compares arrows (do they point at the same object?), not string contents
  • Why a = b makes a point at the same object as b
  • Why passing an object reference to a method does not copy the object

Why Java Banished Pointer Arithmetic

Pointer arithmetic is the root enabler of many memory-safety bugs. By removing it entirely, Java eliminates at the language level:

VulnerabilityHow Java Prevents It
Buffer overrunArray access is bounds-checked at runtime; no pointer arithmetic to bypass bounds
Dangling pointer (use-after-free)No manual free; GC only reclaims unreachable objects
Double freeNo manual deallocation
Type confusion via castCasts are checked at runtime; no reinterpretation of raw memory
Arbitrary memory read/writeNo integer-to-pointer casts; no pointer arithmetic

This is the core of Java’s claim to being a “safe” language. The trade-off is loss of fine-grained control over memory layout and allocation — you cannot implement a custom memory allocator or data structure that relies on pointer arithmetic directly in Java (though you can use sun.misc.Unsafe or JNI for low-level work, bypassing the safety guarantees).

Reference Types vs Primitive Types

In Java, variables of reference types (class types, interface types, array types, String) hold references. Variables of primitive types (int, double, boolean, char, byte, short, long, float) hold the actual values directly.

int a = 5;        // a holds 5
int b = a;        // b gets a copy of 5 — independent
String s = "hi";  // s holds a reference to the String object
String t = s;     // t gets a copy of the reference — both point to the same String

Primitives live in the stack frame (or inside an object on the heap as a field). References live in the stack frame (or inside an object), but the objects they refer to always live on the heap.

Null

A reference that points at no object is written null. In the box-and-arrow model, null is an arrow that points at nothing. Attempting to call a method or access a field through null throws NullPointerException. This is the most common runtime exception in Java, and it signals a bug — the programmer expected a reference to be non-null when it was not.