Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ FAQ _ Директивы компилятора

Автор: Altair 28.01.2005 11:34

Директивы компилятора
Почему-то мало кто пользуется директивами компилятора в полном масштабе...
Еще {$M} {$N} {$E} {$I} приходится видеть, но вот такие:
{$ifdef}...{$else}...{$endif} - почти никогда; несмотря на то, что они открывают довольно широкие возможности для программиста, а именно: позволяют писать код, успешно компилируемый на разных компиляторах Паскаля, в том числе и 32 битных, включая FPC и TMT.

Еще одно удобство на мой взгяд - широкие возможности при отладке приложений.

Общий синтаксис этих команд таков:
{$define <условный_символ>} - установить условный_символ.
{$ifdef <условный_символ>} - код следующий после этой директивы компилируется только в том случае, если условный_символ был установлен.
{$else} - эта директива обозначает начало альтернативного участка кода.
{$endif} - ограничивает действие директив {$ifdef} и {$else}.

Режимы компиляции. Отладка.
Предположим, что в программе необходимо на этапе отладки выводить какие-то данные для тестирования алгоритма... Можно, конечно, в окончательном варианте кода закомментировать такие отладочные выводы, или удалить, но лучше заключить "отладочные операторы" в такую конструкцию, с использованием директив:

Код
{$define debug}
var
  x:byte;
begin
  x:=1;
  inc(x);
  {$ifdef debug}
    writeln(x);
  {$endif}
end.

Таким образом, при работе программы на экран будет выведено значение "2". Если отладку надо отменить, мы можем убрать символ "$" из директивы {$define debug} - и теперь это будет просто коментарий, и следовательно на экран ничего не будет выведено.

Универсальность кода
Хорошо, когда мы берем старый код из TP и он компилируется, и программа правильно работает в компиляторе постарше...
А почему бы не реализовать совместимость наоборот или напрямую? Часто при компиляции, возникают ошибки только из-за того, что код оптимизирован под конкретный компилятор, например под BP, и в TP отказывается компилироваться...
Выходом из такой ситуации является правильное написание кода, а именно отладка кода под различные компиляторы (конечно если это требуется). Для примера рассмотрим программу для построения графиков некоторых функций (в полярной системе координат) http://pascal.dax.ru/forum/oleg_z/prog/elgr.zip

{$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
Для работы с этими типами, необходима эмуляция сопроцессора. Просто добавьте в начало программы директивы:
{$E+, $N+}

Оптимизация
Для уменьшения размера программ желательно добавить следующие директивы:
{$B-} - быстрое вычисление логических условий;

Но с оптимизацией будьте очень осторожны !! Например, вот такой пример будет компилироваться, но выдаст неверный результат (попробуйте запустить программу без ключей {$B-} и {$B+}...):

Код
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

{$D-} - отключить информацию для отладки ­(пропадает возможность отлаживать программу!!! (через F7)­). При этом размер сократится на 100-150 байт...

Описание других директив

{$N+} - сопроцессор
{$E+} - эмуляция сопроцессора
{$I-} - отключение проверок ввода/вывода
{$R-} - отключение проверок на границы массивов
{$S-} - отмена проверки на переполнение стека
{$Q-} - отмена проверок на границы типов ­(overflow, underflow)

Автор: volvo 29.01.2005 6:10

Директивы компилятора (продолжение)

Директивы без параметров:

Директивы с параметрами:Кроме линковки с OBJ файлами данная директива очень часто используется для включения BGI драйверов и шрифтов в EXE файл (для полностью автономной работы программы). Для того чтобы произвести эту операцию нужно сделать следующее:

1. С помощью утилиты BINOBJ.EXE (входящей в дистрибутив Турбо Паскаля) преобразовать BGI файл в OBJ-формат (работа с утилитой BINOBJ осуществляется из командной строки):

Цитата
BINOBJ EGAVGA.BGI EGAVGA.OBJ EGAVGADriverProc

2. Подключить полученный OBJ файл к программе:

Код
{$L EGAVGA.OBJ}
Procedure EGAVGADriverProc; External;

Сама инициализация графики (например, режима VGA) будет осуществляться так:

Код
...
Var grDriver, grMode, ErrCode : Integer;
...
Begin
  If RegisterBGIDriver(@EGAVGADriverProc) < 0 Then
    Begin
      WriteLn('Error registering driver : ',
        GraphErrorMsg(GraphResult)); Halt(100)
    End;

  grDriver := VGA; grMode := VGAHi;
  InitGraph(grDriver, grMode, '');
  ErrCode := GraphResult;
  If ErrCode <> grOk Then
    Begin
      WriteLn('Graphics error : ', GraphErrorMsg(ErrCode));
      Halt(100)
    End;
...
End.

После этого можно не заботиться о наличии в текущей директории соответствующего BGI драйвера.