2 Preprocesor jazyka C
Pod pojmom preprocesor jazyka C rozumieme štandardne zavedenú množinu príkazov, ktoré sú spracované pred vlastnou kompiláciou samotného zdrojového textu. Toto predspracovanie má na starosti tzv. preprocesor. Príkazy preprocesora umožňujú vkladanie a skladanie textu z rôznych súborov, podmienený preklad a definíciu symbolických konštánt a makier. Každý príkaz preprocesora musí byť napísaný na samostatnom riadku a začínať znakom #, za ktorým nasleduje (bez medzery) príkaz a jeho parametre, oddelené medzerami (alebo tabelátormi). Príkazy nie sú ukončené bodkočiarkou (nejde o príkazy jazyka C). Parametre príkazov preprocesora je zvykom písať malými písmenami.
2.1 Definícia symbolických konštánt a makier
Príkaz na definíciu symbolických konštánt má tvar
#define NAME konstantný_výraz
kde NAME je názov symbolickej konštanty
konstantný_výraz je hodnota, ktorú symbolická konštanta nadobúda
Typ konštantného výrazu určuje typ symbolickej konštanty. Použitie tohoto príkazu umožňuje sústrediť (spravidla na začiatku programu) niektoré dôležité konštanty, čo umožňuje v prípade potreby ich modifikáciou zmeniť celý rad príkazov v programe (je to analógia príkazu CONST v jazyku Pascal, s tým rozdielom, že sa musí písať pred každou definíciou). Príkaz sa najčastejšie používa na definíciu rôznych konštánt, napríklad :
#define MAX 100 #define LINE 80 #define PI 3.14 #define NAME1 Pavel #define NAME2 "Peter"
respektíve
#define 4PI 4*PI #define MAXLIN 10 + LINE/2
Prvé tri sú číselné symbolické konštanty. MAX nadobudne hodnotu 100, LINE hodnotu 80, PI 3,14. NAME1 je znaková konštanta s hodnotou Pavel (všade namiesto NAME1 sa dosadí Pavel), NAME2 je reťazec "Peter".
Príkaz define môže v rámci konštantného výrazu použiť skôr definovanú symbolickú konštantu (viď posledné dva príklady). Príkaz define sa často používa pre definíciu rozmerov polí, napríklad :
#define M 10 #define N 5 ... int a[M]; float b[N], c[M][N]; char d[M+1];
Definícia makra spočíva v pridaní jedného či viacerých parametrov k názvu symbolickej konštanty (symbolickú konštantu je možné pokladať za makro bez parametrov) a doplnení výrazu pre jeho výpočet :
príklad
/* priklad p2_1.c
pouzitie makier pre vypocet objemov telies */
#include <stdio.h>
#define PI 3.14159
#define KOCKA(a) (a*a*a)
#define VALEC(v,p) (PI*p*p*v)
#define GULA(r) (4*PI/3*r*r*r)
void main(void)
{
float hr=2,v=3;
printf("Objem kocky so stranou %f je %f\n",hr,KOCKA(hr));
printf("Objem valca (r=%f, v=%f) je %f\n",hr,v,VALEC(v,hr));
printf("Objem gule (r=%f) je %f\n",hr,GULA(hr));
}
Uvedené makrá slúžia pre výpočet objemu príslušných geometrických telies. Identifikátory v hlavičke makra a, v, p, r predstavujú formálne parametre, ktoré sú pri vyvolaní makra textovo nahradené skutočnými. Počet formálnych a skutočných parametrov musí byť rovnaký. Poradie vykonávania jednotlivých operácií v rámci makra možno riadiť zátvorkami.
Ďalší príklad ilustruje použitie štandardnej funkcie printf v tele makra. Všimnite si, že je v takom prípade zakončená bodkočiarkou - je to normálny príkaz jazyka C.
/* priklad p2_2.c
pouzitie makra s prikazom tlace */
#include <stdio.h>
#define PM(n) printf("Parameter makra PM = %d\n",(n))
void main(void)
{
PM(1);
}
Program vypíše :
Parameter makra PM = 1
Posledný príklad ilustruje možnosť spojovania na textovej úrovni - parametre i, j v makre SP pomocou dvojice znakov ##. Je samozrejme na programátorovi, že objekt vytvorený uvedeným mechanizmom spojenia musí byť v programe deklarovaný. V opačnom prípade kompilátor hlási chybu - nedeklarovanú premennú. Tento príklad súčasne ilustruje možnosť dosadenia hodnoty parametra makra v textovej forme pomocou znaku # (#n v makre PM) :
/* priklad p2_3.c
makro na spojenie plus dosadenie v textovej forme */
#include <stdio.h>
#define SP(i,j) (i##j)
#define PM(n) printf("Hodnota "#n" = %d\n",(n))
void main(void)
{
int i1=1, i2=2, i3=3;
PM(SP(i,2));
}
Program vypíše hodnotu premennej i2 v tvare:
Hodnota SP(i,2) = 2
Vykonaním makra SP vznikne premenná i2. Dosadenie parametra makra PM v textovej forme je znázornené textom "SP(i,2)". Symbolickú konštantu možno definovať aj bez hodnoty príkazom #define a zrušiť jej definíciu príkazom #undef (na príslušnom mieste programu)
#define DEBUG ... #undef DEBUG
Takéto definovanie, resp. zrušenie definície je možné využívať v ďalších príkazoch preprocesora - pri podmienenom preklade, vetvení, zaradzovaní rôznych (ladiacich) výpisov a pod. (viď kap. 2.3).
2.2 Vkladanie textu
Príkaz na vkladanie textu #include [meno súboru] spôsobí vloženie obsahu súboru "meno súboru" pred prekladom do zdrojového súboru, v ktorom sa vyššie uvedený príkaz nachádza. To umožňuje vkladanie tak systémových súborov (tzv. hlavičkových súborov, s príponou .h, napr. <stdio.h>), ako aj vlastných užívateľských súborov, čo predstavuje jednu z možností modulárnej výstavby programu.Príkaz na vloženie môže mať dva tvary (v prvom prípade je názov súboru uvedený v lomených zátvorkách, v druhom prípade v dvojitých uvodzovkách):
#include <file.h> #include "path_file"
Prvý tvar sa používa pre vkladanie systémových súborov, ktoré sú uložené v príslušnom systémovom podadresári (INCLUDE). Všetky štandardné funkcie, konštanty a makrá v jazyku C sú deklarované v takýchto súboroch. Názov príslušného súboru, resp. súborov je uvedený vždy pri popise príslušnej funkcie. Vloženie príslušného súboru umožňuje prekladaču kontrolu správnosti volania štandardnej funkcie.
Druhý tvar sa používa pre vkladanie užívateľských súborov, pričom sa prehľadáva aktuálny adresár, resp. adresár, uvedený v špecifikácii vkladaného súboru.
2.3 Podmienený preklad
Príkazy pre podmienený preklad majú tvar:
#if podmienka #ifdef symb_konštanta #ifndef symb_konštanta #else #endif
a umožňujú v závislosti na splnení daných podmienok zaradzovať, resp. vynechávať určité časti programu z prekladu. Tieto podmienky sa môžu vzťahovať napr. na ladenie programu, rôzne výpisy, prítomnosť hardware (napr. koprocesor), príp. programového vybavenia. Každý z príkazov #if, #ifdef, resp. #ifndef musí byť ukončený príkazom #endif. Nasledujúci príklad je ukážkou možnosti jednoduchého zaradzovania ladiacich výpisov v programe:
/* priklad p2_4.c ladiace výpisy */
#include <stdio.h>
#define DEB
void main(void)
{
int i1=1, i2=2, i3=3;
#ifdef DEB
printf("Hodnota i1 (krok 1) = %d\n",i1);
#endif
i1=i2*i3;
#ifdef DEB
printf("Hodnota i1 (krok 2) = %d\n",i1);
i1=i1*i3;
printf("Hodnota i1 (krok 3) = %d\n",i1);
#endif
}
Druhá dvojica príkazov #ifdef - #endif obsahuje okrem výpisov aj priradzovací príkaz, čo nemusí byť algoritmicky správne - je potrebné to zvážiť podľa situácie.
Program vypíše (lebo je definavaná premenná DEB - príkaz #define):
Hodnota i1 (krok 1) = 1 Hodnota i1 (krok 2) = 6 Hodnota i1 (krok 3) = 18