Структура данных - это организация данных, которая имеет для
программиста определенный смысл. Как показывает опыт, мы определяем
структуры данных когда одна и та же совокупность данных
используется более чем одной программой или программистами.
Благодаря определению, обе стороны имеют четкий образ этих данных.
Если программа A передает некотрые данные программе B, то
определение структуры данных гарантирует, что каждая из программ
ищет данные в одном и том же месте.
У нас уже был хороший пример структуры данных. Блок управления
файлом FCB является структурой данных. Блок FCB используется
программами для обмена информацией о файле с DOS. В блоке FCB
содержатся такие важные данные об обрабатываемом файле, как номер
текущей записи, длина файла и т.д. Кроме того, в блоке FCB имеется
зарезервированное поле, которое содержит информацию, используемую
только DOS. В блоке FCB находится вся информация, необходимая для
DOS и прикладных программ. Эта структура данных служит для передачи
параметров файла между DOS и прикладной программой.
Теперь нужно найти такой способ определения структур данных,
чтобы программа могла с удобством ими пользоваться. В
Макроассемблере фирмы IBM имеется оператор STRUC, позволяющий
определять структуру данных. С точки зрения программиста структура
данных выглядит как еще один сегмент. Определение данных
ассемблируется так же, как и обычные операторы данных, и описание
структуры, как и описание сегмента, заканчивается оператором ENDS.
Однако в действительности структура не генерирует данные. Оператор
STRUC определяет структуру данных для ассемблера. В дальнейшем имя
этой структуры данных используется в ассемблируемой программе для
генерации соответствующей области данных.
Если рассматривать оператор STRUC описанным выше образом, то он
больше похож на оператор MACRO. Программа определяет структуру
данных в одном месте, а ее вызов осуществляет позднее. Фактическая
генерация данных происходит при вызове структуры. Фиг. 6.14 поможет
понять работу оператора STRUC.
Microsoft (R) Macro Assembler Version 5.00 1/1/80 04:03:36
Фиг. 6.14 Структуры Page 1-1
PAGE ,132
TITLE Фиг. 6.14 Структуры
FCB STRUC
0000 00 DRIVE DB 0 ; Номер устройства
0001 20 20 20 20 20 20 20 FILE_NAME DB ' ' ; Имя файла
20
0009 20 20 20 FILE_EXT DB ' ' ; Тип файла
000C 0000 CURRENT_BLOCK DW 0 ; Номер текущего блока
000E 0080 RECORD_SIZE DW 80H ; Логический размер записи
0010 00000000 FILE_SIZE DD 0 ; Размер файла в байтах
0014 0000 DATE DW 0 ; Дата последнего изменения
Фиг. 6.14 Структуры (начало)
0016 000A[ RESERVED DB 10 DUP (?) ; Зарезервировано ДОС
??
]
0020 00 SEQ_NUMBER DB 0 ; Номер текущей записи
0021 00000000 RANDOM_NUMBER DD 0 ; Номер записи при прямом
; доступе
0025 FCB ENDS
0000 STACK SEGMENT STACK
0000 0040[ DW 64 DUP (?)
????
]
0080 STACK ENDS
0000 CODE SEGMENT
ASSUME CS:CODE
0000 01 INPUT FCB <1,'FIG6-14','INP'>
0001 464947362D313420
0009 494E50
000C 0000
000E 0080
0010 00000000
0014 0000
0016 000A[
??
]
0020 00
0021 00000000
0025 02 OUTPUT FCB <2,'EXAMPLE','TST'>
0026 4558414D504C4520
002E 545354
0031 0000
0033 0080
0035 00000000
0039 0000
003B 000A[
??
]
0045 00
0046 00000000
004A STRUCTURES PROC FAR
004A 1E PUSH DS ; Установка адреса возврата
004B B8 0000 MOV AX,0
004E 50 PUSH AX
004F 0E PUSH CS ; Установка DS на сегмент CODE
0050 1F POP DS
ASSUME DS:CODE
0051 8D 16 0000 R LEA DX,INPUT ; Открытие вводимого файл
Фиг. 6.14 Структуры (продолжение)
0055 B4 0F MOV AH,0FH
0057 CD 21 INT 21H
0059 8D 16 0025 R LEA DX,OUTPUT ; Создание выводимого файла
005D B4 16 MOV AH,16H
005F CD 21 INT 21H
0061 8D 1E 0000 R LEA BX,INPUT
0065 C7 47 0E 0010 MOV [BX].RECORD_SIZE,16 ; Установка размера записи для ввода
006A C6 47 20 01 MOV [BX].SEQ_NUMBER,1 ; Пропуск первой записи
006E C7 06 0033 R 0010 MOV OUTPUT.RECORD_SIZE,16 ; Установка размера записи для
; вывода
0074 8D 16 0000 R LEA DX,INPUT ; Чтение второй записи из файла
0078 B4 14 MOV AH,14H
007A CD 21 INT 21H
007C 8D 16 0025 R LEA DX,OUTPUT ; Вывод введенной записи
0080 B4 15 MOV AH,15H
0082 CD 21 INT 21H
0084 B4 10 MOV AH,10H ; Закрытие выводимого файла
0086 CD 21 INT 21H
0088 CB RET
0089 STRUCTURES ENDP
0089 CODE ENDS
END
Microsoft (R) Macro Assembler Version 4.00 4/16/89 23:15:19
Фиг. 6.14 Структуры Symbols-1
Structures and Records:
N a m e Width # fields
Shift Width Mask Initial
FCB . . . . . . . . . . . . . . 0025 000A
DRIVE . . . . . . . . . . . . 0000
FILE_NAME . . . . . . . . . . 0001
FILE_EXT . . . . . . . . . . . 0009
CURRENT_BLOCK . . . . . . . . 000C
RECORD_SIZE . . . . . . . . . 000E
FILE_SIZE . . . . . . . . . . 0010
DATE . . . . . . . . . . . . . 0014
RESERVED . . . . . . . . . . . 0016
SEQ_NUMBER . . . . . . . . . . 0020
RANDOM_NUMBER . . . . . . . . 0021
Фиг. 6.14 Структуры (продолжение)
На Фиг. 6.14 приведена очень простая программа, которая
использует файлы системы DOS. Эта программа открывает файл DOS на
носителе в дисководе A:, считывает вторую запись этого файла и
записывает ее в файл на носителе в дисководе B:. Маловероятно,
чтобы вы когда-нибудь применили эту программу чтобы сделать
что-либо существенное, но сейчас она дает нам возможность
использовать структуру данных для блока FCB.
Первая часть программы на Фиг. 6.14 определяет структуру данных
FCB. Оператор языка ассемблера STRUC отмечает начало определения
структуры. Метка FCB является здесь именем данной конкретной
структуры. В примере определяется каждое поле структуры данных FCB.
Обратите внимание, что в столбцах слева ассемблер генерирует
объектный код данной структуры. Однако, при редактировании связей
ассемблированного объектного кода, область данных в программе
отсутствует. Ассемблер распечаьываеь данную структуру данных в
оттранслированном виде исключительно для вашего сведения.
Точно так же, как и вслучае макрокоманды, имя FCB становится
как бы новым оператором языка ассемблера. Первым оператором в
сегменте CODE является вызов структуры данных FCB. В примере этой
структуре данных присваивается имя INPUT. Данная структура FCB
идентифицирует входной набор данных. Заметьте, что в этом операторе
FCB имеются операнды. Они заменяют или перекрывают значения,
которые были включены в исходное определение структуры данных.
Если мы сравним объектный код определения структуры FCB с
объектным кодом структуры INPUT, то увидим, что они различаются по
значениям первых трех полей. В определении структуры данных поле
DRIVE равно 0, в структуре INPUT - 1. Первый операнд в угловых
скобках определения структуры INPUT равен 1. Это значение заменяет
исходно определенное значение 0. Аналогично в данном примере
изменяются значения второго и третьего полей, относящихся к имени
файла. Закрывающая угловая скобка в определении структуры INPUT
завершает процедуру замены значений полей структуры данных.
Оставшаяся часть структуры INPUT идентична определеной в структуре
данных FCB.
Программа может изменять любой из полей структуры FCB, если
определение этого поля содержит только один элемент. В
рассматриваемом примере программа может изменять любое поле
структруыр FCB, за исключением поля RESERVED. Мы определили это
поле поле как 10 отдельных элементов, и оно не может быть изменено.
Аналогично, если поле определено оператором
DB 10,20
то его нельзя перекрыть. При вызове структуры можно изменять только
поля, состоящие из единственного элемента. Символьная строка,
включающая несколько символов, рассматривается ассемблером как один
элемент. В данном примере поле FILE_NAME содержит несколько
символов, но является одним элементом, значение которого можно
изменить.
Операнды, перечисленные в угловых скобках, заменяют операнды,
входящие в определение, по позиционному принципу, как и в
макрокоманде. Если вы не хотите модифицировать заданное в
определении значение, но желаете изменить следующее за ним поле, то
в список значений нужно включить пустой параметр. Например, для
модификации полей FILE_NAME и CURRENT_BLOCK, оставляя в то же время
по умолчанию заданные значения полей DRIVE и FILE_EXT, структуру
FCB следует вызвать оператором:
EXAMPLE FCB <,NEWNAME,,12>
Первый параметр пуст, так что ассемблер использует для него
значение по умолчанию. В следующем поле NEWNAME заменяет строку
пробелов. Для значения поля FILE_EXT по умолчанию используется
строка пробелов, и наконец, нулевое значение поля CURRENT_BLOCK
заменяется на 12.
В следующей исходной программной строке на Фиг. 6.14 программа
определяет структуру FCB под именем OUTPUT для выходного файла.
Здесь снова модифицируются первые три поля определения данных путем
включения новых значений в поля операндов оператора FCB.
Выигрыш от использования структур данных проявляется в
действительных командах программы. Программа может ссылаться на
имена INPUT и OUTPUT так же, как и на любые другие метки в
программе. Вы можете видеть это, в том участке программы, где
открывается входной файл INPUT и оператор
LEA DX,INPUT
используется для установки адреса входной структуры данных FCB.
В программе можно использовать каждое из указанных полей
структуры данных. Значению каждого имени соответствует смещение
какого-либо поля в структуре данных. Например, программа помещает в
регистр BX адрес FCB INPUT. После этого программа обращается к
полям RECORD_SIZE и SEQ_NUMBER в режиме адресации по базе. Так как
регистр BX уже указывает на структуру данных FCB, то нужно задать
смещение относительно этой базы. Способ адресации
[BX].RECORD_SIZ
указывает ассемблеру, что в команде, которую он должен
сгенерировать, смещение поля RECORD_SIZE складывается со значением
базы, хранящимся в регистре BX. Если вы рассмотрите соответствующие
команды на машинном языке, то увидите, что в них присутствуют
смещения для полей RECORD_SIZE (0EH) и SEQ_NUMBER (20H). Символ "."
идентифицирует имена полей как смещения в структуре данных.
Помимо режимов адресации по базе и индексу можно использовать
структуру данных и для прямой адресации. Следующий участок
программы непосредственно изменяет поле RECORD_SIZE в FCB OUTPUT.
Программа именует это поле OUTPUT.RECORD_SIZE. Имя OUTPUT
определяет конкретную структуру данных, а RECORD_SIZE - имя поля в
этой структуре данных.
Прежде, чем покончить с этим примером, посмотрим, какой
информацией о структуре данных располагает ассемблер. Фиг. 6.14
включает в себя фрагмент таблицы символических имен. Ассемблер
выделяет один из разделов этой таблицы для структур и записей.
Ассемблер показывает вам всю информацию о структуре данных, которой
располагает. Этот раздел имеет заголовок "Structures and records"
("Структуры и записи"). В первой строке этого раздела для структуры
FCB из нашего примера указано, что эта структура имеет длину 25H
байт и содержит 0AH полей. Далее ассемблер перечисляет все эти
поля, печатая их с отступом по отношению к имени структуры. Для
каждого из полей приводится значение соответствующего смещения. Для
структур ассемблер использует два столбца, обозначеные "width"
(ширина) и "#dfields" (число полей). Вторая строка в меток колонок
используется для записей. Структуры данных, которые ассемблер
интерпретирует как записи, будут рассмотрены в следующем разделе.
Программа, приведенная на Фиг. 6.14, не делает ничего
полезного. Кроме того, в ней нет никакой обработки ошибок. Однако
она хорошо иллюстрирует применение оператора STRUC. Этот способ
определения данных особенно подходит для часто используемых
структур данных. Использование имен полей в качестве значений
смещений очень удобно при модификации структур данных на этапе
разработки программы. Если вы внесете изменения в структуру данных,
то ассемблер автоматически изменит значения смещений при повторном
ассемблировании программы. Кроме того, использование структур
данных делает программу на языке ассемблера более читабельной и
понятной.