Программирование конструкций языков высокого уровня. Массивы и структуры. Реализация конструкций языков высокого уровня

Страницы работы

Фрагмент текста работы

Глава 11. Программирование конструкций языков высокого уровня

1.1.  Массивы и структуры

В языках высокого уровня массивом называется переменная, представляющая собой совокупность нескольких элементов одного типа. Эта совокупность имеет одно имя, а доступ к ее отдельным элементам осуществляется при помощи индекса.

В Ассемблере массивом можно назвать несколько подряд идущих в памяти байт, слов или двойных слов, но все элементы массива должны быть либо байтами, либо словами, либо двойными словами, т.е. иметь одинаковую длину. В качестве имени массива используется символическое имя адреса (смещения) первого байта первого элемента массива. Массивы байт иногда называют строками.

Следует особо подчеркнуть: о том, что данная последовательность элементов является массивом, знает только программист, компьютер об этом "не знает". Поэтому, естественно, при компиляции и выполнении программы не производится какого-либо контроля на выход за пределы массива и проч. Например, программист может определить массив из 10 элементов, а затем обратиться к 15-му элементу этого массива – ответственность за такое обращение будет целиком лежать на программисте.

В целом можно констатировать, что подход к массивам в Ассемблере очень близок к подходу к массивам в языке С: весь массив задается адресом первого элемента и размером элементов, "конец" массива никак не фиксируется.

В языках высокого уровня структурой (или записью) называется переменная, представляющая собой совокупность нескольких элементов (в общем случае) разных типов. Эта совокупность имеет одно имя, а доступ к отдельным элементам (называемым полями) осуществляется при помощи составного имени (составленного из имени самой структуры и имени поля).

Аналогично в Ассемблере структурой можно назвать несколько подряд идущих в памяти байт, слов или двойных слов, причем элементы (поля) могут быть и байтами, и словами, и двойными словами, т.е. иметь различную длину. В качестве имени структуры используется символическое имя адреса (смещения) первого байта структуры, а в качестве имен полей используются символические имена смещений полей относительно имени структуры.

Следует подчеркнуть разницу между описаниями массивов и структур в Ассемблере (которая имеется, впрочем, и в языках высокого уровня). Когда мы описываем массив, компилятор выделяет память для него. Со структурами дело обстоит сложнее.  Сначала мы должны описать тип структуры, т.е. указать перечень полей, из которых она состоит. На этом этапе память компилятором еще не выделяется. После того, как тип структуры описан, мы можем описывать переменные, являющиеся конкретными воплощениями (объектами, ипостасями, экземплярами) описанного выше типа структуры. Именно на этом этапе компилятор выделяет память под экземпляры структур.

1.1.1.  Работа с массивами

Продемонстрируем работу с массивами на простейшем примере. Имеются два массива 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 и хранить в первой ячейке этого массива его "динамическую" длину, а символы самой строки хранить в остальных байтах, причем следует считать, что все, что отстоит от начала массива на расстояние, большее "динамической" длины, к строке не относится. При изменении длины строки программист должен сам заботиться об изменении значения в ее первом байте.

Можно пойти по пути языка С и выделить некоторый символ "конца строки

Похожие материалы

Информация о работе