100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Pointers and Indirection

How addresses, dereferencing, pointer arithmetic, LEA, and multiple levels of indirection work at the machine level in x86-64 assembly.

Memory & AddressingIntermediate10 min readJul 10, 2026
Analogies

Pointers and Indirection

A pointer is simply a register or memory cell holding a memory address rather than a value of interest itself; dereferencing means reading or writing the memory that address points to, rather than the address value. In assembly this distinction is made purely through the addressing mode used: mov eax, rbx moves the address held in rbx into eax, while mov eax, [rbx] moves the four bytes stored at the address rbx points to. Confusing the two is one of the most common assembly bugs, since both are syntactically just a register name apart.

🏏

Cricket analogy: A scorer noting 'seat B12' on a ticket versus actually sitting in seat B12 is the difference between holding an address and dereferencing it — the ticket (pointer) is not the seat (value) itself.

Pointer Arithmetic and the LEA Instruction

Because pointers are just numbers, they support arithmetic: adding 4 to a pointer into a doubleword array moves it to the next element, and this is exactly how array traversal and pointer-based loops work at the machine level. The LEA (Load Effective Address) instruction is the workhorse for this — it computes an address using the same base+index*scale+displacement logic as memory operands, but stores the computed address itself into a register instead of dereferencing it, making it a fast way to do arbitrary integer arithmetic as a side effect.

🏏

Cricket analogy: Moving a fielding position marker forward by exactly one field-placement slot on a tactics board is like advancing a pointer by one element size — the marker's new coordinate is computed, not the ball itself moved.

nasm
section .data
    arr: dd 100, 200, 300, 400

section .text
global _start
_start:
    lea     rsi, [arr]          ; rsi = address of arr (no dereference)
    mov     eax, [rsi]          ; eax = 100 (dereference: read value at rsi)
    lea     rsi, [rsi + 4]      ; rsi now points to arr[1]; pure arithmetic
    mov     ebx, [rsi]          ; ebx = 200

    ; pointer to a pointer: rdi holds the address of rsi's saved copy
    push    rsi
    mov     rdi, rsp            ; rdi = address of the stack slot holding rsi
    mov     rcx, [rdi]          ; rcx = rsi (one level of indirection)
    mov     edx, [rcx]          ; edx = 200 (two levels: rdi -> rsi -> value)

    mov     eax, 60
    xor     edi, edi
    syscall

LEA never touches memory — it is pure address arithmetic executed by the CPU's address-generation unit. Compilers routinely emit lea eax, [rbx + rcx*2 + 8] purely to compute rbx + rcx*2 + 8 into eax as an integer, with no array or pointer involved at all.

Double Indirection and Pointer Chains

A pointer to a pointer requires two dereferences to reach the final value: the outer pointer is read to get an address, and that address is itself read to get another address (or the value). This pattern shows up whenever a function needs to modify a caller's pointer variable (passing a pointer to that pointer), or when traversing structures like linked lists of pointers or arrays of string pointers, where each element is itself an address rather than data.

🏏

Cricket analogy: A scorer's assistant who is told 'ask the umpire for today's card, then read line 3 of that card' performs two lookups in sequence, exactly like double dereferencing a pointer to a pointer.

Dereferencing a pointer that holds garbage, zero, or a freed address does not politely fail in assembly — it either reads/writes whatever happens to be at that address (silent corruption) or triggers a hardware page fault (segmentation fault) if the address maps to unmapped memory. There is no language-level null check; you must add one explicitly with a cmp reg, 0 / conditional jump before every dereference where the pointer might be invalid.

  • A pointer is a register or memory cell holding an address; dereferencing reads or writes the memory at that address, not the address value.
  • mov eax, rbx copies an address; mov eax, [rbx] dereferences it to copy the value stored there — the bracket is the entire distinction.
  • LEA computes an address using base+index*scale+displacement arithmetic without touching memory, making it useful for both real addressing and general integer math.
  • Pointer arithmetic advances by element size (e.g. +4 for a doubleword array), not by raw byte 1 unless the elements are bytes.
  • Double indirection (pointer to a pointer) requires two sequential dereferences and appears in out-parameters, linked structures, and arrays of pointers.
  • There is no automatic null-pointer or invalid-address check in assembly; dereferencing bad addresses either corrupts memory silently or causes a segmentation fault.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#AssemblyLanguageStudyNotes#PointersAndIndirection#Pointers#Indirection#Pointer#Arithmetic#StudyNotes#SkillVeris#ExamPrep