Direktivy překladače

Direktivy překladače umožňují přímo ve zdrojovém kódu sdělit, jakým způsobem ho má kompilátor překládat. Pokud např. chcete zajistit, že Váš program bude vždy přeložen s povolením rozšířené syntaxe, stačí na první řádku zdrojového kódu (tj. ještě před vyhrazené slovo program) připsat {$X+}.

Direktivy překladače se dělí do tří skup. První skupinu tvoří globální volby, jejichž nastavení má platnost pro celý překládaný soubor, ve druhé skupině jsou lokální direktivy, které můžeme v průběhu souboru několikrát změnit. Třetí a nejdůležitější skupinu pak tvoří direktivy sloužící k řízení podmíněného překladu.

Globální direktivy

Globální direktivy odpovídají jednotlivým položkám nastavení kompilátoru. Tyto direktivy určují, jakým způsobem se bude kompilátor chovat k celému souboru. Programátor tak může překladači např. říct: překládej můj program se zapnutou podporou rozšířené syntaxe.

My si probereme tyto direktivy v pořadí podle důležitosti.

Užitečné globální direktivy.

Rozšířená syntaxe ({$X})

Přepínač {$X+} zapne rozšířenou syntaxi. Přepínač {$X-} ji zase vypne.

{$X+}
program Rozsirena_syntaxe;
{Tento program se prelozi a spusti bez ohledu na to, zda mate v nastaveni rozsirenou syntaxi povolenou ci ne (rozsirena syntaxe umoznuje volat funkce stejne jako procedury, nepotrebujeme-li znat jejich navratovou hodnotu).}
function Blesk:integer;
begin
 writeln('Rana!... Hrom!');
 Blesk=1;
end;

begin
 Blesk;
end.

Zkuste v Options/Compiler vypnout Extended syntax a program přeložit. Pokud nyní smažete {$X+}, program přeložit nepůjde. Zapněte v Menu Extended syntax a ejhle! program přeložit jde. Nyní na začátek připište {$X-} a překladač ho znova odmítne přeložit. Doufám, že z tohoto malého příkladu a trocha experimentování je vám již význam přepínače {$X} jasný.

