Pointers & References
Why Do We Need Pointers and References?
In the previous chapters, we learned that:
- programs store data in memory,
- memory locations have addresses,
- the stack manages function calls,
- the heap stores dynamically allocated data.
Now consider this:
How can one piece of data refer to another piece of data?
This is where pointers and references become important.
They allow a program to work with data indirectly rather than always working with the data itself.
This idea is fundamental to many data structures:
Linked List ↓Tree ↓Graph ↓Dynamic data structuresIf you understand pointers and references well, these structures become much easier to visualize.
What Is a Memory Address?
Every addressable location in memory has an address.
Address Value 0x1000 420x1004 170x1008 91Suppose the value 42 is stored at 0x1000. Then the value tells us what is stored, and the address tells us where it is stored.
This distinction is the foundation of pointers.
What Is a Pointer?
Definition
A pointer is a variable that stores the memory address of another value or object.
Instead of storing the actual data, a pointer stores information about where that data can be found.
So a pointer stores an address, and that address points to data. That is why it is called a pointer: it points to another location.
Pointers in C++
C++ gives us direct access to pointers.
int score = 100;int* p = &score;Here score holds 100, and p holds the address of score.
The & operator gets the address of score. The * in int* p declares p as a pointer to an integer.
The pointer does not contain 100. It contains the address where 100 is stored.
Dereferencing a Pointer
Knowing an address is useful only if we can use it to access the value stored there. This is called dereferencing.
int score = 100;int* p = &score; cout << *p; // 100The *p means: go to the memory location stored inside p and access the value there.
p↓0x1000↓memory at 0x1000↓100So there are two different operations:
&score → address of a variable*p → value at the address stored in a pointerBe careful: the meaning of * depends on where it is used. In int* p it declares a pointer. In *p it dereferences one.
Pointer arithmetic
In C++ you can add to a pointer, and the result is not what plain arithmetic suggests:
int* p = &numbers[0]; // suppose this is address 1000 p + 1 // 1004, not 1001Adding 1 advances by one element, not one byte - four bytes here, because that is what an int occupies. The compiler knows the pointed-to type, so it scales the step for you.
That is the same base + index × size arithmetic the Memory chapter used for array indexing, which is not a coincidence: in C++ numbers[i] is defined as *(numbers + i). Array indexing is pointer arithmetic with nicer syntax.
Java has no pointer arithmetic at all. There is no way to ask for "the reference one past this one", which is precisely why you cannot run off the end of an array by accident.
Reaching a member: the arrow
When a pointer points at something with fields, dereferencing and then reading a field is so common that C++ gives it its own operator:
Node* n = new Node(); (*n).value = 10; // correct, and nobody writes thisn->value = 10; // the same thing, and what you will actually seen->value is pure shorthand for (*n).value: dereference the pointer, then take the field. The parentheses in the long form are not optional, because . binds tighter than * - which is exactly the sort of thing the arrow exists to stop you getting wrong.
Java has no arrow. A reference is dereferenced implicitly, so you write n.value and the runtime does the indirection for you. Every linked list, tree and graph in the chapters ahead is written one of these two ways.
Changing Data Through a Pointer
A pointer can also be used to modify the value it points to.
int score = 100;int* p = &score; *p = 200; // score is now 200Why? Because p points to the memory location containing score. The pointer gives us another way to access the same data.
Multiple Names, One Value
Consider:
int score = 100;int* p = &score;Now both score and *p access the same value.
┌─────────┐score ───→│ 100 │ └─────────┘ ↑ │p ────────────┘There is still only one value. There are simply two ways to reach it.
This idea becomes extremely important when working with linked lists, trees, and graphs.
What Is a Reference?
Pointers are not the only way to refer to existing data. Many languages provide references.
Definition
A reference is another name or handle used to access an existing value or object.
Unlike a pointer, a reference is generally designed to be used as an alias rather than as an explicitly manipulated memory address.
For example, C++ supports references:
int score = 100;int& ref = score; ref = 200;cout << score; // 200Both score and ref refer to the same integer, so changing one changes the underlying value.
Pointer vs Reference
Pointers and references are related, but they are not the same thing.
| Pointer | Reference |
|---|---|
| Stores an address | Acts as an alias to existing data |
| Can usually be changed to point elsewhere | Usually remains bound to the same object |
| Can represent "points to nothing" | Typically must refer to an existing object |
| Can be dereferenced explicitly | Used like the original variable |
| Supports pointer arithmetic in languages such as C++ | Does not support pointer arithmetic |
| More explicit and flexible | Simpler for many use cases |
For example, a pointer can be redirected:
int a = 10;int b = 20; int* p = &a;p = &b; // p now points to bA reference such as int& ref = a; stays bound to a.
The important mental model is:
Pointer→ "Here is the address." Reference→ "Use this existing value by another name."Null: When Nothing Is Being Referenced
Sometimes a pointer does not currently point to a valid object.
int* p = nullptr;A null pointer does not mean that the pointer contains a useful memory location containing zero. It means:
The pointer currently does not point to an object or valid value.
Trying to dereference it is an error, because there is no valid object to access.
Different languages use different representations:
| Language | Represents nothing as |
|---|---|
| C / C++ | nullptr or NULL |
| Java | null |
| Python | None |
| JavaScript | null or undefined |
The exact behavior differs, but the underlying idea is similar: there is no usable object or value being referenced.
Pointers and the Heap
Now connect this to the previous chapter.
int* p = new int(100);Stack Heap p┌─────────┐│ 0x2000 │└────┬────┘ │ ↓ ┌─────────┐ │ 100 │ └─────────┘The pointer variable p is associated with the current execution context. The dynamically allocated integer exists in the heap. The pointer provides a way to reach that heap memory.
When the memory is no longer needed, delete p; releases it. Modern languages often handle this differently: Java uses garbage collection rather than requiring the programmer to call delete.
Why Pointers Matter in Data Structures
Now we reach the most important connection to DSA. Consider a linked list:
┌──────────────┐│ 10 │ next ───┼──────┐└──────────────┘ │ ↓ ┌──────────────┐ │ 20 │ next ───┼──────┐ └──────────────┘ │ ↓ ┌──────────────┐ │ 30 │ null │ └──────────────┘Each node contains a value plus a reference or pointer to another node.
The nodes do not need to sit next to each other in memory. The links connect them. This is the fundamental idea behind linked structures.
The same idea in trees
A tree node holds references to its children instead of to a single successor:
[10] / \ ↓ ↓ [5] [20]The node does not physically contain its subtree. It only needs a way to reach its children.
And in graphs
A graph drops the restriction that each node has one parent, so a node can reference any number of others, in any arrangement:
A / \ B C \ / D A → B, CB → A, DC → A, DD → B, CEach connection can be a reference, a pointer, or an index into an array, depending on the implementation.
All three structures are the same idea with different wiring. The important part is:
References allow one piece of data to connect to another.
That connection is the foundation of linked lists, trees, graphs, and many other structures.
Passing Data to Functions
Pointers and references also matter when calling functions.
void increment(int x) { x++;} int value = 10;increment(value); // value is still 10This does not modify the original value in C++ because x receives a copy. Changing x changes only the copy.
But a reference can allow the function to work with the original value:
void increment(int& x) { x++;} int value = 10;increment(value); // value is now 11There is no separate integer being modified. Both names refer to the same value.
A Note About "Pass by Reference"
You will often hear: "this language passes objects by reference."
Be careful with this statement. Different languages use different parameter-passing models, and terminology is often used loosely.
For example, Java uses pass-by-value. When an object is passed, the value being copied is the reference to that object.
Original reference ↓ [Object] ↑ │Copied referenceBoth references can reach the same object, but the reference itself was passed by value.
This distinction becomes important when you start comparing languages.
One consequence is worth spelling out, because it is the classic interview trap:
void swap(Student a, Student b) { Student temp = a; a = b; b = temp; // swaps the parameters, not the caller's variables}The method runs, nothing fails, and the caller's two variables are exactly where they were. Reassigning a and b only moves the copies of the references. Had the method written a.name = "..." instead, the caller would have seen it immediately - mutation reaches through, reassignment does not.
This is the same point the Functions chapter makes about pass by value, and it is worth meeting twice.
Common Pointer Mistakes
Watch out
Dereferencing a null pointer. There is no valid object to access.
int* p = nullptr;cout << *p; // no valid objectUsing an invalid pointer. A pointer can become invalid if the memory it points to has already been released. The pointer still contains an address, but that memory is no longer valid for the original object.
int* p = new int(10);delete p; cout << *p; // invalidMemory leaks. If dynamically allocated memory is never released when required, the program can continue consuming memory unnecessarily. This is called a memory leak.
Confusing a pointer with the value. Given int* p = &score;, then p is an address and *p is a value. They are not the same thing.
Do You Need Pointers in Every Language?
No. Languages expose memory differently.
C / C++ make pointers explicit and powerful. You can directly work with memory addresses.
Java does not expose raw pointers. Object references are used instead:
Student s = new Student();You can think of s as a reference to the object, but you cannot directly manipulate its memory address like you can in C++.
Python also hides raw memory addresses behind object references, and handles the underlying memory management for you.
This gives you an important distinction:
Pointer→ explicit memory-address concept Reference→ language-level way to access another object/valueThe exact implementation differs between languages.
Quick Check
- What is a memory address?
- What does a pointer store?
- What is dereferencing?
- What is the difference between
pand*pin C++? - What is a reference?
- How is a pointer different from a reference?
- What does
nullptrmean? - Why are pointers and references important for linked lists?
- How are child nodes connected in a tree?
- Does Java use raw pointers like C++?
Show answerHide answer
- A number identifying one byte of memory. Memory is byte-addressable, so every byte has one, and a 4-byte value occupies four consecutive addresses.
- An address. That is the whole of it: a pointer is a variable whose value is where something else lives.
- Following a pointer to the place it names and using the value there. In C++ that is
*p; in Java it happens implicitly whenever you writen.value. pis the address the pointer holds;*pis the value sitting at that address. Assigning topmakes it point somewhere else, assigning to*pchanges the data it points at.- Another name for an existing variable. It is bound once, cannot be rebound, and needs no dereference operator to use.
- A pointer is a variable in its own right: it has an address, it can be reassigned, and it can be null. A reference is an alias, so it has none of those three properties.
- That the pointer deliberately points at nothing. It is a stated absence, not an uninitialised value, which is what makes it safe to test for.
- Because the nodes are not next to each other in memory. Nothing about a node's position tells you where the next one is, so each node has to carry a link to it.
- The same way: a node holds a reference to each child rather than containing the subtree itself.
- No. Java has references, which point at objects and are dereferenced for you. There is no
&, no*, and no pointer arithmetic, so a Java reference cannot be made to point at an arbitrary address.
Quick Recap
- A memory address identifies where data is stored.
- A pointer stores a memory address.
- Dereferencing accesses the value at the address stored by a pointer.
- A pointer can be used to access or modify the data it points to.
- A reference provides another way to refer to existing data.
- Pointers can generally be redirected to another location; references are typically bound to an existing object.
- A null pointer or reference represents the absence of a usable object or value.
- Dynamically allocated data can be accessed through pointers or references.
- Linked lists, trees, and graphs rely heavily on connections between objects.
- Languages expose pointers and references differently.
Where This Leads
You now have the complete memory foundation:
Memory ↓How data is stored ↓Stack & Heap ↓Where data lives ↓Pointers & References ↓How data can connectNow we can finally start using these ideas to understand Data Structures.
The first major structure is one you have already seen several times: Arrays. An array takes values and organizes them into a structure that allows efficient indexed access.
From there, we will gradually move from arrays to linked lists, stacks and queues, trees, and graphs.
Key takeaway
Pointers and references provide ways to reach existing data indirectly. A pointer explicitly stores an address, while a reference provides a language-level way to refer to existing data. These concepts form the foundation for understanding linked lists, trees, graphs, and many dynamic data structures.