Для ассемблера не существует многомерных массивов - все данные в наборе располагаются в памяти последовательно. Однако на логическом уровне может быть удобно представлять данные в виде многомерного массива. Например, возьмем двухмерные массивы, которые можно представить в виде таблицы, где строки представляют одно измерение, а столбцы - второе измерение:
numbers dword 1, 2, 3, 4 dword 5, 6, 7, 8 dword 9, 10, 11, 12
Здесь условно у нас есть три строки. В каждой строке по четрые столбца. В памяти такой набор данных будет располагаться последовательно, то есть фактически будет аналогичен следующему определению:
numbers dword 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
Но в ряде задач довольно удобно работать с данными как с двухмерными массивами, например, при математических вычислениях с матрицами, для определения карт в играх и т.д. Соотвественно может возникнуть вопрос получения конкретного элемента из подобного двухмерного массива.
Если речь идет о двухмерном массиве/таблице, то формула получения элемента из определенной строки и столбца выглядит следующим образом:
element_address = base_address + (row_index * col_size + col_index) *element_size
Где base_address
- адрес начала массива (его первого элемента), row_index
- номер строки элемента, col_size
- количество столбцов/элементов в строке, col_index
- номер столбца элемента, а
element_size
- размер элементов в байтах. Нумерация строк и столбцов начинается с нуля, то есть номер первой строки или первого столбца - 0.
Например, возьмем выше определенный двухмерных массив numbers. Предположим, нам надо получить элемент в 2-й строке и 3-столбце (число 7). Тогда расчет адреса элемента будет выглядеть следующим образом:
element_address = адрес_numbers + (1 * 4 + 2) *4
То есть мы получим, что для получения адреса числа 7 нам надо к адресу массива numbers прибавить (1 * 4 + 2) *4 = 6 * 4 = 24 байт. Посмотрим, как это будет выглядеть в программе на ассемблере:
.data numbers dword 1, 2, 3, 4 dword 5, 6, 7, 8 dword 9, 10, 11, 12 elemSize = 4 ; размер одного элемента - dword rows = 3 ; кол-во строк cols = 4 ; кол-во столбцов rowIndex dword 1 ; обращаемся к 2-й строке colIndex dword 2 ; обращаемся к 3-му столбцу ; Нам надо получить [rbx + (cols * rowIndex + colIndex) * elemSize] .code main proc lea rbx, numbers ; в RBX - адрес массива numbers xor rdx, rdx mov edx, rowIndex ; в RDX индекс строки - rowIndex imul edx, cols ; cols * rowIndex add edx, colIndex ; cols * rowIndex + colIndex mov eax, [rbx + rdx * elemSize] ; EAX = 7 ret main endp end
В регистр RBX помещаем адрес массива numbers. Это будет базовый адрес, относительно которого будут идти все расчеты
lea rbx, numbers
Для вычисления эффективного адреса сначала в регистр EDX помещаем индекс строки элемента - переменную rowIndex
mov edx, rowIndex
Затем умножаем индекс строки на количество столбцов
imul edx, cols
Затем прибавляем к значению в EDX индекс столбца нужного элемента:
add edx, colIndex
Таким образом мы получили индекс элемента внутри массива numbers. Далее мы можем умножить этот индекс на размер элементов и прибавить к базовому адресу - адресу первого элемента в numbers. И таким образом получим адрес числа 7:
mov eax, [rbx + rdx * elemSize]
Стоит отметить, что если нам надо определить неинициализированный многомерный массив или многомерный массив, где все элементы имеют какое-то одно начальное значение, то мы можем использовать оператор dup:
numbers dword 3*4 dup (1)
Здесь определяется массив numbers из 12 элементов, каждый из которых равен 1.
MASM позволяет использовать вложенные выражения dup
. Так, вместо предыдущего определения массива мы могли бы написать так:
numbers dword 3 dup (4 dup(1))
Можно прочитать эту запись так: определяем набор из 3 элементов, где каждый вложенный элемент представляет набор из 4 чисел dword. То есть по сути 3 строки, каждая из которых содержит 4 столбца.