Predchozi kapitola

Obsah

Konec

Nasledujici kapitola

4. PREPROCESOR

4.1. Definice maker.

Symbolicke konstanty
Makra

4.2. Standardni preddefinovana makra.

Operatory # a ##.
Podmineny preklad.
Zbyvajici direktivy.


Nazev kapitoly napovida, ze se budeme venovat prostredku, ktery predchazi prekladac. Preprocesor zpracovava vstupni text jako text, provadi v nem textove zmeny a jeho vystupem je opet text. Od preprocesoru tedy nemuzeme cekat kontrolu syntaxe, natoz pak typovou kontrolu. Preprocesor zpracovava hlavickove soubory, rozviji makra, nepropousti komentare a umoznuje provadet podmineny preklad zdrojoveho textu.

Pri spusteni prekladu je nejprve proveden rozvoj maker, teprve vystup zpracovava prekladac. Pokud chceme ziskat vystup, ktery preprocesor produkuje, muzeme jej zavolat samostatne prikazem cpp (od C Pre Processor). Preprocesor neprovadi rozvoj maker tam, kde nemohou byt umisteny ani prikazy jazyka C (napriklad v komentarich a retezcich).

C preprocesor prijima tyto direktivy:

#define #elif #else #endif
#error #if #ifdef #ifndef
#include #line #pragma #undef

Jednotlive direktivy popiseme v ramci nasledujicich podkapitol.

Direktiva preprocesoru musi byt vzdy uvozena znakem #. # navic musi byt na radku prvnim jinym znakem nez jsou oddelovace. Od direktivy samotne jej opet mohou oddelovat oddelovace1.

Zdurazneme jeste jednu dulezitou skutecnost. Direktiva preprocesoru neni prikaz jazyka C. Neukoncujme ji proto strednikem.

4.1. Definice maker.zpet

Definice maker ve vyznamu rozsahu poli je snad typickym prikladem pouziti preprocesoru2. Ve zdrojovem textu se neodvolavame na magicka cisla, ale na vhodne symbolicky pojmenovana makra. Program to nejen zprehledni, ale pripadnou zmenu hodnoty makra provedeme na jednom miste.

Pomoci preprocesoru a maker muzeme vytvaret konstrukce, ktere zvysi citelnost programu. Muzeme napriklad oznacit zacatek a konec bloku prave identifikatory zacatek a konec, ktere pomoci preprocesoru spravne prevedeme na znaky { a }. Poznamejme ovsem, ze jsme popsali spise moznost. Makra maji zvysit citelnost programu, nemaji za ukol udelat z programu tezko srozumitelny rebus.

Pokud se text makra nevejde na jeden radek, muzeme jej rozdelit na vice nasledujicich radku. Skutecnost, ze makro pokracuje na nasledujicim radku, se urci umistenim znaku \ jako posledniho znaku na radku.

Pro vetsi prehlednost si makra rozdelme na symbolicke konstanty a makra. Klicem necht je skutecnost, ze makro na rozdil od symbolicke konstanty ma argumenty.

Symbolicke konstantyzpet

Jejich definovani a oddefinovani muzeme syntakticky popsat takto:

#define macro_id [token_sequence]
#undef macro_id

kde

macro_id predstavuje jmeno (identifikator) makra
token_sequence je nepovinny souvisly retezec

Pri sve cinnosti prohledava preprocesor vstupni text a pri vyskytu retezce macro_id3 provadi jeho nahrazeni retezcem token_sequence. Teto cinnosti se rika rozvoj (expanze) makra. Z tohoto popisu je jasne, proc se preprocesoru nekdy zjednodusene rika makroprocesor.

