How Arrays Work
Contiguous blocks of memory with instant access by index. How they are stored, why random access is O(1), and their limitations.
What Is It?
An array is the simplest data structure. It stores items one after another in memory, all packed together with no gaps. This side-by-side arrangement has a name: contiguous storage.
That one property — items stored right next to each other — gives arrays their superpower: you can jump to any item instantly, by index.
How? Because each slot is the same size, and they all start at a known position. Finding item number 3 is just math: start at the beginning, skip 3 slots forward, done. No searching. No guessing.
This lesson explains how that works under the hood, and why it matters for writing fast code.
Analogy
The Parking Lot
Imagine a parking lot with numbered spaces: Space 0, Space 1, Space 2, and so on. Every space is exactly the same size, laid out in one long row.
If someone tells you "your car is in space 47", you don't walk past every space checking labels. You know each space is 10 feet wide, so you walk directly to the 470-foot mark. That's random access — you calculate the position with math, not searching.
Now imagine the lot is completely full and someone needs a new spot. There's no room — you'd have to find a bigger lot and move every car. That's the fixed-size problem of arrays.
And if someone needs to squeeze into space 3 (not at the end), every car in spaces 3, 4, 5, ... has to shift one spot. That's why inserting in the middle of an array is slow.
Try It Yourself
How It Works
Memory Layout of an Array
When you create an array of 5 integers, here's what happens in memory:
Base address: 1000 · Element size: 4 bytes · Total: 20 bytes
Each integer takes 4 bytes. Five integers take 20 bytes total. They sit right next to each other, starting at address 1000.
The Address Formula
This simple formula is the key to why arrays are fast:
address of array[i] = base_address + (i × element_size)
Let's trace it for our example (base address 1000, element size 4 bytes):
This works because:
- Every element is the same size (4 bytes for integers)
- Elements are stored right next to each other (no gaps)
- We know where the array starts (the base address)
It doesn't matter if you want index 0 or index 99,999 — the math takes the same amount of time. This is what O(1) means: always fast, no matter the size.
Fixed Size — No Growing Allowed
When you declare an array of 5 slots, the system reserves exactly 20 bytes. Not 21. You get 5 slots and that's it.
Address 1020 belongs to something else — writing there is a buffer overflow
Writing to array[5] would touch memory that doesn't belong to you. This is called a buffer overflow — a serious bug that can crash programs or corrupt data.
If you need a 6th element, you have to:
- Allocate a new, larger array
- Copy all 5 elements into it
- Add the 6th element
- Throw away the old array
Most languages hide this behind dynamic arrays (covered in the next lesson), which do this automatically.
Bounds Checking — Staying Safe
Using an index that's too large (or negative) is called an out-of-bounds access. Different languages handle it differently:
Some languages check every access and throw an error immediately — safer, but a tiny bit slower. Others don't check and silently read whatever memory is at that calculated address — faster, but dangerous and a common source of bugs.
Here's what safe bounds checking looks like:
function get(array, index):if index < 0 or index >= array.length:error "Index out of bounds"return array[index]
Why Looping Through Arrays Is Fast
Because all the elements are stored next to each other, your computer can grab several of them in a single trip to memory. When you access the first element, it quietly loads the neighboring elements too. By the time your loop reaches them, they're already nearby and ready — no extra waiting.
Accessing a1–a15 after loading a0: ~1ns each (cache hit) vs ~100ns from RAM
This is why a simple loop over an array is one of the fastest things you can do in a program. Contrast this with jumping around to random positions — that's slower because the computer can't predict what to load ahead of time.
Multi-Dimensional Arrays
A 2D array (like a grid or matrix) is still just one flat block of memory. A 3×3 grid is stored as 9 elements in a row:
Matrix (logical view)
3 × 3 grid
Memory (physical layout)
address = base + (row × cols + col) × size
9 contiguous elements
The formula to find element at row r, column c:
address = base + (r × num_cols + c) × element_size
Examples
Example 1: Declaring and accessing
// Create an array of 5 integers (stored starting at address 2000)array = [10, 20, 30, 40, 50]// Access element at index 3address = 2000 + (3 × 4) = 2012value at 2012 = 40 ✓
Example 2: Byte-level view
Storing three integers — 255, 256, 0 — in memory:
Each integer = 4 bytes. 255 fits in one byte (0xFF), 256 needs two (little-endian)
Each integer uses 4 bytes, no matter the value. Even the number 0 occupies 4 bytes.
Example 3: Out-of-bounds danger
array = [10, 20, 30] // 3 elements starting at address 800// Some other variable sitting right after our array:other_var = 999 // happens to be stored at address 812// Without bounds checking:array[3] = 0 // calculates: 800 + (3 × 4) = 812// This OVERWRITES other_var!// The program doesn't crash — it silently corrupts data.
Common Mistakes
- Off-by-one errors. Arrays are zero-indexed. An array of size 5 has valid indices 0 through 4. Accessing
array[5]is out of bounds. This is the single most common array bug.
- Assuming arrays can grow. A fixed-size array cannot accommodate more elements than its declared size. Writing past the end corrupts other data or crashes the program.
- Confusing size with last valid index. If
length = 5, the last valid index is4(which islength - 1). Usinglengthas an index is always out of bounds.
- Thinking access time varies by index. Accessing
array[0]andarray[99999]take exactly the same time. The address formula is equally fast for any index — that's the whole point.
Best Practices
- Always check that your index is valid before using it — either manually or by relying on your language's built-in bounds checking.
- Use arrays when you need fast access by position and know roughly how many elements you'll have.
- When in doubt, use your language's built-in list or dynamic array type — it handles sizing for you.
- Iterate through arrays from start to end whenever possible — this is the most predictable and efficient pattern.