You are on page 1of 5

Leksiki pretprocesor

Dodatak B Leksiki pretprocesor


Pojam "pretprocesor'' se koristi za oznaku prve faze obrade izvornog koda. Kod nekih C kompajlera
pretprocesor je izveden kao zasebni program. Pretprocesor ne analizira sintaksu zapisanog koda, ve
se njime se vri obrada izvornog koda na tri naina:
1. Umee se sadraj druge datotake u izvorni kod, pomou #include direktive
2. Vri se supstitucija teksta prema #define direktivi
3. Vri se selekcija koda koji e se kompajlirati pomou #if, #elif, #else i #endif direktiva

B.1 #include direktiva


Koriste se dvije varijante #include direktive:
#include <ime_datoteke>
#include "ime_datoteke"

pomou kojih se vri umetanje neke datoteke u izvorni kod, i to na mjestu gdje ja zapisana direktiva.
Ako je ime datotke zapisano unutar navodnih znakova, traenje datoteke poinje u direktoriju gdje se
nalazi izvorni kod, a ako nije tamo pronaena, ili ako je ime zapisano unutar < >, traenje datoteke se
vri u direktoriju u kojem su smjetene datoteke s deklaracijama standardnih funkcija.
Ime datoteke moe sadravati potpuni ili djelomini opis staze do datoteke. Zapis ovisi o pravilima
operativnog sustava. Primjerice, na Unix-u moe biti oblik
#include "/usr/src/include/classZ.h"
#include "../include/classZ.h"

ili na Windowsima
#include "C:\src\include\classZ.h"
#include "..\include\classZ.h"

B.2 #define direktiva za makro-supstitucije


Leksika supstitucija teksta (makro-supstitucija) se definira pomou #define direktive u dva oblika.
Prvi oblik
#define identifikator supstitucijski-tekst

smo ve upoznali i pomou njega smo imenovali konstante ili dijelove izvornog koda, primjerice
#define EPSILON 1.0e-6
#define PRINT_LINE printf("----------\n");
#define LONG_SUBSTITION if(a>b){ cout << "a>b"; } \
else { cout << "a<=b"; }

Znaenje ove direktive je da se u izvornom kodu, na mjestima gdje se je makro identifikator, umetne
supstitucijski-tekst koji predstavlja niz tokena. Supstitucijski tekst se zapisuje u jednoj ili vie linija.
Znak nove linije oznaava kraj zapisa, ali ako je posljednji znak obrnuta kosa crta tada supstitucijskom
tekstu pripada i zapis u slijedeoj liniji.
Drugi oblik #define direktive omoguuje da se uz makro identifikator navodi vie argumenata:
#define identifikator(argument, ... , argument) supstitucijski-tekst-s-argumentima

Dodatak B Leksiki pretprocesor

Leksiki pretprocesor
tako da supstitucijski tekst moe biti razliit za razliita pozivanja makroa. Primjerice, makroi
#define KVADRAT(x)
x*x
#define POSTOTAK(x,y) x*100.0/y

se u izvornom kodu, ovisno o argumentima, supstituiraju na slijedei nain:


poziv makroa

rezultira kodom

y = KVADRAT(z);

y = z*z;

y = POSTOTAK(a,b);

y = a*100.0/b;

y = KVADRAT(a+b);

y = a+b*a+b;

y = POSTOTAK(a+b,c+d); y = a+b*100.0/c+d;

Dva zadnja primjera pokazuju najeu greku kod primjene makroa. Dobijeni kod ne odgovara
namjeri da se dobije kvadrat vrijednosti (a/b) jer se zbog prioriteta operatora izraz (a+b*a+b) tretira
kao (a+(b*a)+b), a ne kako je bila intencija programera, tj. kao ((a+b)*(a+b)).
Zbog toga se preporuuje da se argumenti makroa u supstitucijskom tekstu uvijek piu u zagradama,
primjerice:
#define KVADRAT(x)
((x)*(x))
#define POSTOTAK(x,y) ((x)*100.0/(y))

Tada se ekspanzija makroa uvijek vri s istim znaajem, primjerice


poziv makroa

rezultira kodom

y = KVADRAT(a+b)

y = ((a+b)*(a+b))

y = POSTOTAK(a+b,c+d) y = ((a+b)*100.0/(c+d))

Zapamti: Makro argumente u supstitucijskom tekstu treba uvijek pisati unutar zagrada.
Time se osigurava da e se ekspanzija makroa izvriti s istim efektom za bilo koji oblik
argumenata. Takoer, poeljno je i da se cijeli supstitucijski tekst zapie u zagradama.
Zapamti: Poziv makroa izgleda kao poziv funkcije, ali nema znaaj poziva funkcije (ne
vri se prijenos parametara funkcije), ve se radi o supstituciji teksta prije procesa
kompajliranja.
Simboli unutar supstitucijskog teksta mogu biti prethodno definirani #define identifikatori, primjerice
#define JEDAN 1
#define DVA
(JEDAN +1)
#define TRI
(DVA +1)
....

Ekspanzija koju pretprocesor radi s izrazom JEDAN+DVA+TRI je 1+(1+1)+((1+1)+1), to daje


vrijednost 6. Ovaj raun se provodi tijekom kompajliranja.

String operatori # i ##
Ekspanzija makro argumenata se ne vri ako je argument u supstitucijskom tekstu zapisan u navodnim
znakovima, odnosno u stringu.
Ponekad trebamo da se neki argument u ekspanziji makroa pojavljuje literalno kao string. To se
postie tako da se u supstitucujskom tekstu nepesredno ispred imena argumenta zapie znak # (string
operator). U ekspanziji makroa tada se taj argument pojavljuje zapisan unutar navodnih znakova.

