---
title: 'Registers and Main Memory'
source: 'https://academia.sh/en/courses/how-computers-work/registers-and-main-memory'
course: 'How Computers Work'
language: en
updated: '2026-08-17T18:08:13+00:00'
license: 'CC BY-SA 4.0'
---

# Registers and Main Memory

The model of memory as an addressed array, the role of registers, and the access-cost gap between them.

The previous topic established how data is represented as bit patterns. These bit
patterns have to reside somewhere, and where they reside directly determines program
performance. This topic examines the structure of that place and how data is carried to
the processor.

This lesson asks: how does memory appear to a program, and where does the processor
keep the data it operates on?

## Memory: An Addressed Array

The memory model a program sees is plain: an array of cells, each holding one byte,
numbered starting from zero. A cell's number is called its **address**, its contents
its **value**.

| Address | 1000 | 1001 | 1002 | 1003 | 1004 |
|---|---|---|---|---|---|
| Value | `41` | `42` | `43` | `44` | `00` |

This model has two consequences. First, accessing memory requires two pieces of
information: which address, and how many bytes. The value `0x41424344` from the
previous topic is four bytes; reading it means requesting four bytes starting at
address `1000`. How many bytes are interpreted, and in what order, is the rule defined
in the byte order lesson.

Second, an address is itself a number and can be stored in memory. A variable that
holds another variable's address — a **pointer** — follows directly from this
observation. Address width determines the largest amount of memory that can be
addressed: 32-bit addresses can address $2^{32}$ distinct bytes, roughly four billion
cells; the limit for 64-bit addresses is a size no hardware reaches in practice.

## Registers

The processor does not keep the values it operates on in main memory; the arithmetic
and logic unit can only read from small, fast storage units built into the processor
itself, called **registers**.

Registers have three distinguishing traits:

- **They are few.** A processor's general-purpose registers number in the tens; main
  memory's cells number in the billions.
- **They have no addresses.** Registers are referenced directly by name in an
  instruction, not by number.
- **Their access is nearly free.** Reading a register takes on the order of one
  processor clock cycle.

Some registers serve special roles: the program counter holds the address of the next
instruction, the stack pointer holds the top of the call stack. Their function is
defined in later lessons.

An addition is therefore not a single step. Computing `a + b` first loads the two
values from memory into registers, performs the addition between registers, and writes
the result back to memory. This load–operate–store pattern is the common skeleton of
processor architectures.

## The Access-Cost Gap

The speed gap between registers and main memory is large enough to shape program
design. Exact durations vary by hardware; what does not vary is the **order-of-magnitude
gap**:

| Layer | Access order (clock cycles) |
|---|---|
| Register | ~1 |
| First-level cache | a few |
| Last-level cache | tens |
| Main memory | hundreds |
| Persistent storage | tens of thousands and up |

Each row is noticeably slower and noticeably larger than the one before it. This
layering is called the **memory hierarchy**, and its design rationale is economic: fast
storage is expensive, cheap storage is slow. The hierarchy strikes a balance between the
two.

The consequence is this: a program's speed depends as much on where it accesses data
from as on how many operations it performs. A single value read from main memory can
take as long as hundreds of arithmetic operations. This observation is the reason the
cache, covered in the next lesson, exists at all.

## How Storage Media Differ

The layers of the hierarchy differ not only in speed and capacity but in physical mode
of operation.

Registers and cache are built from circuits that hold state as long as supply voltage
is present; access is fast, cost per unit high. Main memory instead holds state in tiny
electrical charges that leak over time, so it must be **refreshed** regularly.
Refreshing is why main memory is both slower and cheaper: it uses fewer circuit
elements per cell.

These three layers share one property: **volatility**. Their contents are lost when
power is cut. Persistent storage, by contrast, retains its contents without power, but
its access is orders of magnitude slower and writes are typically performed at the
block level.

This volatility distinction explains why data a running program holds must be saved
separately: state in memory is bounded by the lifetime of the process.

## Word Width and Alignment

A **word** is the data width a processor natively operates on; it coincides with the
width of its general-purpose registers. On a 64-bit processor, a word is eight bytes.

Hardware performs multi-byte accesses relative to word boundaries. Because of this, the
placement of data structures in memory follows the **alignment** rule: the address of
an $n$-byte value is generally chosen to be a multiple of $n$. A four-byte integer is
placed at address `1000`, not at `1001`.

The cost of an unaligned access varies by hardware: some processors slow it down by
splitting it into two memory operations, others raise a fault.

The visible consequence of the alignment rule is **padding**. When small and large
fields are declared consecutively in a structure, the compiler inserts unused bytes
between them:

```
struct A            struct B
  1-byte field         8-byte field
  7 bytes padding       1-byte field
  8-byte field          7 bytes padding
  total: 16 bytes      total: 16 bytes
```

Declaring the same fields in a different order can change the structure's size in
memory. Ordering fields from largest to smallest width is a common habit that minimizes
padding. This detail also explains why the previous lesson noted that a raw memory copy
is not portable: the amount of padding depends on the compiler and the hardware.

## Seeing Memory Layout

The following program examines different representations of the same data and the
space an object occupies in memory:

```python
import sys, array

value = 0x41424344

# Four bytes to be written to memory (in big-endian order)
raw = value.to_bytes(4, "big")
for offset, byte in enumerate(raw):
    print(f"address+{offset}: {byte:#04x}")     # 0x41 0x42 0x43 0x44

# Fixed-width arrays keep elements contiguous and aligned.
arr = array.array("I", [1, 2, 3, 4])         # 'I': 4-byte unsigned integer
print(arr.itemsize, len(arr))                # 4 4
print(arr.buffer_info()[1] * arr.itemsize)   # 16  — total bytes

# Python objects take up far more space than raw data.
print(sys.getsizeof(1), sys.getsizeof(arr))  # sizes include object header
```

The `array` module stores elements at fixed width and contiguously; this is the closest
Python equivalent to a hardware-friendly data structure layout. A general-purpose list,
by contrast, holds pointers to its elements: the values themselves sit scattered in
memory. The difference between the two layouts becomes visible in the cache behavior
covered in the next lesson.

## Summary

- To a program, memory is an array of addressed byte cells; access requires both an
  address and a width.
- An address is itself a number and can be stored in memory; the concept of a pointer
  follows from this observation.
- The processor operates only on registers; computation proceeds in a
  load–operate–store pattern.
- There is an order-of-magnitude speed gap among registers, cache, main memory, and
  persistent storage; this layering is the memory hierarchy.
- Multi-byte values are placed according to the alignment rule; the rule causes padding
  bytes and order-dependent size differences in structures.

## Next Step

Where data resides has now been defined. Instructions themselves reside in memory too,
and share the same address space. The next lesson covers the cycle by which the
processor fetches an instruction from memory and executes it, and how that cycle
produces program flow; there, the pattern `0x41424344` will be read once more, this
time as an instruction.
