Under the Hood: How Arrays Work in Memory (With Visual Analogy)

Asif SiddiqueAsif Siddique
5 min read

Whenever we read a blog or watch a video about data structures—especially arrays—we often come across the phrase "arrays are contiguous blocks of memory" followed by a couple of basic examples. And that's usually where it ends. But have you ever wondered how things actually get stored in memory? Or why we use the term "contiguous" in the first place? In this article, we’ll take a closer look at the internal workings of arrays, including how data is stored and accessed in memory. We'll also touch on multidimensional arrays and how they work under the hood.

Introduction

Arrays are a fundamental data structure with varying behaviors across programming languages. While low-level languages like C and C++ store single data types with direct memory access, high-level languages like JavaScript and Python offer more flexibility. In this article, we'll focus on the internal workings—how arrays are stored in memory, what contiguous means, and how multi-dimensional arrays work. All examples use C++ for better visibility into low-level memory behavior.

How Arrays Get Created?

int arr[5] = {5,4,3,2,1};

When we write the above line in C++, the compiler allocates memory for the array during compilation (or runtime, depending on the context). Since we're creating an array of 5 integers, and each int typically takes up 4 bytes, the compiler reserves a total of 20 bytes (5 × 4 bytes) in memory.

The most important aspect here is that this memory is contiguous—meaning all five blocks of 4 bytes are laid out next to each other in a continuous block. This contiguous allocation is what allows arrays to provide fast and predictable access using index-based addressing.

As shown in the diagram above, arr points to the base address (e.g., 100), and each subsequent value is stored 4 bytes apart. This layout ensures O(1) time access for reading or writing any array element.

Why Is Contigous Memory Important?

The term contiguous is important because when we create an array:

int arr[5] = {5, 4, 3, 2, 1};

what actually gets assigned to arr is the base address—that is, the memory address of the first element (arr[0]).

Thanks to the contiguous memory layout, when we access an element like arr[3], the compiler can calculate its address using simple arithmetic:

address = base_address + (index × size_of_each_element)

In this case:

arr[3] = 100 + (3 × 4) = 112

As shown in the memory layout diagram, index 3 refers to the 4th element (since array indices start at 0), and the value stored at address 112 is 2.

This predictable layout is what allows arrays to support constant-time (O(1)) access to any element using its index.

Two Dimensional Arrays

We’ve seen how 1D arrays are created, represented, and accessed in memory. Now, let’s move to two-dimensional arrays.

int arr[3][3] = {{1,2,3},{4,5,6},{7,8,9}};

You might think that the allocation of a 2D array is different because we access it using two indices, like arr[1][1]. But actually, 2D arrays are stored in memory as contiguous blocks, just like 1D arrays. The only difference is how we interpret the memory layout.

Memory Layout: Row-Major Order

In C++, arrays are stored in row-major order, meaning that entire rows are stored one after the other in memory, as we can see below.

We can image the 2D array as a Matrix,

To compute the address of arr[i][j], we use the formula:

address = base_address + ((i * number_of_columns + j) * size_of_element)

For arr[1][1], with base_address = 100, number_of_columns = 3, and size_of_element = 4:

address = 100 + ((1 * 3 + 1) * 4)
         = 100 + (4 * 4)
         = 100 + 16
         = 116

So, arr[1][1] points to address 116 and holds the value 5.

Analogy: Building as 2D Array in Memory

We want to go to room 32, which means:

  • Floor: i = 3 (0-based index → 0, 1, 2, 3)

  • Apartment on floor: j = 2

  • Number of apartments (columns) per floor: number_of_columns = 4

Now lets break down the arithmetic statement which we’ve written above that is:

address = base_address + ((i * number_of_columns + j) * size_of_element)
TermMeaning in AnalogyExample Value
base_addressGround floor (0th floor, 0th room) addressLet's say 100
number_of_columnsApartments per floor4
iFloor number (0-based)3
jApartment number on that floor (0-based)2
size_of_elementSize of each apartment in bytes (say, 4B)4
address = 100 + ((3 * 4 + 2) * 4)
        = 100 + (14 * 4)
        = 100 + 56
        = 156

Conclusion

In this post, we explored how 1D and 2D arrays are stored and accessed in memory. Higher-dimensional arrays follow the same principles — it’s just an extension of what we’ve already seen.We also uncovered why array element lookup is a constant-time operation — thanks to the underlying memory arithmetic — and used a real-world building analogy to make sense of it visually.This is just the tip of the iceberg when it comes to understanding data structures. In upcoming blogs, we’ll dive deeper into other structures and how they are represented under the hood.

1
Subscribe to my newsletter

Read articles from Asif Siddique directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Asif Siddique
Asif Siddique