Dodatak B Leksiki pretprocesor

Leksiki pretprocesor
Primjerice, za definiciju
#define PRINT(x)

cout << #x "=" << x

vri se ekspanzija PRINT(max) u cout << "max" " = " << x , a poto se dva uzastopna
navodnika ponitavaju, efekt je da se kompajlira naredba cout << "max = " << max.
Zadatak: Provjerite ispis slijedeeg programa:
/* program fun.cpp */
#include <iostream>
#include <cmath>
using namespace std;
#define PRINT_FUN(fun, x)

cout << #fun "=" << fun((x)) << endl

int main()
{
PRINT_FUN(sin, 3);
PRINT_FUN(cos, 3);
return 0;
}

Trebali bi dobiti ispis:


sin=0.14112
cos=-0.989992

Operator ## djeluje na dva susjedna simbola na nain da preprocesor izbaci taj operator i sva prazna
mjesta, pa se dobije jedan novi simbol s identifikatorom sastavljenim od ta dva susjedna simbola.
Primjerice, za definiciju
#define NIZ(ime, tip, n)

tip

ime ## _array_ ## tip[N]

vri se ekspanzija
NIZ(x, int, 100)

int x_array_int [100]

NIZ(vektor, double, N)

double vektor_array_double [N]

Zadatak: Provjerite ispis slijedeeg programa:


/* program strop.cpp */
#include <iostream>
using namespace std;
#define N 10
#define PRINT(x)
cout << #x "= " << x << endl
#define NIZ(ime, tip, n)
tip ime ## _array [n]
int main()
{
int i, y=7;
NIZ(x, int, N);
for(i=0; i<N;i++) x_array[i] = 10;

PRINT(y);
PRINT(x_array[y]);
return 0;

Dodatak B Leksiki pretprocesor

Leksiki pretprocesor
Trebali bi dobiti ispis:
y=7
x_array[y]=10

#undef direktiva
Ponekad je poeljno da se redefinira znaaj nekog identifikatora. U tu svrhu moemo se posluiti
#undef direktivom:
#undef identifikator
kojom se ponitava prethodni znaaj identifikatora.
#define SIMBOL_X VALUE_FOR_X
....
....
/* podruje gdje koristimo SIMBOL_X */
....
#undef SIMBOL_X
....
....
/* ovdje ne moemo koristiti SIMBOL_X */
....
#define SIMBOL_X NEW_VALUE_FOR_X
....
....
/* ovdje ponovo moemo koristiti SIMBOL_X */
....
/* s nekim drugim znaenjem */

Ako se pokua redefinirati neki simbol bez prethodne #undef direktive, C kompajler e dojaviti
greku poput
"Attempt to re-define macro SIMBOL_X"

B.3 Direktive za uvjetno kompajliranje


Mogue je pomou posebnih direktiva kontrolirati sam proces predprocesiranja. U tu svrhu se koriste
direktive #if, #ifdef, #ifndef, #elif, #else i #endif.
Iza #if i #elif (else-if) direktive mogu se koristiti prosti izrazi koji rezultiraju cjelobrojnom
vrijednou (ne smiju se koristiti sizeof i cast operatori, i enum konstante). Ako je vrijednost tih
izraza razliita od nule, tada se u proces kompajliranja ukljuuje tekst koji slijedi iza ovih direktiva sve
do slijedee direktive (#elif, #else ili #endif.) U slijedeem primjeru pokazano je kako se
pomou ovih direktiva odreuje koja e datoteka zaglavlja biti ukljuena u proces kompajliranja,
ovisno o tipu operativnog sustava:
#if SISTEM == LINUX
#define HDR "linx.h"
#elif SISTEM == MSDOS
#define HDR "msdos.h"
#elif SISTEM == WIN32
#define HDR "win32.h"
#else
#define HDR "default.h"
#endif
#include HDR

Dodatak B Leksiki pretprocesor

Leksiki pretprocesor

Pretpostavljeno je da je prethodno definirana vrijednost simbola: LINUX, WIN32, MSDOS i


SISTEM.
Direktiva #elif ima znaenje else if.
Iza #if i #elif moe se koristiti izraz
defined(identifikator)

koji daje 1 ako je identifikator prethodno registriran kao neki simbol (ukljuujui i makro simbole),
inae je 0.
Primjerice, da bi osigurali da se sadraj neke datoteke moe ukljuiti u izvorni kod samo jedan put,
esto se koriste direktive po slijedeem obrascu :
/* datoteka header.h */
#if !defined(HEADER_H)
#define HEADER_H
/* ovdje se zapisuje sadraj od header.h */
#endif

Pri prvom ukljuenju ove datoteke definira se simbol HEADER_H i sadraj datoteke se uljuuje u
izvorni kod. Ako se ova datoteka ukljui po drugi put, nee se koristiti njen sadraj jer tada postoji
definiran simbol HEADER_H.
Direktive #ifdef i #ifndef su specijalizirane za ispitivanje da li je neki identifikator definiran (ili
nije definiran). Prethodni se primjer moe zapisati i u obliku:
#ifndef HEADER_H
#define HEADER_H
/* ovdje se zapisuje sadraj od header.h */
#endif

Zapamtimo:

#ifdef identifikator
je ekvivalentno #if defined(identifikator).
#ifndef identifikator
je ekvivalentno #if !defined(identifikator).
Iza svake #if, #ifdef ili #ifndef direktiva mora biti #endif direktiva.

Dodatak B Leksiki pretprocesor

You might also like