Professional Documents
Culture Documents
B Pretprocesor
B Pretprocesor
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"
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
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
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))
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)
....
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.
Leksiki pretprocesor
Primjerice, za definiciju
#define PRINT(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)
int main()
{
PRINT_FUN(sin, 3);
PRINT_FUN(cos, 3);
return 0;
}
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
vri se ekspanzija
NIZ(x, int, 100)
NIZ(vektor, double, N)
PRINT(y);
PRINT(x_array[y]);
return 0;
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"
Leksiki pretprocesor
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.