Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Working Example

Course: DSA › Arrays › Introduction

Now that we know how an array is stored in memory, let’s walk through a complete end-to-end example — from logical representation all the way down to how the CPU locates and reads a value using the subscript operator [].

Given below is the logical representation of an integer array with 5 data items:

---
config:
  theme: base
  themeVariables:
    primaryColor: "#dbeafe"
    primaryBorderColor: "#3b82f6"
    primaryTextColor: "#1e3a5f"
    lineColor: "#64748b"
    secondaryColor: "#ede9fe"
    tertiaryColor: "#fef9c3"
---
flowchart LR
    decl["array[5]"] -->|"logical representation"| v1
    v1["value1"] --- v2["value2"] --- v3["value3"] --- v4["value4"] --- v5["value5"]

Logical representation of an integer array with 5 data items.


Layout in Memory

We map the array into memory starting at base address 2. Because this is an integer array, we consider the size of each data item to be 4 bytes for this example.

---
config:
  theme: base
  themeVariables:
    primaryColor: "#dbeafe"
    primaryBorderColor: "#3b82f6"
    primaryTextColor: "#1e3a5f"
    lineColor: "#64748b"
    secondaryColor: "#ede9fe"
    tertiaryColor: "#fef9c3"
---
flowchart LR
    base(["base address = 2"]) --> e0

    e0["value1<br/>index: 0<br/>addr: 2 → 5"]
    e1["value2<br/>index: 1<br/>addr: 6 → 9"]
    e2["value3<br/>index: 2<br/>addr: 10 → 13"]
    e3["value4<br/>index: 3<br/>addr: 14 → 17"]
    e4["value5<br/>index: 4<br/>addr: 18 → 21"]

    e0 --- e1 --- e2 --- e3 --- e4

An array of 5 integers mapped into continuous memory starting at address 2.

Each element occupies exactly 4 consecutive bytes. The elements are laid out back to back with no gaps — that’s what “contiguous” means.


Calculating the Address of Data Items

When we write array[2] or array[3], the program uses the formula we learned to calculate the exact memory address of that element:

address(index) = base_address + (size_of_datatype × index)

The program already knows the base address and the size of the data type — this is all it needs. Let’s see it in action for two accesses:

---
config:
  theme: base
  themeVariables:
    primaryColor: "#dbeafe"
    primaryBorderColor: "#3b82f6"
    primaryTextColor: "#1e3a5f"
    lineColor: "#64748b"
    secondaryColor: "#ede9fe"
    tertiaryColor: "#fef9c3"
---
flowchart LR
    c2["array[2]<br/>2 + (2 × 4) = 10"] --> e2
    c3["array[3]<br/>2 + (3 × 4) = 14"] --> e3

    e0["value1<br/>addr: 2"]
    e1["value2<br/>addr: 6"]
    e2["value3<br/>addr: 10"]
    e3["value4<br/>addr: 14"]
    e4["value5<br/>addr: 18"]

    e0 --- e1 --- e2 --- e3 --- e4

    style c2 fill:#fef9c3,stroke:#d97706,color:#78350f
    style c3 fill:#dcfce7,stroke:#16a34a,color:#14532d
    style e2 fill:#fef9c3,stroke:#d97706,color:#78350f
    style e3 fill:#dcfce7,stroke:#16a34a,color:#14532d

Calculating the address for array[2] and array[3] using the subscript operator.

The CPU performs one multiplication and one addition — and it’s done. No scanning, no searching. That’s why access is always O(1), regardless of array size.


Dereferencing the Value

Calculating the address is only half the work. The next step is to actually read the value stored there — this is called dereferencing.

The program knows the type of data stored in the array (integer, in our case). So starting from the calculated address, it reads exactly size_of_datatype bytes (4 bytes for an int) and interprets them as the value.

Dereferencing: Accessing the value stored at the memory address held by a pointer. The pointer’s data type determines how many bytes to read and how to interpret them.

---
config:
  theme: base
  themeVariables:
    primaryColor: "#dbeafe"
    primaryBorderColor: "#3b82f6"
    primaryTextColor: "#1e3a5f"
    lineColor: "#64748b"
    secondaryColor: "#ede9fe"
    tertiaryColor: "#fef9c3"
---
flowchart LR
    a2["array[2] = 10"] -->|"read 4 bytes at addr 10"| v3["value3"]
    a3["array[3] = 14"] -->|"read 4 bytes at addr 14"| v4["value4"]

    style a2 fill:#fef9c3,stroke:#d97706,color:#78350f
    style a3 fill:#dcfce7,stroke:#16a34a,color:#14532d
    style v3 fill:#fef9c3,stroke:#d97706,color:#78350f
    style v4 fill:#dcfce7,stroke:#16a34a,color:#14532d

Dereferencing: reading the value at the calculated address using the datatype to determine how many bytes to interpret.

Note: Lower-level languages like C and C++ expose this mechanism directly through pointers — variables that store memory addresses and let you read or manipulate any part of a contiguous memory segment. In Python and most high-level languages, this all happens invisibly.


Putting It All Together

Here’s the full sequence of what happens every time you write array[i]:

---
config:
  theme: base
  themeVariables:
    primaryColor: "#dbeafe"
    primaryBorderColor: "#3b82f6"
    primaryTextColor: "#1e3a5f"
    lineColor: "#64748b"
    secondaryColor: "#ede9fe"
    tertiaryColor: "#fef9c3"
---
flowchart LR
    A["array[i]"] --> B["Calculate address<br/>base + size × i"]
    B --> C["Jump to<br/>that address"]
    C --> D["Read size bytes<br/>interpret as datatype"]
    D --> E["Return value"]

The full pipeline for a single array element access.

We used 4-byte integers in this example, but the underlying mechanism is identical for any datatype — 1-byte chars, 8-byte doubles, or any custom struct. The formula stays the same; only size_of_datatype changes.

All of this happens automatically under the hood in modern programming languages. As a programmer, you just write array[i] — the language handles the address arithmetic and dereferencing for you.


Key Takeaway

The subscript operator array[i] is not magic — it’s one multiplication, one addition, and one memory read. The CPU knows exactly where to go, reads exactly the right number of bytes, and hands the value back. That’s what makes arrays so fast and so fundamental.

# See the full pipeline in Python
base_address = 2
size_of_int = 4

def access(index: int) -> str:
    addr = base_address + (size_of_int * index)
    return f"array[{index}] → address {addr} → value{index + 1}"

for i in range(5):
    print(access(i))