就像高階語言一樣,陣列是某資料型態的資料在記憶體等距分佈的集合,並且陣列名稱指向第一個元素。 假設我們創建了兩個陣列:第一個是 BYTE 的陣列(arrayA),第二個是 DWORD 的陣列(arrayB)。每個陣列包含四個元素,但是在 Assembly Language 以陣列名稱 "arrayA" 或 "arrayB" 存取陣列只會得到每個陣列中的第一個元素的位址。
那我們如何存取後續元素? 使用高階語言的陣列時,我們使用索引來存取從索引 0 開始的元素(例如 arrayA[0],arrayA[1] 等)。 編譯器會確定陣列元素的大小並按每個索引將 location counter 移動適當的位元組個數。
在 Assembly 中,您會負責這艱鉅的工作(笑,即確定元素大小並執行索引計算。 為了存取陣列的資料,我們必須了解如何計算每個元素的位址。
記憶體是按位元組定址的。由於 arrayA 是一個 BYTE 陣列,因此每個元素都位於距離前一個元素一個位元組的位置。 但是,arrayB 是一個 DWORD 陣列(DWORD 為四個位元組),這意味著 arrayB 中的每個元素與前一個元素相距四個位元組。
陣列定義(使用 NASM):
segment .data
arrayA: dd 1, 3, 8, 9
len: EQU ($ - arrayA)
在 Assembly Language 創建陣列的方法是在一個 identifier 後使用逗號分隔多個 initializer。 由單個 identifier 去參考多於一個大小相同的值的序列稱為「陣列」。我們沒有創建四個單獨的 identifier,而是創建了一個 identifier。那個 identifier(arrayA)代表一個「四值序列」的開始位址,每個值的大小為一個 DWORD(32-bit)。 每個陣列的 identifier 本質上其實也是第一個元素的記憶體位址。
如要宣告含有重複元素的陣列可使用 times 指令,例如宣個包含十個值為 0 的 DWORD 元素的陣列:
segment .data
a3: times 10 dd 0
如要宣告未知值的陣列,必需在 segment .bss 宣告(NASM 不允計在 segment .data 宣告),並使用 resb, resw, resd 來告訴組譯器保留的 BYTE, WORD, DWORD 數目是多少。例如要宣告包含 200 個未知值的 WORD 元素陣列:
segment .bss
a6: resw 200
可以使用 current location counter ($) 來計算陣列的大小(以位元組為單位)。$ 表示程式碼中它自身所在的記憶體位址。 $ 可用於從當前位置減去前一個陣列的起始存儲位址,從而得出陣列的大小(以位元組為單位)。
現在,我們需要以特定的位元組常數作為偏移量來加載記憶體位址。 下面的程式碼示例示範如何存取 arrayA 的元素 [1] 和 arrayB 的元素 [2],同時展示了如何將陣列元素用作 source operand 和 destination operand。被方括號包圍表示它是一個位址(在高階語言的指標),而不是資料。
; eax = 4
mov eax, [arrayA + 1]
; arrayB[2] = 10
mov DWORD [arrayB + 8], 10
存取陣列元素時,請確保計算了正確的常數偏移量。 組譯器沒有自動邊界檢查。 如果您添加三個位元組而不是四個位元組來存取 DWORD 陣列的下一個元素,那麼你的 Program 不會產生錯誤,只是您存取的資料將會不是您所期望的,甚至導致更嚴重的崩潰…
一些程式設計師可能更喜歡使陣列與高階語言盡可能相似,即是在指令中使用索引號,而不是使用常數來計算位元組偏移量。 我們可以修改前面的示例以使用索引號。 例如當我們要存取 arrayB 的最後一個元素(索引 [3])。
mov edx, 3
mov edx, [arrayB + edx * 4]
通過使用陣列索引來使 Assembly Language 的程式碼與高階語言相似的代價是需要額外加一條指令,您可以自己決定哪種方法最適合您。
如果要在函數內將陣列定義為區域變數呢?假設需要的空間有 8 個 BYTE、20 個 WORD、10 個 DWORD 元素的陣列,一共需要 8 x 1 + 20 x 2 + 10 x 4 = 8 個位元組的空間。透過 sub esp, 88 的方式就可以保留足夠的堆疊記憶體給區域變數了。