Preprocesare
Preprocesarea
este prelucrarea textului sursa al programului inaintea etapei de compilare. In
limbajul C++ preprocesarea se realizeaza cu ajutorul directivelor de
preprocesare. Acestea sunt recunoscute de compilator prin prezenta caracterului
“#”.
Includerea
fisierelor sursa se realizeaza prin intermediul directivei #include care are doua forme:
#include <fisier_sursa>
#include "fisier_sursa"
Ambele forme au
ca efect includerea totala a fisierului sursa in fisierul care contine
directiva. Diferentele intre cele doua forme se refera la locatia unde este
cautat fisierul sursa. In cazul in care se foloseste prima varianta, fisierul
este cautat in directoarele standard (specificate prin optiuni sau prin
variabile de mediu in functie de compilator). In in cazul celei de-a doua
varianta fisierul este cautat intai in directorul curent, iar dupa aceea, daca
nu este gasit, in directoarele standard.
Forma cu „” permite si specificarea caii complete catre fisierul inclus; in
acest caz, nu se mai face cautarea si in directoarele standard.
Exemple:
// include fisierul stdio.h
// din directoarele standard
#include <stdio.h>
// include fiserul ListeSimple.h;
// cautarea se face intai in directorul
// curent si dupa aceea in directoarele standard
#include “liste.h”
// include fisierul Masive.cpp din directorul
// c:\Biblioteci; daca fisierul nu exista nu
// mai este cautat in alta parte si se genereaza
// o eroare de compilare
#include
"C:\Biblioteci\Masive.cpp"
Definirea
constantelor simbolice se face prin intermediul directivei #define cu sintaxa:
#define simbol valoare
Directiva are ca
efect inlocuirea tuturor aparitiilor lui simbol
in codul sursa (cu exceptia aparitiilor in cadrul unor constante de tip sir, in
comentarii sau in componenta unui alt identificator) cu valoare inaintea compilarii textului sursa.
valoare este cosiderata intreaga portiune
pana la sfarsitul liniei. Daca se doreste definirea unei valori pe mai multe
linii, atunci se va folosi caracterul \ la sfarsitul fiecarei linii ce
urmeaza a fi continuata. In cazul in
care valoare lipseste, textul
specificat prin simbol va fi eliminat
din codul sursa.
O constanta
simbolica poate fi redefinita in cadrul celuiasi fisier folosind inca o data
directiva define: #define simbol
alta_valoare.
Valabilitatea
unei definiri se incheie in urmatoarele cazuri:
Exemplu de
utilizare:
// definire parametru
#define DIM_VECTOR 20
// definire tip vector
#define TIP double
// definire mesaj
#define MESAJ "Calcul suma"
// definire cod pe mai multe linii
#define SEPARATOR cout \
<<
"----------------" \
<< endl;
TIP suma(TIP v[], int n)
{
//
validare lungime
if
(n > DIM_VECTOR)
//
DIM_MAX nu este inlocuit in sirul de caractere
//
cau in comentariu
cout <<
"dimensiunea este mai mare ca DIM_VECTOR" << endl;
//
calcul suma
TIP suma = 0;
for
(int i = 0; i < n; i++)
suma += v[i];
return
suma;
}
void main()
{
//
declarare vector folosind
//
simbolurile specificate
TIP v[DIM_VECTOR] = {1.1, 3.23,
6.62};
int
n = 3;
//
utilizare simboluri pentru mesaj
cout << MESAJ;
//
inserare cod prin preprocesor
SEPARATOR;
//
apel functie
suma(v, 3);
}
Codul sursa
rezultat in urma preprocesarii va fi:
// definire parametru
// definire tip vector
// definire mesaj
// definire cod pe mai multe linii
double suma(double
v[], int n)
{
//
validare lungime
if
(n > 20)
// DIM_MAX nu este inlocuit in sirul de caractere
// cau in comentariu
cout <<
"dimensiunea este mai mare ca DIM_VECTOR" << endl;
//
calcul suma
double
suma = 0;
for
(int i = 0; i < n; i++)
suma += v[i];
return
suma;
}
void main()
{
//
declarare vector folosind
//
simbolurile specificate
double
v[20] = {1.1, 3.23, 6.62};
int
n = 3;
//
utilizare simboluri pentru mesaj
cout << "Calcul
suma";
//
inserare cod prin preprocesor
cout <<
"----------------" << endl;;
//
apel functie
suma(v, 3);
}
Compilarea
conditionata permite includerea/excluderea unor sectiuni din codul sursa in
functie de anumite criterii. In C++ se folosesc directivele de compilare
conditionata #if, #else, #elsif si #endif.
Sintaxa este:
#if expresie_1
sectiune_1
#elif expresie_2
sectiune_2
…
#else
sectiunie_n
#endif
unde:
In cadrul
expresiilor se poate folosi cuvantul cheie defined
pentru a testa daca un anumit simbol
de preprocesare a fost definit (de exemplu: #if defined(DIM_MAX) …. ). In cazul
in care se doreste doar testarea existentei unei variabile se pot folosi
directivele #ifdef (echivalent cu #if defined) sau #ifndef (echivaled cu #if !defined)
Exemplu:
#if defined
DIM_VECTOR && DIM_VECTOR < 20
//
este inclus in codul sursa doar
//
daca simbolul DIM_VECTOR este
//
definit are o valoare mai mica de 20
cout << "Suma vector
de intregi cu " << DIM_VECTOR << "elemente";
#else
// va
fi inclus daca DIM_VECTOR nu
//
este dafinit sau este mai mare
// de
20
cout << "Vectorul
trebuie sa aiba mai putin de 20 elemente." << endl;
return;
#endif
Cele mai uzuale
utilizari pentru directivele de compilare conditionata sunt:
a)
compilarea
unor portiuni din codul sursa doar in pentru versiunile de depanare:
int CalculSuma(int v[], int n)
{
// se va afisa doar in versiunea de DEBUG
#if _DEBUG
cout << "Calcul suma
pentru vector cu "
<< n <<
" elemente." << endl;
#endif
//
calcul suma
int
suma = 0;
for
(int i = 0; i < n; i++)
suma += v[i];
#if _DEBUG
cout << "Suma
calculata este " << suma << endl;
#endif
return
suma;
}
Mesajele vor fi
afisate doar daca programul este compilat in versiunea de depanare. Simbolul
_DEBUG este definit automat de Visual Studio in cazul in care se selecteaza versiunea
Debug din Build->Set active configuration.
b)
evitarea
dublei incluziuni a fisierelor header:
#ifndef LISTE_H
#define LISTE_H
// definitii obiecte
// ...
#endif //LISTE_H
In acest fel se
poate evita includerea fisierului de mai multe ori. La prima includere conditia
este true si se va include continutul
format din declararea simbolului si codul sursa. La cea de-a doua includere
conditia va fi false si continutul nu
va mai fi inclus.
Macrodefinitiile sunt
secvente de cod parametrizate care pot fi inlocuite in textul sursa. Pentru
definirea lor se foloseste o extensie a directivei #define:
#define macro(param) corp
Unde:
Macrodefinitiile
sunt expandate in textul sursa inainte de compilare in doua etape:
Exemplu:
// definire macro
#define MAX(x,y) (x>y ? x : y)
// exemplu de utilizare
int a = 7, b = 10, c;
c = MAX(a,b);
Desi apelul de
macrodefinitii este similar unui apel de functie, cele doua constructii
sintactice nu sunt echivalente. In cazul macrodefinitiilor, parametrii nu sunt
evaluati inaintea apelului, fapt care poate crea confuzii:
//
macrodefinitie
#define PATRAT(x) x*x;
int x = 2, y;
// problema 1:
folosirea parantezelor pentru a impune prioritatilor
y = PATRAT(x+5) // => va fi expandata ca y = x+5*x+5; <=> y = 17
// problema 2:
constructii cu efecte colaterale
int x = 2;
y = PATRAT(++x) // => va fi expandata ca y = ++x*++x de unde
// va rezulta x = 4 (preincrementat de 2 ori) si y=16
In cadrul
corpului macrodefinitiilor de pot utiliza doi operatori speciali:
#param ŕ operator de sirificare; converteste parametrul
intr-o constanta de tip sir de caractere
##param ŕ operator de concatenare pentru simboluri; permite
crearea de simboluri (nume de functii sau de variabile pe baza parametrului)
Exemple de utilizare pentru macrodefinitii
1. Macrodefinitie pentru afisarea de mesaje de atentionare in cazul in care apare o conditie. Mesajul de va afisa doar in cazul in care aplicatia este compilata in modul „Debug”:
#ifdef _DEBUG
// in cazul in care compilam in mod
debug afisam atentionarile
#define ATENTIONARE(EXPRESIE)
\
if (EXPRESIE) \
cerr << "ATENTIE: "#EXPRESIE
<< endl;
#else
// in modul release le ignoram
#define ATENTIONARE(EXPRESIE)
#endif
// exemplu de
utilizare
double CalculMedie(int note[], int
numarNote)
{
ATENTIONARE(numarNote == 0)
double suma = 0;
for (int
i = 0; i < numarNote; i++)
suma += note[i];
return suma / numarNote;
}
2. Generare
de functii similare diferite tipuri de date folosind macrodefinitii:
#define FUNCTIE_SUMA(TIP) \
TIP suma_##TIP(TIP vector[], int
nrElem) \
{ \
TIP suma = 0; \
for (int i = 0; i < nrElem; i++) \
suma += vector[i]; \
return suma; \
}
// exemplu de
utilizare pentru generarea de functii
// (va genera
functiile suma_int si suma_double
FUNCTIE_SUMA(int)
FUNCTIE_SUMA(double)
3. Evitarea repetarii unor constructii similare:
#define CICLU(NR_ITERATII,CORP) \
for (int i = 0; i < NR_ITERATII; i++) { CORP; }
// exemplu de
utilizare
CICLU(10,cout
<< "Un mesaj..." << endl;)