Arrays in Assembly
An array in assembly is nothing more than a contiguous block of memory holding elements of the same size, one after another, with no metadata attached. Unlike a high-level language, there is no built-in bounds checking or length field; the programmer must track the element count and element size separately and compute every address by hand.
Cricket analogy: Think of a cricket scoreboard showing every over's runs in a single row of boxes: the scorer knows each box is exactly one over wide, so finding over 37 means walking 37 boxes from the start, exactly like an assembly array has no labels, just fixed-width slots.
Declaring and Laying Out Arrays
In NASM, a fixed array of doublewords is declared in the .data section with the dd directive followed by comma-separated values, or with times to reserve uninitialized space, such as buf: resd 100 in .bss. The assembler simply lays these values end to end; the label buf refers to the address of the very first element, and every subsequent element sits at buf + 4*i because each dd entry occupies four bytes.
Cricket analogy: Declaring an array with dd 1,2,3,4,5 is like a team manager writing a fixed batting order on a card before the toss: once written, the fifth name is always four slots after the first, just as buf+16 always reaches the fifth doubleword.
section .data
arr: dd 10, 20, 30, 40, 50 ; five doublewords, 4 bytes each
count equ 5
section .bss
buf: resd 100 ; 100 uninitialized doublewords
section .text
global _start
_start:
xor rcx, rcx ; i = 0
xor eax, eax ; sum = 0
.loop:
cmp rcx, count
jge .done
mov edx, [arr + rcx*4] ; scaled addressing: arr + i*4
add eax, edx
inc rcx
jmp .loop
.done:
mov [buf], eax ; store sum into buf[0]
mov eax, 60
xor edi, edi
syscall
Indexing with Scaled Addressing
x86-64 supports base+index*scale+displacement addressing directly in a single instruction, so [arr + rcx*4] computes arr + rcx*4 in hardware without a separate multiply. The scale factor must match the element size — 1, 2, 4, or 8 bytes — so a byte array uses *1, a word array *2, a doubleword array *4, and a quadword array *8. Getting the scale wrong silently reads the wrong element rather than crashing.
Cricket analogy: A commentator predicting the score at over 12 by multiplying overs by the average run rate is doing the same single-step scaling that [arr + rcx*4] performs — one multiply-and-add, no separate steps.
On x86-64, the scale field in the ModRM/SIB byte only encodes 1, 2, 4, or 8 — there is no hardware support for scale-by-3 or scale-by-12 for oddly sized structs. For those, compute the offset with an explicit imul before adding it to the base address.
Multi-Dimensional Arrays and Row-Major Layout
A 2D array declared as rows of columns is stored in row-major order: row 0's elements come first, then row 1's, and so on. The address of element [i][j] in an array with COLS columns of 4-byte elements is base + (i*COLS + j)*4. This means i must be scaled by the full row width before j is added, so the compiler or hand-written code typically computes i*COLS with an imul first, then adds j and applies the final *4 scale.
Cricket analogy: A Test match scorecard laid out as one long list of every ball bowled in over order, rather than a grid, means finding over 5 ball 3 requires computing 5*6+3 balls from the start — exactly like row-major indexing.
Row-major and column-major layouts are not interchangeable. Code written assuming row-major (C, most assembly examples) will silently read the wrong elements if the data was actually produced in column-major order (as in Fortran), because the same (i,j) pair maps to a different byte offset in each convention.
- An assembly array is just a contiguous, unlabeled block of same-sized elements; the programmer tracks count and element size manually.
- NASM declares initialized arrays with dd/dw/db and comma-separated values, and reserves uninitialized space with resd/resw/resb in .bss.
- x86-64's base+index*scale+displacement addressing mode computes arr + i*scale in one instruction, with scale restricted to 1, 2, 4, or 8.
- Using the wrong scale factor for the element size does not crash — it silently reads or writes the wrong element.
- 2D arrays are typically stored row-major, so element [i][j] is at base + (i*COLS + j)*elemSize, requiring an explicit multiply for the row term.
- There is no automatic bounds checking; reading or writing past the declared array length corrupts adjacent memory without any warning.
Practice what you learned
1. In NASM, what does `resd 100` in the .bss section do?
2. Which scale factors are valid in x86-64's base+index*scale addressing mode?
3. For a row-major 2D array with COLS columns of 4-byte elements, what is the address formula for element [i][j]?
4. What happens if you use scale*1 instead of scale*4 when indexing a doubleword array?
5. Why does assembly have no built-in array bounds checking?
Was this page helpful?
You May Also Like
Strings in Assembly
How byte-sequence strings are terminated, measured, copied, and compared in x86 assembly, including the REP-prefixed string instructions.
Pointers and Indirection
How addresses, dereferencing, pointer arithmetic, LEA, and multiple levels of indirection work at the machine level in x86-64 assembly.
Structures in Assembly
How fixed-layout structures are declared with NASM's struc directive, addressed by named offset, nested, and arrayed in x86-64 assembly.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics