Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2014- СП 3.0 ЛАБЫ ОБЯЗАТ.doc
Скачиваний:
99
Добавлен:
01.03.2016
Размер:
896 Кб
Скачать
        1. Автоматическое размещение внутренних переменных

Усложним нашу процедуру. Предположим, программисту в процедуре понадобились две внутренние переменные по 4 байта каждая, с именами хиу. Чтобы компилятор создал их, достаточно в процедуру сразу после заголовка вставить одну строку:

LOCAL x:dword, y:dword

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

add esp, 0FFFFFFF8h

Кто не забыл компьютерную арифметику, тот во втором операнде быстро узнает число -8. Этот оператор резервирует 8 байт ниже базы, вычитая 8 (фактически прибавляя -8) из указателя стека ESP.

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

        1. Повышение надежности и облегчение вызовов – макрос invoke

Процедуры нередко имеют много параметров. Если программист предпочёл работать «вручную», самостоятельно прописывая всё, то ему нужно внимательно следить, сколько, каких и в каком порядке величин он передает в стек командами PUSHперед собственно вызовом процедуры командойCALL.

Но люди несовершенны. Предположим, наш программист не углядел. и передал параметры чуточку не в том порядке, переставил местами второй и третий. И что теперь?

А ничего хорошего. Обращение к параметрам любая процедура ведет по смещениям относительно базы. Оператор программы читает [EBP+16], программист уверен, что читается параметр 3, а на его месте стоит значение параметра 2… Ну и дальше в том же духе.

Чтобы уменьшить влияние человеческого фактора при организации вызовов процедур следует применять макрос invoke(перевод названия – «вызвать», как ни странно).

Что он делает?

        1. Проверяет, все ли фактические параметры передаются процедуре и правильного ли они типа (байтовой ширины).

        2. Записывает в исходный текст программы оператор PUSHдля каждого фактического параметра. Список параметров вinvoke-вызове просматривается, как правило, от последнего параметра к первому («задом наперед»). Слова «как правило» относятся к случаю, когда в начале программы записана директива .MODELFLAT,STDCALL. Если в качестве второго операнда директивы «.MODEL» указывать другие разрешенные имена (С,PASCAL,SYSCALLи некоторые другие), то в некоторых случаях это будет означать просмотр списка параметров не в обратном, а в прямом порядке. Впрочем, мы всегда будем работать с параметромSTDCALL, поэтому для нас стандартное направление просмотра списка параметров – от последнего к первому. Дополнительным полезным эффектом от параметраSTDCALLявляется то, что он заставляет компилятор применять в командеRETоперанд «число» для очистки стека от параметров. Таким образом, попутно решается и эта проблема.

        3. Создает команду CALLимя_процедуры.

Выполнение действия (а) требует, чтобы макросу invokeбыло заранее откуда-то известно, сколько и каких параметров у каждой процедуры. Об этом тоже должен позаботиться сам программист. Он должен ДО вызова процедуры макросомinvokeразместить в исходнике так называемыйпрототип процедуры.

Это такое вот описание для нашего примера с процедурой SpendTime:

SpendTime PROTO :DWORD, :DWORD

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