Макроподстановки и макрорасширения 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Макроподстановки и макрорасширения



Теперь рассмотрим, что происходит с макрокомандами. Когда макрогенератор, просматривая исходный текст программы, встречает макрокоманду, то он выполняет макроподстановку: находит описание макроса с указанным именем, берет его тело, заменяет в этом теле все формальные параметры на со­ответствующие фактические параметры и полученный таким образом текст (он называется макрорасширением) подставляет в программу вместо макрокоманды.

Примеры (над стрелкой указано, какие формальные параметры на какие фактические параметры заменяются):

Х→А, У→ЕS:В

SUM A, ES:B ────────────> MOV AX,ES: B

ADD A, AX

 

NM→, TP→W, VL→1,2

VAR,W,<1,2> ─────────────────> DW 1,2

В общем случае макрокоманда заменяется на несколько команд, т.е. она как бы представляет собой целую группу команд. Этим и объясняется название "макрокоманда" - "большая команда".

 

Примеры использования макросов

Пример 1 (описание крупных операций в виде макросов)

Одним из существенных недостатков машинного языка, который сохраняется и в ЯА, является то, что при программировании на этих языках приходится применять мелкие операции. Например, ЭВМ умеет складывать два числа, но вот три числа она сама по себе уже сложить не может, поэтому мы вынуждены указывать ей, как это делается. И такое сведение к мелким операциям приходится делать для любого алгоритма, сколь бы сложным он ни был. В этом и заключается основная сложность программирования на ЯА. В определенной мере этот недостаток можно устранить с помощью макросов. Для этого надо в виде макросов описать более крупные операции, а затем составлять программу с использованием этих макросов.

Пусть, к примеру, в нашей программе многократно встречается условный переход "по меньше": if х<у then goto L. Эта операция реализуется тремя командами. Чтобы их каждый раз не выписывать заново, имеет смысл описать их как макрос, а затем пользоваться им. Давайте так и сделаем. Дадим этому макросу имя if_less и будем считать, что числа у нас знаковые и размером в слово:

IF_LESS МAСRО Х,Y,L

MOV АХ, Х

СМРАХ,Y

JL L

ENDM

Имея такой макрос, можно, к примеру, следующим образом описать вычисление минимума трех чисел DХ=min(А,В,С):

MOV DX,A MOV DX,A

IF_LESS A, B, M1 ──── X→DX, Y→C, L→M2 MOV AX,A

MOV DX,B ‌‌ CMP AX,B

M1: IF_LESS DX, C, M2 ──── J L M1

MOV DX,C MOV DX,B

M2: ... X→DX, Y→C, L→M2 M1:

MOV AX, DX

CMP AX,C

J L M2

MOV DX,C

M2 :...

Слева указан текст программы, который составляет ее автор, а справа - те команды, которые будет реально выполняться. (Замечание: если макрокоманда помечена меткой, то в макрорасширении эта метка размещается в отдельной строке, а тело макроса начинается со следующей строки, т. к. в общем случае первая команда тела макроса может быть помечена своей меткой). Как видно, использование макросов сокращает размеры исходного текста программы и, что не менее важно, позволяет составлять программу в терминах более крупных операций. Если в виде макросов описать все часто используемые операции, то мы фактически построим новый язык, программировать на котором существенно проще, чем на "чистом" ЯА.

Пример 2 (макросы и обращения к процедурам)

Предположим, что имеется процедура NOD, вычисляющая наибольший общий делитель двух чисел: Z=NOD(Х,Y), и что параметр Х передается ее через регистр АХ, параметр Y - через ВХ, а результат Z возвращается через АХ. И пусть надо вычислить СX=NОD(А,В)+NОD(С,D). Тогда фрагмент программы, реализующий это вычисление, выглядит так:

МOV АХ,А

МОV ВХ,В

CALL. NOD

МОV СХ,АХ

МОV АХ,С

МОV ВХ,D

САLL NOD

АDD СХ,АХ

Легко заметить, что при каждом обращении к процедуре приходится выписывать фактически одну и ту же группу команд. Так вот, чтобы их не выписывать каждый раз заново, можно эту группу команд описатькак макрос, а затем его и использовать. Описание этого макроса выглядит так:

CALL_NOD MACRO X,Y

MOV АХ,Х

MOV BX,Y

CALL NOD

ENDM

а нужный нам фрагмент программы:

САLL_NOD А,В

MOV СХ,АХ

САLL_NOD С,D

АDD СХ,АХ