Přesnější matematika ({$N}, {$E}

Přepínač $N zapne generování instrukcí pro numerický koprocesor 8087. To vám umožní používat datové typy Extended, Comp, Longint. Tento koprocesor ale dávno zastaral, takže ho již počítače nemají, záleží tedy na vašem počítači, zda systém emuluje numerický procesor, či ne. Proto k volbě $N existuje volba $E, jež řekne kompilátoru, že má numerický koprocesor emulovat sám. Raději na praktickém příkladě. Zkuste změnit některou z voleb na opačnou a pozorujte, co se stane.

{$E+,N+}
program Numerika;
begin
 writeln(Pi);
end.

Otevřené řetězcové parametry ({$P})

Tento přepínač se stará o to, zda když funkci předáte jakýkoli parametr řetězcového typu, bude s ním nakládat, jako by se jednalo o OpenString. Tj. do dané funkce bude možné vkládat i jakékoli jiné řetězcové parametry. Pokud je přepínač vypnut, kompilátor požaduje přesnou shodu řetězcových typů. Raději na příkladě. I nyní zkuste přepínač $P vypnout změnou na {$P-}.

{$P+,X+}
program Retezec;
type tzprava:string[8];
var s:string[12]; 

function Pis(s:tzprava);
begin
 writeln(s);
 Pis:=length(s);
end;

begin
 c='Koncime!';
 Pis(c);
end. 

Informace pro ladění ({$D} ,{$L}, {$Y})

Tyto volby určují, zda a jak podrobné informace pro ladění programu se budou generovat. Přepínač {$D} zapne generování debuggovacích informací. Do spustitelného (.EXE) programu se tedy zapíše, na jakém místě zdrojového kódu se právě program nachází. Přepínač {$L} do výsledného souboru zapíše i jména jednotlivých lokálních proměnných - budete tak moci sledovat, jakých hodnota nabývají lokální proměnné v procedurách a funkcích. Přepínač {$Y} pak zapne generování informací i o objektech a unitách, tento přepínač však funguje jenom tehdy, když jsou zapnuté oba dva předchozí.

Všechny tyto volby značně zvyšují výslednou velikost programu, takže by měly být zapnuty skutečně pouze tehdy, když chceme program ladit. Při finálním prodeji našeho produktu se vyplatí tyto volby vypnout.

Zarovnání dat na sudé bloky ({$A})

Tento přepínač určuje, zda budou data zarovnávána v paměti na sudé bloky (v paměti tak zůstane spousta volného nevyužitelného místa, ale přístup k proměnným bude dvakrát rychlejší), či zda se bude kompilátor snažit o maximální úsporu paměti a nenechá ani kousek volný (ale k proměnným začínajícím na lichém paměťovém bloku přistupuje procesor dvakrát pomaleji.)

Obecně platí, že dneska je tato direktiva na nic. Ale i tak se v moderních programovacích jazycích vyplatí proměnné a objekty definovat tak, aby začínaly na místech, které jsou programu snadno přístupné. Vhodným zvětšením či zmenšením velikostí některých struktur a objektů tak můžeme docílit citelného zrychlení programu.

Nedůležité globální přepínače

Následující direktivy nejsou příliš podstatné, proto si ani nebudeme uvádět praktické příklady.

Chování unit v paměti ({$C})

Tento přepínač řídí, jak se bude daná unita chovat v paměti. MOVABLE říká, že umístění unity (programu) může být měněno operačním systémem, FIXED naproti tomu takovéto chování zakazuje. PRELOAD načte unitu do paměti okamžitě při zahájení programu, DEMANDLOAD ji načítá unitu až v okamžiku, kdy je zapotřebí, PERMANENT - jakmile je unita jednou načtena, zůstává celou dobu v paměti, DISCARDABLE - unita může být uvolněna z paměti, když není zapotřebí, kvůli úspoře místa. Defaultní nastavení je {$C MOVABLE DEMANDLOAD DISCARDABLE}.

Chytré volání funkcí ({$K})

Tento přepínač řídí, zda má být pro funkce generován speciální kód, který umožní, aby je efektivně využívaly i Windows. (To se hodí např. u DLL knihoven.)

Velikost zásobníku a hromady ({$M})

Tato direktiva říká, jak velký zásobník (pro volání funkcí) a jak velkou hromadu (pro dynamické proměnné) má program alokovat. Její použití se liší podle toho, pro který cíl (WINDOWS, REAL, PROTECTED) program překládáte.

Deskriptor programu ({$D})

Tato direktiva určuje jméno knihovny pro potřeby systému (Library description name). V DLL či programu se smí objevit nejvýše jednou. {$D hovadina}.

Maximální velikost segmentu ({$S})

Tato direktiva říká se používá jako {$S Size} a udává, kolik nejvíce bytů paměti má zabírat jeden segment kódu. (Kolik se toho linker pokusí nacpat do jednoho segmentu, než začne nový.)

Generování overlay kódu ({$O})

Zapne generování kódu, který lze použít v Overlay unitách. Pokud chcete použít svoji unitu jako overlay, určitě byste ji měli zkompilovat s {$O+} (jinak jako overlay ani nepůjde použít) a protože většina funkcí v overlay musí být volána vzdáleně, nezapomeňte ani na {$F+}.

Lokální direktivy

Lokální direktivy se můžou objevit na jakémkoli místě našeho programu. Efekt mají od tohoto místa dál. Lze je tedy i několikrát v průběhu zdrojového kódu přepnout.

Užitečné lokální direktivy

Úplné vyhodnocování Booleovských výrazů ({$B})

Tento přepínač zapíná či vypíná úplné vyhodnocování boolovských výrazů. Je-li nastaveno {$B-}, program přestane vyhodnocovat boolovský výraz, je-li jasné, jak dopadne výsledek. Oproti tomu je-li nastaveno {$B+}, provedou se všechny výpočty až do konce. To je obzvláště užitečné, má-li některá z vyhodnocovaných funkcí ještě nějaké další efekty, o které bychom nechtěli přijít. Vyplatí se tedy u jakéhokoli vyhodnocování Booleovských výrazů, kde nechceme přijít o postranní efekty našich funkcí, připsat {$B+}. Raději na ukázce.

program Neco;
function Lez:Boolean;
begin
   Lez:=False;
end;

function Pravda:Boolean;
begin
   Pravda:=True;
end;

function Stek:Boolean;
begin
   writeln('Haf haf');
   Stek:=true;
end;

begin
{$B+} 
  if (Lez and Stek) then writeln('To jsem jelen.') else writeln('Po vyhodnoceni prvniho vyrazu');
{$B-}
  if (Pravda or Stek)then writeln('Po vyhodnoceni druheho vyrazu');
end;

Kontrola přetečení ({$Q})

Tento přepínač zapne kontrolu přetečení. Tato kontrola značně prodlužuje výsledný kód i rychlost programu, takže je lepší mít ji zapnutou pouze pro debuggovací účely. Na druhou stranu při ladění je neocenitelná. Jako vždy zkuste v následují ukázce kontrolu přetečení vypnout.

program Pretec;
var I:integer;
begin
 I:=32000;
 writeln('I', I);
 {$Q+}
 I:=I*3; 
 writeln('I*3 = ',I);
end.

Kontrola přetečení má však jistá omezení -- např. se nikdy nevztahuje na operace Inc a Dec.

Kontrola rozsahu ({$R})

Tento přepínač zapíná generování kódu na kontrolu rozsahu. Kontroluje se tak např. zda se index pole vejde do definovaného rozměru pole, či zda operátor přiřazení není mimo udaný rozsah. I tento přepínač se používá pouze při ladění programu, neboť výsledný program zpomaluje a dosti prodlužuje.

{$R+}
program Range;
var A:array[5..8] of integer;
    I:1..5;
begin
 I:=6;
 A[4]:=7;
 readln;
end.

Kontrola zásobníku ({$S})

Tento přepínač zapíná kontrolu, zda je při každém volání funkce dostatek místa v zásobníku. Pokud ne, vypíše chybové hlášení. Pokud je tento přepínač vypnutý a program se pokusí umístit funkci do paměti, kde pro ni nezbývá místo, může se stát téměř cokoli. Nejčastěji se zcela nepředvídatelným způsobem změní obsah dynamických (i jiných) proměnných či program spadne. I zde si vyzkoušejte přepínač změnit.

program Fibonnaci;
{$S-}
function F(n:integer;)
begin
 if n<=0 then F:=0;
 if n=1 then F:=1;
 if n>1 then F(n):=F(n-1)+F(n-2);
end;

begin
 writeln(F(100));
 readln;
end.

Typový ukazatel @ ({$T})

Tento přepínač určuje, zda výsledkem @typ bude obecný ukazatel typu pointer ({$T-}), nebo typový ukazatel typu ^typ ({$T+}). Je asi vždy lepší pracovat s {$T+}.

Vzdálená volání ({$F})

Tento přepínač vynucuje vzdálené volání funkcí. To je např. zapotřebí pokud má naše funkce být procedurálního typu, či pokud k ní chceme přistupovat i z jiné unity. Opět na příkladu:

program Vzdalene;
type tfunc=function(A:integer); 

{$F+}
function square(A:integer):integer;
begin
 square:=A*A;
end;

{$F-}
function cube(A:integer):integer;
begin
 cube:=A*A;
end;

var func:tfunc;

begin
 func:=@square;
 func:=@cube;
end.

Přísná kontrola řetězců ({$V})

Tato direktiva určuje, zda se mají typy řetězcových proměnných při volání funkcí kontrolovat, nebo zda se uvažuje řetězec jak řetězec. Je lepší používat globální přepínač {$P+}.

Vložení přeloženého souboru ({$L})

Tato direktiva se používá jako {$L nazev_souboru}. Říká linkeru, že k našemu programu ještě patří nějaký další (již do .obj přeložený) soubor, ve kterém jsou potřebné funkce, které zde používáme jako externí. To nám umožňuje k Pascalskému programu připojit i výstupy např. z assembleru, C++, apod.

Nedůležité lokální direktivy

Windowsí zdroje ({$R})

Tato direktiva říká, kde je umístěn Windowsí resource soubor. (Tj. popis toho, jak má vypadat okno naší aplikace. Na *.RES soubory jsou kladeny jisté požadavky, které zde momentálně nebudu zmiňovat.)

Real mód pro Windows ({$W})

Tato direktiva říká kompilátoru, že má far funkce generovat se speciálním kódem, který umožní jejich přesun v paměti i ve windowsím real módu. Kdo dneska spouští Windows v Real mode? Direktiva na nic.

Umístění unit do overlaye ({$O})

Použití této direktivy je nasnadě {$O nazevunity1, nazevunity2, ...}. Tato direktiva patří hned za uses. A říká kompilátoru, které unity má místo do .EXE umístit do overlayového souboru .OVR. Dneska úplně na nic.

Přepište všechny své programy tak, aby šly přeložit nezávisle na nastavení kompilátoru z menu Options/Compiler.