Директивы компилятора
Почему-то мало кто пользуется директивами компилятора в полном масштабе...
Еще приходится видеть, но вот такие:
{$ifdef}
...
{$else}
...
{$endif}
- почти никогда; несмотря на то, что они открывают довольно широкие возможности для программиста, а именно: позволяют писать код,
успешно компилируемый на разных компиляторах Паскаля, в том числе и 32 битных, включая FPC и TMT.
Еще одно удобство на мой взгляд - широкие возможности при отладке приложений.
Общий синтаксис этих команд таков:
- - установить условный_символ.
- - код следующий после этой директивы компилируется
только в том случае, если условный_символ был установлен.
- - эта директива обозначает начало альтернативного участка кода.
- - ограничивает действие директив и
.
Режимы компиляции. Отладка.
Предположим, что в программе необходимо на этапе отладки выводить какие-то данные для тестирования алгоритма...
Можно, конечно, в окончательном варианте кода закомментировать такие отладочные выводы, или удалить, но лучше заключить
"отладочные операторы" в такую конструкцию, с использованием директив:
{$define debug}
var
x:byte;
begin
x:=1;
inc(x);
{$ifdef debug}
writeln(x);
{$endif}
end.
Таким образом, при работе программы на экран будет выведено значение "2". Если отладку надо отменить, мы можем убрать символ "$"
из директивы - и теперь это будет просто коментарий, и следовательно на экран ничего не будет
выведено.
Универсальность кода
Хорошо, когда мы берем старый код из TP и он компилируется, и программа правильно работает в компиляторе постарше...
А почему бы не реализовать совместимость наоборот или напрямую? Часто при компиляции, возникают ошибки только из-за того, что
код оптимизирован под конкретный компилятор, например под BP, и в TP отказывается компилироваться...
Выходом из такой ситуации является правильное написание кода, а именно отладка кода под различные компиляторы (конечно если
это требуется). Для примера рассмотрим программу для построения графиков некоторых функций (в полярной системе координат):
{$ifdef Win32}
{$APPTYPE GUI}
{$else}
{$E+} {$N+}
{$endif}
Uses
{$ifdef Win32}
WinCrt,Windows,
{$else}
CRT,
{$endif}
Graph;
Var {GLOBAL}
Gd,Gm:Integer;
a,b:Longint;
g:byte;
Procedure INIT;
begin
{$ifdef Win32}
gd:=d8bit; gm:=m800x600;
{$else}
gd:=Vga; Gm:=VgaHi;
{$endif}
InitGraph(gd,gm,'');
SetColor(11);
SetBkColor(8);
End; {INIT}
Procedure Graphic;
Function Ro(Fi:Double):Double;
begin
case g of
1:ro:=fi;
2:ro:=Sin(7*fi);
3:ro:=1-cos(fi);
4:ro:=5*cos(fi)+2;
5:if cos(2*fi)>=0 then ro:=sqrt(2*cos(2*fi));
end;
end;
Const
c=2*pi; step=0.0005;
r=220;
{$ifdef Win32}
x0=400; y0=300;
{$else}
x0=320; y0=220;
{$endif}
var
k, max, fi: Double;
Begin
fi:=0; max:=0.0001;
repeat
if max < abs(ro(fi)) then max:=abs(ro(fi));
fi:=fi+0.01
until fi > c;
fi:=0;
repeat
k:=ro(fi)/max;
if k < 1 then putpixel(x0+Round(r*cos(a*fi)*k),
y0+Round(r*sin(b*fi)*k),15);
If Keypressed then Exit else fi:=fi+step
until fi > c
end; {graphic}
Begin
INIT;
Repeat
clearViewPort;
Outtextxy(20,14,'Case graphic:');
Outtextxy(20,22,'1. Spiral Arhimeda');
Outtextxy(20,30,'2. seeds petal rose');
Outtextxy(20,38,'3. Kardioida');
Outtextxy(20,46,'4. Snail Pascal');
Outtextxy(20,54,'5. Lemniskatta Bernuli');
g:=ord(readkey)-48;
a:=5; b:=5;
clearViewPort;
Graphic;
Outtextxy(20,450,'Esc-exit, Enter-repeat')
until readkey=#27;
CloseGraph
end.
Этот код можно скомпилировать вот в этих компиляторах: FPC(target: Win32, DOS), TP7, BP7, BPW (target:real, protected mode), TMT.
Неплохо, правда? И везде он будет работать одинаково правильно!
Конечно при компиляции в FPC под Win32, это будет Windows-приложение, а в остальных случаях консольное DOS (16 битное) приложение...
Прокоментирую первые строчки программы (а дальше я думаю все понятно):
{$ifdef Win32} { - если компилируем под Win32 }
{ (это сработает только в FPC, TMT) }
{$APPTYPE GUI} { - режим компиляции: графическое Windows приложение. }
{$else} { - компилируем в 16 битных компиляторах. }
{$E+} {$N+} { - включаем эмуляцию сопроцессора, т.к.
в программе используются тип double }
{$endif} { - конец условного участка кода. }
Uses
{$ifdef Win32} { - если компилируем под Win32 }
WinCrt, Windows, { подключаем соотв. модули. }
{$else} { - иначе компиляция в 16 битных компиляторах }
CRT,
{$endif} { - конец условного куска кода. }
Graph; { - а этот модуль подключается в любом случае. }
Эмуляция сопроцессора
Очень часто у многих возникает вопрос - почему при компиляции у меня возникает ошибка "Error 116:
Must be in 8087 mode to compile this" ?
Ответ: Вы используете один из следующих вещественных типов: Single, Double, Extended, или Comp.
Для работы с этими типами необходима эмуляция сопроцессора. Просто добавьте в начало программы директивы:
Оптимизация
Для уменьшения размера программ (и оптимизации по скорости) можно пользоваться следующими директивами:
Директивы без параметров
- - Выравнивание данных (Глобальная директива)
Эта директива позволяет переключаться между выравниванием переменных и типизированных констант по границе
слова (состояние {А+}) и по границе байта (состояние {A-}). При выравнивании на границу слова адресация
происходит за 1 цикл обращения к памяти вместо двух.
- - Быстрое вычисление логических условий (Локальная директива)
Но с такой оптимизацией будьте очень осторожны !!
Например, вот такой пример будет компилироваться, но выдаст неверный результат
(попробуйте запустить программу без ключей и
...):
var x, y: integer;
function do_it: boolean;
begin
x := 5; { Здесь меняется значение глобальной переменной }
do_it := true;
end;
begin
x := 2;
{$B-}
if (x > 5) and do_it then y := x
else y := 2 * x;
{$B+}
{ При "быстром" вычислении логических условий результат будет неверный }
writeln('y = ', y);
writeln('x = ', x)
end.
Добавлено: Volvo
- - отключить информацию для отладки. Внимание:
пропадает возможность отлаживать программу через F4 / F7 / F8 !!! При этом размер сократится на 100 - 150 байт...
- - Эмуляция математического сопроцессора (Глобальная директива)
Разрешает () или запрещает () компоновку с
библиотекой, которая будет эмулировать работу сопроцессора в случае его отсутствия.
- При компиляции в режиме Паскаль выполняет компоновку с полным эмулятором сопроцессора.
Полученный EXE файл может выполняться на любой машине независимо от присутствия сопроцессора.
- В состоянии компоновка осуществляется с гораздо меньшей по размеру библиотекой,
которая может использоваться только при наличии сопроцессора.
(Эта директива не производит никаких действий при использовании ее в модуле. Она действует только при компоновке программы)
- - Выбор модели вызова (Far/Near) (Локальная директива)
Управляет выбором типа вызова компилируемых процедур и функций.
В процессоре поддерживается два типа вызовов и инструкций возврата управления - ближние (NEAR) и дальние (FAR).
Ближние вызовы передают управление другой ячейке в пределах того же программного сегмента, а дальние вызовы позволяют
перейти в другой программный сегмент.
Инструкция ближнего обращения CALL помещает в стек 16-битовый адрес возврата (только смещение), а инструкция дальнего
вызова помещает в стек 32-битовый адрес возврата (адрес сегмента и смещение). Соответствующая инструкция RET извлекает
из стека только смещение или адрес сегмента и смещение.
При компиляции в режиме все процедуры и функции всегда используют дальнюю модель
вызова (Far Call). При указании директивы компилятор Паскаля автоматически выбирает
необходимый тип обращений: дальний, если процедура или функция описывается в интерфейсном разделе модуля (с расчетом
на вызов из других блоков), и ближний в случае описания в разделе Implementation.
Существует несколько случаев, когда требуется использовать директиву . Например, при
использовании оверлейных модулей и при передаче процедур/функций в качестве параметров они должны быть откомпилированы
с расчетом на дальнюю модель вызова.
- - Режим контроля ввода/вывода (Локальная директива)
Этот ключ задает () или отменяет () генерацию кода,
проверяющего результат выполнения операций ввода/вывода. Если операция ввода/вывода не может завершиться корректно и включен
режим , происходит ошибка времени выполнения (Run-time Error). При отключенном
контроле (режим ) аварийного останова программы не происходит, и результат операции может
быть проанализирован с помощью функции IOResult.
Внимание: после первого обращения к IOResult, флаг ошибки сбрасывается, и
проанализировать ошибку во второй раз не получится... Чтобы это сделать, нужно сохранить значение IOResult в какой-либо
переменной, и проверять именно значение этой переменной !!!
- - Использование сопроцессора (Глобальная директива).
При указании режима генерируется код для программного выполнения всех вещественных
вычислений. При режиме генерируется код для выполнения таких вычислений аппаратно,
с помощью сопроцессора.
- - отмена проверок на границы типов (overflow, underflow)
- - Режим проверки границ (Локальная директива)
Приводит в действие () или отменяет () генерирование кода
проверки границ. При указании все выражения со строками и массивы проверяются на нахождение
индекса в допустимых пределах (все операторы присваивания переменным скалярных типов проверяются на принадлежность границам типа).
При нарушении диапазона происходит ошибка времени исполнения.
Эта директива увеличивает размер программы и уменьшает скорость ее исполнения. Поэтому желательно использовать режим
при отладке, выключая его в окончательной версии.
- - Режим проверки переполнения стека (Локальная директива)
Приводит в действие () или отменяет () генерирование
кода проверки границ. При указании генерируется код, проверяющий, достаточно ли места в
стеке выделено для локальных переменных. При недостатке места в стеке происходит ошибка времени исполнения. В режиме
наиболее вероятно произойдет "зависание" системы.
- - Режим проверки параметров строкового типа (Локальная директива)
В состоянии выполняется строгая проверка типа, прикоторой требуется, чтобы формальный и
фактический параметр имели идентичные строковые типы. В состоянии в качестве фактического
параметра допускается использовать любую переменную строкового типа, даже если ее описанная длинна не совпадает с длиной
соответствующего формального параметра.
Директивы с параметрами
- - Компоновка файла объектных кодов
Данная директива предписывает компилятору скомпоновать указанный файл с компилируемой программой или модулем. Директива
используется для компоновки кода, написанного на ассемблере.
Кроме линковки с OBJ файлами данная директива очень часто используется для включения BGI драйверов и шрифтов в EXE файл
(для полностью автономной работы программы). Для того чтобы произвести эту операцию нужно сделать следующее:
Как включить BGI драйвер в EXE файл?