Массивы
.pdfМассивы Массив - структурированный тип данных, состоящий из некоторого числа элементов одного типа.
В Ассемблере массивом можно назвать несколько подряд идущих в памяти байт, слов или двойных слов, но все элементы массива должны быть либо байтами, либо словами, либо двойными словами, т.е. иметь одинаковую длину. В качестве имени массива используется символическое имя адреса (смещения) первого байта первого элемента массива.
Для понимания расположения элементов массива в памяти и обращения к ним необходимо вспомнить основные типы данных микропроцессора и расположение байтов в них (рис 1).
Рис 1.Основные типы данных микропроцессора
Пример описания и определения массива:
- из 5 элементов, размер каждого элемента 4 байта:
mas1 dd 1,2,3,4,5 ;mas –имя массива, dd-тип элементов массива двойное слово
- используя оператор повторения dup:
;массив из 5 нулевых элементов, размер каждого элемента 2 байта: mas2 dw 5 dup (0)
При работе с массивами необходимо четко представлять себе, что все элементы массива располагаются в памяти компьютера последовательно. Только программист с помощью составленного им алгоритма обработки определяет, как нужно трактовать эту последовательность байт, составляющих массив. Так, одну и ту же область памяти можно трактовать как одномерный массив, и одновременно те же самые данные могут трактоваться как двухмерный массив. Все зависит только от алгоритма обработки этих данных в конкретной программе. Сами по себе данные не несут никакой информации о своем “смысловом”, или логическом, типе. Помните об этом принципиальном моменте.
Эти же соображения можно распространить и на индексы элементов массива. Ассемблер не подозревает об их существовании и ему абсолютно все равно, каковы их численные смысловые значения.
Для того чтобы обратиться к элементу массива, к его имени нужно добавить индекс. В языке ассемблера индексы массивов — это обычные
адреса.
Чтобы получить доступ к третьему элементу, нужно к адресу массива mas3 прибавить 6. Нумерация элементов массива в ассемблере начинается с нуля. То есть в нашем случае речь, фактически, идет о 4-м элементе массива — 7, но об этом знает только программист; микропроцессору в данном случае все равно — ему нужен только адрес.
Архитектура микропроцессора предоставляет достаточно удобные программно-аппаратные средства для работы с массивами. К ним относятся базовые и индексные регистры, позволяющие реализовать несколько режимов адресации данных. Используя данные режимы адресации, можно организовать эффективную работу с массивами в памяти. Вспомним эти режимы: индексная адресация со смещением — режим адресации, при котором эффективный адрес (т.е. смещение данных относительно начала сегмента данных) формируется из двух компонентов:
o постоянного (базового) — указанием прямого адреса массива в виде имени идентификатора, обозначающего начало массива; o переменного (индексного) — указанием имени индексного регистра.
Пример обращения к элементам массива:
mas3 |
dw |
0,5,6,7,8,9 ;определили одномерный массив с размерностью элементов в слово (2 байта) |
... |
|
|
mov |
si,4 |
;поместить в регистр SI индекс 4 для обращения к третьему элементу массива |
;поместить 3-й элемент массива mas в регистр АХ: |
||
mov |
ax,mas[si] |
В общем случае для получения адреса элемента в массиве необходимо начальный (базовый) адрес массива сложить с произведением индекса (номер элемента минус единица) этого элемента на размер элемента массива:
база + (индекс*размер элемента).
Микропроцессор позволяет масштабировать индекс. Это означает, что если указать после имени индексного регистра знак умножения “*” с последующей цифрой 2, 4 или 8, то содержимое индексного регистра будет умножаться на 2, 4 или 8, то есть масштабироваться. Применение масштабирования облегчает работу с массивами, которые имеют размер элементов, равный 2, 4 или 8 байт, так как микропроцессор сам производит коррекцию индекса для получения адреса очередного элемента массива. Нам нужно лишь загрузить в индексный регистр значение требуемого индекса (считая от 0).
Возможность масштабирования появилась в микропроцессорах Intel, начиная с модели i486. По этой причине в программах необходимо писать директиву
.486.
Пример использования масштабирования: |
|
||
;просмотр элементов массива |
|
||
mas |
dw |
2,7,0,0,1,9,3,6,0,8 |
;исходный массив |
… |
|
|
|
mov |
cx,10 |
;значение счетчика цикла в cx |
|
mov |
esi,0 |
;индекс в esi |
|
see: |
|
|
|
mov |
dx,mas[esi*2];первый элемент массива в dx |
||
inc |
esi |
;на следующий элемент |
|
loop see |
|
|
|
Упражнение 1. |
|
|
|
|
Упражнение 2. |
|
|
|
|
|
||
Использование цикла для инициализации значениями области памяти, |
Просмотр массива, состоящего из слов, и сравнение его элементов с |
|||||||||||
которую можно будет впоследствии трактовать как массив. |
нулем. Выводится соответствующее сообщение. |
|||||||||||
TITLE Инициализация массива из 10 элементов внутри программы числами |
.686 |
|
|
|
|
|
|
|||||
option casemap:none |
|
|
|
|||||||||
;0 1 2 3 4 5 6 7 8 9 |
|
|
|
|
|
|
|
|
|
|||
.686 |
|
|
|
|
|
include \masm32\include\masm32rt.inc |
|
|
||||
option casemap:none |
|
|
|
.data |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
include \masm32\include\masm32rt.inc |
|
ConsoleTitle db 'Concole input-output ',0 |
;наименования консоли |
|||||||||
|
|
|
|
|
|
msg_1 |
db |
' not equality 0!', 0ah, 0dh,0 |
|
;сообщение если элемент |
||
.data |
|
|
|
|
|
|
|
|
|
|
|
;массива "не равен нулю" |
ConsoleTitle db 'Concole input-output ',0 |
;определение строки для |
msg_2 |
db |
|
' equality 0!', 0ah, 0dh,0 |
;сообщение если елемент |
||||||
|
|
|
|
|
;наименования консоли |
|
|
|
|
|
|
;массива "равен нулю" |
msg db 'Result massive :',0ah, 0dh, 0 ; комментарий к выводимому массиву |
msg_3 |
db |
|
0ah, 0dh, 'Element ',0 |
|
;сообщение: "Элемент" |
||||||
mas db 10 dup (?) |
|
|
;исходный массив (неинициализированный) |
mas |
dw |
|
2,7,0,0,1,9,3,6,0,8 |
|
;исходный массив |
|||
i db 0 |
|
|
;переменная для инициализации массива |
format db '%d',0 ;формат выводимых целых чисел (для invoke crt_printf) |
||||||||
format db '%d',0 |
;формат выводимых целых чисел (для invoke crt_printf) |
|
|
|
|
|
|
|
||||
format_c db '%c',0 |
|
;формат для вывода символов (для invoke crt_printf) |
.code |
|
|
|
|
|
|
|||
|
|
|
|
|
|
main: |
|
|
|
|
|
|
.code |
|
|
|
|
|
invoke SetConsoleTitle, addr ConsoleTitle |
|
;вывод имени консоли |
||||
main: |
|
|
|
|
|
xor eax,eax |
|
|
|
|
|
|
invoke SetConsoleTitle, addr ConsoleTitle |
;вывод имени консоли |
|
;обнуление eax |
|
|
|||||||
|
|
|
|
|
|
prepare: |
|
|
|
|
|
|
|
xor eax,eax |
|
;обнуление eax |
|
mov |
|
ecx,10 |
;значение счетчика цикла в ecx |
||||
|
mov ecx,10 |
|
;значение счетчика цикла в ecx |
|
mov |
|
esi,0 |
;индекс в esi |
||||
|
mov esi,0 |
|
|
;индекс начального элемента в esi |
compare: |
|
|
|
|
|
||
go: |
|
|
;цикл инициализации |
|
mov |
|
dx,mas[esi*2] ;первый элемент массива в dx |
|||||
|
mov bh,i |
|
;i в bh |
|
|
|
cmp |
|
dx,0 |
;сравнение dx c 0 |
||
|
mov mas[esi],bh |
;запись в массив значения i |
|
je |
|
equal |
;переход, если равно 0 |
|||||
|
inc i |
|
|
;инкремент i (i=i+1) |
not_equal: |
|
|
;не равно 0 |
|
|
||
|
inc esi |
;продвижение к следующему элементу массива |
|
push ecx |
|
;сохранить значение ECX в стеке |
||||||
|
loop go |
;повторить цикл |
|
invoke crt_printf, addr msg_3 |
;вывод сообщения msg_3 |
|||||||
|
|
|
|
|
|
invoke |
crt_printf, addr format, esi ;вывод номера элемента массива на экран |
|||||
;вывод на экран получившегося массива |
|
invoke crt_printf, addr msg_1 |
;вывод сообщения msg_1 |
|||||||||
invoke crt_printf, addr msg |
;Сообщение о выводе массива |
|
pop ecx |
|
;восстановление из стека значения ECX |
|||||||
mov |
ecx,10 |
|
|
;значение счетчика цикла в ECX |
|
inc |
|
esi |
;на следующий элемент |
|||
mov |
esi,0 |
|
|
;присвоение индексному регистру значения 0 |
|
dec |
|
ecx |
;условие для выхода из цикла |
|||
show: |
|
|
;цикл вывода на экран получившегося массива |
|
jecxz |
exit_ |
;ecx=0? Если да — на выход |
|||||
xor edx,edx |
|
|
|
|
|
jmp |
|
compare ;нет — повторить цикл |
||||
mov dl,mas[esi] |
|
|
;значение элемента массива |
equal: |
|
|
|
;равно 0 |
|
|
||
push ecx |
|
;сохранение значения регистра ECX в стеке |
push ecx |
|
|
|
|
|
||||
invoke crt_printf, addr format, edx ;вывод очередного элемента массива |
invoke crt_printf, addr msg_3 |
;вывод сообщения msg_3 |
||||||||||
invoke |
crt_printf, addr format_c,' ' ;вывод пробела между выводимыми |
invoke |
crt_printf, addr format, esi ;вывод номера элемента массива на экран |
|||||||||
pop ecx |
|
|
|
;элем. массива |
invoke crt_printf, addr msg_2 |
;вывод сообщения msg_2 |
||||||
;возвращение сохраненного значения регистра ECX из стека |
|
pop ecx |
|
|
|
|
||||||
inc esi |
|
|
|
|
|
|
inc |
|
esi |
;на следующий элемент |
||
loop show |
|
;повторить цикл |
|
dec |
|
ecx |
;все элементы обработаны? |
|||||
invoke Sleep, INFINITE ;задержка закрытия консоли для просмотра результатов |
|
jecxz |
exit_ |
|
|
|
||||||
|
jmp |
|
compare |
|
|
|
||||||
invoke ExitProcess,0 |
|
;передача управления Windows |
exit_: |
|
|
|
|
|
|
|||
end main |
|
|
;конец программы |
invoke Sleep, INFINITE ;задержка закрытия консоли для просмотра результатов |
||||||||
Результат работы программы: |
|
invoke ExitProcess,0 |
|
;передача управления Windows |
||||||||
|
end main |
|
|
;конец программы |
||||||||
Result massive : |
|
|
|
|
Ррезультат работы программы: |
|
|
|||||
0 1 2 3 4 5 6 7 8 9 |
|
|
|
|
Element 0 not equality 0! |
|
|
|
||||
Задание 1. Инициализируйте область памяти числами 9,8,7,6,5,4,3,2,1,0 |
Element 1 not equality 0! |
|
|
|
||||||||
Element 2 not equality 0! |
|
|
|
|||||||||
|
|
|
|
|
|
Element 3 equality 0! |
|
|
|
|
||
|
|
|
|
|
|
Element 4 equality 0! |
|
|
|
|
||
|
|
|
|
|
|
Element 5 equality 0! |
|
|
|
|
||
|
|
|
|
|
|
Element 6 equality 0! |
|
|
|
|
||
|
|
|
|
|
|
Element 7 not equality 0! |
|
|
|
|||
|
|
|
|
|
|
Element 8 not equality 0! |
|
|
|
|||
|
|
|
|
|
|
Element 9 not equality 0! |
|
|
|
|||
|
|
|
|
|
|
Задание 2. Программа выводит неверный результат. Исправьте код |
||||||
|
|
|
|
|
|
программы, чтобы она выдавала корректный результат. |
||||||
Масштабирование эффективно лишь тогда, когда размерность элементов |
Упражнение 4. |
Ввод и вывод элементов массива в консоли |
||||||||||
массива равна 2, 4 или 8 байт. Если же размерность элементов другая, то |
TITLE Ввод размерности массива |
|
|
|||||||||
организовывать обращение к элементам массива нужно обычным способом, |
TITLE Ввод элементов массива через пробел |
|||||||||||
как описано ранее. |
|
|
|
|
TITLE Удвоение элементов массива |
|
|
|||||
Рассмотрим пример работы с массивом из пяти трехбайтовых элементов. |
TITLE Вывод элементов массива |
|
|
|||||||||
Младший байт в каждом из этих элементов представляет собой некий |
.686 |
|
|
|
|
|
|
|||||
счетчик, а старшие два байта — что-то еще, для нас не имеющее никакого |
option casemap:none |
|
|
|
||||||||
значения. Необходимо последовательно обработать элементы данного |
|
|
|
|
|
|
|
|||||
массива, увеличив значения счетчиков на единицу. |
include \masm32\include\masm32rt.inc |
|
|
|||||||||
Упражнение 3. Обработка массива элементов с нечетной длиной |
.data |
|
|
|
|
|
|
|||||
.686 |
|
|
|
|
|
ConsoleTitle db 'Concole input-output ',0 |
;наименования консоли |
|||||
option casemap:none |
|
|
|
msg_1 db 'Input elements array: ',0 ;сообщение о вводе элементов массива |
||||||||
|
|
|
|
|
|
msg_2 db 'Input number elements array n=: ',0 ;сообщение о вводе кол-ва |
||||||
include \masm32\include\masm32rt.inc |
|
msg_3 db 'Result array: ',0 |
|
|
;элем. массива |
|||||||
|
|
|
|
|
|
;сообщение о результативном массиве |
||||||
.data |
|
|
|
|
|
msg_4 db ' ',0 ;переменная содержащая ASCII код пробела |
ConsoleTitle db 'Concole input-output ',0 ;наименования консоли
N=5 |
;количество элементов массива |
mas db 5 dup (3 dup (0)) |
;исходный массив из 5-и элем. по 3 байта в |
|
;каждом и во всех 15 байтов записано число 0 |
msg db ' ',0
msg_1 db 'Byte counter: ',0 ;сообщение: Байт-счетчик
format db '%d',0 |
;формат выводимых целых чисел (для invoke crt_printf) |
||||
.code |
|
|
|
|
|
main: |
|
|
|
|
|
invoke SetConsoleTitle, addr ConsoleTitle |
;вывод имени консоли |
||||
xor eax,eax |
|
;обнуление eax |
|
||
|
mov |
|
esi,0 |
|
;0 в esi |
|
mov |
|
ecx,N |
;N в ecx |
|
go: |
|
|
|
|
|
xor edx,edx |
|
;обнуление edx |
|
||
mov |
dl,mas[esi] |
;первый байт поля в dl |
|||
inc |
dl |
;увеличение dl на 1 (по условию) |
|||
mov |
mas[esi],dl |
;заслать обратно в массив |
|||
add |
esi,3 |
|
;сдвиг на следующий элемент массива |
||
loop |
go |
|
;повтор цикла |
|
|
invoke crt_printf, addr msg_1 |
|
|
|||
mov |
esi,0 ;подготовка к выводу на экран элементов массива |
||||
|
|
;содержащие байт-счетчик |
|||
mov |
ecx,N |
|
|
|
|
show: |
;вывод на экран содержимого первых байт полей |
||||
xor edx,edx |
|
|
|
|
|
mov dl,mas[esi] |
;1-й байт для вывода, затем 3, 6, 9 и 12 |
||||
push ecx |
|
|
|
|
|
invoke |
crt_printf, addr format, edx |
|
|
invoke crt_printf, addr msg ; вывод пробела между выводимыми элементами add esi,3
pop ecx loop show
exit_:
invoke Sleep, INFINITE invoke ExitProcess,0 end main
Результат работы программы:
Byte counter: 1 1 1 1 1
mas dd 128 dup (?) ;исходный массив с неопределенными элементами
x_mas dd ? |
;переменная для хранения одного введенного элемента |
|||
массива |
|
|
|
|
n dd ? |
;кол-во элементов массива |
|
||
formats db '%d',0 |
;формат вводимых и выводимых целых чисел |
|||
.code |
|
|
|
|
main: |
|
|
|
|
invoke SetConsoleTitle, addr ConsoleTitle |
;вывод имени консоли |
|||
invoke crt_printf, addr msg_2 |
|
;вывод сообщения о вводе n |
||
invoke crt_scanf, ADDR formats, ADDR n |
;ввод n |
|||
invoke crt_printf, addr msg_1 |
;вывод сообщения о вводе элем. массива |
mov esi,0 mov ecx,n m1:
push ecx
invoke crt_scanf,ADDR formats,ADDR x_mas ;ввод x_mas как символьной
;строки и преобр. ее в число, с помещ. в яч. x_mas mov eax,x_mas ;1-й, 2-й и т.д. введенный элемент поместить в EAX
mov mas[esi*4],eax ;1-й, 2-й и т.д. введенные элем. в mas[0], mas[1], и т д. inc esi ;продвижение к вводу следующего элемента массива
pop ecx
loop m1 ;повторить цикл ввода элементов массива
;вывод на экран получившегося массива
invoke crt_printf, addr msg_3 |
;сообщение о выводе массива |
||
mov |
ecx,n |
|
;значение счетчика цикла в ECX |
mov |
esi,0 |
|
;присвоение индексному регистру значения 0 |
show: |
|
;цикл вывода на экран получившегося массива |
xor edx,edx
imul eax,mas[esi*4],2 ;удвоение элементов массива командой
mov edx,eax |
;целочисленного умножения со знаком |
;значение элемента массива в EDX |
|
push ecx |
;сохранение значения регистра ECX в стеке |
invoke crt_printf, addr formats, edx ;вывод очередного элемента массива invoke crt_printf, addr msg_4 ;вывод пробела между выводимыми элем. массива
pop ecx |
;возвращение сохраненного значения регистра ECX из стека |
inc esi |
|
loop show |
;повторить цикл |
invoke Sleep, INFINITE ;задержка закрытия консоли invoke ExitProcess,0
end main
Результат работы программы:
Input number elements array n=: 5 Input elements array: 0 -23 5 7 -4 Result array: 0 -46 10 14 -8
Задача 1. Сложить соответствующие элементы двух массивов, результат вывести на экран.
masx |
dw |
2,1,2,1,2,1,2,1,2,1 |
masy |
dw |
1,2,1,2,1,2,1,2,1,2 |