Skip to content

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:

Code
Linked List     Tree     Graph     Dynamic data structures

If 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.

Code
Address       Value 0x1000        420x1004        170x1008        91

Suppose 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.

Code
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.

Code
int score = 100;int* p = &score; cout << *p;   // 100

The *p means: go to the memory location stored inside p and access the value there.

Code
p0x1000memory at 0x1000100

So there are two different operations:

Code
&score   address of a variable*p       value at the address stored in a pointer

Be careful: the meaning of * depends on where it is used. In int* p it declares a pointer. In *p it dereferences one.


Changing Data Through a Pointer

A pointer can also be used to modify the value it points to.

Code
int score = 100;int* p = &score; *p = 200;     // score is now 200

Why? 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:

Code
int score = 100;int* p = &score;

Now both score and *p access the same value.

Code
          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:

Code
int score = 100;int& ref = score; ref = 200;cout << score;   // 200

Both 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.

PointerReference
Stores an addressActs as an alias to existing data
Can usually be changed to point elsewhereUsually remains bound to the same object
Can represent "points to nothing"Typically must refer to an existing object
Can be dereferenced explicitlyUsed like the original variable
Supports pointer arithmetic in languages such as C++Does not support pointer arithmetic
More explicit and flexibleSimpler for many use cases

For example, a pointer can be redirected:

Code
int a = 10;int b = 20; int* p = &a;p = &b;        // p now points to b

A reference such as int& ref = a; stays bound to a.

The important mental model is:

Code
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.

Code
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:

LanguageRepresents nothing as
C / C++nullptr or NULL
Javanull
PythonNone
JavaScriptnull 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.

Code
int* p = new int(100);
Code
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:

Code
 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.


References in Trees

The same idea appears in trees. A node may contain references to its children:

Code
          [10]          /  \                    [5]   [20]

The node does not need to physically contain the entire subtree. It only needs a way to reach the child nodes.

This is one reason pointers and references are so important in DSA.


References in Graphs

Graphs take the idea even further. A graph can contain many connections:

Code
     A    / \   B   C    \ /     D A  B, CB  A, DC  A, DD  B, C

Each connection can be represented using references, pointers, or indexes depending on the implementation.

The important idea 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.

Code
void increment(int x) {    x++;} int value = 10;increment(value);   // value is still 10

This 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:

Code
void increment(int& x) {    x++;} int value = 10;increment(value);   // value is now 11

There 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.

Code
Original reference           [Object]              Copied reference

Both references can reach the same object, but the reference itself was passed by value.

This distinction becomes important when you start comparing languages.


Common Pointer Mistakes

Watch out

Dereferencing a null pointer. There is no valid object to access.

Code
int* p = nullptr;cout << *p;   // no valid object

Using 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.

Code
int* p = new int(10);delete p; cout << *p;   // invalid

Memory 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:

Code
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:

Code
Pointer explicit memory-address concept Reference language-level way to access another object/value

The exact implementation differs between languages.


Quick Check

  1. What is a memory address?
  2. What does a pointer store?
  3. What is dereferencing?
  4. What is the difference between p and *p in C++?
  5. What is a reference?
  6. How is a pointer different from a reference?
  7. What does nullptr mean?
  8. Why are pointers and references important for linked lists?
  9. How are child nodes connected in a tree?
  10. Does Java use raw pointers like C++?

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:

Code
Memory  How data is stored  Stack & Heap  Where data lives  Pointers & References  How data can connect

Now 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.