#define      START            2
#define      PRIRUSTEK        1
#define      DELKA_RADKU      100
...
int main()
{
 int      pocet = 0,
	alokovano = START,
	prirustek = PRIRUSTEK;
 pole_retezcu p_ret = NULL;
 ...

Makrazpet

Makra jiz podle naseho deleni maji argumenty. Definujeme je takto:

#define macro_id([arg_list]) [token_sequence]

kde (ostatni polozky jsou stejne, jako jsme jiz uvedli u symbolickych konstant):

arg_list predstavuje seznam argumentu navzajem oddelenych jen carkou.

Jako klasicky priklad makra si uvedme vraceni maximalni hodnoty ze dvou:

#define max(a,b) ((a>b)?a:b)

Vyhodou i nevyhodou je, ze nepracuje s typy. Vyhodou proto, ze pokud bychom chteli definovat podobnou funkci, museli bychom napsat tolik jejich verzi, kolik by bylo navzajem neslucitelnych variant datovych typu argumentu. Nevyhodou je netypovost makra tehdy, uvedeme-li trebas omylem jako argumenty retezce (pak by se porovnavaly adresy jejich prvnich znaku) nebo dva argumenty neporovnatelnych typu (struktura a cislo, ...). Takove chyby pak (nekdy) odhali az prekladac.

Pri definici makra max nas mozna prekvapi zdanlive nadbytecne zavorky oddelujici token_sequence. Musime jen pripomenout, ze makra nejsou prikazy jazyka C. Jejich rozvoj probiha na textove urovni. Preprocesor tedy nemuze v zavislosti na kontextu jednou nadbytecne zavorky vypustit, jindy chybejici pridat. Proto radeji sami nadbytecne zavorky nevypoustime.

Z textovosti rozvoje makra mohou plynout i necekane problemy. Porovnejme makro a funkci, pocitajici druhou mocninu argumentu:

#define SQR(x)   (x*x)
int sqr(int x)
{
 return x * x;
}

a predstavme si jejich pouziti:

int x, y, n = 3;
x = sqr(n+1);    /* sqr(4) -> 4*4 = 16           */
y = SQR(n+1);    /* (n+1*n+1) t.j. (4+1*4+1) = 9 */

coz nam v pripade makra dava zcela jiny (nespravny) vysledek, nez jsme ocekavali. Pokud opravime (x*x) na spravnejsi ((x)*(x)), dostaneme tentokrat sice vysledek spravny, ale opet najdeme priklad4, kdy spravny nebude.

Jde o skutecnost, ze pri volani funkce se argument vyhodnoti jen jednou. U makra tomu tak byt nemusi. Podivejme (s lepsi variantou makra):

int x, y, n = 3;
x = sqr(++n);    /* sqr(4) -> 4*4 = 16                */
y = SQR(++n);    /* ((++n)*(++n)) t.j. ((4)*(5)) = 20 */

Opet dostaneme chybny vysledek u makra SQR().

Prave z duvodu popsanych vedlejsich efektu a netypovosti maker se nedoporucuje pouzivat makra pro nahradu funkci. Doporucuje se pouziti funkci s pripadnym modifikatorem inline.

4.2. Standardni preddefinovana makra.zpet

Podle ANSI standardu musi preprocesor C identifikovat a v uvedenem vyznamu vyhodnocovat nasledujici makra (identifikatory maker jsou obklopeny dvema podtrzitky):

__DATE__ datum prekladu, mmm dd yyyy, pr. Nov 14 1993
__FILE__ jmeno zdrojoveho souboru
__LINE__ prave zpracovavany radek ve zdrojovem souboru
__STDC__ definuje typ (uroven) prekladu (STanDard C)
__TIME__ cas prekladu, hh:mm:ss, (hh 00-24), pr. 16:02:59

Ovsem i vyrobci prekladacu vybavuji sve produkty radou preddefinovanych maker. Zejmena takovych, ktera nam umoznuji pouzit specialni vlastnosti jejich produktu. Z duvodu prenositelnosti se jim radeji vyhneme.

Na druhe strane jsou preddefinovana makra popisujici operacni system, pripadne jeho verzi. Pokud piseme program pro vice OS (obvykle se hovori o platformach), zrejme se odvolame na tyto symbolicke preddefinovane konstanty v mistech, kde jsou volane funkce zavisle na OS. Tuto moznost popiseme dale v podkapitole venovane podminenemu prekladu.

Uvedme si alespon nektera nestandardni makrodefinice:

_DECVAX, IAPX286, MWC, COHERENT, _IEEE, _I386

z produktu Coherent, a nektere z BC z prostredi MS-DOS:

__CDECL__, __cplusplus, __MSDOS__, __OVERLAY__, __PASCAL__,

a jeste MS-DOSovska makra pro pouzity pametovy model

__TINY__, __SMALL_, __COMPACT__, __MEDIUM__, __LARGE__, __HUGE__.

Operatory # a ##.zpet

ANSI definuje tyto dva operatory a urcuje jejich vyhodnoceni takto: Operator # provadi prevod argumentu na retezec (jednoduse receno umisti argument mezi par uvozovek.

Napriklad definujeme-li

#define display(x) show((long)(x), #x)

pak preprocesor rozvine radek

display(abs(-5));

na radek

show((long)(abs(-5)), "abs(-5)");

Operator ## provadi spojovani tokenu tak, ze argumenty oddelene timto operatorem po rozvoji makra vytvori jeden celek (retezec).

Opet si ukazme cinnost popisovaneho operatoru.

Definujeme-li

#define printvar(x) printf("%d\n", variable ## x)

pak nasledujici radek

printvar(3);

prelozi preprocesor na

printf("%d\n", variable3);

Jak je z ukazky patrne, mohou byt mezi argumenty a operatorem ## mezery.

Podmineny preklad.zpet

Preprocesor muze behem sve cinnosti vyhodnocovat, je-li nejake makro definovano ci nikoliv. Pri pouziti klicoveho slova preprocesoru defined pak muze spojovat takova vyhodnoceni do rozsahlejsich logickych vyrazu. Argument defined nemusi byt uzavren do zavorek. Muze se vsak vyskytnout jen za #if nebo #elif. Napriklad si ukazme slozitejsi podminku:

#if defined LIMIT && defined OSTRA && LIMIT==10

V zavislosti na splneni ci nesplneni podminky muzeme urcit, bude-li ohraniceny usek programu dale zpracovan, nebo bude-li odfiltrovan a tak nebude tedy prelozen. Teto moznosti pouziti preprocesoru rikame podmineny preklad.

Vzdy musi byt jasno, kde podminena cast zdrojoveho textu zacina a kde konci. Proto nesmime zapominat na #endif ci #elif. Podminene casti musi byt ukonceny a omezeny v ramci jednoho zdrojoveho textu. Jinak oznami preprocesor chybu. Podminky velmi pripominaji konstrukce jazyka C. Navic je oproti C zavedena i podminka #elif. Nenechme se vsak mylit. Vyhodnoceni podminek provadi jiz preprocesor.

Ukazka neuplneho programu s jednoduchym podminenym prekladem nasleduje.

#define LADENI
#if defined(LADENI)
 #include <conio.h>
#endif
#if defined(LADENI)
 void volno(void)
 ...
 void uvolni(pole_retezcu *p_r, int pocet)
 ...
#endif
int main(void)
 ...
#if defined(LADENI)
 volno();
#endif
 if (alokace(&p_ret, alokovano))
 ...
#if defined(LADENI)
 uvolni(&p_ret, pocet);
 volno();
#endif
 ...

Zbyvajici direktivy.zpet

Dosud jsme nepopsali ctyri direktivy preprocesoru. Tedy postupne.

#include

je direktivou naprosto nepostradatelnou. Pouzivame ji pro vcleneni zdrojoveho textu jineho souboru. Tento soubor muze byt urcen vice zpusoby. Proto ma direktiva #include tri mozne formy (pro snadnejsi odkazy je ocislujme):

  1. #include <header_name>
  2. #include "header_name"
  3. #include macro_identifier

ktere postupne znamenaji:

Poznamenejme, ze pri praci nad velkym projektem se i vlastni hlavickove soubory umistuji do zvlastniho adresare. Pak se pochopitelne pripojuji podle 1. varianty. Protoze ovsem 2. varianta pri neuspechu prechazi do 1., muzeme i v tomto pripade popsanym zpusobem odlisit vlastni a standardni hlavickove soubory. Nesmime ovsem zapomenout definovat vice nez jeden standardni adresar.

#error

je direktivou, kterou muzeme zajistit vystup nami zadaneho chyboveho hlaseni. Nejcasteji se pouziva v souvislosti s podminenym prekladem. Ma format:

#error chybove hlaseni

kde chybove hlaseni bude soucasti protokolu o prekladu.

#line

umoznuje nastavit hodnotu standardniho makra __LINE__ a pripadne i __FILE__. Pouziva se zejmena u strojove generovanych zdrojovych textu. Ma format

#line cislo ["jmeno"]

kde cislo udava hodnotu ulozenou do __LINE__ a platnou pro nasledujici zdrojovy radek.

jmeno udava nepovinnou hodnotu ulozenou do makra __FILE__.

#pragma

je specialni direktivou, ktera ma uvozovat vsechny implementacne zavisle direktivy. Pokud jiny prekladac specialni direktivu nezna, proste ji bez chyboveho stavu ignoruje.


Vysvetlivky:

1 Poznamejme vsak, ze tato moznost je pouzivana jen zridka.
2 Moderni styl C v tomto pripade dava prednost konstantam.
3 Symbolicke konstanty obvykle piseme velkymi pismeny, abychom je v textu snadno odlisili od ostatnich identifikatoru jazyka.
4 Jde o priklad bezprostredne nasledujici


Predchozi kapitola

Obsah

Zacatek

Nasledujici kapitola


Nazev: Programovani v jazyce C
Autor: Petr Saloun
Do HTML prevedl: Kristian Wiglasz
Posledni uprava: 29.11.1996