Глава 11. Программирование конструкций языков высокого уровня
В языках высокого уровня массивом называется переменная, представляющая собой совокупность нескольких элементов одного типа. Эта совокупность имеет одно имя, а доступ к ее отдельным элементам осуществляется при помощи индекса.
В Ассемблере массивом можно назвать несколько подряд идущих в памяти байт, слов или двойных слов, но все элементы массива должны быть либо байтами, либо словами, либо двойными словами, т.е. иметь одинаковую длину. В качестве имени массива используется символическое имя адреса (смещения) первого байта первого элемента массива. Массивы байт иногда называют строками.
Следует особо подчеркнуть: о том, что данная последовательность элементов является массивом, знает только программист, компьютер об этом "не знает". Поэтому, естественно, при компиляции и выполнении программы не производится какого-либо контроля на выход за пределы массива и проч. Например, программист может определить массив из 10 элементов, а затем обратиться к 15-му элементу этого массива – ответственность за такое обращение будет целиком лежать на программисте.
В целом можно констатировать, что подход к массивам в Ассемблере очень близок к подходу к массивам в языке С: весь массив задается адресом первого элемента и размером элементов, "конец" массива никак не фиксируется.
В языках высокого уровня структурой (или записью) называется переменная, представляющая собой совокупность нескольких элементов (в общем случае) разных типов. Эта совокупность имеет одно имя, а доступ к отдельным элементам (называемым полями) осуществляется при помощи составного имени (составленного из имени самой структуры и имени поля).
Аналогично в Ассемблере структурой можно назвать несколько подряд идущих в памяти байт, слов или двойных слов, причем элементы (поля) могут быть и байтами, и словами, и двойными словами, т.е. иметь различную длину. В качестве имени структуры используется символическое имя адреса (смещения) первого байта структуры, а в качестве имен полей используются символические имена смещений полей относительно имени структуры.
Следует подчеркнуть разницу между описаниями массивов и структур в Ассемблере (которая имеется, впрочем, и в языках высокого уровня). Когда мы описываем массив, компилятор выделяет память для него. Со структурами дело обстоит сложнее. Сначала мы должны описать тип структуры, т.е. указать перечень полей, из которых она состоит. На этом этапе память компилятором еще не выделяется. После того, как тип структуры описан, мы можем описывать переменные, являющиеся конкретными воплощениями (объектами, ипостасями, экземплярами) описанного выше типа структуры. Именно на этом этапе компилятор выделяет память под экземпляры структур.
Продемонстрируем работу с массивами на простейшем примере. Имеются два массива x и y с 10 элементами. Необходимо получить массив z, элементы которого являются суммами соответствующих элементов массивов x и y, а также скалярное произведение x и y.
.model small
.stack 128
.data
n equ 10
x dw 1,2,3,4, n–4 dup(1); Массив:1,2,3,4,1,1,1,1,1,1
y dw 5,6,7,8,9,n–5 dup(2); Массив:5,6,7,8,9,2,2,2,2,2
z dw n dup (?) ;Для массива-суммы
s dw 0 ;Для скалярного произведения
.code
Entry: mov AX, @data
mov DS, AX
xor SI,SI ;Обнуляем индекс-регистр
mov CX,n ;Загружаем счетчик цикла
Cycle: mov AX,x[SI] ;AX:=xi
add AX,y[SI] ;AX:= xi +yi
mov z[SI],AX ;zi =xi +yi
mov AX,x[SI] ;AX:=xi
imul y[SI] ;AX:= xi* yi
add s,AX ;s:=s + xi* yi
add SI,2 ;Наращиваем индекс на 2
loop Cycle ;Повторить n раз
mov AH, 4Сh
int 21h
end Entry
Продемонстрируем работу с двумерными массивами. Пусть имеется матрица слов a размером 5´6. Необходимо ввести ее элементы. Если элементы матрицы обозначаются индексами i=0,1,2,3,4 и j=0,1,2,3,4,5, то очевидно, смещение (i,j)-го элемента матрицы относительно ее начала будет 20*(2i)+(2j). (Двойки появились потому, что элементы матрицы – слова).
include io.asm ;Учебное расш. ввода/вывода
.model small
.stack 128
.data
m equ 5
n equ 6
a dw m dup( n dup(?)) ;Двумерный массив
Invit db "Next number: $" ;Текст приглашения
.code
Entry: mov AX, @data
mov DS, AX
lea DX, Invit ;Адрес строки приглаш. в DX
xor BX,BX ;Обнуляем регистр-модиф.
mov CX,m ;Загр. счетчик внутр. цикла
C_Ext: push CX ;Сохр. счетчик внешн. цикла
mov CX,n ;Загр. счетчик внутр. цикла
xor SI,SI ;Обнуляем индекс-регистр
C_Int: outstr ;Вывод приглашения
inint a[BX][SI] ;Ввод очередного числа
add SI,2 ;Наращиваем внутр. индекс
loop C_Int ;Повторить n раз
add BX,2*n ;Наращиваем внешн. индекс
pop CX ;Восст. счетчик внешн. цикла
loop C_Ext ;Повторить m раз
mov AH, 4Сh
int 21h
end Entry
Следует подчеркнуть, что для двойного индексирования мы можем применять пары регистров (BX,SI), (BX,DI), (BP,SI), (BP,DI), но не можем применять пары (SI,DI) и (BX,BP).
Оформим эту программу в виде процедуры. Будем предполагать, что значение m передается в регистре AX, значение n передается в регистре DX, адрес матрицы a передается в регистре BX. Заметим, что в этом случае нам не удобно размещать строку приглашения в сегменте данных, удобнее разместить ее вывод внутри процедуры.
InMatr proc
pusha ;Сохраняем значения РОН
mov CX,AX ;Загр. счетчик внешн. цикла
L1: push CX ;Сохр. счетчик внешн. цикла
mov CX,DX ;Загр. счетчик внутр. цикла
xor SI,SI ;Обнуляем индекс-регистр
L2: outch '>' ;Вывод приглашения Next:
inint [BX][SI] ;Ввод очередного числа
add SI,2 ;Наращиваем внутр. индекс
loop L2 ;Повторить n раз
shl DX,1 ;DX:=2*n
add BX,DX ;Наращиваем внешн. индекс
shr DX,1 ;DX:=n
pop CX ;Восст. счетчик внешн. цикла
loop L1 ;Повторить m раз
popa ;Восстанавливаем РОН
ret ;Возврат из процедуры
InMatr endp
Теперь нетрудно написать процедуру вывода матрицы при тех же самых предположениях.
OutMatr proc
pusha ;Сохраняем значения РОН
mov CX,AX ;Загр. счетчик внешн. цикла
L3: push CX ;Сохр. счетчик внешн. цикла
mov CX,DX ;Загр. счетчик внутр. цикла
xor SI,SI ;Обнуляем индекс-регистр
L4: outint [BX][SI],4 ;Вывод очередного числа
add SI,2 ;Наращиваем внутр. индекс
loop L4 ;Повторить n раз
newline ;Переход на новую строку
shl DX,1 ;DX:=2*n
add BX,DX ;Наращиваем внешн. индекс
shr DX,1 ;DX:=n
pop CX ;Восст. счетчик внешнего цикла
loop L3 ;Повторить m раз
popa ;Восст. значения РОН
ret ;Возврат из процедуры
OutMatr endp
Для этих процедур данные могли бы выглядеть так:
m equ 5
n equ 6
a dw m dup (n dup(?))
Вызов этих процедур мог бы выглядеть так:
mov AX,m
mov DX,n
lea BX,a
InMatr
OutMatr
Напомним, что в Ассемблере предусмотрена целая группа команд для работы с цепочками, т.е. с одномерными массивами (команды MOVS, CMPS, SCAS, STOS, LODS), позволяющие обрабатывать массивы гораздо более эффективно.
При работе со строками всегда возникает вопрос о создании "динамических" строк, т.е. строк переменной длины. Здесь Ассемблер не оказывает программисту никакой помощи, и программист должен сам позаботиться о создании таких строк.
Во всех случаях необходимо задать максимальную длину строки, т.е. описать массив байт, например:
MyString db 256 dup(?) ;Длина строки не более 256 символов
Затем можно пойти, например, по пути языка PASCAL и хранить в первой ячейке этого массива его "динамическую" длину, а символы самой строки хранить в остальных байтах, причем следует считать, что все, что отстоит от начала массива на расстояние, большее "динамической" длины, к строке не относится. При изменении длины строки программист должен сам заботиться об изменении значения в ее первом байте.
Можно пойти по пути языка С и выделить некоторый символ "конца строки
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.