Как видно, получилось более наглядно и короче. После же макроподстановок получится тот же текст программы, что и прежде, но построением такого текста будет заниматься уже макрогенератор, а не мы.

Пример 3 (макросы и блоки повторения)

При входе в процедуру, как правило, требуется спасать в стеке содержимое регистров, для чего приходится выписывать несколько команд РUSН. И если в программе много процедур, если такая группа команд встречается многократно, то имеет смысл описать ее в виде макроса. Рассмотрим, как выглядит такой макрос. Прежде всего отметим, что по смыслу у этого макроса может быть любое число фактических параметров (разное число названий регистров). Поскольку в ЯА можно определять макросы только с фиксированным числом формальных параметров, то в подобных ситуациях обычно поступают так: макрос описывают с одним формальным параметром, но при обращении к нему (в макрокоманде) указывают через запятую нужное число фактических параметров и заключают весь их список в угловые скобки, в результате чего синтаксически получается один параметр. В теле же макроса от этого списка "отщепляют" по одному настоящему параметру и что-то с ним делают. Далее. Макрос должен поставлять в текст окончательной программы (в макрорасширение) несколько однотипных команд РUSН r. Ясно, что здесь следует воспользоваться блоком повторения. С учетом всего сказанного получаем такое определение нашего макроса:

SAVE МАСRО RЕGS;;запись в стек регистров из списка REGS

IPR R,<REGS>

PUSH R

ENDM

ENDM

Рассмотрим, как макрогенератор обрабатывает следующее обращение к нашему макросу:

SAVE <АХ,SI,ВР>

Прежде всего, как обычно, выписывается тело макроса с заменой формального параметра на фактический:

IRР R,<АХ, SI, ВР>

РUSH R

ЕNDM

Напомним, что уголки, в которые был заключен фактический параметр, считаются не относящимися к параметру и при макроподстановке удаляются. Однако в директиве IRP были свои уголки, именно они-то и сохранились в тексте, так что эта директива сохранила правильный синтаксис. Получившийся текст содержит конструкцию макроязыка, а в такой ситуации, как уже отмечалось, макрогенератор продолжает свою работу. В данном случае он "раскручивает" IRP-блок, в результате чего получает уже текст на "чистом" ЯА:

PUSH AX

РUSН SI

РUSH ВР

который и является окончательным макрорасширением нашей макрокоманды.

 

Макросы и процедуры

Прежде чем продолжить рассказ про макросы, сравним их с процедурами. В чем выгода от макросов? Если у нас в тексте программы многократно встречается один и тот же (или почти один и тот же) фрагмент, то чтобы не выписывать его каждый раз заново, мы описываем его как макрос, а затем в нужных местах программы указываем лишь короткие макрокоманды, чем существенно облегчаем себе жизнь. Таким образом, макросы - это средство борьбы с повторяющимися действиями. Но процедуры - это тоже средство борьбы с повторяющимися действиями: многократно повторяющуюся группу команд мы можем описать как процедуру, а затем в нужным местах программы будем указывать команды вызова процедуры.

Итак, имеется два средства решения одной и той же проблемы. Естественно, возникает вопрос: какое из этих средств лучше? Чтобы ответить на этот вопрос, надо понять различие между макросами и процедурами. А оно проявляется в следующем.

И макрос, и процедура описываются в программе только раз. В обоих случаях в нужных местах программы мы указываем короткие ссылки на это описание. Таким образом, с точки зрения процесса написания текста программы, особого различия между макросами и процедурами нет. Но вот после того, как мы написали программу, дальнейшая судьба макросов и процедур оказывается совершенно различной.

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

Однако процедуры проигрывают по времени. В самом деле, при обращении к процедуре мы должны выполнить команды засылки ее параметров в те или иные регистры (либо в стек) и выполнить команду вызова процедуры. Кроме того, по окончании работы процедуры мы выполняем команду возврата из нее. На выполнение всех этих команд, естественно, затрачивается время. Если же используется макрос, то все эти команды выполнять не надо. Действительно, поскольку команды макроса подставляются в то место программы, где их надо выполнить, то команды перехода не нужны. Не нужны и команды передачи параметров, поскольку настройка на них происходит при макроподстановке, когда в теле макроса формальные параметры заменяются на фактические. Тем самым при использовании макроса мы не тратим время на переходы и передачу параметров, а поэтому и получаем выигрыш во времени.

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

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

Итак, большие фрагменты рекомендуется описывать как процедуры, а ма­ленькие - как макросы.



Поделиться:


Последнее изменение этой страницы: 2016-12-13; просмотров: 257; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.15.235.196 (0.018 с.)