Язык ассемблера - Массивы
Массив — это набор данных одного типа, хранящихся непрерывно. В ассемблере массив — это непрерывный блок памяти, и доступ к каждому элементу осуществляется с помощью базового адреса плюс смещения.
Определение одномерного массива
Пример
section .data
; Способ 1: перечислить элементы по одному
arr1 dd 10, 20, 30, 40, 50 ; Массив из 5 элементов doubleword
; Способ 2: инициализация с помощью dup
arr2 dd 10 dup(0) ; 10 элементов doubleword, все 0
; Способ 3: массив байтов
bytes db 1, 2, 3, 4, 5, 6 ; 6 байт
; Метод 4: массив символов (строка)
chars db 'example', 0 ; 7 байт
; Вычисление длины массива (во время компиляции)
arr1_len equ ($ - arr1) / 4 ; Количество элементов в массиве двойных слов
arr2_len equ ($ - arr2) / 4
bytes_len equ ($ - bytes) ; Массив байтов, деление не требуется
section .bss
; Неинициализированный массив
buffer resd 100 ; Зарезервировать место для 100 двойных слов
Доступ к элементам массива
Для доступа к элементам массива используется режим адресацииbase + index × element size:
Пример
; Чтение и запись элементов массива
section .data
nums dd 100, 200, 300, 400, 500 ; 5 элементов
nums_len equ ($ - nums) / 4
section .text
global _start
_start:
; Доступ к 0-му элементу (индекс 0)
mov eax, [nums] ; eax = 100
; Доступ к 2-му элементу (индекс 2)
mov eax, [nums + 2 * 4] ; eax = nums[2] = 300
; 2 * 4 = 8, поэтому смещение 8 байт от nums
; Использовать регистр как индекс
mov esi, 3 ; Индекс = 3
mov eax, [nums + esi * 4] ; eax = nums[3] = 400
; Изменить элементы массива
mov dword [nums + 4], 250 ; nums[1] = 250
; Теперь массив содержит: 100, 250, 300, 400, 500
; Использовать EBX в качестве базового регистра
mov ebx, nums ; ebx = базовый адрес массива
mov eax, [ebx + 4 * 4] ; eax = nums[4] = 500
mov eax, 1
mov ebx, 0
int 0x80
Примечание:
[nums + esi * 4]Умножение на 4 объясняется тем, что каждый элемент занимает 4 байта (двойное слово). Для массива байтов (db) записывайте это напрямую как[arr + esi]; для массива слов (dw) записывайте это как[arr + esi * 2]。
Обход массива
Пример
; Пройти по массиву и вычислить сумму
section .data
numbers dd 5, 10, 15, 20, 25, 30, 35, 40, 45, 50
count equ ($ - numbers) / 4 ; Количество элементов
section .text
global _start
_start:
mov ecx, count ; Счётчик цикла
mov esi, 0 ; Индекс (начиная с 0)
mov eax, 0 ; Накопленная сумма
sum_loop:
add eax, [numbers + esi * 4] ; Накопление элементов массива
inc esi ; Индекс + 1
loop sum_loop
; eax = 5+10+15+...+50 = 275
; Другой способ обхода: с использованием указателя
mov ecx, count
mov ebx, numbers ; ebx указывает на начало массива
mov eax, 0 ; Накопленная сумма
sum_loop2:
add eax, [ebx] ; Суммировать текущий элемент
add ebx, 4 ; Переместить указатель на следующий элемент
loop sum_loop2
; eax = 275
mov ebx, eax ; Код выхода = накопленная сумма
mov eax, 1
int 0x80
Поиск в массиве
Пример
; Поиск заданного значения в массиве
section .data
data dd 12, 45, 67, 23, 89, 34, 56, 78, 90, 11
data_len equ ($ - data) / 4
target dd 23 ; Значение для поиска
found_msg db 'Найден по индексу: ', 0
found_len equ $ - found_msg
notfound_msg db 'Не найдено (пример)', 0xA
notfound_len equ $ - notfound_msg
newline db 0xA
section .text
global _start
_start:
mov ecx, data_len ; Счётчик цикла
mov esi, 0 ; Текущий индекс
search_loop:
mov eax, [data + esi * 4] ; Загрузить текущий элемент
cmp eax, [target] ; Равно ли оно целевому значению
je found ; Найдено
inc esi ; Индекс + 1
loop search_loop
; Не найдено
mov eax, 4
mov ebx, 1
mov ecx, notfound_msg
mov edx, notfound_len
int 0x80
jmp exit
found:
; Найдено (esi — это индекс)
mov eax, 4
mov ebx, 1
mov ecx, found_msg
mov edx, found_len
int 0x80
; Преобразовать индекс в символ ASCII и вывести его
add esi, '0' ; Преобразовать однозначный индекс в символ
push esi ; Поместить в стек для временного хранения
mov eax, 4
mov ebx, 1
mov ecx, esp ; Адрес вершины стека
mov edx, 1
int 0x80
pop esi
; Вывести символ новой строки
mov eax, 4
mov ebx, 1
mov ecx, newline
mov edx, 1
int 0x80
exit:
mov eax, 1
mov ebx, 0
int 0x80
Двумерный массив
Двумерный массив разворачивается построчно в одномерное хранилище в памяти. Формула адреса для доступаarr[i][j]имеет вид:
地址 = 基址 + (i*列数 + j) * 元素大小
Пример
= 10 (строка 3, столбец 2)
section .data
matrix dd 1, 2, 3, 4
dd 5, 6, 7, 8
dd 9, 10, 11, 12
rows equ 3
cols equ 4
elem_size equ 4 ; DWORD = 4 bytes
section .text
global _start
_start:
; Доступ к matrix
; Адрес = matrix + (1*4 + 2) * 4 = matrix + 24
mov eax, [matrix + (1 * cols + 2) * elem_size]
; eax = 7
; Динамическое вычисление с использованием регистров (предположим i=2, j=1)
; matrix
mov esi, 2 ; Строка i = 2
mov edi, 1 ; Столбец j = 1
mov eax, cols ; Количество столбцов
mul esi ; eax = i * cols = 2*4 = 8
add eax, edi ; eax = i*cols + j = 8+1 = 9
; eax = eax * elem_size
; Примечание: результат MUL находится в EAX, используйте его непосредственно здесь
mov eax, [matrix + eax * elem_size]
; eax = 10
; Обход всех элементов двумерного массива
mov ecx, rows * cols ; Общее количество элементов = 12
mov esi, 0 ; Индекс
mov ebx, 0 ; Накопленная сумма
traverse:
add ebx, [matrix + esi * elem_size]
inc esi
loop traverse
; ebx = 1+2+3+...+12 = 78
mov eax, 1
int 0x80
Полный пример пузырьковой сортировки
Пример
; Алгоритм пузырьковой сортировки
section .data
array dd 64, 34, 25, 12, 22, 11, 90, 78
array_len equ ($ - array) / 4
section .text
global _start
_start:
mov ecx, array_len ; Внешний цикл: n раз
dec ecx ; Внешнему циклу нужно только n-1 раз
outer_loop:
push ecx ; Сохранить счётчик внешнего цикла
mov esi, 0 ; Индекс внутреннего цикла начинается с 0
mov ecx, array_len - 1 ; Количество итераций внутреннего цикла
inner_loop:
mov eax, [array + esi * 4] ; a[j]
mov ebx, [array + esi * 4 + 4] ; a[j+1]
cmp eax, ebx ; a[j] > a[j+1] ?
jle no_swap ; Нет, не менять местами
; Поменять местами a[j] и a[j+1]
mov [array + esi * 4], ebx
mov [array + esi * 4 + 4], eax
no_swap:
inc esi
loop inner_loop
pop ecx
loop outer_loop
; Массив теперь отсортирован: 11, 12, 22, 25, 34, 64, 78, 90
mov eax, 1
mov ebx, 0
int 0x80
Другие расширенияМассивы в языке ассемблера не имеют проверки границ. Обращение к индексу за пределами допустимых границ не вызовет ошибку; вместо этого оно без предупреждения читает или записывает данные в соседнюю память, что может привести к ошибкам, которые трудно отладить. Обязательно сами следите за тем, чтобы индекс находился в допустимых границах.