4.1. Definice maker.
4.2. Standardni preddefinovana makra.
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.
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.
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_id
3 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;
...
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
.
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__.
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.
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
...
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):
#include <header_name>
#include "header_name"
#include macro_identifier
ktere postupne znamenaji:
header_name
je hledan ve
standardnim adresari pro include
. Takto
se zpravidla zaclenuji standardni hlavickove
soubory. Neni-li soubor nalezen, je ohlasena chyba. header_name
je hledan v aktivnim
(pracovnim) adresari. Neni-li tam nalezen, postupuje
se podle prvni moznosti. Takto se zpravidla
zaclenuji nase (uzivatelske) hlavickove soubory. macro_identifier
je nahrazen. Dalsi
cinnost podle 1. nebo 2. varianty. 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
Nazev: | Programovani v jazyce C |
Autor: | Petr Saloun |
Do HTML prevedl: | Kristian Wiglasz |
Posledni uprava: | 29.11.1996 |