Sunteți pe pagina 1din 244

CPP

1
1 Constante, variabile i expresii .................................................................................... 5
1.1 Tipuri fundamentale ............................................................................................. 5
1.2 Variabile ............................................................................................................... 5
1.3 Modificatori de tip ................................................................................................ 6
1.4 Operatorul typedef ................................................................................................ 7
1.5 Constante .............................................................................................................. 7
1.6 Constante cu nume ............................................................................................... 9
1.7 Expresii aritmetice .............................................................................................. 11
1.8 Tablouri .............................................................................................................. 13
1.9 Instruciunea de atribuire .................................................................................... 15
1.10 Prototipuri de funcii. Biblioteci de prototipuri ............................................... 16
1.11 Operaii de intrare / ieire ................................................................................ 17
1.12 Funcia main .................................................................................................... 21
1.13 Execuia unui program .................................................................................... 22
1.14 Operatorul sizeof ............................................................................................. 22
1.15 Operatorii ++ i - - ........................................................................................... 24
1.16 Operaii cu numere ntregi la nivel de bit ........................................................ 27
1.16.1 Operatori de deplasare ............................................................................... 27
1.16.2 Operaii logice la nivel de bit .................................................................... 28
2 Structuri de control fundamentale ............................................................................. 32
2.1 Algoritme ........................................................................................................... 32
2.2 Expresii relaionale ............................................................................................ 33
2.3 Expresii booleene .............................................................................................. 35
2.4 Operatorul if ...................................................................................................... 36
2.5 Operatorul switch .............................................................................................. 39
2.6 Operatorul ? ....................................................................................................... 40
2.7 Operatorul while ................................................................................................ 40
2.8 Operatorul do-while .......................................................................................... 43
2.9 Operatorul for .................................................................................................... 44
2.10 Operatorul , ...................................................................................................... 49
3 Funcii ........................................................................................................................ 51
3.1 Funcii standard .................................................................................................. 51
3.1.1 Funcii C standard de manipulare a caracterelor ......................................... 51
3.2 Definirea funciilor ............................................................................................. 54
3.3 Prototipuri de funcii .......................................................................................... 56
3.4 Compilarea separat a funciilor ......................................................................... 57
3.5 Funcii cu parametri tablouri ............................................................................. 58
3.6 Suprancrcarea funciilor .................................................................................. 64
3.7 Transmiterea parametrilor ctre funcii .............................................................. 66
3.8 Recursivitatea ..................................................................................................... 71
3.9 Funcii generice .................................................................................................. 72
4 Pointeri i referine .................................................................................................... 74
4.1 Pointeri ............................................................................................................... 74
4.1.1 Declararea variabilelor tip pointer ............................................................... 75
4.2 Referine ............................................................................................................. 79
4.3 Pointeri la funcii ................................................................................................ 82
4.4 Interpretarea instruciunilor ce conin pointeri ................................................... 83
4.5 Pointeri i tablouri unidimensionale ................................................................... 85
4.6 Poineri i iruri tip C .......................................................................................... 89
2
4.7 Pointeri i tablouri multidimensionale .............................................................. 92
4.8 Parametrii funciei main. Parametrii liniei de comand ..................................... 97
4.9 Alocarea dinamic a memoriei .......................................................................... 97
5 Fiiere tip C ............................................................................................................. 106
5.1 Fiiere tip text ................................................................................................... 107
5.1.1 Funcii intrare / ieire cu format ................................................................ 108
5.1.2 Funcii intrare / ieire tip caracter .............................................................. 112
5.2 Fiiere text tip ir de caractere .......................................................................... 115
5.3 Fiiere binare .................................................................................................... 117
6 Structuri tip C i uniuni ........................................................................................... 122
6.1 Structuri ............................................................................................................ 122
6.2 Uniuni ............................................................................................................... 125
7 Clase ....................................................................................................................... 127
7.1 Definirea unei clase .......................................................................................... 128
7.1.1 Definirea unei clase ................................................................................... 128
7.1.2 Pointerul this .............................................................................................. 133
7.1.3 Spaii de nume ........................................................................................... 133
7.2 Constructori i destructori ................................................................................ 134
7.2.1 Constructori ............................................................................................... 134
7.2.2 Destructori ................................................................................................. 138
7.3 Funcii prietene ................................................................................................. 140
7.4 Determinarea tipului unei expresii ................................................................... 142
8 Siruri tip C++ ........................................................................................................... 144
8.1 Clasa string ....................................................................................................... 144
9 Suprancrcarea operatorilor ................................................................................... 148
9.1 Suprancrcarea operatorilor aritmetici ............................................................ 148
9.2 Suprancrcarea operatorului de atribuire ........................................................ 150
9.3 Suprancrcarea operatorilor << i >> .............................................................. 152
10 Motenirea ............................................................................................................. 155
10.1 Pointeri la obiecte. Operatorii new i delete .................................................. 155
10.2 Motenirea .................................................................................................... 160
10.2.1 Definirea unei clase derivate ................................................................... 160
10.2.2 Specificatorii de acces ............................................................................. 162
10.3 Funcii virtuale. Polimorfism ........................................................................ 168
10.4 Destructori virtuali ........................................................................................ 175
10.5 Date i funcii statice ..................................................................................... 177
10.5.1 Date statice .............................................................................................. 177
10.5.2 Funcii statice .......................................................................................... 178
11 Fiiere tip C++ ....................................................................................................... 181
11.1 Fiiere text ..................................................................................................... 182
11.1.1 Funcii intrare / ieire cu format .............................................................. 182
11.1.2 Funcii intrare / ieire tip caracter ............................................................ 183
11.2 Fiiere binare ................................................................................................. 186
11.3 Fiiere text tip string ...................................................................................... 189
12 Tratarea excepiilor ................................................................................................ 191
12.1 Excepii .......................................................................................................... 191
12.2 Excepii lansate de funcii ............................................................................. 193
12.3 Excepii standard ........................................................................................... 194
12.4 Excepii intrare / ieire .................................................................................. 196
13 Aplicaii ................................................................................................................ 199
3
13.1 Funcii de timp ............................................................................................... 199
13.2 Fire de execuie ............................................................................................. 200
13.3 Funcia system ............................................................................................... 202
14 Biblioteca de abloane standard ............................................................................ 204
14.1 Clase generice ................................................................................................ 204
14.2 Containere, iteratori i algoritme generice ................................................... 206
14.3 Vectori ........................................................................................................... 207
14.3.1 Parcurgerea vectorilor cu iteratori ........................................................... 209
14.3.2 Stergerea i inserarea de elemente .......................................................... 211
14.3.3 Sortarea componentelor unui vector ........................................................ 213
14.3.4 Cutarea unui element ntr-un vector ...................................................... 215
14.3.5 Copierea unui container. Iteratori pentru streamuri ................................ 217
14.4 Liste ............................................................................................................... 219
14.4.1 Parcurgerea listelor .................................................................................. 219
14.4.2 Sortarea listelor ........................................................................................ 220
14.4.3 Inversarea ordinii elementelor listelor ..................................................... 221
14.4.4 Inserarea i tergerea elementelor din liste .............................................. 222
14.4.5 Copierea listelor ...................................................................................... 224
14.5 Parcurgerea irurilor tip string cu iteratori .................................................... 225
14.6 Numere complexe .......................................................................................... 226
Anexa 1. Reprezentarea informaiilor ....................................................................... 229
A.1 Reprezentarea numerelor ntregi n sistemul binar ......................................... 229
A.2 Deplasarea numerelor binare cu semn ........................................................... 233
A.3 Reprezentarea numerelor reale ....................................................................... 234
A.4 Reprezentarea caracterelor ............................................................................. 236
Anexa 2. Prioritile i asociativitatea operatorilor ................................................... 238
Anexa 3. Tipul complex n limbajul C ...................................................................... 239
4
Partea I Programarea procedural
1 Constante, variabile i expresii
1.1 Tipuri fundamentale
In matematic variabilele se clasific dup tipul lor. Tipul unei variabile este
mulimea valorilor pe care le poate lua acea variabil i operaiile ce se pot efectua cu
acele valori. De exemplu, operaiile tipului real sunt + - * /, pentru tipul ntreg se
adaug restul mpririi a dou numere ntregi, simbolizat %. Tipul unei variabile este
fcut explicit cu o declaraie de tip. Tipurile predefinite n limbajul C++ sunt cele din
tabelul 1.
Tabelul 1. Tipuri de baz ale limbajului C++
Tip Semnificaie
Dimensiune
n octei
Domeniu de
valori
Operaii
int Numere ntregi 4
[-2
31
2
31
)
+-*/%
float
Numere reale n virgul
mobil scurt
4 ) 10 10 (
38 38
+-*/
double
Numere reale n virgul
mobil lung
8 ) 10 10 (
307 307
+-*/
char
Caractere n cod ASCII i
numere ntregi pe un octet
1
[-2
7
2
7
)
+-*/%
wchar_t
Numere pozitive i caractere
UNICODE pe doi octei
2
[0 2
16
)
+-*/%
bool Valoare boolean 1 false,true
and, or,
not
Pentru fiecare tip exist constante predefinite ce dau limitele minim i maxim ale
domeniului de valori.
Menionm c, pentru tipurile char i int, primul bit este utilizat pentru semn, ceilali
bii sunt bii ai numrului. In cazul tipului bool valoarea false este reprezentat prin
zero iar true prin unu.
1.2 Variabile
Numele unei variabile este format din cifre, litere i caracterul _ (underscore), i
ncepe totdeauna cu o liter sau caracterul _. Literele mari sunt diferite de cele mici.
De exemplu abc i Abc sunt dou nume de variabile diferite. Instruciunea de
declarare a tipului are forma
tip list de variabile;
Lista de variabile este format din nume de variabile separate de virgule. Diagrama
sintactic este cea de mai jos.
5
De exemplu, instruciunile
int a;
char b;
declar o variabil a de tipul int i o variabil b de tipul char. Conform instruciunii
de declarare a tipului de mai sus, mai multe variabile de acelai tip pot fi declarate cu
o singur instruciune, scriind numele lor separate de virgule. De exemplu,
instruciunea
float f1, f2, f3;
declar trei variabile de tipul float. O variabil poate fi iniializat la declararea tipului
ei, conform urmtoarei diagrame sintactice
De exemplu, instruciunea
double d = 1 + sin(2.4);
declar o variabil de tip double pe care o iniializeaz la valoarea 1+sin(2.4). O alt
mod de iniializare a unei variabile la declararea tipului ei este dat de diagrama de mai
jos
Valoarea cu care se iniializeaz variabila este expresia din paranteze. De exemplu,
instruciunea
double d (1.2 cos(1.7));
declar o variabil de tip double i o iniializeaz la valoarea 1.2 cos(1.7);
Menionm c tipul bool nu este definit n limbajul C.
1.3 Modificatori de tip
Modificatorii de tip schimb domeniul de valori pe care le poate lua o variabil de tip
int sau char. Aceti modificatori sunt:
unsigned. La utilizarea acestui modificator, valorile variabilelor sunt pozitive.
Toi biii sunt bii ai numrului. De exemplu, tipul unsigned int are domeniul
de valori [0 2
31
),

iar tipul unsigned char are domeniul de valori [0 2
8
) ,
signed. Valorile variabilelor sunt numere cu semn. Primul bit este bit de semn,
6
short. Acest modificator se aplic tipului int. De exemplu, tipul short int are
domeniul de valori [-2
15
2
15
),

iar tipul unsigned short int are domeniul de
valori [0 2
16
). Tipul wchar_t este tipul unsigned short int predefinit.
long
1.4 Operatorul typedef
Operatorul typedef permite s definim tipuri de date bazate pe tipuri existente. Forma
instruciunii este
typedef tip-existent tip-nou
Exemple. Instructiunea
typedef float REAL32
definete tipul REAL32 ce poate fi utilizat n locul tipului float.
In limbajul C nu exist tipul predefinit bool. Putem s definim tipul bool folosind
tipul char cu instruciunea
typedef char bool
1.5 Constante
Tipurile de constante din limbaj corespund tipurilor de date.
Constante ntregi
Constantele ntregi sunt numere ntregi scrise n bazele 8, 10 sau 16.
constantele zecimale sunt numere ntregi reprezentate n baza 10. Ele nu pot
ncepe cu cifra 0. Exemple de constante zecimale sunt :
275, -325, +12
constante hexazecimale sunt numere ntregi reprezentate n baza 16. Ele ncep
cu 0x sau 0X. Cifrele hexazecimale sunt 0, , 9 i A F sau a f. Exemple
de constante hexazecimale :
0x1E sau 0x1e sau 0X1e sau 0X1E care reprezint valoarea zecimal
30
-0x2f sau -0x2F, sau 0x02F reprezint valoarea zecimal -47.
constante octale sunt numere ntregi reprezentate n baza 8. Ele ncep
obligatoriu cu cifra 0. Exemple de constante octale :
016 reprezint numrul zecimal 14 (s se arate c (14)
10
= (16)
8
)
-014 reprezint numrul zecimal -12 (s se arate c (-12)
10
= (-14)
8
).
Constante reale
Constantele reale sunt numere reale n simpl sau dubl precizie. Partea subunitar
este separat de cea ntreag prin punct zecimal.
constante reale n virgul mobil scurt. Exemple :
1.5 -4.23 27.0
Constantele reale pot avea exponent, un numr ntreg ce reprezint puterea lui
zece cu care se nmulete constanta. De exemplu numrul 2.31*10
1
se scrie ca
2.31e1 sau 2.31e+1 sau +2.31E1
Numrul real -41.2* 10
-1
se scrie ca i constant real
-41.2e-1 sau -4.12
7
constante reale n virgul mobil lung. Se scriu dup aceleai reguli ca i
constantele reale n virgul mobil scurt i sunt urmate de sufixul L sau l. De
exemplu, numrul real 1,5 se scrie ca i constant n virgul mobil lung
1.5L sau 15e-1L
Constante de tip caracter
constante de tip caracter reprezint un caracter al codului ASCII pe un octet.
Caracterele se scriu ntre apostrofuri. De exemplu, caracterul ASCII a se scrie
a sau echivalent, ca i constante ntregi, 0x61 sau 97
Caracterul ASCII A se scrie
A sau echivalent, 0x41 sau 65.
Caracterul 1 n codul ASCII este
1 sau echivalent, 0x31 sau 49.
Un alt mod de a defini constante tip caracter este de a le scrie sub forma \xhh
unde h este o cifr hexazecimal. Numrul hexazecimal hh reprezint
echivalentul hexazecimal al caracterului n codul ASCII. De exemplu,
caracterele a i A se reprezint ca \x61 i respective \x41. Caracterul 1
se reprezint ca \x31. Caracterele speciale ale codului ASCII se reprezint
folosind secvena de evitare
\n CR
\r LF
\t tab orizontal
\ ghilimele
\ apostrof
\\ backslash
\? semnul intrebrii
\0 null (valoarea 0 pe un octet)
constante tip ir de caractere. Ele reprezint un ir de caractere ASCII scris
ntre ghilimele. De exemplu
abcd
Un ir de caractere este reprezentat de codurile ASCII ale caracterelor pe cte
un octet, urmate de un octet ce conine valoarea 0. Sirul anterior se reprezint
ca
a b c d \0
Caracterele speciale se reprezint n interiorul unui ir folosind secvena de
evitare. De exemplu
ab\c
a\\bc
abc\n
Menionm diferena ntre constanta tip caracter a i constanta tip ir de
caractere a. Constanta tip caracter a se reprezint pe un singur octet,
constanta a se reprezint pe doi octei.
8
constante tip caracter UNICODE corespund tipului wchar_t. Ele se definesc
sub forma L\xhhhh unde h reprezint o cifr hexazecimal.
Constante booleene
constante booleene sunt true i false i se reprezint prin valorile unu i respective
zero.
1.6 Constante cu nume
In program putem defini constante cu nume n urmtoarele feluri
utilizarea directivei define . Compilatoarele limbajelor C i C++ au un
preprocessor care modific programul surs nainte de compilare.
Preprocesorul citete diversele directive i modific programul surs
corespunztor acestora. Directiva define ce definete o constant are forma
# define nume valoare
unde valoare este interpretat de preprocessor ca un ir de caractere. De
exemplu, directiva
# define PI 3.14
are ca efect nlocuirea numelui PI n program cu irul de caractere 3.14
enumerri. O enumerare definete constante ntregi sau un tip ntreg i valorile
asociate acelui tip. Instruciunea enum de definire a unor constante ntregi are
forma
Instruciunea enum de declarare a unui tip ntreg i a valorilor asociate lui are
forma
De exemplu,
enum CLRX {aaa, bbb, ccc};
definete tipul CLRX i trei constante aaa, bbb, ccc aferente acestui tip, prima
are valoarea zero i fiecare constant urmtoare are o valoare mrit cu unu :
aaa = 0
bbb = 1
ccc = 2
9
In program constantele sunt nlocuite cu valorile lor. Valoarea fiecrei
constante poate fi specificat printr-o expresie ntreag constant. De exemplu
enum cstval {ppp = 2, dd, cc = -1, nn = 2 + 3 * 4};
definete tipul cstval i constantele de acest tip
ppp = 2
dd = 3
cc = -1
nn = 14
Instructiunea
enum BAZA {BIN = 2, HEX = 16, OCT = 8, DEC = 10};
definete tipul BAZA i constantele aferente
BIN = 2
HEX = 16
OCT = 8
DEC = 10
Putem defini variabile corespunznd tipului definit prin enum. Aceste
variabile pot primi ca valori doar constantele definite n instruciunea enum.
Fie de exemplu tipul enumx definit mai jos
enum enumx {ena, enb, enc};
Instruciunea urmtoare definete variabila z de tipul enumx
enumx z;
Putem atribui o valoare variabilei z astfel
z = ena;
Instruciunea enum are i o form mai simpl
enum {list de nume};
care definete doar constante i nu un tip. Orice constant definit cu
instruciunea enum poate fi utilizat ca orice constant ntreag.
utilizarea cuvntului cheie const. O asemenea instruciune are forma
diagramelor sintactice de mai jos

unde tip este un tip al limbajului iar valoare este o expresie constant. De
exemplu, instruciunile :
const int a = 7, n(-2);
const char b = a;
const float c = 2.5e-2 * 4.14;
const double x = sin(0.2) + cos(1.5);
const float z = log(12.5) / 2.3, y(2 + ln(1.7));
definesc constantele a, n, b, c, x, z i y, ce au valorile specificate n
instruciune.
10
1.7 Expresii aritmetice
Expresiile aritmetice sunt formate din constante, variabile i funcii. Operatorii sunt +
- * i / pentru operanzi reali, iar n cazul operanzilor de tip ntreg, i % (restul
mpririi a dou numere ntregi). Restul mpririi a dou numere ntregi, a i b, se
definete astfel
a % b = a (a / b) * b
De exemplu
19 % 4 = 3 19 / 4 = 4
19 % (-4) = 3 19 / (-4) = -4
-19 % 4 = -3 -19 / 4 = -4
-19%(-4) = -3 -19 / (-4) = 4
La schimbarea semnului unui operand, se poate schimba doar semnul ctului i
restului, i nu valoarea lor absolut.
Pentru gruparea termenilor se folosesc paranteze rotunde, ( i ). De exemplu, expresia
b a
b a
+

se scrie ca
(a b) / (a + b)
iar expresia
2
2
2
* *
m
x c x b a
+
+ +
se scrie ca
(a + b * x + c * x * x) / (2 + m*m)
Expresia
b
c
c
b
a
+
se scrie ca
a / (b / c + c / b)
Funciile matematice standard uzuale ale limbajelor C i C++ sunt cele de mai jos
acos cos exp ceil fabs pow
asin sin log floor sqrt
atan tan log10
Funcia floor(x) calculeaz valoarea ]
x
(cel mai mare numr ntreg cuprins n x), iar
funcia ceil(x) calculeaz valoarea 1
x
(cel mai mic numr ntreg mai mare ca x).
Toate funciile de mai sus au argumente de tip double i rezultatul de tip double.
Funcia pow are prototipul
double pow(double a, double b)
i calculeaz expresia a
b
. Apelarea unei funcii se face scriind numele funciei ca
termen ntr-o expresie urmat n paranteze de parametrii actuali. Exemple de
expresii aritmetice i scrierea lor sunt prezentate mai jos. Vom presupune c
variabilele din aceste expresii au fost declarate n prealabil de tip double i au primit
valori.
11
x
x b x a
+
+
75 . 2
) sin( * ) ( cos *
2
(a*cos(x)*cos(x)+b*sin(x))/(2.75+fabs(x))
5
2x x
e e

+
(exp(x)+exp(-2*x))/5
) cos( 1 ln ) 2 log( x x + + +
log10(fabs(x)+2)+log(fabs(1+cos(x)))
5 . 3
) (
+
+
a
y x pow(x+y, a+3.5)
Menionm c, n expresii putem folosi orice fel de constante ntregi, reale sau
caracter. De exemplu, expresia a + 27 se poate scrie
a + 27
sau
a + 0x1b
sau
a + 033
Constantele hexazecimal 0x1b i octal 033 reprezint valoarea zecimal 27.
Expresia
x + 100 se poate scrie
x + 100
sau
x + 0x64
sau
x + d
Codul ASCII al caracterului d este 0x64. La evaluarea unei expresii constantele
hexazecimale, octale sau caracter sunt convertite n numrul ntreg corespunztor.
Evaluarea expresiilor aritmetice se face innd cont de prioritile operatorilor i de
asociativitatea lor.
Prioritatea operatorilor Asociativitate
+ unar, - unar la dreapta
* / % la stnga
+ - la stnga
Conform tabelei de mai sus operatorii *, / i % au o prioritate mai mare dect + i - .
Asociativitatea la stnga a operatorilor nseamn urmtorul mod de execuie. Expresia
a / b / c
este interpretat ca
(a / b) / c
De exemplu, expresia 8 / 4 / 2 este interpretat ca (8 / 4) / 2 i are rezultatul 1.
Expresia
x / y * z
este interpretat ca
(x / y ) * z
adic operatorii / i * ce au aceeai prioritate se execut de la stnga la dreapta.
Reamintim c, pentru a modifica ordinea de execuie o operaiilor, se utilizeaz
paranteze rotunde.
Deoarece n expresii intervin operanzi de diverse tipuri, se fac conversii. Regulile
dup care compilatorul face aceste conversii sunt urmtoarele:
tipul float se convertete la double,
tipurile char i short int se convertesc la int,
12
tipurile unsigned char i unsigned short int se convertesc la unsigned int,
dac n expresie exist un operand de tipul long int, tipul int i unsigned int
se convertesc la long int. Tipul expresiei se determin conform tabelei de mai
jos
int long double
int int long double
long long long double
double double double double

Menionm n final c, tipurile char, short int i variantele lor unsigned char, unsigned
short int i float sunt doar pentru memorare. Calculele se efectueaz doar cu variabile
de tip int, long int i double.
Valoarea unei expresii poate fi convertit ntr-un tip diferit dac este nevoie.
Exist dou forme ale expresiilor de conversie a tipurilor.
Prima form a expresiei de conversie de tip este
(tip) expresie
De exemplu, dac i este o variabil ntreag cu valoarea 7 i f este o variabil de tip
double cu valoarea 3.47
int i = 7 ;
double = 3.47 ;
expresia
(i + f) % 2
nu este corect deoarece expresia in parantezele rotunde are tipul double. Expresia
(int)(i + f) % 2
are tipul int i este corect, rezultatul ei este 5.
In limbajul C++ este posibil nc o form de convertire a expresiilor, cu forma
tip(expresie)
De exemplu, expresia anterioar se poate scrie
int(i + f) % 2
Menionm c se pot face conversii ntre toate tipurile standard existente n limbaje.
Conversia de la un tip real la un tip ntreg se face prin trunchiere, valoarea 3.71
convertit la int este 3, iar valoarea -3.4 convertit la int este -3.
1.8 Tablouri
Un tablou este o mulime de elemente de acelai tip. Tablourile sunt tipuri structurate
simple. Instruciunea de declarare a unui tablou cu o dimensiune este urmtoarea
tip nume [ numr ntreg] ;
unde numr ntreg reprezint numrul de elemente ale tabloului. Elementele tabloului
sunt
nume[0], nume[1], , nume[numr ntreg 1]
De exemplu instruciunea
int a[10];
declar un vector cu zece elemente de tip int. Elementele vectorului sunt
a[0], a[1], , a[9].
Dimensiunea unui tablou este fixat la declararea sa i nu se poate schimba.
Un tablou poate fi iniializat la declararea sa scriind valorile elementelor ntre acolade,
{}, i separate de virgule. Exemple de instruciuni ce declar tablouri i le atribuie
valori
double x[3] = {1.32, -2.15, 4.45};
13
char c[4] = {a, b, c, x};
Ultimul tablou este reprezentat n memorie astfel
a b c x
In cazul n care lista de valori este mai scurt dect numrul de elemente declarate,
ultimele elemente sunt iniializate cu zero. In cazul iniializrii unui tablou, la
declararea lui putem omite numrul de elemente al tabloului. De exemplu, putem scrie
char x[] = {#, >, m};
Compilatorul calculeaz dimensiunea tabloului din numrul de valori utilizate pentru
iniializare. Instruciunea precedent este echivalent cu instruciunea
char x[3] = {#, >, m};
Menionm c un vector de caractere poate fi iniializat cu o constant tip ir de
caractere. De exemplu, putem scrie
char s[] = abc;
Reamintim c o constant ir de caractere este terminat printr-un octet 0, deci
vectorul declarat are 4 componente, a, b, c i \0 si este reprezentat n memorie
ca
a b

c \0
Instruciunea precedent este echivalent cu oricare dintre instruciunile urmtoare:
char s[4] = abc;
char s[4] = {a, b, c , \0};
char s[4] = {0x61, 0x62, 0x63, 0};
char s[4] = {97, 98, 99, 0} ;
Un tablou poate avea oricte dimensiuni. Diagrama sintactic a declaraiei unui tablou
este urmtoarea
De exemplu, instruciunea
float b[7][3]
declar o matrice cu apte linii i trei coloane. Elementele matricei sunt :
b[0][0] b[0][1] b[0][2]

b[6][0] b[6][1] b[6][2]
Elementele tablourilor sunt memorate pe linii. Un tablou cu mai multe dimensiuni
poate fi de asemenea iniializat la declararea sa. Fie de definit matricea m cu elemente
ntregi, pozitive
1
]
1

3 7 3
5 2 1
m
Instruciunea corespunztoare este
int m[2][3] = {{1, 2, 5},{3, 7, -3}};
La utilizarea ntr-o expresie, indicii elementelor tablourilor pot fi orice expresii
ntregi. Presupunem urmtoarele declaraii de tablouri
14
double a[10], b;
int i, j, y[3][4];
Urmtoarele expresii ce conin elemente de tablouri :
) sin( ) cos(
*
b b
y b a
ij i

+
(a[i]+b*y[i][j])/(cos(b)-sin(b))
1 ,
) cos(
+
+
j i
b
y e
exp(cos(b)) + y[i][j + 1]
2
3 2
+
n jk
b a
a[j][k] + b[2 * n 3] * b[2 * n 3]
23 12
* y x
x[1][2] * y[2][3]
La utilizarea elementelor unui tablou, [] este un operator de selecie, ce are doi
operanzi: un nume de tablou i un indice. El se aplic asupra unui nume de
tablou i selecteaz un element al acelui tablou. De exemplu, a[0] selecteaz
primul element al tabloului a, iar b[0] selecteaz prima linie a tabloului b.
Aplicnd nc o dat operatorul de selecie asupra lui b[0], de exemplu b[0][0],
selecteaz primul element din prima linie a lui b. Operatorul [] este asociativ la
stnga.
In acelai mod, operatorul de apelare a unei funcii (), aplicat asupra unui nume
de funcie, returneaz valoarea calculat de funcie. De exemplu cos(1.2)
reprezint aplicarea operatorului () asupra numelui funciei cos.
Prioritile i asociativitatea operatorilor
Operator Asociativitate
[] () la stnga
+ unar, - unar, (tip) la dreapta
* / % la stnga
+ - la stnga
1.9 Instruciunea de atribuire
O operaie definit pentru toate tipurile fundamentale este atribuirea. Operatorul
de atribuire = atribuie o valoare unei variabile. Forma instruciunii de atribuire este
variabil = expresie;
De exemplu, urmtoarele instruciuni atribuie valori variabilelor x , y i z .
float x, y, z;
x = -1.34;
y = sin(x) + cos(x * x);
z = (x + y) / (y + cos(x) * cos(x));
Menionm c, la atribuire, expresia din partea dreapt este convertit n tipul
variabilei din stnga. Fie de exemplu instruciunile
int a;
double x = 2.7;
a = x;
Variabila ntreag a primeste valoarea 2.
Limbajele C i C++ au operatori speciali pentru scrierea prescurtat a instruciunilor
de atribuire, +=, -=, *=, /= i %=. Aceti operatori se definesc astfel:
15
Considerm o variabil x i o expresie e. Instruciunea
x op= e;
este echivalent cu
x = x op e;
De exemplu, instruciunea
x = x + cos(y) ;
se scrie prescurtat
x += cos(y) ;
Tabelul urmtor prezint aceti operatori
Forma prescurtat a operatorilor de atribuire
Instruciune Forma prescurtat
x = x + e x += e
x = x e x -= e
x = x * e x *= e
x = x / e x /= e
x = x % e x %= e
Ali operatori de acest tip vor fi prezentai ulterior. Menionm n final c, un operator
de atribuire are doi operanzi i ca rezultat valoarea operandului din stnga. Operanzii
de atribuire sunt asociativi la dreapta (se execut de la dreapta la stnga). Fie de
exemplu instruciunea
int x, y, z = 1;
Instruciunea
x = y = z;
atribuie variabilelor x i y valoarea 1. Instruciunea
x += y += z;
atribuie variabilei y valoarea 2 i variabilei z valoarea 3. De ce?
1.10 Prototipuri de funcii. Biblioteci de prototipuri
Atunci cnd compilatorul ntlnete un apel la o funcie, el trebuie s poat verifica
concordana ntre parametrii actuali i cei formali i are nevoie de tipul rezultatului
funciei pentru a face conversiile necesare evalurii expresiei. Pentru aceasta este
nevoie de o definiie a funciei n care apar tipurile parametrilor i tipul rezultatului.
Aceast definiie se numete ablon sau prototip i are forma
tip numefuncie(tip, tip, , );
De exemplu, funcia sin are un singur parametru de tip double,iar rezultatul este de tip
double; prototipul funciei sin este
double sin(double);
Limbajele C i C++ au biblioteci standard cu prototipurile funciilor limbajului. De
asemenea, utilizatorul poate defini biblioteci cu prototipuri. Toate aceste biblioteci
sunt semnalate compilatorului cu directiva include cu forma
# include nume_bibliotec
Aceste biblioteci sunt fiiere numite fiiere header sau antet. Directiva include este
diferit n cazul limbajului C de cea din limbajul C++.
In cazul limbajului C fiierele header au extensia h. De exemplu, biblioteca cu
prototipurile funciilor intrare/ieire tip C este stdio.h, biblioteca cu prototipurile
funciilor matematice este math.h, biblioteca cu prototipurile funciilor de prelucrat
16
iruri tip C este string.h, etc. In consecin, n cazul unui program n limbajul C vom
semnala compilatorului aceste biblioteci cu directivele
# include <stdio.h>
# include <math.h>
In limbajul C++ fiierele header nu au extensie. De exemplu, biblioteca cu
prototipurile funciilor intrare/ieire tip C++ este iostream. La utilizarea n limbajul
C++, bibliotecile specifice limbajului C sunt redefinite ca <cstdio>, <cmath>,
<cstring>, etc. In plus, toate funciile standard ale limbajelor C i C++ sunt grupate
ntr-un spaiu de nume denumit std. In consecin, putem semnala compilatorului
bibliotecile standard iostream i math astfel
# include <iostream>
# include <cmath>
using namespace std;
Putem defini propriile biblioteci cu prototipuri pe care s le semnalm compilatorului
cu directiva include. Numele propriilor biblioteci cu prototipuri sunt scrise ntre
ghilimele.
1.11 Operaii de intrare / ieire
Orice aplicaie ce ruleaz pe un calculator are un director curent asociat i fiiere
standard de intrare i ieire. Fiierul standard de intrare este tastatura iar cel de ieire
este ecranul. In program, orice fiier este asociat unui obiect numit stream. Exist
dou tipuri de fiiere: text i binare. Ele vor fi prezentate detaliat ntr-un capitol
ulterior. Fiierele standard asociate unei aplicaii sunt de tipul text. Fiierul text este
este compus dintr-un ir de caractere grupate n linii. Liniile constau din zero sau mai
multe caractere urmate de un caracter \n. Streamul de intrare asociat tastaturii are
denumirea cin, streamul de ieire asociat ecranului se numete cout. Mai exist alte
dou streamuri cerr i clog pentru scrierea mesajelor de eroare.
Operatorul de scriere a datelor
Operatorul de scriere a datelor este <<. El insereaz date n streamul de ieire. De
exemplu, secvena de instruciuni
int i = 123;
cout << i = << i;
afiaz pe ecran
i = 123
Operatorul << insereaz n stream valoarea expresiei din dreapta sa. De exemplu,
expresia
<< i=
insereaz n stream irul de caractere i= , expresia
<< i
insereaz n stream valoarea variabilei i, 123, etc.
Pentru a afia valori pe mai multe linii, trebuie ca dup fiecare linie s scriem
caracterul \n, care este predefinit ca endl. De exemplu, fie instruciunile
int j = -7;
double x = 1 + sin(0.2);
Pentru a afia valorile variabilelor j si x pe dou linii, vom scrie
cout << j = << j << endl << x = << x << endl;
sau
cout << j = << j << \n << x = << x << \n;
sau
cout << j = << j << \n << x = << x << \n;
17
sau, echivalent, cu doua instruciuni cout
cout << j = << j << endl;
cout << x = << x << endl;
Operatorul << poate scrie orice tip predefinit de date: int, float, iruri de caractere, etc.
Operatorul de citire a datelor
Operatorul de citire dintr-un stream este >>. El extrage date din streamul de intrare i
le atribuie unor variabile. Operatorul >> este urmat de numele variabilei ce va
memora valoarea citit. De exemplu, secvena de instruciuni
int a;
cin >> a;
va citi o valoare introdus de la tastatur i o va atribui variabilei a (expresia >>a
citete o valoare de la tastatur i o atribuie variabilei a). O instruciune cin poate citi
oricte date din stream. Fie instruciunea
int a, b ;
Instruciunea
cin >> a >> b;
citete dou valori de la tastatur i le atribuie variabilelor a i b. Ea poate fi scris ca
cin >> a;
cin >> b;
Valorile introduse de la tastatur sunt analizate i atribuite variabilelor de ctre
instruciunea cin. Pentru fiecare variabil se procedeaz astfel :
se citesc i se ignor toate caracterele spaiu, \t sau \n (acesta din urm este
generat la apsarea tastei Return),
se citesc urmtoarele caractere corespunznd tipului variabilei sau pn la
ntlnirea unui caracter spaiu, \t sau \n i valoarea citit se atribuie
variabilei.
Fie de exemplu valorile 25 i 17 de citit pentru variabilele de tip ntreg a i b cu una
din instruciunile precedente. Ele pot fi introduse de la tastatur separate de un spaiu
ca
irul introdus este analizat astfel :
se citesc toate caracterele spaiu, \t sau \n pn la prima cifra ntlnit i se
ignor (n acest caz primul caracter este diferit de spaiu, \t sau \n),
se citesc apoi toate caracterele pn la primul caracter diferit de cifr, deoarece
trebuie citit un numr ntreg, i rezult numrul 25.
Se procedeaz la fel pentru al doilea numr :
se citete i se ignor spaiul,
se citesc cifrele pn la primul caracter diferit de cifr i rezult numrul 17.
Aceleai valori pot fi introduce ca
Fie de iniializat prin citire o variabil x tip caracter la valoarea A. Variabila este
definit cu instruciunea
char x;
Instruciunea de citire este
cin >> x;
Putem introduce caracterul A astfel:
18
sau
Citirea caracterului se face dup regula de mai sus : se citesc i se ignor toate
caracterele spaiu, \t sau \n i se citete un caracter care este atribuit variabilei x.
Valorile introduse de la tastatur sunt analizate i atribuite variabilelor de ctre
instruciunea cin doar dup introducerea unui caracter \n.
Reamintim c biblioteca de prototipuri pentru streamurile cin, cout, etc are numele
iostream. Pentru a modifica formatul de scriere sau citire a datelor putem utiliza
manipulatori definii n biblioteca iomanip. Pentru scrierea sau citirea unui numr
ntreg n instruciunile cout sau cin n diferite baze se utilizeaz manipulatorii
hex pentru baza 16
dec pentru baza 10
oct pentru baza 8
La nceperea execuiei unui program baza 10 este implicit.
Fie urmtorul exemplu
int a ;
cin >> hex >> a ;
cout << a << endl;
Presupunem introdus de la tastatur irul de caractere de mai jos
Valoarea atribuit prin citire variabilei a este 27. De ce ?
In urmtorul exemplu vom citi numrul negativ -44 n baza 16.
int i ;
cin >> hex >> i ;
Numrul poate fi introdus ca n tabelul de mai jos.
Valoarea afiat este 27. Pentru a afia valoarea variabilei a n baza 16 trebuie s
scriem
cout << hex << a << endl ;
Pentru afiarea bazei se utilizeaz manipulatorii:
showbase pentru scrierea bazei
noshowbase pentru a nu scrie baza
Exemplu. Fie instruciunea
int k = 20;
Tabloul urmtor prezint exemple de utilizare a manipulatorilor pentru a afia pe
ecran valoarea variabilei k.
cout << k; 20
cout << hex << k; 14
cout << showbase << hex << k; 0x14
cout << oct << k; 24
cout << showbase << oct << k; 024
In cazul numerelor reale avem manipulatorii:
19
fixed numrul este scris fr exponent
scientific numrul este scris cu exponent
Exemplu. Fie instruciunea
float x = 122.63;
Tabloul urmtor prezint exemple de utilizare a manipulatorilor pentru a afia pe
ecran valoarea variabilei x.
cout << x; 122.63
cout << fixed << x; 122.63
cout << scientific << x; 1.226300e+002
Funciile urmtoare sunt apelate de operatorul de scriere << :
setbase(int)
setw(int)
setfill(char)
setprecision(int)
Funcia setbase(int) indic baza n care va fi afiat un numr ntreg, 8, 10 sau 16.
Funcia setw(int) d dimensiunea cmpului n caractere pe care este scris un numr.
Valoarea implicit a acestui manipulator este 0. Dac dimensiunea cmpului nu este
specificat, sau este prea mic, numrul este scris pe cte caractere este necesar.
Funcia setfill(char) indic un caracter cu care se umplu spaiile libere ale unui cmp.
Valoarea implicit a acestui caracter este spaiul. Funcia setprecision(int) d numrul
de cifre cu care este scris un numr real.
Exemplu. Fie instruciunile
int x = 23;
double pi(3.1459);
Tabloul urmtor prezint exemple de utilizare a funciilor operatorului <<.
cout << setw(5) << x; 23
cout << x; 23
cout << setbase(16) << x: 17
cout << dec << setw(3) << setfill(*) << x; *23
cout << pi << endl; 3.1459
cout << setprecision(3) << pi << endl; 3.15
cout << setprecision(4) << pi << endl; 3.146
cout << setprecision(6) << pi << endl; 3.1459
Exemplu. Fie instruciunea
float z = 12.64;
Tabelul urmtor prezint exemple de utilizare a funciilor anterioare la afiarea
variabilei z cu instruciunea cout.
cout << setprecision(3) << z; 12.6
cout << setw(8) << z; 12.64
cout << setw(8) << setfill(*) << z; ***12.64
cout << setw(15) << scientific << z; 1.264000e+001
Manipulatorii urmtori specific modul de cadrare al valorilor scrise :
left - cadrare la stnga
20
right cadrare la dreapta
Menionm c dimensiunea cmpului prescris de funcia setw(int) se aplic
doar urmtorului numr de scris. Ceilali manipulatori rmn la valoarea
prescris pn sunt modificai.
Exemplu. Fie instruciunile
int x ;
cin >> x ;
cout << hex << x << endl ;
cout << x << endl ;
Valoarea variabilei x este afiat n baza 16 cu ambele instruciuni, deoarece
manipulatorul de scriere a bazei are valoarea hex. Pentru a afia din nou variabilele
ntregi n baza 10 trebuie s scriem o instruciune
cout << dec << x << endl ;
1.12 Funcia main
Orice program scris n limbajele C sau C++ se compune din funcii care se apeleaz
unele pe altele. Definiia unei funcii este
tip numefunctie (lista de parametri)
{
instruciuni
}
Una din funcii are numele main iar execuia programului ncepe cu aceast funcie.
Prototipul acestei funcii este
int main();
Semnificaia acestui prototip este urmtoarea. Funcia main are ca rezultat o valoare
ntreag i nu are parametri. Corpul funciei este o instruciune compus, adic o
secven de instruciuni scrise ntre acolade, { i }. Corpul funciei conine i o
instruciune return ce termin execuia funciei i transmite n programul appelant
valoarea calculat de funcie.
Programul poate conine comentarii. Comentariile plasate ntre delimitatorii /* i */ se
pot ntinde pe mai multe linii. Comentariile ce ncep cu caracterele // se ntind pe o
singur linie.
Putem scrie acum primul program care citete o valoare ntreag de la tastatur i
afieaz ptratul ei.
// program care se citeste o valoare intreaga si calculeaza patratul ei
# include <iostream>
using namespace std;
int main()
{
int i, j;
cout << introduceti o valoare intreaga << endl;
/* se citeste o valoare intreaga */
cin >> i;
cout << valoarea introdusa este << i << endl;
/* se calculeaza patratul valorii citite */
j = i * i;
/* se afisaza valoarea calculata */
cout << patratul valorii este << j << endl;
return 0;
21
}
Menionm c funcia main() returneaz valoarea 0 cu instruciunea
return 0;
Limbajul C++ definete constanta EXIT_SUCCESS ce are valoarea zero. In
consecin, putem scrie
return EXIT_SUCCESS;
Rezultatul rulrii programului este cel de mai jos.
Pentru claritatea programelor, limbaajul definete i constanta EXIT_FAILURE ce
are valoarea unu.
1.13 Execuia unui program
Reamintim etapele de execuie a unui program.
Prima etap este compilarea programului. In aceast etap programul este
verificat pentru erori sintactice. Dac programul nu conine erori sintactice
compilatorul genereaz un program obiect traducnd fiecare instruciune a
programului ntr-o serie de instruciuni elementare ale calculatorului. Fiecare
fiier surs este compilat separat. Din fiecare fiier surs rezult un fiier
obiect.
Etapa a doua este editarea legturilor. In aceast etap sunt ataate
programului funciile din biblioteci. Atunci cnd compilatorul ntlnete un
apel de funcie (de exemplu sin, cos, etc.), sau o operaie intrare/ieire, el
genereaz doar o secven de apel la funcie. Funciile respective sunt
precompilate n biblioteci speciale i programul editor de legturi ataeaz
aceste funcii programului obiect. Editorul de legturi genereaz un program
executabil din toate fiierele obiect. El este un fiier cu extensia exe.
In etapa a treia programul excutabil este ncrcat n memorie i executat.
1.14 Operatorul sizeof
Operatorul sizeof se aplic asupra unei expresii sau asupra unui tip i are ca rezultat
numrul de octei de memorie utilizai. Exist dou forme ale acestui operator
sizeof (tip)
sizeof (expresie)
De exemplu, expresia
sizeof(int)
are ca rezultat valoarea 4, expresia
sizeof (char)
are valoarea 1, etc.
In cazul unui tablou rezultatul este numrul total de octei ocupat de tablou. Fie
instruciunea
double a[5];
Expresia
22
sizeof(a)
are valoarea 40 iar expresia
sizeof(a) / sizeof(a[0])
d numrul elementelor tabloului a.
In cazul cand operandul este o expresie, operatorul sizeof d numrul de octei ocupat
de rezultatul expresiei. Fie de exemplu instruciunile
int x;
double m;
Expresia
sizeof(x + m)
are valoarea 8, deoarece tipul expresiei este double.
Exemplu. Vom scrie un program care s afieze numrul de octei utilizai pentru
memorarea tipurilor fundamentale. Vrem ca rezultatele s fie afiate pe ecran astfel:
Numarul de octeti utilizati
int : ....
char : ....
float : ....
double : .
vectorul float a[10] : .
expresia a[0]+b : ....
Pentru a afia datele deplasate la stnga cu un numr de spaii vom scrie un caracter
tab, \t. Programul este urmtorul :
// dimensiunea tipurilor standard
# include <iostream>
using namespace std;
int main()
{
float a[10], b;
cout << Numarul de octeti utilizati: << endl;
// scrie dimensiunea tipurilor standard
cout << \tint: << sizeof(int) << endl;
cout << \tchar: << sizeof(char) << endl;
cout << \tfloat: << sizeof(float) << endl;
cout << \tdouble: << sizeof(double) << endl;
// scrie dimensiunea unui vector
cout << \tvectorul float a[10]: << sizeof(a) << endl;
// scrie dimensiunea rezultatului unei expresii
cout << \texpresia a[0]+b: << sizeof(a[0]+ b) << \n;
}
Rezultatul rulrii programului este prezentat mai jos.
23
Menionm c este echivalent dac n instruciunea cout utilizm caracterul \n, sau
endl, sau irul de caractere \n. irului de caractere \tint: duce la scrierea
caracterului \t i a irului int: . Instruciunea
cout << \tint: << sizeof(int) << endl;
putea fi scris ca
cout << \t << int: << sizeof(int) << endl;
1.15 Operatorii ++ i - -
Operatorul ++ incrementeaz o variabil ntreag cu valoarea unu, operatorul - -
decrementeaz o variabil ntreag cu unu. Aceti operatori pot fi :
prefix
++x, --x
postfix
x++, x- -
Expresiile ++x i x++ reprezint instruciunea
x = x + 1
iar expresiile - -x i x- - reprezint instruciunea
x = x 1
Cazul operatorilor prefix, ++x, --x
O expresie de forma
f(++x);
este scrierea prescurtat a expresiilor:
x = x + 1;
f(x);
In acelai fel, expresia
f(--x);
reprezint scrierea prescurtat a expresiilor:
x = x 1;
f(x);
Modul de execuie a expresiei este urmtorul:
1. se incrementeaz sau decrementeaz valoarea variabilei x cu unu,
2. valoarea incrementat sau decrementat se utilizeaz mai departe n calcule.
Exemplu. Fie declaraia de variabile
int i = 1, x;
Instruciunea
x = ++i;
reprezint scrierea prescurtat a secvenei de instruciuni
i = i + 1;
x = i;
Dup execuia instruciunii x = ++i variabilele au valorile i = 2 i x = 2.
Exemplu. Fie declaraia de variabile
int j = 3, k;
Instruciunea
k = --j;
reprezint scrierea prescurtat a secvenei de instruciuni
j = j 1;
k = j;
Dup execuia instruciunii variabilele au valorile j = 2 i k = 2.
Exemplu. Fie secventa de instruciuni
24
int i = 1;
cout << i = << i << endl;
cout << i = << ++i << endl;
cout << i = << i << endl;
Valorile afiate vor fi
i = 1
i = 2
i = 2
deoarece a doua instructiune cout este echivalent cu instruciunile
cout << i = ;
i = i + 1;
cout << i << endl;
Exemplu. Fie urmtoarele declaraii.
double a[4] = {1.2, -5, 8, 3};
double r;
int i = 1, k = 1;
Secvena de instruciuni
i = i +1;
r = a[i];
se poate scrie
r = a[++i];
Expresia ++i este evaluat la 2, iar r primete valoarea a[2] = 8.
Secvena de instruciuni
k = k 1;
r = a[k];
se poate scrie
r = a[--k];
Expresia --k este evaluat la 0 iar r primete valoarea a[0] = 1.2
Cazul operatorilor postfix, x++, x--
O expresia de forma
f(x++);
reprezint scrierea prescurtat a expresiilor
f(x);
x = x + 1;
In acelai mod, expresia
f(x--);
reprezint scrierea prescurtat a expresiilor:
f(x);
x = x 1;
Modul de execuie a expresiei este urmtorul :
1. se utilizeaz n calcule valoarea variabilei x (neincrementate sau
nedecrementate),
2. se incrementeaz / decrementeaz apoi variabila x.
Exemplu. Fie cele dou secvene de instruciuni de mai jos :
int a, b;
b = 3;
a = b++;
Expresia
a = b++;
corespunde secvenei de instruciuni :
25
a = b;
b = b + 1;
In consecin, expresia b++ are valoarea 3 (valoarea neincrementat a variabilei) i
apoi se incrementeaz b. Avem rezultatul
a = 3
b = 4
Exemplu. Fie instructiunile :
int j = 1;
cout << j = << j << endl;
cout << j = << j++ << endl;
cout << j = << j << endl;
Valorile afiate vor fi :
j = 1
j = 1
j = 2
deoarece a doua instruciune cout este echivalent cu instruciunile :
cout << j = << j;
j = j + 1;
cout << endl;
Exemplu. Fie secvena de instruciuni de mai jos :
int i;
double x[4] = {2.4e1, 14.4, -3.1 0};
double d;
i = 1;
d = x[i++];
Instruciunea
d = x[i++];
corespunde secvenei de instruciuni :
d = x[i];
i = i + 1;
Expresia i++ are valoarea 1 i apoi se incrementeaz i. Avem deci
d = 14.4
i
i = 2
Fie acum secvena de instruciuni urmtoare :
int j;
double x[4] = {2.4e1, 14.4, -3.1 0};
double d;
j = 1;
d = x[++j];
Instruciunea
d = x[++j];
corespunde secvenei de instruciuni :
j = j + 1;
d = x[j];
In consecin expresia ++j are valoarea 2 i avem
d = -3.1
i
j = 2
26
1.16 Operaii cu numere ntregi la nivel de bit
1.16.1 Operatori de deplasare
Deplasarea la stnga a unui ntreg cu un bit reprezint nmulirea acelui numr cu doi.
Deplasarea la dreapta a unui ntreg cu un bit reprezint ctul mpririi acelui numr
cu doi. De exemplu, prin deplasarea numrului 7 la dreapta cu un bit se obine
rezultatul 3. Operatorii de deplasare a unui numr ntreg cu un numr de bii sunt <<
pentru deplasare la stnga i >> pentru deplasare la dreapta. Expresia de deplasare a
unui numr ntreg are forma
Rezultatul expresiei din stnga este numrul ntreg ce va fi deplasat. Expresia din
dreapta d numrul de bii cu care se face deplasarea. Deplasarea se face dup regulile
deplasrii numerelor binare cu semn, vezi anexa. (La deplasarea la dreapta se propag
bitul de semn, la deplasarea la stnga se adaug zerouri). Dac expresia de deplasat
este de tipul unsigned biii adugai sunt zerouri. Operatorii de deplasare, << i >>
sunt operatori aritmetici.
Exemple. Fie instruciunea
int x = 7 >> 1;
Variabila x primete valoarea 3 (ctul npririi lui 7 la 2 este 3).
Fie instruciunile de mai jos
int a = 0xff; int y = 0xff;
int b; int c;
b = a << 4; c = y >> 4;
Numrul a, reprezentat pe patru octei, are valoarea hexazecimal
a = 000000ff
Numrul a deplasat la stnga cu 4 bii este
00000ff0
Numrul y deplasat la dreapta cu 4 bii este
0000000f
Numrul 0xff convertit n zecimal este 255. Care este valoarea numerelor 0xf i 0xff0
n zecimal?
Fie dou variabile ntregi, i si j i secvena de instruciuni :
i = 7;
j = i >> 1;
Variabila j va primi valoarea 3. Reamintim c 7 / 2 = 3.
Ca un alt exemplu, s definim constantele X, Y, Z i R care s aibe valorile 1, 2, 4, 8,
folosind instruciunea enum.
enum {X = 1, Y = X << 1, Z = Y << 1, R = X << 3};
Limbajele C i C++ au operatorii de atribuire <<= i >>= pentru scrierea prescurtat a
instruc iunilor ce conin operatorii << i >>. Instruciunea
x = x << n ;
se scrie prescurtat
x <<= n ;
iar instruciunea
x = x >> n ;
se scrie prescurtat
27
x >>= n ;
Vom ncheia acest paragraf cu un program care deplaseaz numere ntregi i afiaz
valoarea lor n zecimal i hexazecimal. Reamintim c, pentru scrierea sau citirea unui
numr ntreg n instruciunile cout sau cin n diferite baze, se utilizeaz manipulatorii :
hex pentru baza 16
dec pentru baza 10
oct pentru baza 8
La nceperea execuiei unui program baza 10 este implicit.
Vom exemplifica utilizarea operatorilor de deplasare deplasnd dou variabile ntregi
la stnga i la dreapta cu un bit i apoi cu trei bii i vom afia rezultatele n bazele 10
i 16.
# include <iostream>
using namespace std;
/* uitlizarea operatorilor de deplasare */
int main()
{
int x = 10;
cout << x = << dec << x << << hex << showbase << x << endl;
// deplaseaz variabila a la stanga cu un bit
x = x << 1;
cout << x deplasat la stanga cu 1 bit =
<< dec << x << << hex << showbase << x << endl;
int b = 50;
cout << b = << dec << b << << hex << showbase << b << endl;
// deplaseaz variabila b la dreapta cu 3 pozitii
b = b >> 3;
cout << b deplasat la dreapta cu 3 biti =
<< dec << b << << hex << showbase << b << endl;
return 0;
}
Rezultatele rulrii programului sunt prezentate n figura de mai jos.
1.16.2 Operaii logice la nivel de bit
Limbajele C i C++ au urmtorii operatori pentru operaii logice la nivel de bit
i logic (and) notat &
sau logic (or) notat |
sau excusiv (xor) notat ^
complement fa de unu notat ~
Aceti operatori se definesc cu tabelele de adevr urmtoare
28
a b a&b a|b a^b
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0
a ~a
0 1
1 0

Operatorii &, | ^ sunt operatori binari. Expresiile cu aceti operatori logici au forma
urmtoare
Operaiile sunt aplicate asupra fiecrei perechi de bii din cei doi operanzi. Operatorul
~ este operator unar. Expresiile cu acest operator au forma urmtoare
Operatorul ~ complementeaz fiecare bit din expresia ntreag. Operatorii &, | i ~
sunt operatori aritmetici.
Exemple. Fie urmtoarea secven de program :
int a, b, c, d, e;
a = 0xf000;
b = 0xabcd;
c = a & b;
d = a | b;
e = a ^ b;
Rezultatele sunt
c = 0xa000
d = 0xfbcd
e = 0x5bcd
In primul caz, la calculul valorii c = a & b, vom exemplifica doar calculul expresiei
0xf & 0xa = 0xa
Rezultatul se obine conform calculului de mai jos.
0 1 0 1
0 1 0 1
& 1 1 1 1
Pentru calculul valorii e = a ^ b vom calcula expresia
0xf ^ 0xa = 0x5
Rezultatul se obine conform calculului de mai jos
1 0 1 0
0 1 0 1
^ 1 1 1 1
Pentru calculul valorii d = a | b vom calcula expresia
0xf | 0xa = 0xf
29
Rezultatul se obine conform calculului de mai jos
1 1 1 1 |
1 0 1 0
------------------
1 1 1 1
Expresia ~a are valoarea 0x0fff. De ce?
Limbajele C i C++ au operatorii &=, |= i ^= pentru scrierea prescurtat a
instruciunilor de atribuire ce conin operatori &, | i ^. De exemplu expresia
x = x & a ;
Se scrie prescurtat
x &= a ;
Pentru a selecta sau modifica anumii bii dintr-un numr ntreg se definete un ir de
bii numit masc ce conine 1 pe poziiile biilor ce trebuie selectai sau modificai.
Pentru selectarea biilor se efectueaz operaia & ntre numrul iniial i
masc. De exemplu, pentru a selecta biii unei cifre hexazecimale vom utiliza
ca masc valorile 0x1, 0x2, 0x4 i 0x8. Pentru a selecta cifrele octale dintr-un
numr ntreg vom utiliza ca masc valorile 07, 070, 0700, etc. Pentru a selecta
cifrele hexazecimale dintr-un numr ntreg vom utiliza ca masc valorile 0xf,
0xf0, 0xf00, etc.
Pentru inversarea biilor se efectueaz operaia ^ ntre numr i masc.
Pentru a pune biii din numr la valoarea 1 se execut operaia | ntre numr i
masc.
Pentru a pune biii din numr la valoarea 0 se execu operaia & ntre numr
i ~masc.
Exemplu. Fie variabila ntreag a = 0x6dbc. Vrem ca n variabila ntreag b s
selectm ultimul octet, apoi ultimii 10 bii i n final primii 4 bii ai variabilei a.
Inversarea unor bii din variabila a este prezentat n finalul exemplului.
// operatii logice la nivel de bit
# include <iostream>
using namespace std;
int main()
{
int a = 0x6dbc, b;
cout << a = << hex << showbase << a << endl;
// selecteaza ultimul octet din a
b = a & 0xff;
cout << ultimul octet din a = << hex << showbase << b << endl;
b = a & 0x3ff;
// selecteaza ultimii 10 biti din a
cout << ultimii 10 biti din a = << hex << showbase << b << endl;
// selecteaz primii patru biti din a
b = a & 0xf000;
cout << primii 4 biti din a = << hex << showbase << b << endl;
// pastram primii opt biti si inversam ultimii opt biti din a
b = a ^ 0x00ff;
cout << ultimii 8 biti inversati din a = << hex << showbase << b << endl;
return 0;
}
30
Rezultatul rulrii programului este cel de mai jos.
S se explice de ce, n cazul al doilea, masca este 0x3ff;
31
2 Structuri de control fundamentale
2.1 Algoritme
Programele reprezint formulri concrete ale unor algoritme ce prelucreaz structuri
de date. Un algoritm este format dintr-un ir finit de aciuni bine definite i neambigue
pentru rezolvarea unei probleme. Fiecare aciune trebuie s poat fi executat ntr-un
interval finit de timp.
Orice algoritm se poate descompune ntr-un numr finit de etape. El poate fi
reprezentat printr-un ir de aciuni astfel nct efectul general al acestor aciuni s
conduc la rezultatul dorit al calculului.
In cazul cel mai simplu un algoritm poate fi descompus ntr-un numr de aciuni
secveniale care se reprezint n felul urmtor
s1; s2; sn;
Pentru executarea unei aciuni n funcie de ndeplinirea unei condiii avem operatori
condiionali
Operatorul if cu formele
if (condiie)
s1;
sau
if (condiie)
s1;
else
s2;
condiie este o expresie boolean care are valoarea advrat sau fals. Aciunea
s1 se execut cnd condiie are valoarea adevrat.
Operatorul switch este generalizarea operatorului if
switch(i)
i = 1: s1;
i = 2: s2;
..
i = n: sn;
In funcie de valoarea lui i se execut una dintre aciunile s1, s2, , sn.
Aceti operatori se caracterizeaz prin faptul c au o singur intrare i o singur ieire.
Fiecare operator este interpretat n irul de calcule ca o singur aciune, indiferent de
coninutul su. Operatorii anteriori sunt suficieni pentru a descrie clasele de calcule
ce se pot descompune ntr-un numr cunoscut de aciuni.
Pentru a descrie calculele repetate cnd numrul de aciuni nu este cunoscut exist
operatorii while i do.
Operatorul while are forma
while (condiie)
s;
Aciunea s se execut atta timp ct expresia boolean condiie are valoarea
adevrat.
Operatorul do are forma
do
s;
while (condiie)
In acest caz aciunea s se execut atta timp ct condiie are valoarea adevrat.
32
Menionm c n cazul operatorului do aciunea s se execut cel puin o dat,
n timp ce pentru operatorul while este posibil ca aciunea s s nu se execute
niciodat.
Operatorul for se utilizeaz atunci cnd numrul de execuii ale unei aciuni
este dinainte cunoscut. Operatorul are o variabil de control ce se poate
modifica la fiecare iteraie. Forma acestui operator este
for (i = instruciune1; condiie; instruciune2)
s;
Operatorul include o instruciune1 ce specific valoarea iniial a variabilei
de control, i o instruciune2 ce poate modifica valoarea variabilei de control
dup fiecare iteraie. Aciunea s se execut att timp ct expresia boolean
condiie are valoarea adevrat. Operatorul for este echivalent cu urmtoarele
instruciuni
instruciune1;
while(condiie)
s;
instruciune2;
Menionm c testul condiiei se face nainte de execuia aciunii s.
In toi operatorii de mai sus, aciunea este o instruciune simpl sau o
instruciune compus (un bloc). In program, o instruciune compus (un bloc),
este un grup de instruciuni inclus ntre acolade, { i }. Intr-un bloc putem defini
variabile locale, al cror domeniu de existen este limitat la acel bloc.
Variabilele locale sunt create la intrarea n bloc i sunt terse la ieirea din bloc.
Operatorii prezentai se numesc structuri de control fundamentale. In construcia unui
program structurile de control i structurile de date sunt inseparabile. Structurile
fundamentale de date sunt
structuri simple: numere ntregi, reale, caractere.
structuri complexe: tablouri, fiiere.
Tablourile au un numr cunoscut de elemente. Ele se prelucreaz de regul cu
operatorul for. Fiierele secveniale au un numr de elemente necunoscut n avans. Ele
se prelucreaz de regul cu operatorul while.
Exemplu. Algoritmul de calcul pentru n! care este definit ca

n
i
i n
1
!
s = 1;
for i = 1; i <= n; i = i + 1
s = s * i;
2.2 Expresii relaionale
O operaie definit pentru tipurile de date fundamentale este compararea.
Operatorii relaionali ai limbajelor C i C++ sunt, n ordinea prioritilor
<, <=, >, >=
= =, !=
O expresie relaional are urmtoarea form
33
Rezultatul evalurii unei expresii relaionale este fals sau adevrat (false sau true).
Prioritile operatorilor aritmetici sunt mai mari dect ale operatorilor relaionali.
Exemple. Fie urmtoarele instruciuni de atribuire
int a = 2, b = 3, c = 6;
In tabela de mai jos sunt prezentate exemple de expresii relaionale i rezultatul
evalurii lor.
Expresie relaional Valoare
a*b >= c true
b+2 > a *c false
a+b = = 3 false
a != b true
b/a = = 1 true
Reamintim c n limbajul C++ valoarea true este reprezentat prin 1 iar valoarea false
prin 0. La scrierea valorii unei variabile booleene valoarea afiat este 0 sau 1, dup
cum variabila are valoarea false, respectiv true. Pentru afiarea valorii booleene ca
true, respectiv false se utilizeaz manipulatorul boolalpha.
Fie urmtoarea instruciune de declarare a unei variabile de tip bool
bool r;
Putem avea urmtoarele instruciuni de atribuire
r = a + b = = c;
r = a b >= 2;
r = a * a != -b;
Variabila r poate avea valorile true sau false.
Un exemplu de program este cel de mai jos.
#include <iostream>
using namespace std;
int main()
{
int a = 3, b = -2, c = 5;
bool r;
cout << " a = " << a << " b = " << b << " c = " << c << endl;
r = a + b == c;
cout << "a + b == c " << boolalpha << r << endl;
r = a - b >= 2;
cout << "a - b >= 2 " << boolalpha << r << endl;
r = a * a != - b;
cout << "a * a != - b " << boolalpha << r << endl;
return EXIT_SUCCESS;
}
34
Rezultatul rulrii programului este prezentat n continuare.
In final reamintim c operatorul = este operator de atribuire, iar operatorul = = este
operator de comparare. Instruciunea
a = b
atribuie variabilei a valoarea variabilei b, iar expresia
a = = b
este o expresia relaional care are ca rezultat valoarea true sau valoarea false.
2.3 Expresii booleene
Operatorii booleeni ai limbajelor C i C++ sunt, n ordinea prioritilor
!
&&
| |
care reprezint operatorii nu (not), i (and), respectiv sau (or). Operatorul nu este
operator unar. Aceti operatori se definesc folosind tabelele de adevr.
x y x && y x | | y
false false false false
false true false true
true false false true
true true false true
x !x
false true
true false
Rezultatul evalurii unei expresii booleene este true sau false. In cursurile de logic
operatorii booleeni se noteaz astfel:
nu (not)
i (and)
sau (or)
Exemple de expresii booleene i scrierea lor sunt prezentate mai jos :
) || &( & ) (
||! !
|| & &
) || ( ! ) (
& &
c b a c b a
b a b a
c b a c b a
b a b a
b a b a



Pentru scrierea simpl a expresiilor booleene sunt importante dou teoreme


numite legile lui DeMorgan
35
b a b a
b a b a


) (
) (
care se demonstreaz cu ajutorul tabelelor de adevr.
In final menionm c operatorii booleeni and i or sunt
comutativi
a b b a
a b b a


asociativi la stnga
c b a c b a
c b a c b a


) (
) (
distributivi
) ( ) ( ) (
) ( ) ( ) (
c a b a c b a
c a b a c b a


In final prezentm prioritatea (precedena) i asociativitatea operatorilor
Operator Asociativitate
[ ] ( ) stnga
++ -- + - ! ~ sizeof (tip) dreapta
* / % stnga
+ - stnga
<< >> stnga
< <= > >= stnga
= = != stnga
& stnga
^ stnga
| stnga
&& stnga
| | stnga
= += -= *= /= %= <<= >>= &= |= ^= stnga
2.4 Operatorul if
Acest operator execut o anumit instruciune n funcie dac o anumit condiie este
ndeplinit sau nu. Forma operatorului if este
if(condiie)
S1;
else
S2;
Modul de execuie al operatorului if este urmtorul:
1. se evalueaz condiia,
2. dac valoarea condiiei este diferit de zero se execut instruciunea S1 altfel
instruciunea S2.
Operatorul if poate avea i o form simplificat
if(expresie)
S;
n care se execut instruciunea S cnd condiia are valoare diferit de zero.
36
Ca prim exemplu vom scrie un program care s testeze dac un ntreg este divizibil cu
altul. Cei doi ntregi se citesc de la tastatur. Pentru a testa dac un ntreg n este
divizibil cu un alt ntreg d, vom calcula expresia n % d, restul mpririi lui n la d.
# include <iostream>
using namespace std;
int main()
{
int n, d;
cout << introduceti doi intregi : ;
cin >> n >> d;
if(n % d == 0)
cout << n << este divizibil cu << d << endl;
else
cout << n << nu este divizibil cu << d << endl;
return 0;
}
Un exemplu de execuie a programului este dat mai jos.
Un alt exemplu este calculul maximului a dou numere ntregi citite de la tastatur.
// calculul maximului a doua numere intregi
# include <iostream>
using namespace std;
int main()
{
int n, m;
cout << introduceti doi intregi : << endl;
cin >> n >> m;
cout << maximul dintre << m << si << n << este ;
if(n > m)
cout << n << endl;
else
cout << m << endl;
return 0;
}
Un exemplu de rulare a programului este prezentat mai jos.
37
Instruciunile din expresia operatorului if pot fi instruciuni simple sau
instruciuni compuse. O instruciune compus, (un bloc), este un grup de
instruciuni cuprinse ntre acolade, { i }.
Exemplu. Ordonarea descresctoare a dou numere ntregi citite de la tastatur.

// ordonarea descrescatoare a dou numere intregi citite de la tastatura
# include <iostream>
using namespace std;
int main()
{
int x, y;
cout << introduceti doi intregi : ;
cin >> x >> y;
if(x < y)
{
int temp = x;
x = y;
y = temp;
}
cout << numerele ordonate descrescator : << x << , << y << endl;
return 0;
}
Un exemplu de execuie a programului este cel de mai jos.
In acest exemplu, instruciunile ce permut variabilele x i y constituie un bloc,
executat cnd x < y. Blocul definete variabila local temp.
Menionm c o instruciune if poate conine alte instruciuni if. De exemplu, putem
avea instruciunea compus
if (e1)
if (e2)
s1;
else
s2;
else
if(e3)
s3;
else
s4;
unde e1, e2, e3 sunt expresii booleene. Dac expresia boolean e1 are valoarea
adevrat, (este diferit se zero), se execut instruciunea if(e2), n caz contrar se
execut instruciunea if(e3).
38
2.5 Operatorul switch
Acest operator execut o instruciune din mai multe posibile. Fiecare instruciune este
etichetat cu o constant ntreag. Instruciunea conine o expresie ntreag ce
determin ce etichet trebuie aleas. Forma operatorului switch este urmtoarea
switch(expresie)
{
case expint: instructiune
case expint: instructiune
.
case expint: instructiune
}
Una dintre etichete poate fi default. In acest caz, dac expresia din instruciunea
switch nu are nici una din valorile etichetelor din instruciunile case, se trece la
execuia instruciunilor cu eticheta default.
Pentru ieirea din instruciunea switch se utilizeaz instruciunea break.
Exemplu. Vom citi dou numere ntregi n dou variabile x i y i un operator +, -, *, /
sau % ntr-o variabil tip char i vom calcula rezultatul operaiei corespunztoare.
Programul este urmtorul.
// calculator cu numere intregi
#include <iostream>
using namespace std;
int main()
{
int x, y;
char oper;
cout << intoduceti doi intregi : ;
cin >> x >> y;
cout << introduceti un operator : ;
cin >> oper;
switch(oper)
{
case + :
cout << x << oper << y << "=" << x + y;
break;
case - :
cout << x << oper << y << "=" << x y;
break;
case * :
cout << x << oper << y << "=" << x * y;
break;
case / :
cout << x << oper << y << "=" << x / y;
break;
case % :
cout << x << oper << y << "=" << x % y;
break;
default :
39
cout << eroare;
break;
}
cout << endl;
return 0;
}
Menionm c instruciunile corespunztoare etichetelor sunt urmate de instruciunea
break ce asigur ieirea din operatorul switch. Rezultatul rulrii programului este cel
de mai jos.
2.6 Operatorul ?
Acest operator este o form simplificat a operatorului if. Forma lui este
expresie ? S1 : S2;
Dac expresie are o valoare diferit de zero, se execut instruciunea S1, altfel
instruciunea S2.
Vom rezolva din nou problema divizibilitii a dou numere.
# include <iostream>
using namespace std;
int main()
{
int k, m;
cout << intoduceti doi intregi : ;
cin >> k >> m;
cout << k << ((k % m)? " nu ": "") << " este divizibil cu " << m
<< endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Instruciunea
((k % m)? " nu ": "")
are ca rezultat irul de caractere nu cnd k nu divide m sau irul n caz contrar.
2.7 Operatorul while
Operatorul while execut repetat un grup de instruciuni. Forma operatorului while
este
while (condiie)
S;
40
instruciunea S se execut atta timp ct condiia este diferit de zero (are valoarea
adevrat). Instruciunea S poate fi o instruciune simpl sau o instruciune
compus, (un bloc), format dintr-un grup de instruciuni, cuprinse ntre
acolade, { i }.
Vom exemplifica utilizarea acestui operator calculnd suma elementelor unui vector a
cu patru componente. Elementele vectorului a au indicii 0, 1, 2 i 3.

3
0 i
i
a s
Vom utiliza o variabil f de tip float pentru a calcula suma, care va fi pus iniial la
valoarea zero i vom executa repetat instruciunea
i
a s s +
pentru i lund valori de la 0 la 3. Programul pseudocod este urmtorul.
s = 0;
i = 0;
while(i < 4)
s = s + a[i];
i = i + 1;
Programul este urmtorul
#include <iostream>
using namespace std;
/* calculul sumei componentelor unui vector */
int main()
{
float a[4] = {2.34, -7.32, 2.5e-1, 73};
int i;
float s;
s = 0;
i = 0;
while(i < 4)
{
s = s + a[i];
i = i + 1;
}
cout << suma componentelor este << s << endl;
return 0;
}
Instruciunile
s = s + a[i];
i = i + 1;
constituie un bloc ce este executat repetat, ct timp i < 4.
Putem rescrie partea de calcul din program astfel
s = 0;
i = 0;
while(i < 4)
{
s += a[i];
41
i++;
}
sau
s = 0;
i = 0;
while(i < 4)
{
s += a[i++];
}
In urmtorul exemplu vom calcula media unui ir de numere reale citite de la
tastatur. Fie n variabila ntreag n care vom citi lungimea irului de numere, i o
variabil n care numrm valorile citite i x o variabil n care citim cte un numr.
Variabila suma va conine suma numerelor deja citite. Programul pseudocod este
urmtorul.
i = 1;
suma = 0;
read n;
while (i <= n)
{
read x;
suma = suma + x;
i = i + 1;
}
media = suma / n;
write media;
Programul corespunztor este urmtorul
// calculul mediei unui sir de numere reale citite de la tastatura
# include <iostream>
using namespace std;
int main()
{
int n, i = 1;
double suma = 0, media, x;
cout << "cate numere ? ";
cin >> n;
// calculul sumelor partiale
while(i <= n)
{
cin >> x;
suma = suma + x;
i = i + 1;
}
// calculul mediei
media = suma / n;
42
cout << "media este : " << media << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
2.8 Operatorul do-while
Operatorul do-while este tot un operator repetitiv. Acest operator are forma
do
S
while (condiie);
Instruciunea S se execut atta timp ct condiia este diferit de zero (are valoarea
adevrat). Instruciunea S poate fi o instruciune simpl sau o instruciune
compus, (un bloc), format dintr-un ir de instruciuni ntre acolade, { i }.
Exemplu. Vom calcula valoarea 5! care se definete ca

5
1
! 5
i
i
Vom utiliza o variabil n pus iniial la valoarea 1 i vom executa repetat
instruciunea
n = n * i
pentru i lund valori de la 1 la 5. Programul pseudocod este urmtorul.
n = 1;
i = 1
do
n = n * i;
i = i + 1;
while (i < 6)
Programul corespunztor este urmtorul
# include <iostream>
using namespace std;
/* calculul valorii 5! */
int main()
{
int i, n;
i = 1;
n = 1;
do
43
{
n = n * i;
i = i + 1;
}
while(i < 6);
cout << 5! are valoarea << n << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Menionm c, deoarece dorim s executm repetat mai multe instruciuni cu
instruciunea do-while
n = n * i;
i = i + 1;
scriem aceste instruciuni ca o instruciune compus, ntre acolade, { i }.
Putem rescrie partea de calcul din program astfel
int i = 1, n = 1;
do
{
n *= i;
i++;
}
while(i < 6);
sau
int i = 1, n = 1;
do
n *=i++;
while(i < 6);
Reamintim c instruciunea
n *= i++;
este echivalent cu instruciunile
n *= i;
i++;
conform modului de execuie a operatorului postfix ++ : valoarea expresiei este chiar
valoarea variabilei i neincrementate, dup care variabila i este incrementat.
2.9 Operatorul for
Operatorul for permite execuia repetat a unui grup de instruciuni cu modificarea
unei variabile de control dup fiecare iteraie. Operatorul for are forma
for(expresie1; condiie; expresie3)
44
S
expresie1 are rolul de a iniializa variabila de control. Operatorul for execut repetat
instruciunea S, att timp ct condiie este diferit de zero. expresie3 are rolul de a
modifica valoarea variabilei de control dup fiecare execuie. Instruciunea S,
executat repetat, poate fi o instruciune simpl sau o instruciune compus, (un
bloc), ce const dintr-un grup de instruciuni ntre acolade, { i }.
Exemplu. Vom calcula valoarea expresiei x x e
x
+ +2 pentru x cuprins ntre 1 i 2
cu pasul 0.2. O prim variant de program pseudocod este urmtorul
for (i = 0; i < 6; i = i + 1)
{
x = 1 + 0.2 * i
y = x x e
x
+ +2
}
Vrem ca rezultatul s fie afiat n dou coloane cu antetul x i y, ca mai jos.
x y
. .
. .
Programul este urmtorul
# include <iostream>
# include <cmath>
# include <iomanip>
using namespace std;
int main()
{
int i;
double x, y;
cout << setw(4) << "x" << '\t' << setw(5) << "y" << endl;
for(i = 0; i < 6; i = i + 1)
{
x = 1 + 0.2 * i;
y = exp(x) + 2 * x + sqrt(x);
cout << setw(4) << x << '\t' << setw(5) << y << endl;
}
return 0;
}
Pentru a scrie antetul tabelului cu valori am utilizat instruciunea
cout << setw(4) << "x" << '\t' << setw(5) << "y" << endl;
care scrie irurile de caractere x i y pe cte 4, respectiv 5 coloane, separate de
caracterul \t (tab). In program rezultatele sunt scrise de asemenea separate de tab.
Pentru a prescrie numrul de coloane al unui cmp se utilizeaz funcia setw() din
biblioteca <iomanip>. Rezultatul rulrii programului este cel de mai jos.
45
Instruciunile dintre acolade formeaz instruciunea compus executat repetat de
instruciunea for.
Menionm c instruciunea for se putea scrie astfel
for(i = 0; i < 6; i++)
sau
for(i = 0; i < 6; ++i)
Este posibil s definim variabila de control chiar n instruciunea for, n
expresie1 din definiia instruciunii for. De exemplu, n loc de instruciunile
int i;
for (i = 0; i < 6; i++)
putem scrie
for(int i = 0; i < 6; i++)
Variabilele x i y sunt utilizate doar n instruciunea compus executat de for. Ele pot
fi definite n interiorul acestei instruciuni compuse (bloc). In acest fel programul este
mai clar.
int main()
{
cout << setw(4) << "x" << '\t' << setw(5) << "y" << endl;
for(int i = 0; i < 6; i = i + 1)
{
double x, y;
x = 1 + 0.2 * i;
y = exp(x) + 2 * x + sqrt(x);
cout << setw(4) << x << '\t' << setw(5) << y << endl;
}
return 0;
}
Reamintim c, variabilele definite ntr-un bloc exist doar n acel bloc. Ele sunt
create la intrarea n bloc i terse la ieirea din bloc.
Un alt mod de a calcula valorile expresiei de mai sus este de a utiliza instruciunea
for(x = 1; x <= 2; x = x + 0.2)
care modific pe x de la 1 la 2 cu pasul 0.2. Programul este urmtorul
# include <iostream>
# include <cmath>
# include <iomanip>
using namespace std;
int main()
{
double x;
46
cout << setw(4) << "x" << '\t' << setw(5) << "y" << endl;
for(x = 1; x <= 2; x = x + 0.2)
{
double y;
y = exp(x) + 2 * x + sqrt(x);
cout << setw(4) << x << '\t' << setw(5) << y << endl;
}
return 0;
}
In loc de instruciunile
double x;
for (x = 1; x <= 2; x = x + 0.2)
putem scrie
for (double x = 1; x <= 2; x = x + 0.2)
In acest caz, variabila de control definit n instruciunea for exist doar n interiorul
instruciunii for. Ea poate fi utilizat doar n instruciunile executate repetat de for.
Exemplul urmtor calculeaz suma componentelor pare i impare ale unui vector x cu
ase elemente. Programul pseudocod pentru calculul sumelor este urmtorul
s1 = 0;
s2 = 0;
for( i = 0; i < 6; i=i+1)
if (i % 2 = = 0)
s1 = s1 +
i
x
else
s2 = s2 +
i
x
Elementul
i
x
este adunat la suma componentelor pare s1 sau impare s2 dup cum
expresia i % 2 are valoarea zero sau nu.
Programul este urmtorul.
#include <iostream>
using namespace std;
/* calculul sumei componentelor pare si impare ale unui vector */
int main()
{
float s1 = 0, s2 = 0;
float x[6] ;
cout << "Dati componentele vectorului"<< endl;
for(int i = 0; i < 6; i = i + 1)
{
cin >> x[i];
if(i % 2 == 0)
s1 = s1 + x[i];
else
s2 = s2 + x[i];
}
cout << suma elementelor pare este << s1 << endl
<< suma elementelor impare este << s2 << endl;
47
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
In acest program, variabila i poate fi utilizat doar n instruciunile executate repetat
de for, ntre { i }.
Vom ncheia acest paragraf cu un program care s calculeze suma a doi vectori cu
cte trei componente reale fiecare, iniializai la definirea lor n program.
/* calculul sumei a doi vectori */
# include <iostream.h>
int main()
{
float x[3] = {1.1e+1, -2.57, 13.2};
float y[3] = {-2.12, 13.5, 3.41}, float z[3];
for(int i = 0; i < 3; i++)
z[i] = x[i] + y[i];
for(int i = 0; i < 3; i++)
cout << z[i] << \t;
cout << \n;
return 0;
}
In final, menionm instruciunile break i continue. Instruciunea break produce
ieirea imediat din instruciunile for, while, do-while sau switch. Instruciunea
continue trece la urmtoarea iteraie a instruciunii for, while sau do-while.
Exemplu. Vom citi n numere reale de la tastatur i vom calcula media celor pozitive.
La fiecare iteraie a instruciunii for, vom citi cte un numr i vom testa dac este
negativ. Dac da, vom trece la urmtoarea iteraie cu instruciunea continue, n caz
contrar vom prelucra numrul citit.

#include <iostream>
using namespace std;
int main()
{
int n, m = 0, cnt;
float x, sum = 0, media;
cout <<"media numerelor pozitive" << endl;
cout << "cate numere ? ";
cin >> n;
48
for(cnt = 1; cnt <= n; ++cnt)
{
cout << "dati un numar : ";
cin >> x;
if(x < 0)
continue;
sum = sum + x;
m = m + 1;
}
media = sum / m;
cout << "media = " << media << endl;
return 0;
}
Rezultatul rulrii programului este prezentat mai jos.
2.10 Operatorul ,
Operatorul , are urmtoarea form
expresie1, expresie2
Rezultatul operatorului este expresie2, rezultatul evalurii expresie1 se neglijaz.
Operatorul , se poate utiliza oriunde n mod normal se utilizeaz o singur expresie,
dar sunt necesare dou expresii. Un exemplu este instruciunea for, cnd este necesar
s iniializm dou expresii, exp1 i exp2.
for(exp1, exp2; condiie; exp3)
Exemplu. Vom testa dac un cuvnt este palindrom (este acelai cuvnt citit n ambele
sensuri). In program vom citi un ir de caractere ntr-un vector de caractere str. Fie n
numrul de caractere citite. Vom compara literele din prima jumtate a cuvntului cu
cele din a doua jumtate, utiliznd dou variabile, una ce crete de la zero la (n - 1) /
2 , iar alta ce scade de la (n 1). Vom iniializa cele dou variabile n instruciunea for
cu operatorul ,. Pentru a calcula lungimea unui ir de caractere vom utilize funcia
strlen() cu prototipul
int strlen(char[]);
din biblioteca <cstring> . Programul este urmtorul:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char str[80];
int cntup, cntdn, n, nst;
bool pn = true;
49
cout << "introduceti un cuvant " << endl;
cin >> str;
n = strlen(str);
nst = (n - 1) / 2;
// initializarea a doua variabile cu operatorul ,
for(cntup = 0, cntdn = n - 1; cntup <= nst; cntup++, cntdn--)
if(str[cntup] != str[cntdn])
pn = false;
if(pn)
cout << str << " este palindrom" << endl;
else
cout << str << " nu este palindrom" << endl;
return 0;
}
Rezultatul rulrii programului este prezentat mai jos.
50
3 Funcii
Limbajele de programare au funcii predefinite ce corespund funciile matematice
uzuale : sin, cos, exp, log, log10, etc.
In plus, limbajele de programare permit definirea de funcii care s realizeze anumite
calcule. Atunci cnd un grup de instruciuni se utilizeaz n mai multe locuri din
program, aceste instruciuni se plaseaz ntr-o funcie care este apelat unde este
necesar. In acest fel programele mari pot fi formate din module. Descompunerea
unui program n module are avantaje. Funciile pot fi grupate n biblioteci i utilizate
n diverse programe. Funciile pot fi testate separat i programele sunt mai clare.
Un program const din una sau mai multe funcii. Una dintre funcii are numele main
i execuia programului ncepe cu aceast funcie.
3.1 Funcii standard
Limbajele C i C++ au multe funcii standard, matematice, de prelucrare a
caracterelor, etc. Mai nainte am prezentat cele mai utile funcii matematice. Vom
prezenta funciile de manipulare a caracterelor.
3.1.1 Funcii C standard de manipulare a caracterelor
Funciile ce manipuleaz caractere tip ASCII sunt un exemplu de funcii standard ale
limbajelor C i C++. In cazul programelor n limbajul C++, prototipurile acestor
funcii sunt definite n bibliotecile <cstdlib>, <cctype> i <cstring>, n cazul
limbajului C prototipurile acestor funcii sunt definite n bibliotecile <stdlib.h>,
<ctype.h> i <string.h>.
Funciile urmtoare testeaz tipul unui caracter. Prototipurile lor se afl n bibliotecile
<cctype> i <ctype.h>.
Funcie Descriere
int isalnum (int c); Test dac un caracter este alfanumeric
int isalpha (int c); Test dac un caracter este alfabetic
int isdigit (int c); Test dac un caracter este o cifr zecimal
int isxdigit(int c); Test dac un caracter este o cifr hexazecimal
int islower (int c); Test dac un caracter este liter mic
int isupper (int c); Test dac un caracter este liter mare
int isspace (int c); Test dac un caracter este spaiu ( , \n, \t)
Funciile au un rezultat diferit de zero dac argumentul este conform descrierii
funciei. Urmtoarele funcii convertesc literele mari n litere mici i invers.
Funcie Descriere
int tolower(int c); convertete n litere mici
int toupper(int c); convertete n litere mari
Exemple de utilizare a acestor funcii sunt prezentate n tabelul de mai jos.
tolower(A) a
tolower (a) a
tolower (*) *
51
O funcie util la prelucrarea irurilor este strlen() care d lungimea unui ir de
caractere, fr caracterul \0 terminal. Prototipul acestei funcii este
int strlen(char[]);
i se gsete n bibliotecile <cstring> i <string.h>.
Funcia
int strcmp(const char s1[], const char s2[]);
compar cele dou iruri, caracter cu caracter, pn la primul caracter diferit.
Rezultatul este un numr < 0, = 0, sau > 0, dup cum sunt caracterele diferite
comparate. Exemple de utilizare a funciei strcmp sunt prezentate n tabelul de mai jos
strcmp(abc, bcd) < 0 "abc" < "bcd" deoarece a < b
strcmp(xyz, xyz) = 0 irurile sunt egale
strcmp(abcd, abc) > 0 irul abcd este mai lung
Prototipul acestei funcii se gsete n bibliotecile <cstring> i <string.h>.
Vom scrie o funcie care s numere literele mici, literele mari i cifrele dintr-un ir
citit de la tastatur. Programul este urmtorul.
# include <iostream>
# include <cstring>
# include <cctype>
using namespace std;
// calculul numarului de litere mici, litere mari si cifre dintr-un sir
int main()
{
char x[50];
int i, n, cnt;
cout << introduceti un sir;
cin >> x;
// calculeaza lungimea sirului
n = strlen(x);
// numara literele mici din sir
cnt = 0;
for(i = 0; i < n; i++)
if(islower(x[i]))
cnt = cnt + 1;
cout << sirul contine : << cnt << litere mici << endl;
// numara literele mari din sir
cnt = 0;
for(i = 0; i < n; i++)
if(isupper(x[i]))
cnt++;
cout << sirul contine : << cnt << litere mari << endl;
// numara cifrele din sir
cnt = 0;
for(i = 0; i < n; i++)
if(isdigit(x[i]))
cnt = cnt + 1;
cout << sirul contine : << cnt << cifre << endl;
return 0;
52
}
Rezultatul rulrii programului este urmtorul.
Funcia strlen() are un parametru i calculeaz o valoare. Apelarea ei se face scriind
numele funciei urmat n paranteze de argumentul funciei ntr-o expresie. Funcia
strlen() este apelat n membrul drept al unei instruciuni de atribuire. Apelarea
funciei i transmiterea valorii calculate de funcie n punctual din program unde este
apelat este prezentat schematic mai jos.
Exerciiu. S se scrie un program care s converteasc literele mici ale unui ir citit de
la tastatur n litere mari.
Alte funcii ce prelucreaz iruri de caractere sunt
int atoi(char s[]);
double atof(char s[]);
care convertesc un ir de caractere ntr-un numr ntreg i respectiv real. Prototipurile
acestor funcii se afl n bibliotecile <cstdlib> i <stdlib.h>.
Un exemplu de utilizare a acestor funcii poate fi urmtorul. Fie dou iruri ce conin
numere. Vom converti aceste iruri n numere i vom efectua produsul lor.
# include <cstdlib>
# include <iostream>
using namespace std;
int main()
{
char s1[] = -123;
char s2[] = 1.22e-1;
int x;
double y, z;
// scrie sirurile
cout << sirul s1 : << s1 << endl
<< sirul s2 : << s2 << endl;
// converteste sirurile in numere
x = atoi(s1);
y = atof(s2);
z = x * y;
53
// scrie numerele si produsul lor
cout << x = << x << y = << y << endl;
cout << x * y = << z << endl;
return 0 ;
}
Rezultatul rulrii programului este cel de mai jos.
3.2 Definirea funciilor
O funcie calculeaz o valoare pe baza valorilor argumentelor sale, furnizate de un
program care apeleaz funcia. Orice funcie are un nume i parametri sau argumente.
Definiia unei funcii este
tip numefuncie (lista de parametri)
{
corpul funciei
}
Prima linie conine :
tip este tipul valorii calculate de funcie (se mai numete tipul funciei),
numefuncie este numele funciei ales de programator,
lista de parametri (argumente ale funciei) are forma :
tip1 arg1, tip2 arg2, , tipn argn
unde tip1, tip2, , tipn reprezint tipurile parametrilor arg1, arg2, , argn.
Parametrii din definiia funciei se numesc parametri formali.
Corpul funciei este format dintr-o secven de instruciuni ntre acolade, { i }, ce
descriu calculele efectuate de funcie. Valoarea calculat de funcie este transmis n
punctul de apelare a funciei cu instruciunea return ce are forma
return expresie;
Tipul acestei expresii trebuie s fie acelai cu tipul funciei. Instruciunea return
termin totodat i execuia funciei.
Exemplu. S definim o funcie care s calculeze suma a dou numere reale de tip
double.
Numele funciei va fi suma, parametrii ei vor fi dou variabile de tip double, x i y,
valoarea calculat de funcie va fi de tip double. In corpul funciei vom defini o
variabil de tip double, z, n care vom calcula rezultatul.
/* functie ce calculeaza suma a doua numere reale */
double suma (double x, double y)
{
double z ;
z = x + y ;
return z ;
}
54
Parametri x i y din definiia funciei suma sunt parametri de intrare, ei conin valori
ce sunt utilizate de funcie n calcule.
Variabilele definite ntr-o funcie exist doar n timpul execuiei funciei. Din aceast
cauz, ele se numesc variabile locale. Variabila z din funcia de mai sus este local.
Variabilele nu pot fi utilizate n afara funciei.
Apelarea unei funcii se face scriind numele funciei urmat de lista de parametrii
inclus n paranteze, ca termen ntr-o expresie. Parametrii din list sunt separai
de virgule. Parametrii de apelare a funciei se numesc parametri actuali sau
argumente. Parametrii de intrare actuali ai unei funcii pot fi orice constante, variabile
sau expresii.
Funciile sunt recursive, adic o funcie se poate apela pe ea nsi.
Vom utiliza funcia definit mai sus, n cadrul funciei main pentru calculul sumei a
dou numere reale, citite de la tastatur. Vom defini dou variabile de tip double, a i
b, ce vor conine valorile citite de la tastatur i o variabil c de tip double, ce va
conine rezultatul. a, b i c sunt variabile locale ale funciei main.
int main()
{
double a, b ;
cout << " a = " ; cin >> a ;
cout << " b = " ; cin >> b ;
double c ;
c = suma(a, b) ;
cout << " a + b = " << c << endl ;
return 0 ;
}
Rezultatul rulrii programului este sel de mai jos.
Funcia suma este utilizat n membrul drept al unei instruciuni de atribuire, deoarece
calculeaz o valoare asociat numelui. Ea este apelat n instruciunea
c = suma(a, b);
Reamintim c parametrii x i y din definiia funciei sunt parametrii de intrare. In
consecin, putem apela funcia suma cu parametrii orice constante, variabile sau
expresii, de exemplu
c = suma(1.73, -2.22);
sau
c = suma(1.73 * 2.5 + a, a b + 2);
Menionm c numele parametrilor actuali (din instruciunea de apelare a funciei) pot
fi diferite de cele ale parametrilor formali din definiia funciei.
Pasarea parametrilor ctre funcii se face prin intermediul unei stive. La apelarea
funciei, se aloc spaiu ntr-o stiv pentru parametrii funciei i pentru variabilele
locale ale funciei. In spaiul alocat pentru parametrii funciei se pun valorile acestor
parametri. Funcia poate utiliza valorile acestor parametrii n calcule. Valoarea
calculat de funcie se transmite n punctul de apelare de ctre instruciunea return.
55
Menionm c este posibil ca o funcie s nu calculeze nici o valoare. In acest caz
tipul ei este void, iar instruciunea return are forma
return;
Menionm c, n general, o funcie poate calcula mai multe valori care s fie
transmise n programul apelant, asociate unor parametri. In acest caz o parte din
parametrii funciei sunt parametri de intrare i ei conin valorile ce vor fi prelucrate de
funcie, ceilali parametri vor conine valorile calculate de funcie ,ce sunt transmise n
programul apelant i vor fi numii parametri de ieire. Modul de definire a
parametrilor de ieire va fi artat ulterior.
3.3 Prototipuri de funcii
La ntlnirea unei instruciuni n care se apeleaz o funcie, compilatorul trebuie s
cunoasc prototipul funciei adic numrul i tipurile parametrilor i tipul rezultatului
(tipul valorii calculate de funcie). Compilatorul utilizeaz numrul i tipurile
parametrilor pentru a verifica dac funcia este apelat corect. Tipul rezultatului este
utilizat pentru a converti rezultatul funciei dac funcia este un termen al unei
expresii. In cazul n care funcia este definit n program nainte a fi apelat,
compilatorul cunoate informaiile necesare din linia de definiie a funciei. Acesta
este cazul programului de mai sus. Este posibil ca ntr-un program s definim funcii
i dup ce ele au fost utilizate. In acest caz compilatorul nu cunoate prototipul
funciei n momentul apelrii ei i este obligatoriu s definim prototipul funciei
nainte de a o utiliza, astfel nct compilatorul poate verifica dac apelarea funciei
este corect (tipul i numrul parametrilor, etc.).. Prototipul unei funcii are
urmtoarea definiie
tip nume(lista de parametri);
In prototip putem s identificm doar tipurile parametrilor.
In cazul funciei suma definit anterior prototipul este
double suma(double, double);
sau
double suma(double x, double y) ;
De remarcat c prototipul unei funcii este chiar linia de definiiei a funciei
urmat de caracterul ;.
Prototipurile funciilor matematice standard sunt definite n bibliotecile
standard. De exemplu, biblioteca <iostream> conine prototipurile funciilor intrare /
ieire pentru fiierele standard ale sistemului (tastatura i monitorul), biblioteca
<cmath> conine prototipurile funciilor matematice, etc. Aceste biblioteci sunt
semnalate compilatorului cu directiva # include.
Exemplu. Vom defini o funcia max() care s calculeze maximul a dou numere de tip
double. Definiia funcie max va fi scris dup definiia funciei main(). In acest caz
trebuie s definim prototipul funciei max() nainte de definiia funciei main().
#include <iostream>
using namespace std;
// prototipul functiei max(). Functia max() este apelata
// in functia main(), dar este definite ulterior
double max (double, double) ;
// definitia functiei main()
56
int main()
{
double a, b, c ;
cout << " a = " ; cin >> a ;
cout << " b = " ; cin >> b ;
c = max(a, b) ;
cout << " max(a, b) = " << c << endl ;
return 0 ;
}
// definitia functiei max()
double max (double x, double y)
{
if( x > y)
return x ;
else
return y ;
}
Rezultatul rulrii programului este cel de mai jos.
De remarcat c, o funcie, poate avea mai multe instruciuni return.
3.4 Compilarea separat a funciilor
Funciile definite de programator pot fi scrise n fiiere separate. Aceste fiiere vor fi
compilate separat. Avantajul compilrii separate a funciilor este acela c ele pot fi
testate separat, nainte de a fi utilizate. Funciile definite n fiiere separate sunt
semnalate compilatorului, n fiierele n care sunt utilizate cu directive include, la fel
ca i funciile standard. Forma directivei include este
#include nume fisier
Numele fiierului este scris aici ntre ghilimele. De regul, aceste fiiere au extensia h.
Programul anterior este rescris cu funcia max() definit n fiierul max.h. Cele dou
fiiere sunt prezentate mai jos.
Fiierul main.cpp
#include <iostream>
using namespace std;
#include "max.h"
int main(int argc, char *argv[])
{
double a, b, c ;
57
cout << " a = " ; cin >> a ;
cout << " b = " ; cin >> b ;
c = max(a, b) ;
cout << " max(a, b) = " << c << endl ;
return 0;
}
Fiierul max.h
// definitia functiei max()
double max (double x, double y)
{
if( x > y)
return x ;
else
return y ;
}
3.5 Funcii cu parametri tablouri
Funciile pot avea ca parametri tablouri, vectori, matrice, etc. Am spus mai nainte c
pasarea parametrilor ctre funcii se face prin intermediul unei stive. Prin definiie,
atunci cnd un parametru este un tablou, n stiv se pune adresa tabloului. In
consecin, parametrul formal tablou este un parametru de ieire. Dac elementele
tabloului sunt modificate n funcie, modificarea apare i n programul ce a apelat
funcia.
In linia de definiie a unei funcii, parametrii formali tablouri au forma
tip numetablou [dim1] [dim2] [dimn]
unde tip este tipul elementelor, iar dim1, dim2, , dimn sunt numere, dimensiunile
tabloului. Fie de exemplu o funcie cu numele fun cu doi parametri formali, o matrice
A cu dou linii i trei coloane cu elemente tip double i un vector X cu apte
componente ntregi iar rezultatul funciei de tip double. Linia de definiie a funciei
are forma
double fun (double A[2] [3], int X[7])
La apelarea unei funcii cu parametri tablouri, parametrul actual tablou este numele
tabloului, fr paranteze sau indici.
La utilizarea unui element al unui tablou, se calculeaz adresa acestui element relativ
la nceputul tabloului. Fie de exemplu matricea A[2] [3] de mai sus. Elementele
matricei au indicii din tabelul de mai jos.
(0, 0) (0, 1) (0, 2)
(1, 0) (1, 1) (1, 2)
In memorie, elementele matricei sunt memorate pe linii, ca mai jos.
(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2)
Poziia primului element a
00
este 0, poziia elementului a
01
este 1, etc. Poziia
elementului a
ij
este dat de formula
i * 3 + j
58
De exemplu, a
10
ocup poziia 3, a
12
ocup poziia 5, a
01
ocup poziia 1. In general, la
o matrice cu n linii i m coloane, poziia elementului a
ij
este
i * m + j
Se vede c, n aceast formul, nu intervine prima dimensiune a tabloului. In
consecin, n linia de definiie a unei funcii, parametrii formali tablouri au i forma
tip numetablou [] [dim2] [dimn]
n care prima dimensiune nu se specific. De exemplu, linia de definiie a funcie din
exemplul de mai sus este
double fun (double A[] [3], int X[])
Vom considera pentru nceput parametrii formali vectori. Un parametru formal vector
are forma
tip numevector[]
sau
tip numevector[dim]
unde dim este numrul de componente ale vectorului.
Exemplu. Vom scrie o funcie care s calculeze suma componentelor unui vector a cu
elemente numere reale de tip float. Parametrii funciei vor fi vectorul a i dimensiunea
sa. Valoarea calculat va fi ataat numelui funciei. Vom presupune c, la apelarea
funciei elementele vectorului sunt iniializate. Dup cum am spus mai sus, parametrul
formal vector are forma
float a[]
Definiia funciei va fi precedat de un comentariu ce descrie parametrii funciei, (de
intrare, de ieire), condiii asupra parametrilor de intrare, valoarea calculat de funcie,
etc.
Definiia funciei este urmtoarea
/* funcie ce calculeaza suma componentelor unui vector
float suma(float a[], int n);
Parametri de intrare
a vector
n dimensiunea vectorului a
Iesire
suma suma componentelor vectorului a
Preconditie :
elementele vectorului a sunt initializate
*/
float suma(float a[], int n)
{
int i;
float s = 0;
// calculeaza suma componentelor
for (i = 0; i < n; i = i + 1)
s = s + a[i];
return s;
}
O funcie, odat scris, trebuie testat. Un program simplu ce testeaz funcia de mai
sus este urmtorul.
59
/* testarea functiei suma */
int main()
{
float x[5] = {11.3, -2.67, 0.34, -2.5, 14};
float z;
int i;
// calculeaza suma componentelor
z = suma(x, 5);
// scrie componentele vectorului
cout << suma componentelor vectorului << endl;
for(i = 0; i < 5; i++)
cout << x[i] << \t;
cout << endl;
// scrie rezultatul
cout << este << z << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Funcia este apelat cu instruciunea
z = suma(x, 5);
Dup cum am spus mai sus, cnd un parametru formal al unei funcii este vector, la
apelarea funciei parametrul actual este numele vectorului, fr indici sau paranteze
drepte.
Componentele vectorului sunt afiate pe ecran separate de caracterul tab pe un rnd
iar rezultatul pe rndul urmtor. In exemplul anterior definiia funciei este scris
nainte de utilizarea ei n funcia main. Reamintim c, este posibil de a scrie definiia
unei funcii dup funcia main(). In acest caz, nainte de definiia funciei main()
trebuie s scriem prototipul funciei, cum s-a artat mai sus.
Exemplu. Cutarea binar ntr-o list ordonat cresctor. Fie un vector x cu n
elemente reale ordonate cresctor. Vrem s testm dac valoarea z se afl printre
componentele vectorului. Fie indicii ls i ld domeniului de cutare n vector. Iniial ls
= 0 i ld =n-1. Metoda cutrii binare const n testarea elementului de la mijloc al
vectorului cu indicele i=(ls+ld)/2 i restrngerea domeniului de cutare dac valoarea
z nu a fost gsit. Domeniul de cutare este restrns astfel:
dac x[i] < z atunci ls = i + 1
dac x[i] > z atunci ld = i 1
La fel ca la funcia precedent, definiia funciei este precedat de un comentariu ce
descie funcia, parametrii, condiiile asupra parametrilor, etc.
Funcia ce implementeaz acest algoritm este urmtoarea
/*
Cautarea binara
60
int find (double x[], int n, double z)
Parametri de intrare :
x - vector cu elemente sortate crescator
n - dimensiunea lui x
z - valoarea cautata
find = indicele elementului z in lista daca exista,
= -1 daca z nu exista in lista.
Preconditie :
x[0] <= x[1] <= ... <= x[n - 1]
*/
int find (double x[], int n, double z)
{
int ls, ld, i;
ls = 0;
ld = n - 1;
while(ls <= ld)
{
i = (ls + ld) / 2;
if(x[i] == z)
return i;
if(x[i] < z)
ls = i +1;
else
ld = i - 1;
}
return -1;
}
Un program de testare a funciei este urmtorul. Reamintim c, la apelarea funciei,
parametrul vector este scris ca numele tabloului, fr indici sau paranteze drepte.
int main()
{
double a[6] = {-2.35, 1.77, 3.14, 4.2, 5.12, 7.09};
double z;
int k;
z = 4.2;
k = find(a, 6, z);
if(k < 6)
cout << "elementul " << z << " are rangul " << k << endl;
else
cout << "elementul " << z << " nu este in lista" << endl;
return 0;
}
Tabela de mai jos prezint iteraiile algoritmului pentru z = 4.2.
iteraia ls ld i x[i]
61
1 0 5 2 3.14
2 3 5 4 5.12
3 3 3 3 4.2
Rezultatul rulrii programului este cel de mai jos.
Reamintim c primul element din vector are indicele zero.
O funcie poate avea ca parametri i matrice, etc. In acest caz, n definiia funciei
parametrul trebuie s conin i numrul de linii i de coloane al matricei, ca n
exemplul de mai jos.
Vom ncheia acest paragraf cu un program ce definete funcii pentru lucrul cu
matrice:
funcie ce citete o matrice de la tastatur,
o funcie ce scrie elementele unei matrice pe ecran,
o funcie ce calculeaz suma a dou matrice.
Matricele pot avea oricte linii i de coloane, ce vor fi prescrise n program cu dou
directive define. Fiecare funcie este precedat de un comentariu ce descrie parametrii
i operaia realizat de funcie.
#include <iostream>
#include <iomanip>
# define NLIN 2
# define NCOL 2
using namespace std;
/*
funcie ce citeste elementele unei matrice
void rdmat(double a[NLIN][NCOL])
Parametrii :
NLIN numarul de linii
NCOL numarul de coloane
Paramertii de iesire:
a matrice cu NLIN linii si NCOL coloane
Elementele matricei se introduc pe linii.
*/
void rdmat(double a[NLIN][NCOL])
{
int i, j;
for(i = 0; i < NLIN; i++)
for(j = 0; j < NCOL; j++)
cin >> a[i][j];
return;
}
62
/*
functie ce afisaza elementele unei matrice pe linii
Parametrii de intrare:
NLIN numarul de linii
NCOL numarul de coloane
a matrice cu NLIN linii si NCOL coloane
*/
void wrmat(double a[NLIN][NCOL])
{
int i, j;
for(i = 0; i < NLIN; i++){
for(j = 0; j < NCOL; j++)
cout << setw(6) << a[i][j] << " ";
cout << endl;
}
return;
}
/*
functie ce aduna doua matrice cu elemente tip double
Parametrii de intrare:
a matrice de intrare cu NLIN linii i NCOL coloane
b matrice de intrare cu NLIN linii i NCOL coloane
Parametrii de iesire
c matricea suma cu NLIN linii i NCOL coloane
Preconditie
Matricele a si b sunt initializate.
*/
void addmat(double a[NLIN][NCOL], double b[NLIN][NCOL],
double c[NLIN][NCOL])
{
int i, j;
for(i = 0; i < NLIN; i++)
for(j = 0; j < NCOL; j++)
c[i][j] = a[i][j] + b[i][j];
return;
}
/*
program pentru testarea functiilor
*/
int main(int argc, char *argv[])
{
double x[NLIN][NCOL], y[NLIN][NCOL], z[NLIN][NCOL];
// citeste elementele matricelor x si y
cout << "introduceti prima matrice pe linii" << endl;
rdmat(x);
cout << "introduceti a doua matrice pe linii" << endl;
rdmat(y);
63
addmat(x, y, z);
cout << "matricea suma" << endl;
wrmat(z);
return 0;
}
Rezultatul rulrii programului este prezentat mai jos.
3.6 Suprancrcarea funciilor
Intr-un program putem defini mai multe funcii cu acelai nume dar cu
parametri de tipuri diferite sau cu un numr de parametri diferit (prototipuri
diferite). Acest lucru se numete suprancrcarea funciilor.
Vom exemplifica suprancrcarea funciilor definind dou funcii cu acelai nume,
min, ce calculeaz minimul a dou, respective trei numere ntregi. Variabilele ce
conin aceti ntregi vor fi parametri de intrare pentru funcii.
# include <iostream>
using namespace std;
// calculul minimului a doua numere intregi
int min(int x, int y)
{
return (x < y? x: y);
}
// calculul minimului a trei numere intregi
int min(int a, int b, int c)
{
int x = a < b? a: b;
if(x < c)
return x;
else
return c;
}
int main()
{
int a = 29, b = 47, c = -32;
cout << "min " << a << "," << b << " este " << min(a, b) << endl;
cout << "min " << a << "," << b << ", " << c << " este "
64
<< min(a, b, c) << endl;
return 0;
}
Rezultatul programului este cel de mai jos.
Vom exemplifica utilizarea prototipurilor definind cele dou funcii cu numele min ce
calculeaz minimul a dou, respectiv trei numere reale dup funcia main. Vom
declara mai nti prototipurile celor dou funcii i vom defini funcia main.
# include <iostream>
using namespace std;
// defineste prototipurile celor doua functii min()
double min(double, double);
double min(double, double, double);
// functia main()
int main()
{
double a = 68.29, b = -77.3, c = 32.5;
cout << "min " << a << "," << b << " este " << min(a, b) << endl;
cout << "min " << a << "," << b << ", " << c << " este "
<< min(a, b, c) << endl;
return 0;
}
Vom defini acum cele dou funcii.
// calculul minimului a doua numere reale
double min(double x, double y)
{
return (x < y? x: y);
}
// calculul minimului a trei numere reale
double min(double a, double b, double c)
{
double x = min(a, b);
return (x < c? x : c);
}
Menionm c prototipurile celor dou funcii puteau fi scrise respectiv
double min(double a, double b);
65
double min(double a, double b, double c);
3.7 Transmiterea parametrilor ctre funcii
La apelarea unei funcii, parametrii actuali i variabilele locale ale funciei sunt
memorate ntr-o stiv. Exist dou moduri de a transmite parametrii ctre funcii: prin
valoare i prin referin (adres). In cazul transmiterii unui parametru prin valoare, n
stiv se pune chiar valoarea parametrului, n cazul transmiterii prin adres n stiv se
pune adresa parametrului.
Cazul parametrilor transmii prin valoare. Dac n corpul funciei modificm
aceti parametri, valoarea lor se modific doar n stiv i nu n programul
apelant. In consecin, parametrii transmii prin valoare sunt parametri de
intrare (nu pot fi parametri de ieire ai funciei),
Cazul parametrilor transmii prin adres (referin). Dac n corpul funciei
modificm aceti parametri, valoarea lor se modific n programul apelant
(deoarece n stiv este adresa acestor parametri). In consecin, pentru ca un
parametru al unei funcii s fie parametru de ieire, el trebuie transmis
prin adres (referin).
Vom rezuma proprietile celor dou moduri de transmitere a parametrilor ctre
funcii, n tabelul de mai jos.
In definiia funciei
Parametri transmii prin valoare Parametri transmii prin adres
Sunt parametri de intrare pentru funcie Sunt parametri de ieire pentru funcie
Parametrul este o copie a argumentului Parametrul este adresa argumentului
Funcia nu poate modifica parametrul Funcia poate modifica parametrul
La apelarea funciei
Parametri transmii prin valoare Parametri transmii prin adres
Argumentul este o constant, variabil
sau expresie
Argumentul este o variabil
In general o funcie poate calcula mai multe valori. Una dintre valori este transmis la
punctul de apelare de instruciunea return. Celelalte valori vor fi asociate unor
parametri de ieire, care vor fi obigatoriu transmii prin adres.
Definirea parametrilor tip referin (adres)
Exist dou moduri de a defini parametri transmii prin adres:
utilizarea parametrilor tip referin,
utilizare parametrilor tip pointer.
Referinele i variabilele tip pointer vor fi prezentate pe larg n capitolul urmtor.
Vom prezenta acum doar parametrii tip referin.
Fie un parametru de un tip T al unei funcii. Referina la un astfel de parametru are
tipul T&.
De exemplu, parametrul formal int x, este transmis funciei prin valoare, parametrul
formal int& x este transmis prin adres.
66
Dac un parametru al funciei este tip referin, la apelarea funciei se va pune n stiv
adresa acestui parametru, i atunci cnd acest parametru este modificat n corpul
funciei el este modificat direct n programul apelant (este parametru de ieire).
Pentru a vedea care este diferena ntre cele dou moduri de transmitere a parametrilor
considerm o funciei care modific valoarile parametrilor si. Primul parametru va fi
transmis prin valoare iar cellalt prin adres.
// functie ce modifica valorile parametrilor
// primul parametru este transmis prin valoare, al doilea prin adresa
# include <iostream>
using namespace std;
void f(int x, int& y)
{
x = 52;
y = 65;
return;
}
int main()
{
int a = 20, b = 30;
cout << a = << a << , b = << b << endl;
f(a, b);
cout << a = << a << , b = << b << endl;
f(2*a + 3, b);
cout << a = << a << , b = << b << endl;
return 0;
}
Rezultate afiate sunt cele de mai jos.
Menionm c primul parametru se modific doar n stiv i nu n programul apelant,
doarece nu este transmis prin referin.
Valorile variabilelor a i b nainte de prima apelare a funciei sunt
La prima apelare a funciei stiva este
Dup prima execuie a funciei valorile variabilelor a i b n program sunt
67
Menionm c valoarea variabilei a nu se modific, deoarece primul parametru al
funciei este transmis prin valoare.
Exemplu. S construim o funciei care s permute valoarea a dou variabile de tip
ntreg. Funcia va avea doi parametri care la apelare au valorile variabilelor ce trebuie
permutate, iar la ieirea din funcie trebuie s aibe valorile permutate.
/* functie ce permute valoarea a doua variabile */
void perm(int& a, int& b)
{
int c;
c = a;
a = b;
b = c;
return;
}
O funcie main() ce testeaz funcia scris este urmtoarea.
int main()
{
int x = 7, y = 12;
cout << valorile initiale x = << x << y = << y << endl;
perm(x, y);
cout << valorile permutate x = << x << y = << y << endl;
}
Testarea acestei funcii produce rezultatele de mai jos.
In stiv se pun adresele variabilor x i y deoarece parametrii funciei sunt de tip
referin. Menionm c apelarea funciei se face utiliznd pentru parametrii referin
nite variabile din programul apelant (nu constante sau expresii), n cazul nostru
perm(x, y)
Vom recapitula acum diferenele ntre parametrii pasai prin valoare i cei pasai prin
adres (referin).
un parametru transmis prin valoare este un parametru de intrare. Valoarea lui
actual este rezultatul evalurii unei constante, variabile sau expresii n
programul apelant.
un parametru tip referin este un parametru de ieire. Argumentul transmis
este o variabil din programul apelant.
Menionm c n cazul n care un parametru este un tablou, n stiv se pune
adresa primului element al tabloului. In consecin, parametri formali tablouri
68
pot fi parametri de ieire, modificarea unui element al tabloului n funcie
produce modificarea lui n programul ce a apelat funcia.
Exemplu. Vom defini o funcie ce calculeaz suma a doi vectori x i y de numere
reale de dimensiune n. Vectorul sum va fi z. Tipul funciei este void deoarece toate
valorile calculate de funcie sunt asociate unor parametrii de ieire.
/*
Calculul sumei a doi vectori
void sumvect(double x[], double y[], double z[], int n) ;
Parametrii de intrare :
x vector
y vector
n dimensiunea vectorilor x, y, z
Parametrii de iesire :
z vector, z = x + y
Preconditii :
Parametrii de intrare sunt initializati
*/
void sumvect(double x[], double y[], double z[], int n)
{
int i;
for(i = 0; i < n; i++)
z[i] = x[i] + y[i];
return;
}
Un program ce testeaz funcia de mai sus este urmtorul
int main()
{
double a[3] = {1.29, -3.15, 6.92}, b[3] = {0.73, 5.25, -3.21};
double c[3];
sumvect(a, b, c, 3);
cout << "vectorul suma" << endl;
for(int i = 0; i < 3; i++)
cout << c[i] << " ";
cout << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Vom ncheia acest paragraf definind o funcie care s calculeze media i dispersia
unui ir de numere n
x x x , ,
2 1 . Valoarea medie a elementelor irului este
69

n
i
i
x
n
m
1
1
iar dispersia

n
i
i
m x
n
d
1
2
) (
1
1
Tipul funciei va fi void. Media i dispersia vor fi asociate unor parametri de ieire ai
funciei. Prototipul funciei va fi
void md(float x[], int n, float& m, float& d);
unde x este vectorul de numere de dimensiune n pentru care calculm media m i
dispersia d. Deoarece valorile calculate, m i d sunt asociate unor parametri, funcia
are tipul void.
#include <iostream>
using namespace std;
/*
Calculul mediei si dispersiei componentelor unui vector
void md(float x[], int n, float& m, float& d);
Parametri de intrare
x vector
n dimensiunea vectorului x
Parametri de iesire
m media componentelor lui x
d dispersia componentelor lui x
Preconditii
Parametrii de intrare sunt initializati
*/
void md(float x[], int n, float& m, float& d)
{
float s = 0;
int i;
// calculeaza media
for(i = 0; i < n; i++)
s = s + x[i];
m = s / n;
// calculeaza dispersia
d = 0;
for(i = 0; i < n; i++)
d = d + (x[i] m) * (x[i] m);
d = d / (n 1);
return;
}
Un program ce testeaz funcia de mai sus este urmtorul.
int main()
{
float a[4] = {1.2e-1, -2.34, 1.5, 3.33};
70
float media, dispersia;
md(a, 4, media, dispersia);
cout << media = << media << dispersia = << dispersia << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
3.8 Recursivitatea
Funciile limbajelor C i C++ sunt recursive, adic o funcie se poate apela pe ea
nsi. Vom exemplifica acest lucru cu o funcie care s calculeze valoarea n! n
variant recursiv i nerecursiv.
Varianta nerecursiv Varianta recursiv

n
i
i n
1
!

'

>

1 )! 1 ( *
1 1
!
n n n
n
n
// calculul factorialului
int fact(int n)
{
int s = 1;
for(int i = 1; i <= n; ++n)
s = s * i;
return s;
}
// calculul factorialului
int fact(int n)
{
int s;
if(n = = 1)
s = 1;
else
s = n * fact(n 1);
return s;
}
Vom prezenta stiva la apelarea variantei recursive pentru n = 3. Reamintim c
parametrul n i variabila local s sunt memorai n stiv. Dup apelul
fact(3)
stiva este
n=3 s=?
Deoarece n este diferit de unu, se execut instruciunea
s = n * fact(n 1)
adic se apeleaz nc o dat funcia, fact(2). Stiva de
n=3 s=? n=2 s=?
Se apeleaz nc o dat funcia, fact(1), i stiva devine
n=3 s=? n=2 s=? n=1 s=1
71
Dup aceast ultim apelare se ajunge la instruciunea return, dup care variabilele
corespunznd ultimei apelri a funciei se sterg din stiv. Stiva devine
n=3 s=? n=2 s=2
Din nou se ajunge la instruciunea return, variabilele corespunznd acestei apelri se
strerg din stiv
n=3 s=6

Dup care se obine rezultatul final, valoarea 6.
3.9 Funcii generice
In limbajul C++ putem s definim funcii generice n care putem defini tipuri generale
pentru parametri i valoarea returnat de funcie. Definirea unei funcii generice se
face cu instruciunea
template <typename identificator, typename identificator >
declaraie de funcie
S definim de exemplu o funcie generic pentru calculul maximului a dou variabile
/* functie generica ce calculeaza maximul a doua variabile */
template <typename T>
T maxval(T a, T b)
{
return (a > b ? a : b);
}
Apelarea unei funcii generice se face astfel
nume_funcie <tip, tip, >(parametri);
De exemplu, putem calcula maximul a dou numere ntregi sau reale astfel.
int main()
{
// maximul a doua numere intregi
int a = 15, x = 20, c;
c = maxval<int>(a, x);
cout << maximul dintre << a << si << x << este << c <<
endl;
// maximul a doua numere reale
float fa = 3.43, fb = -9.3;
cout << maximul dintre << fa << si << fb << este
<< maxval<float>(fa, fb) << endl;
return;
}
Rezultatul rulrii programului este cel de mai jos.
72
Exerciiu. S se defineasc o clas generic ce permut dou variabile de acelai tip.

73
4 Pointeri i referine
Compilatorul aloc oricrei variabile definite n program o zon de memorie egal cu
numrul de octei corespunznd tipului variabilei. In cazul unui tablou se aloc
memorie fiecrui component al tabloului. Fie de exemplu instruciunea
int a, b, x[2], c;
Compilatorul aloc zone de memorie variabilelor ca mai jos
Compilatorul creaz o tabel cu numele variabilelor i adresele lor n memorie, de
exemplu
a 1000
b 1004
x 1008
c 1016
Menionm c unitatea central a calculatoarelor are un registru acumulator utilizat n
calcule. O instruciune de atribuire de forma
a = b ;
este tradus de compilator astfel : se ncarc n registrul acumulator valoarea de la
adresa variabilei b (n cazul nostru adresa 1004) i se memoreaz coninutul
acumulatorului la adresa variabilei a (n cazul nostru adresa 1000). O instruciune de
atribuire de forma
a = b + c;
este tradus de compilator astfel: se ncarc n registrul acumulator valoarea de la
adresa variabilei b (n cazul nostru adresa 1004) se adun la registru valoarea de la
adresa variabilei c (n cazul nostru adresa 1016), i se memoreaz coninutul
registrului acumulator la adresa variabilei a (n cazul nostru adresa 1000). In
programul generat de compilator nu exist nume de variabile, ci doar adrese.
Limbajele C i C++ permit definirea unor variabile ce conin adrese ale altor variabile.
Acestea au tipul pointer i referin. Dup cum vom vedea, aceste variabile se
utilizeaz la transmiterea parametrilor funciilor prin adres; parametrii transmii prin
adres pot fi parametri de ieire ai funciilor. Pointerii sunt utilizai i la prelucrarea
tablourilor.
4.1 Pointeri
Un pointer este o variabil ce conine adresa unei alte variabile. De exemplu, dac n
este o variabil de tip int, ce are valoarea 3, iar pn este o variabil de tipul pointer la
int, ce conine adresa lui n, putem reprezenta cele dou variabile astfel
74
4.1.1 Declararea variabilelor tip pointer
O variabil de tip pointer se definete cu instruciunea
tip * nume;
De exemplu
int * pn;
definete o variabil tip pointer la int ce poate conine adresa unei variabile de tip int.
Instruciunea
int u, *pv;
definete o variabil u de tip int i o variabil pv de tip pointer la int.
Pentru a calcula adresa unei variabile se utilizeaz operatorul &. Dac x este o
variabil oarecare, expresia &x este adresa lui x. Variabilele tip pointer pot fi
iniializate doar cu adrese. De exemplu, putem scrie
int n;
int * pn; // pn este de tipul pointer la int
pn = &n; // initializeaza pn cu adresa lui n
Variabilele de tip pointer pot fi iniializate la declararea lor. Putem scrie de exemplu
int n, *pn = &n;
Pentru a obine valoarea variabilei indicate de un pointer se utilizeaz operatorul *
(numit i operator de adresare indirect).
Fie de exemplu instruciunile
int k, n= 14;
int * pn, *pk;
Imaginea memoriei este urmtoarea
Tabela cu adrese creat de compilator poate fi
k 2000
n 2004
pn 2008
pk 2012
Instruciunile
k = n;
i
pn = &n;
k = *pn;
sunt echivalente, k primete valoarea 14. (Variabila pn a fost iniializat n prealabil
cu adresa lui n). Imaginea memoriei dup execuia ultimelor dou instruciuni este
14 14 adresa lui n (2004)
k n pn pk
75
Reamintim modul de execuie al instruciunii
k = n;
valoarea de la adresa 2004 (adresa lui n) se memoreaz la adresa 2000 (adresa lui k).
Instruciunea
k = *pn ;
se execut astfel : valoarea de la adresa 2008 (adresa lui pn) este adresa operandului,
n cazul nostru 2004, vezi tabela de mai sus. Valoarea de la adresa 2004 se
memoreaz la adresa 2000 (adresa lui k).
Instruciunile
k = n;
i
pk = &k;
*pk = n;
sunt echivalente. In final, instruciunile
k = n;
i
pk = &k;
pn = &n;
*pk = *pn;
sunt echivalente;
Orice variabil tip pointer trebuie iniializat nainte de a fi utilizat. De
exemplu, urmtoarea secven de instruciuni nu este corect :
int *pn;
*pn = 5;
deoarece variabila pn nu a fost iniializat, ea nu conine adresa unei variabile de
tip int. O secven corect este
int *pn;
int x;
pn = &x;
*pn = 5;
In acest caz variabila pn a fost iniializat cu adresa unei variabile de tip ntreg.
Variabilele tip pointer pot fi utilizate n expresii aritmetice n locul variabilelor
aritmetice a cror adres o conin. De exemplu, secvena de instruciuni
int u, v, w;
u = 3;
v = 2 * (u + 5);
w = 2 * ( 3 + u + 5);
poate fi scris cu variabile tip pointer ca
int u, v, w;
int * pu;
u = 3;
pu = &u;
v = 2 * (*pu + 5);
w = 2 * (3 + *pu + 5)
Limbajul C nu are referine. In consecin, singurul mod de a declara parametri
de ieire ai unei funcii n limbajul C, este ca acetia s fie de tip pointer.
Exemplu. Vom scrie o funcie care s permute valorile a dou variabile ce vor fi
parametri de ieire ai funciei. Aceti parametri vor fi variabile tip pointer.
76
// functie ce permuta valorile a doua variabile
void perm(int* a, int* b)
{
int c;
c = *a;
*a = *b;
*b = c;
}
Utilizarea acestei funcii se face astfel :
int main()
{
int x = 3, y = -4;
// scrie variabilele inainte de permutare
cout << valori initiale : << x = << x << y = << y << endl;
perm(&x, &y);
// scrie variabilele dupa permutare
cout << valori permutate : << x = << x << y = << y << endl;
return 0;
}
Rularea programului produce rezultatele de mai jos.
La apelarea funciei perm(), stiva este urmtoarea:
In consecin, instruciunile din funcie
*a = *b;
*b = c;
modific valorile parametrilor (variabilele x i y) n funcia main.
Operatorii * i & sunt unari i asociativi la dreapta. Operatorii * i & au aceeai
prioritate cu operatorii ++ -- + - ! ~ sizeof (tip), vezi Anexa 2. Operatorii * i &
sunt inveri.
Exemplu. Fie instruciunile
int a, b;
a = 7;
Instruciunile
b = a;
i
b = *&a;
sunt echivalente.
77
Exemplu. In programul de mai jos definim dou variabile de tip int, a i b i dou
variabile de tip pointer la tipul int, pa i pb. Iniializm variabila pa cu adresa
variabilei a i afim adresa variabilei a i valoarea variabilei pa, ele trebuie s
coincid. Iniializm variabila pb cu adresa variabilei b i apoi dm o valoare
variabilei b. Afim variabila b i valoarea variabilei a crei adres este memorat n
pb. Cele dou valori trebuie s coincid.
#include <iostream>
using namespace std;
int main()
{
int a, b;
// definim variabile tip pointer
int *pa, *pb;
// initializam variabilele tip pointer
pa = &a;
pb = &b;
// se scrie adresa variabilei a si valoarea variabilei pa
cout << adresa variabilei a : << &a << , << pa << endl;
// se scrie valoarea variabilei b si valoarea variabilei cu adresa in pb
b = 15;
cout << valoarea variabilei b : << b << , << *pb << endl;
return 0 ;
}
Rezultatul programului este cel de mai jos.
Prima instruciune cout afiaz de dou ori aceeai adres. A doua instruciune cout
afiaz de dou ori aceeai valoare, 15.
Menionm c numele unui tablou este, prin definiie, un pointer constant la
primul element al tabloului. Fie de exemplu instruciunile
float x[7], * pf, z;
Un pointer la tabloul x are valoarea &x[0] sau x . Putem deci scrie
pf = &x[0];
sau, echivalent,
pf = x;
Dac scriem
pf = &x[3];
pf va conine adresa celui de al patrulea element al tabloului x. Instruciunile
z = x[3];
i
pf = &x[3] ;
z = *pf;
sunt echivalente. Fie pc o variabil de tip pointer la char. Ea poate primi ca valoare
adresa unui vector de tip char (ir de caractere). Fie instruciunile
78
char *pc, y[7] ;
Putem scrie
pc = y ;
sau putem iniializa pe pc cu adresa unui ir constant
pc = abcd ;
Putem s definim de exemplu un pointer i s-l iniializm n aceeai instruciune
astfel
char * ax = aceg;
Legtura ntre tablouri i variabile tip pointer va fi prezentat pe larg n paragrafele
urmtoare.
Exemplu. S afim adresele elementelor unui vector de numere ntregi.
# include <iostream>
using namespace std;
int main()
{
int x[5];
for(int i = 0; i < 5; i++)
cout << x[ << i << ] adresa : << &x[i] << endl;
return 0;
}
Rezultatul programului este cel de mai jos. Adresele se modific cu 4 octei. De ce ?
4.2 Referine
O referin (sau adres) este un alt nume pentru o variabil. Fie T tipul unei
variabile i fie instruciunea ce definete o variabil
T nume_variabil;
Instruciunea de definire a unei referine este
T& nume_referin = nume_variabil
unde
nume_referin este numele variabilei referin (adres).
Variabila nume_variabil trebuie s fie declarat nainte i s aibe tipul T. De
exemplu instruciunile
int x;
int& rx = x;
declar pe rx ca fiind o referin a lui x (este obligatoriu ca variabila x de tip ntreg a
fost declarat anterior). Secvena anterioar se poate scrie
int x, &rx = x;
Numele x i rx sunt dou nume diferite pentru aceeai variabil. Ele au totdeauna
aceeai valoare. Pentru a verifica acest lucru vom considera urmtorul exemplu n
care scriem valoarea unei variabile i a referinei corespunztoare.
79
int main()
{
float f = 12.8;
float& fr = f;
cout << f = << f << fr = << fr << endl;
fr = fr + 25.3;
cout << f = << f << fr = << fr << endl;
f = f 3.23;
cout << f = << f << fr = << fr << endl;
return 0;
}
Rezultatele rulrii programului sunt cele de mai jos. Dup fiecare instruciune de
scriere, valorile f i fr vor fi aceleai.
Dup cum am vzut n paragraful anterior, dac un parametru formal al unei funcii
este de tip referin, el poate fi un parametru de ieire.
O referin nu este o variabil separat. Variabila i referina au aceeai adres.
Pentru a verifica acest lucru, n urmtorul program vom afia adresa unei variabile i a
referinei la aceast variabil. Vom defini o variabil de tip double dbl, dou variabile
tip referin dbref i dbref2 la dbl i o alt variabil tip referin dbref3 ce va fi
iniializat cu valoarea lui dbref.
int main()
{
double dbl = 23.44;
double& dbref = dbl;
double& dbref2 = dbl;
// scrie adresele variabilelor dbl si dblref
cout << "adresa lui dbl = " << &dbl << endl
<< "adresa lui dbref = " << &dbref << endl;
// scrie adresele variabilelor dbl si dbref2
cout << "adresa lui dbl = " << &dbl << endl
<< "adresa lui dbref2 = " << &dbref2 << endl;
double& dbref3 = dbref;
// scrie adresele variabilelor dbref si dbref3
cout << "adresa lui dbref = " << &dbref << endl
<< "adresa lui dbref3 = " << &dbref3 << endl;
return 0;
}
In toate cazurile se tiprete aceeai valoare dup cum se vede mai jos.
80
Vom ncheia acest paragraf definind o funcie care implementeaz cutarea binar.
Algoritmul a fost prezentat anterior. Funcia va avea ca parametri de intrare vectorul
x, cu elementele sortate cresctor, dimensiunea n a acestui vector i valoarea z care
este cutat printre elementele lui x. Indicele elementului n list este parametrul de
ieire k. Funcia va avea tipul void.
/*
Cautarea binara
void find(double x[], int n, double z, int& k);
Parametri de intrare :
x - vector cu elemente sortate crescator
n - dimensiunea lui x
z - valoarea cautata
Parametri de iesire :
k - indicele elementului z in lista.
Preconditie :
x[0] <= x[1] <= ... <= x[n - 1]
*/
void find(double x[], int n, double z, int& k)
{
int ls, ld;
ls = 0;
ld = n - 1;
while(ls <= ld)
{
k = (ls + ld) / 2;
if(x[k] == z)
return;
if(x[k] < z)
ls = k +1;
else
ld = k - 1;
}
k = n;
return;
}
Un program ce testeaz funcia este urmtorul
int main()
{
double a[6] = {-2.35, 1.77, 3.14, 4.2, 5.12, 7.09};
double z;
int k;
81
z = 3.14;
find(a, 6, z, k);
if(k < 6)
cout << "elementul " << z << " are rangul " << k << endl;
else
cout << "elementul " << z << " nu este in lista" << endl;
return 0;
}
Recapitulm n tabelul de mai jos prototipuri posibile pentru funcia find i modul de
apelare a funciei.
Prototip Apelare
int find(double x[], int n, double z); k = find(a, n, z);
void find(double x[], int n, double z, int& k) ; find(a, n, z, k);
4.3 Pointeri la funcii
O funcie are un punct de intrare care este linia ei de definiie. Acest punct de intrare
este o adres de memorie care poate fi atribuit unei variabile tip pointer i care poate
fi utilizat la apelarea funciei. In programul care apeleaz funcia trebuie s definim o
variabil pointer de tipul funciei respective. Considerm ablonul definiiei unei
funcii
Tip nume(lista de parametri);
O variabil de tip pointer la aceast funcie are definiia
Tip (*ident) (lista de parametri);
unde Tip i lista de parametri din variabila pointer corespund cu tip i lista de
parametri din ablonul definiia funciei, iar ident este numele variabilei pointer.
Exemplu. Fie o funcie f care calculeaz suma a dou numere ntregi x, y. Definiia
funciei este urmtoarea
int f(int x, int y)
{
return x + y;
}
Sablonul funciei este
int f(int, int);
In ablon trebuie s specificm tipul parametrilor, ca mai sus. In ablon putem
specifica i numele parametrilor. Numele parametrilor din ablon pot fi diferite de
cele din definiia funciei. De exemplu, ablonul funciei anterioare putea fi declarat
ca
int f(int a, int b);
O variabil de tip pointer la aceast funcie este
int (*ident)(int , int);
unde ident este numele variabilei tip pointer. Apelarea unei funcii folosind o variabil
de tip pointer se face astfel:
se atribuie variabilei pointer ca valoare numele funciei,
se aplic operatorul () asupra adresei coninut n variabila tip pointer, ca n
exemplul de mai jos.
82
Vom apela acum funcia f definit mai sus direct i folosind o variabil tip pointer.
int main()
{
int a = 5, b = 7, sm;
// apelarea directa a funciei
sm = f(a, b);
cout << suma numerelor << a << si << b << este sm << endl;
// se defineste variabila ptr, pointer la functia f
int (*ptr)(int, int);
// se initializeaza variabila ptr
prt = f;
// se apeleaza functia f prin pointer
sm = (*ptr)(a, b);
cout << suma numerelor << a << si << b << este sm << endl;
return 0;
}
In consecin, apelarea unei funcii se poate face n dou feluri:
se scrie numele funciei, urmat n paranteze de parametri actuali, ca termen
ntr-o expresie (se aplicnd operatorul () asupra numelui funciei),
se definete o variabil de tip pointer la funcie ; se atribuie acestei variabile ca
valoare adresa punctului de intrare n funcie (adresa punctului de intrare este
chiar numele funciei) ; apelarea se face scriind variabila de tip pointer, urmat
n paranteze de parametrii funciei, ca termen ntr-o expresie (aplicnd
operatorul () asupra adresei coninut n variabila tip pointer).
4.4 Interpretarea instruciunilor ce conin pointeri
O instruciune de declarare a unui tip pointer la funcie poate conine urmtorii
operatori, scrii aici n ordinea prioritilor :
1. [], ()
2. *, const
3. tipuri de date
Operatorii [] i () sunt asociativi la stnga. Operatorii * i const sunt asociativi la
dreapta. Instruciunea poate conine i un identificator, care este numele variabilei al
crei tip este definit de ablon. Interpretarea abloanelor se face considernd
operatorii n ordinea prioritii lor, [] reprezint un tablou, () reprezint o funcie, *
reprezint un pointer, etc. Fiecare operator se nlocuiete cu o expresie :
1. () funcie care returneaz,
2. [] tablou de,
3. * - pointer la,
4. const constant,
5. tip tip.
Se ncepe cu identificatorul din ablon.
Exemplu. Fie ablonul
float *f();
Interpretarea lui se face astfel:
float *f() f este
float * () funcie care returneaz
83
float * pointer la o valoare
float tip float
f este o funcie ce returneaz un pointer de tip float.
Exemplu. Fie ablonul
float (*pf) ();
Interpretarea lui se face astfel:
pf este pointer la o funcie ce returneaz o valoare de tip float. Operatorii ()
consecutivi din linia a doua s-au citit de la stnga la dreapta.
Exemplu. Sablonul
int (*r) (int, int);
se interpreteaz ca mai sus :
r este un pointer la o funcie cu doi parametri de tip int, int i care returneaz o valoare
de tip int.
Exemplu. Fie urmtoarea instruciune
const char * pc = abcd;
Reamintim c irul de caractere abcd este un vector cu 5 componente tip caracter,
a, b, c, d i \0 (irurile sunt terminate prin zero). Prin aceast instruciune
compilatorul genereaz un vector cu 5 componente tip caracter i atribuie adresa
acestui vector variabilei tip pointer pc. Interpretarea tipului variabilei pc se face astfel:
const char * pc pc este
const char * pointer la
const char constant
char char
Aceast instruciune declar un pointer la un vector constant de caractere (am inut
cont c operatorii * i const se citesc de la dreapta la stnga). In consecin, nu putem
scrie
pc[3] = c;
deoarece irul de caractere este constant, dar putem scrie
pc = ghij;
adic pointerul poate fi iniializat cu o alt valoare.
Exemplu. Fie acum instruciunea
char * const cp = abcd;
Interpretarea ei se face astfel:
char * const cp cp este
char * const constant
char * pointer la
char char
Instruciunea declar un pointer constant la un vector de caractere. In consecin,
putem modifica vectorul, de exemplu putem scrie
cp[3] = c;
float (*pf)() pf este
float (*) () pointer la
float () funcie care returneaz o valoare
float tip float
84
dar nu putem scrie
cp = ghij;
deoarece pointerul este o constant.
Exemplu. Considerm ablonul
char (*(*f())[])();
Interpretarea lui se face astfel:
char (*(*f())[])() f este
char (*(*())[])() funcie ce returneaz
char (*(*)[])() pointer la
char (*[])() vector de
char (*)() pointeri la
char () funcie ce returneaza o valoare
char tip char
f este o funcie ce returneaz un pointer la un vector de pointeri la o funcie ce
returneaz o valoare de tip char.
4.5 Pointeri i tablouri unidimensionale
Prin definiie, o variabil tip tablou conine adresa primului element al tabloului
(elementul cu indicele zero al tabloului). Variabila tip tablou este un pointer
constant la primul element al tabloului. Fie de exemplu instruciunea
char buffer[20];
Variabila buffer conine adresa primului element al tabloului (adresa elementului
buffer[0]). In consecin, aceast valoare poate fi atribuit unei variabile tip pointer la
tipul elementelor tabloului. Fie instruciunile :
char buffer[20] ;
char * px;
px = buffer;
variabila px conine adresa variabilei buffer[0] i, n consecin,
*px
este valoarea variabilei buffer[0].
La execuia unui program, numele unui tablou este convertit la un pointer la primul
element al tabloului.
Prin definiie, n cazul unui tablou x, scrierile :
x[i]
i
*(x + i)
sunt echivalente. Ele reprezint valoarea variabilei x[i].
La fel, scrierile
&x[i]
i
x + i
sunt echivalente. Ele reprezint adresa variabilei x[i].
Vom rescrie funcia ce permut dou variabile ntregi ca mai jos. Funcia va avea ca
parametri doi vectori cu cte o component.
// functie ce permute valorile a doua variabile intregi
void perm(int a[], int b[])
{
85
int c;
c = a[0];
a[0] = b[0];
b[0] = c;
return;
}
Funcia main() ce apeleaz funcia perm() este urmtoarea.
int main()
{
int x[1] = {3}, y[1] = {-4};
// scrie variabilele inainte de permutare
cout << " valori initiale : " << "x = " << x[0] << " y = " << y[0] <<
endl;
perm(x, y);
// scrie variabilele dupa permutare
cout <<" valori permutate : " << "x = "<< x[0]<<" y = "<<y[0] <<
endl;
return 0;
}
Tabelul de mai jos arat cele dou variante ale funciei ce permut dou variabile, cea
de la nceputul capitolului, cu pointeri i cea de mai sus, cu vectori.
void perm(int* a, int* b)
{
int c ;
c = *a ;
*a = *b ;
*b = c ;
return;
}
void perm(int a[], int b[])
{
int c;
c = a[0];
a[0] = b[0];
b[0] = c;
return;
}
Deoarece instruciunile :
*a
i
a[0]
sunt echivalente, cele dou funcii sunt identice.
Exemplu. Fie x un vector cu 5 componente tip float i un vector z cu 3 componente
ntregi. Se cere s afim componentele vectorilor. Vom face acest lucru utiliznd
elementele vectorului x[i] i pointeri *(z + i). Un program posibil este urmtorul.
#include <iostream>
using namespace std;
int main ()
{
float x[5] = {1.23, 6.89, -8.5e2, 0, 9.01};
int z[3] = {-11, 2, 4};
int i;
86
// scrie componentele vectorului x
for(i = 0; i < 5; i++)
cout << \t << x[i];
cout << endl;
// scrie componentele vectorului z
for(i = 0; i < 3; i++)
cout << \t << *(z + i);
cout << endl;
return 0 ;
}
Rezultatul rulrii programului este cel de mai jos
Programul poate fi rescris astfel. Definim o variabil px de tip pointer la tipul float ce
va conine pe rnd adresele componentelor vectorului x, i o variabil pz de tip pointer
la tipul int pentru vectorul z. Variabilele vor fi initializate cu adresele vectorilor.
# include <iostream>
using namespace std;
int main()
{
float x[5] = {1.23, 6.89, -8.5e2, 0, 9.01};
int z[3] = {-11, 2, 4};
int i;
float * px;
px = x;
// scrie componentele vectorului x
for(i = 0; i < 5; i++)
cout << \t << *(px + i) ;
cout << endl;
int * pz;
pz = z;
// scrie componentele vectorului z
for(i = 0; i < 3; i++)
cout << \t << pz[i];
cout << endl;
return 0;
}
Programul de mai sus are rolul de a arta echivalena ntre pointeri i tablouri.
Operaiile cu variabilele tip pointer sunt adunarea i scderea unor valori ntregi. La
adunarea sau scderea unei valori ntregi dintr-o variabil tip pointer, la variabil se
adun sau se scade valoarea respectiv nmulit cu dimensiunea n octei a tipului
variabilei (unu pentru tipul char, patru pentru tipul int sau float, etc.) astfel nct
variabila tip pointer conine adresa unui alt element de tablou. Fie din nou
instruciunile :
87
char buffer[20];
char * px;
px = buffer;
Expresia px este adresa elementului buffer[0], iar *px este chiar valoarea elementului
buffer[0]. Expresia px + 1 este adresa variabilei buffer[1], iar *(px + 1) este chiar
valoarea componentei buffer[1]. In general, px + i este adresa variabilei buffer[i] iar
*(px + i) este chiar valoarea componentei buffer[i]. Pentru a aduna sau scdea o
unitate dintr-o variabil de tip pointer se pot folosi operatorii ++ i --.
Putem s rescriem programul precedent astfel :
# include <iostream>
using namespace std;
int main()
{
float x[5] = {1.23, 6.89, -8.5e2, 0, 9.01};
int i;
float * px;
px = x;
// scrie componentele vectorului x
for(i = 0; i < 5; i++)
{
cout << \t << *px;
px = px + 1;
}
cout << endl;
return 0;
}
Echivalent, instruciunea for n care scriem poate fi
for(i = 0; i < 5; i++)
{
cout << \t << *px++;
}
Pentru a vedea c ultima form este corect, reamintim modul de execuie a
operatorului postfix ++. Instruciunea
*px++
este echivalent cu instruciunile
*px;
px++;
In expresie se utilizeaz valoarea neincrementat a variabilei, dup care variabila este
incrementat. In consecin, se utilizeaz expresia *px dup care variabila px este
incrementat. Vom incheia acest paragraf cu un program care s scrie adresele
componentelor unui vector utiliznd o variabil tip pointer
# include <iostream>
using namespace std;
int main()
{
88
float x[5] = {1.23, 6.89, -8.5e2, 0, 9.01};
int i;
float * px;
px = x;
// scrie adresele componentele vectorului x
cout << adresele componentelor vectorului << endl;
for(i = 0; i < 5; i++)
{
cout << x[ << i << ]\t << px << endl;
px = px + 1;
}
return 0;
}
Rezultatul rulrii programului este cel de mai jos. Dup cum se observ, adresele
cresc cu 4 octei. De ce ?.
4.6 Poineri i iruri tip C
irurile tip C sunt vectori de caractere ASCII la care ultimul caracter este \0.
Bibliotecile standard ale limbajelor C i C++ au numeroase funcii pentru lucrul cu
iruri tip C i caractere ASCII. Cteva dintre aceste funcii, ale cror prototipuri se
afl n bibliotecile <cstring> sau <string.h>, au fost prezentate mai nainte i sunt
repetate aici.
Funcia
char * strcpy(char * s1, const char * s2);
copiaz irul s2 n s1, inclusiv caracterul \0. Funcia returneaz adresa irului s1.
Parametrul s2 este declarat de tip const, adic funcia nu modific irul s2.
Funcia
int strcmp(const char * s1, const char * s2);
compar cele dou iruri, caracter cu caracter, pn la primul caracter diferit.
Rezultatul este un numr < 0, = 0, sau > 0, dup cum sunt caracterele diferite
comparate.
Funcia
size_t strlen(const char * s);
calculeaz lungimea irului s (numrul de caractere ce preced \0).
Tipul size_t este tipul unsigned int.
Funcia
char * strchr(const char * s, int c);
caut prima apariie a caracterul c (convertit la char) n irul s. Funcia returneaz un
pointer la caracterul gsit sau NULL n caz contrar. Valoarea NULL este predefinit
n limbajele C i C++.
89
Vom exemplifica acum utilizarea funciilor de manipulare a caracterelor ASCII
tiprind caracterele alfabetice, alfanumerice, cifrele, literele mici i mari ale codului
ASCII.
Aceste funcii au fost prezentate anterior i prototipurile lor din bibliotecile <cctype>
sau <ctype.h> sunt repetate aici.
Funcii de manipulare a caracterelor ASCII
Funcie Descriere
int isalnum (int c); Test dac un caracter este alfanumeric
int isalpha (int c); Test dac un caracter este alfabetic
int isdigit (int c); Test dac un caracter este o cifr zecimal
int isxdigit(int c); Test dac un caracter este o cifr hexazecimal
int islower (int c); Test dac un caracter este liter mic
int isspace (int c); Test dac un caracter este spaiu ( , \n, \t)
int isupper (int c); Test dac un caracter este liter mare
Vom utiliza constanta predefinit UCHAR_MAX din biblioteca <climits> sau
<limits.h> care d valoarea maxim a unui obiect de tipul unsigned char (aceast
valoare este 255). Vom construi o funcie care genereaz toate caracterele codului
ASCII (toate numerele de la 0 la 255) i vom tipri n fiecare caz caracterele pentru
care funciile de mai sus returneaz o valoare pozitiv (valoarea adevrat). Funcia pe
care o vom construi va avea ca parametru un pointer de tipul funciilor de mai sus.
Prototipul acestor funcii este
int nume(int);
iar un pointer la o asemenea funcie are tipul
int (* fn)(int);
Caracterele vor fi generate cu o instruciune for a crei variabil de control are tipul
unsigned char. Definiia funciei este urmtoarea.
#include <cstdlib>
#include <climits>
#include <iostream>
using namespace std;
/*
functie care afisaza caractere ASCII
Parametri de intrare
nume numele functiei, sir de caractere
fn pointer la functie
*/
void prtcars(const char * nume, int (*fn)(int))
{
unsigned char c;
// scrie numele functiei
cout << nume << : ;
/* genereaza toate caracterele si scrie pe cele pentru
care functia are valoarea adevarat */
for(c = 0; c < UCHAR_MAX; ++c)
90
if((*fn)(c))
cout << c;
cout << endl;
return;
}
Urmtorul program testeaz aceast funcie.
int main()
{
prtcars(isdigit, &isdigit);
prtcars(islower, &islower);
prtcars(isupper, &isupper);
prtcars(isalpha, &isalpha);
prtcars(isalnum, &isalnum);
prtcars(isxdigit, &isxdigit);
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Vom exemplifica utilizarea pointerilor la tablouri unidimensionale construind o
funcie care copiaz un ir n altul (ca i funcia standard strcpy). Prototipul ei va fi
void copy(char * s1, char * s2);
unde irul s2 este sursa iar irul s1 este destinaia.
O prim variant este cea n care copiem un vector de caractere n alt vector.
void copy(char * s1, char * s2)
{
int i;
int len = strlen(s2);
for(i = 0; i < len + 1; i++)
s1[i] = s2[i];
return;
}
Reamintim c, o variabil tip tablou este un pointer constant la primul element al
tabloului.
O alt variant este
void copy(char * s1, char * s2)
{
int i;
91
for(i = 0; (s1[i] = s2[i]) != \0; i++)
;
return;
}
Condiia care se testeaz pentru execuia instruciunii for este
(s1[i]=s2[i]) != \0;
Ea este executat astfel. Se execut instruciunea de atribuire
s1[i] = s2[i]
dup care rezultatul s1[i] este comparat cu valoarea \0, care este caracterul de
terminare al irului s2. Reamintim c operatorul de atribuire are ca rezultat valoarea
atribuit, n cazul nostru s1[i].
O alt variant utilizeaz pointeri n locul elementelor de tablouri i faptul c irurile
tip C sunt terminate prin zero.
void copy(char * s1, char * s2)
{
while(*s1 = *s2)
{
s1++;
s2++;
}
return;
}
Expresia testat de ctre instruciunea while este
*s1 = *s2
Rezultatul evalurii expresiei este valoarea *s1 (un caracter copiat n irul s1).
Instruciunea while se execut pn cnd aceast valoare este 0 (pn cnd caracterul
\0 al irului s2 este copiat n s1).
In final putem scrie varianta urmtoare.
void copy(char * s1, char * s2)
{
while(*s1++ = *s2++)
;
return;
}
care este o scriere condensat a variantei anterioare.
4.7 Pointeri i tablouri multidimensionale
Tablourile se definesc conform urmroarei diagrame sintactice
92
Elementele tablourilor sunt memorate pe linii. De exemplu, tabloul
int x[2][3];
este compus din elementele :
x[0][0], x[0][1], x[0][2], x[1][0], x[1][1], x[1][2]
El este considerat ca un vector cu 2 elemente, x[0] i x[1], fiecare element fiind un
vector cu trei elemente de tip int. Operatorul [] selecteaz un element al unui
tablou (este operator de selecie). El este un operator binar, operandul stng
fiind un tablou, operandul drept fiind un indice. De exemplu, x[0] selecteaz
primul element din x care este vectorul
x[0][0], x[0][1], x[0][2]
Aplicnd operatorul de selecie asupra lui x[0], selectm un element al acestuia, de
exemplu x[0][1]. Reamintim c operatorul de selecie este asociativ la stnga. Indicii
unui element de tablou pot fi orice expresii ntregi pozitive.
Numele unui tablou este o variabil de tip pointer ce conine adresa primului element
al matricei. In cazul matricei de mai sus x conine adresa elementului x[0][0].
Elementele tabloului pot fi selectate folosind operatorul *.
Fie de exemplu tabloul
double a[3];
Dup cum am spus anterior, prin definiie, expresiile
a[i]
i
*(a + i)
sunt echivalente.
Aplicm aceast regul pentru tabloul de mai sus
int x[2][3];
Expresia
x[i][j]
este echivalent cu
(x[i])[j]
care, conform celor de mai sus, este echivalent cu
*((x[i]) + j)
care este echivalent cu expresia
*(*(x + i) + j)
Reamintim c
x[i]
selecteaz un element al tabloului x, aici o linie, deci, echivalent
*(x + i)
selecteaz o linie a tabloului x.
Vom prezenta un exemplu de utilizare a pointerilor la tablouri cu dou dimensiuni.
Fie o matrice x cu dou linii i trei coloane, cu elemente de tip ntreg. Vom afia
pentru nceput adresele celor dou linii ale matricei, cu trei programe echivalente.
Dup cum am spus mai sus, cele dou linii ale matricei sunt vectori cu trei
componente, iar adresele lor sunt x[0] i x[1], sau, echivalent, *(x + 0) i *(x + 1).
Prima variant a programului este cea de mai jos.
#include <iostream>
using namespace std;
int main()
93
{
int x[2][3];
int i;
// afisarea adreselor liniilor matricei x
for(i = 0; i < 2; i++)
cout << "adresa lui x[" << i << "] = " << x[i] << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Dup cum se observ, adresa lui x[1] este mai mare cu 12 octei (0xc) dect adresa lui
x[0]. De ce?
A doua variant afiaz adresele liniilor matricei x utiliznd expresia echivalent a lui
x[i], care este *(x + i).
#include <iostream>
using namespace std;
int main()
{
int x[2][3];
int i;
// afisarea adreselor liniilor matricei x
for(i = 0; i < 2; i++)
cout << "adresa lui x[" << i << "] = " << *(x + i) << endl;
return 0;
}
A treia variant a programului este urmtoarea. Reamintim c linia matricei este un
vector cu elemente ntregi. Definim o variabil y de tip pointer la ntreg
int * y;
In program, ea va primi ca valoare adresa liniei matricei x (adresa unui vector cu
elemente ntregi).
#include <iostream>
using namespace std;
int main()
{
int x[2][3];
int i;
int * y;
94
for(i = 0; i < 2; i++)
{
y = x[i];
cout << "adresa lui x[" << i << "] = " << y << endl;
}
return 0;
}
Menionm c, n locul instruciunii
y = x[i] ;
se putea scrie
y = *(x + i) ;
Vom afia acum adresele elementelor matricei x cu dou programe echivalente.
Adresele elementelor primei linii sunt x[0] + 0, x[0] + 1, x[0] + 2. Adresele
elementelor limiei a doua sunt x[1] + 0, x[1] + 1, x[1] + 2. In general, adresa
elementului x[i][j] al unei matrice x este
x[i] + j
Prima variant a programului este urmtoarea
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int x[2][3];
int i, j;
// afisarea adreselor elementelor matricei x
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
{
cout << "adresa lui x[" << i << "][" << j << "] = " << x[i] + j << endl;
}
}
return 0;
}
Rezultatul rulrii programului este cel de mai jos. Dup cum se observ, adresele
cresc cu patru octei. De ce ?
O alt variant a programului este cea de mai jos. In aceast variant, expresia x[i] + j
este nlocuit cu expresia *(x + i) + j.
95
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int x[2][3];
int i, j;
// afisarea adreselor elementelor matricei x
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
{
cout << "adresa lui x[" << i << "][" << j << "] = " << *(x + i) + j << endl;
}
}
return 0;
}
In final prezentm un program ce afiaz componentele matricei x. Elementul x[i][j]
al matricei x este, conform regulilor de mai sus,
*(*(x + i) + j)
Elementele matricei sunt afiate pe cte trei coloane, fiecare element este urmat de
dou spaii. Pentru a prescrie dimensiunea cmpului utilizm n instruciunea cout
funcia setw(4).
#include <iostream>
#include <iomanip>
using namespace std;
int main(int argc, char *argv[])
{
int x[2][3] = {{1, 2, 3}, {-2, -4, -6}};
int i, j;
// afisarea elementelor matricei x
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
cout << setw(4) << *(*(x + i) + j) << " ";
cout << endl;
}
return 0;
}
Rezultatul programului este afiat mai jos.
96
4.8 Parametrii funciei main. Parametrii liniei de comand
Prototipul funciei main() utilizat pn acum este
int main();
Funcia main poate avea i urmtorul prototip
int main(int argc, char * argv[]);
Considerm al doilea parametru din aceast definiie
char * argv[]
El este interpretat astfel
char * argv [] argv este
char * [] vector de
char * pointeri la
char char
argv reprezin un vector de pointeri la caracter, iar argc este dimensiunea acesui
vector. Pointerii sunt adresele unor iruri de caractere ce corespund argumentelor
liniei de comand. Primul ir de caractere este chiar numele programului. Ca exemplu
fie un program ce tiprete argumentele liniei de comand.
int main(int argc, char * argv[])
{
int i;
for(i = 0; i < argc; ++i)
cout << argv[i] << endl;
return 0;
}
Un mod de a furniza parametri liniei de comand este de a rula programul ntr-o
fereastr DOS, de la linia de comand
aplicatie.exe parametru1 parametru2 parametrun
Alt mod este de a introduce aceti parametri ntr-o caset de dialog a mediul de
programare.
4.9 Alocarea dinamic a memoriei
Alocarea memoriei unei variabile scalare sau tablou se face astfel :
de ctre compilator, la declararea unei variabile de un tip oarecare
cu funcia malloc()
cu operatorul new
Alocarea memoriei unei variabile cu funcia malloc() sau operatorul new se face la
execuia programului.
In programele n care alocm memorie cu funcia malloc() sau cu operatorul new
trebuie s definim o variabil pointer de un tip corespunztor ce va conine adresa
memoriei alocate.
Alocarea dinamic a memoriei cu funcia malloc()
97
Alocarea de memorie se poate face cu funcia malloc() cu prototipul
void * malloc(int size);
unde size este numrul de octei de alocat. Tipul funciei este void*, adic un pointer
de tip nespecificat, i el trebuie convertit n pointer la tipul alocat. Eliberarea
memoriei alocate se face cu funcia
void free(p);
unde p este o variabil tip pointer ce conine adresa zonei de memorie ce va fi
eliberate. Prototipul acestor funcii se afl n biblioteca <cstdlib> pentru limbajul C+
+, iar n cazul programelor C, numele bibliotecii este <stdlib.h>.
Exemplu. Vom aloca un vector cu 10 componente ntregi, l vom iniializa i l vom
scrie pe ecran. In program definim o variabil v de tip pointer la ntreg ce va conine
adresa memoriei alocate de funcia malloc(). Dup alocare, vectorul poate fi utilizat
pentru calcule. La sfritul programului, memoria alocat trebuie eliberat cu funcia
free().
# include <iostream>
# include <cstdlib>
using namespace std;
int main()
{
int j;
int * v = (int *) malloc(10 * sizeof(int));
for(j = 0; j < 10; j++)
{
// *(v + j) = 2 * j;
v[j] = 2 * j;
}
for(j = 0; j < 10; j++)
{
cout << *(v + j) << ;
// cout << v[j] << ;
}
cout << endl;
free(v); // elibereaza memoria
return 0;
}
Instruciunea
int * v = (int *) malloc(10 * sizeof(int));
aloc un vector cu 10 componente de tipul int. Ea are acelai efect cu instruciunea
int v[10];
dar vectorul v[10] este alocat de compilator i nu poate fi ters, n timp ce vectorul
v[10], alocat cu funcia malloc, poate fi ters. Instruciunea convertete pointerul
furnizat de funcia malloc() la tipul (int *).
Rezultatul rulrii programului este cel de mai jos.
98
In continuare vom arta cum se aloc memorie pentru o matrice. Pentru a determina
tipul variabilei pointer ce va conine memoria alocat cu funcia malloc(), vom analiza
cazul unei matrice x cu elemente ntregi cu dou linii i trei coloane. Definiia acestei
matrice este
int x[2][3] ;
Matricea x este format din doi vectori, x[0] i x[1], ce corespund celor dou linii ale
matricei. Vectorul x[0] de exemplu, conine elementele x[0][0], x[0][1], x[0][2]. Tipul
vectorilor x[0] i x[1] este, aa cum am spus mai sus
int *
Matricea x const deci dintr-un vector cu dou componente al cror tip este int *, deci
tipul variabilei x va fi
int **
Prezentm mai jos un program n care am definit matricea
int x[2][3] = {{1, 2, 3}, {-4, -5, -6}};
Vom afia adresele liniilor matricei, adresele elementelor matricei i elementele
matricei utiliznd o variabil tip pinter.
Definim un vector y cu dou elemente de tipul int * ce va fi iniializat cu adresele
celor dou linii ale matricei x, x[0] i x[1] ;
int * y[2];
y[0] = x[0];
y[1] = x[1];
Definim n final o variabil z de tip pointer ce va fi iniializat cu adresa vectorului y.
Tipul variabilei z va fi int **
int ** z ;
Iniializarea variabilei z cu adresa vectorului y se face cu instruciunea
z = y;
In continuare prezentm secvene din program n care utilizm variabila z pentru a
afia adresele liniilor matricei, adresele elementelor matricei i valorile elementelor
matricei x.
Vom afia adresele liniilor matricei x cu cele dou expresii echivalente, z[i] i *(z + i)
// afisaza adresele liniilor matricei x
cout << "adresele liniilor matricei" << endl;
for(i = 0; i < 2; i++)
{
cout << "adresa lui x[" << i << "] : " << z[i] << endl;
}
// afisaza adresele liniilor matricei x
cout << "adresele liniilor matricei" << endl;
for(i = 0; i < 2; i++)
{
cout << "adresa lui x[" << i << "] : " << *(z + i) << endl;
}
Afim apoi adresele elementelor matricei x cu cele dou expresii echivalente, z[i] + j
i *(z + i) + j
99
// afisaza adresele elementelor matricei
cout << "adresele elementelor matricei" << endl;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
cout << "adresa lui x[" << i << "][" << j << "] : "
<< z[i] + j << endl;
}
// afisaza adresele elementelor matricei
cout << "adresele elementelor matricei" << endl;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
cout << "adresa lui x[" << i << "][" << j << "] : "
<< *(z + i) + j << endl;
}
In final, afim elementele matricei x pe linii, cu cele dou expresii echivalente, z[i][j]
i *(*(z + i) + j
// afisaza elementele matricei x pe linii
cout << "elementele matricei" << endl;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
cout << setw(3) << z[i][j] << " ";
cout << endl;
}
// afisaza elementele matricei x pe linii
cout << "elementele matricei" << endl;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
cout << setw(3) << *(*(z + i) + j) << " ";
cout << endl;
}
Programul complet este cel de mai jos.
#include <iostream>
#include <iomanip>
using namespace std;
int main(int argc, char *argv[])
{
int x[2][3] = {{1, 2, 3}, {-4, -5, -6}};
int * y[2];
int ** z;
100

z = y;

y[0] = x[0];
y[1] = x[1];
int i, j;
// afisaza adresele liniilor matricei x
cout << "adresele liniilor matricei" << endl;
for(i = 0; i < 2; i++)
{
cout << "adresa lui x[" << i << "] : " << z[i] << endl;
}
// afisaza adresele liniilor matricei x
cout << "adresele liniilor matricei" << endl;
for(i = 0; i < 2; i++)
{
cout << "adresa lui x[" << i << "] : " << *(z + i) << endl;
}
// afisaza adresele elementelor matricei x
cout << "adresele elementelor matricei" << endl;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
cout << "adresa lui x[" << i << "][" << j << "] : "
<< z[i] + j << endl;
}
// afisaza adresele elementelor matricei x
cout << "adresele elementelor matricei" << endl;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
cout << "adresa lui x[" << i << "][" << j << "] : "
<< *(z + i) + j << endl;
}
// afisaza elementele matricei x pe linii
cout << "elementele matricei" << endl;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
cout << setw(3) << z[i][j] << " ";
cout << endl;
}
// afisaza elementele matricei x pe linii
cout << "elementele matricei" << endl;
for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
101
cout << setw(3) << *(*(z + i) + j) << " ";
cout << endl;
}
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Exemplu. Vom aloca memorie pentru o matrice cu elemente ntregi cu nlin linii i
ncol coloane, unde nlin i ncol se vor citi de la tastatur. Alocarea se face astfel. Dup
cum am spus mai sus, o matrice este compus din vectori corespunztori liniilor.
Fiecare linie a matricei este un vector cu componente de tip int, deci tipul vectorului
liniei este int*. Se aloc mai nti un vector de dimensiune nlin ce va conine elemente
de tip int*. Variabila z ce conine adresa acestui vector are tipul int **. Apoi, fiecare
element al acestui vector, primete ca valoare adresa unui vector de ncol ntregi. In
final se atribuie valori componentelor matricei
z[i][j] = i * j ;
Programul este urmtorul
# include <iostream>
# include <cstdlib>
using namespace std;
int main()
{
int i, j;
int nlin, ncol;
cout << "introduceti # de coloane "; cin >> ncol;
cout << "introduceti # de linii "; cin >> nlin;
// aloca un vector de dimensiune nlin de pointeri de tip int *
102
int ** z = (int **) malloc(nlin * sizeof(int *));
// aloca pentru fiecare linie un vector de ncol intregi
for(i = 0; i < nlin; i++)
z[i] = (int*) malloc(ncol * sizeof(int));
// atribuie valori componentelor matricei
for(i = 0; i < nlin; i++)
for(j = 0; j < ncol; j++)
z[i][j] = i * j;
for(i = 0; i < nlin; i++)
{
for(j = 0; j < ncol; j++)
cout << z[i][j] << '\t';
cout << endl;
}
return 0;
}
Prima funcie malloc() aloc memorie pentru un vector cu nlin elemente de tipul int*.
A doua funcie malloc() aloc memorie pentru un vector cu ncol elemente de tipul int.
Rezultatul rulrii programului este artat mai jos.
Alocarea dinamic a memoriei cu operatorul new
Limbajul C++ permite alocarea memoriei necesar unei variabile cu operatorul new
cu formele:
pentru alocarea de memorie pentru un scalar
new tip
pentru alocarea de memorie pentru un vector cu un numr de componente dat
de expresie ntreag
new tip[expresie ntreag]
De exemplu, urmtoarele instruciuni aloc memorie pentru o variabil scalar
double * ptr;
ptr = new double;
*ptr = 1.7;
Operatorul new aloc memoria corespunztoare tipului de variabil i are ca rezultat
adresa zonei de memorie alocat. Primele dou instruciuni puteau fi scrise
double *ptr = new double;
Memoria alocat cu operatorul new poate fi eliberat cu operatorul delete cu formele
pentru eliberarea memoriei allocate unei variabile scalare
delete prt;
pentru eliberarea memoriei alocate unui vector
delete [] ptr;
Variabila ptr conine adresa memoriei alocat mai nainte cu new.
Operatorii new i delete sunt utili la operaii cu vectori.
Exemplu. Instruciunea
103
float *pf = new float[10];
aloc un vector de 10 componente de tip float, iat variabila pf conine adresa
vectorului. Un vector alocat cu new poate fi ters cu operatorul delete astfel
delete [] pf;
Exemplu. Vom aloca un vector cu 10 componente ntregi, l vom iniializa i vom
afia pe ecran componentele lui.
int main()
{
int * v = new int[10];
int j;
for(j = 0; j < 10; j++)
{
*(v + j) = j;
// v[j] = j;
}
for(j = 0; j < 10; j++)
{
cout << v[j] << ;
// cout << *(v + j) << ;
}
cout << endl;
delete [] v;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Instruciunea
int * v = new int[10];
are acelai efect cu instruciunea
int x[10];
dar vectorul v[10] alocat cu new poate fi ters, vectorul v[10] alocat de compilator nu
poate fi ters.
Exemplu. Vom aloca o matrice cu nlin linii i ncol coloane, cu elemente ntregi, unde
nlin i ncol se vor citi de la tastatur. Pentru tipul variabilelor utilizate n program vezi
exemplu anterior. Elementele matricei vor primi aceleai valori ca n exemplul
anterior.
# include <iostream>
# include <cstdlib>
using namespace std;
int main()
{
104
int i, j;
int ** v;
int nlin, ncol;
cout << "introduceti # de coloane "; cin >> ncol;
cout << "introduceti # de linii "; cin >> nlin;
v = new int * [nlin];
for(i = 0; i < nlin; i++)
v[i] = new int[ncol];
for(i = 0; i < nlin; i++)
for(j = 0; j < ncol; j++)
v[i][j] = i * j;
for(i = 0; i < nlin; i++)
{
for(j = 0; j < ncol; j++)
cout << v[i][j] << '\t';
cout << endl;
}
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Instruciunea
v = new int * [nlin];
aloc memorie pentru un vector cu nlin elemente de tipul int*. La fel ca mai nainte
variabila tip pointer v are tipul int**.
105
5 Fiiere tip C
In limbajele C i C++ fiierele sunt considerate ca un ir ordonat de octei. Exist
dou tipuri de fiiere, text i binare.
un fiier tip text este un ir ordonat de caractere grupate n linii. Fiecare linie
const din zero sau mai multe caractere, urmate de un caracter \n.
un fiier binar este un ir ordonat de octei.
Menionm c, fiecare fiier pe disc are o etichet ce conine numele fiierului, adresa
de nceput i adresa de sfrit a fiierului.
In program, fiecare fiier este asociat unui obiect numit stream. Un stream este
asociat unui fiier prin operaia de deschidere a fiierului.
Pentru prelucrare, un fiier se deschide n citire. La deschiderea unui fiier n citire,
sistemul de operare caut pe disc fiierul cu numele specificat i memoreaz adresele
de nceput i de sfrit ale fiierului n obiectul stream asociat.
Cnd se creaz un fiier el se deschide n scriere. La deschiderea unui fiier n scriere,
se creaz o etichet corespunztoare acestui fiier i se completez numele fiierului i
adresa de nceput n obiectul stream asociat.
Un stream este disociat de un fiier prin operaia de nchidere a fiierului. La
nchiderea unui fiier n creare se completeaz eticheta de pe disc a fiierului cu
adresa ultimului octet al fiierului.
Fiierele au un indicator de poziie ce d adresa urmtorului octet de citit sau scris.
Acest indicator este iniial pus la zero i este actualizat de operaiile de citire sau
scriere.
Un fiier poate avea o zon de memorie asociat (buffer) n care se citesc date din
fiier sau din care se scriu date n fiier.
Limbajele C i C++ au tipul de structur FILE ce poate nregistra toate informaiile
necesare pentru controlul unul stream, inclusiv:
numele fiierului,
indicatorul de poziie al fiierului,
indicator de eroare, poziionat dac a aprut o eroare intrare/ieire,
indicatorul de sfrit de fiier, eof, (end of file), poziionat la valoare true dac
n timpul operaiei de citire s-a ajuns la sfritul fiierului.
Prototipurile funciilor intrare / ieire pentru fiierele tip C sunt memorate n
fiiere header i trebuie semnalate compilatorului cu directive include.
Prototipurile funciilor ce efectueaz operaii intrare/ieire n limbajul C se afl
n biblioteca <stdio.h>. Pentru limbajul C++ numele fiierului header este
redefinit ca <cstdio>. Reamintim cele dou forme ale directivei include:
n cazul programelor C, directiva include conine numele fiierului antet
(header), inclusiv extensia h. Forma directivei include este
#include <stdio.h>
n cazul programelor C++, directiva include conine doar numele
fiierului antet (header). Forma directivei include este
#include <cstdio>
In exemplele urmtoare vom utiliza stilul C.
Funciile importante pentru prelucrarea fiierelor sunt urmtoarele :
FILE* fopen(const char * filename, const char * mode);
Aceast funcie deschide un fiier. Ea creaz o structur tip FILE cu informaii despre
fiier i are ca rezultat adresa ei. Parametrii acestei funcii sunt
106
1. filename - un pointer la irul de caractere ce d numele fiierului.
2. mode - un pointer la un ir de caractere ce d modul de deschidere.
Parametrul mode poate avea urmtoarele valori:
r fiier text deschis n citire
w - fiier text deschis n scriere
rb fiier binar deschis n citire
wb - fiier binar deschis n scriere
int fclose(FILE * stream); Funcia nchide un fiier. Ea returneaz valoarea
zero dac operaia a avut succes.
int feof(FILE * stream); Funcia returneaz o valoare diferit de zero dac s-a
detectat sfritul unui fiier n timpul operaiei de citire precedente.
int remove(char * filename); terge fiierul cu numele filename. Funcia
returneaz o valoare diferit de zero dac operaia avut succes.
La lansarea n execuie a unui program exist trei streamuri standard tip text
deschise:
stdin fiierul standard de intrare asociat tastaturii,
stdout - fiierul standard de ieire asociat ecranului.
stderr - fiierul standard de ieire pentru scrierea mesajelor de eroare.
Exemplu. Schema prelucrrii unui fiier text n citire este urmtoarea
FILE * fp;
fp = fopen(numefisier, r);
// prelucreaza fisierul
fclose(fp);
Menionm c la citirea secvenial a unui fiier, dup fiecare operaie de citire
trebuie s testm dac s-a ajuns la sfritul fiierului. La ntlnirea sfritului de
fiier indicatorul de sfrit de fiier este poziionat la o valoare diferit de zero
iar funciile de citire returneaz o valoare specific (constanta EOF, predefinit
n biblioteca <stdio.h>). Citirea secvenial a unui fiier se face cu instruciunea
while ce va testa indicatorul de sfrit de fiier cu funcia feof(), sau dac funcia
de citire a returnat valoarea EOF.
In cazul citirii datelor de la tastatur, (din fiierul stdin), sfritul de fiier este
indicat prin Ctrl+Z. Dup aceasta se apas tasta return.
Fiecare aplicaie are un director curent asociat. Atunci cnd parametrul
filename din instruciunea fopen() conine doar numele fiierului, fiierul se afl
n directorul curent. Pentru a prelucra fiiere n alte directoare, trebuie ca
parametrul filename s conin i calea, absolut sau relativ spre fiier.
5.1 Fiiere tip text
In cazul fiierelor tip text avem dou tipuri de funcii pentru operaiile de intrare /
ieire:
funcii pentru intrri / ieiri cu format, la care operaiile de citire / scriere se
fac sub controlul unui format,
funcii care citesc / scriu caractere.
107
5.1.1 Funcii intrare / ieire cu format
Aceste funcii scriu / citesc valori n / din fiiere pe disc, fiierele standard sau fiiere
tip ir de caractere (fiiere memorate n vectori tip char). Pentru scriere, aceste funcii
sunt:
int fprintf(FILE * stream, const char * format, argumente);
int printf( const char * format, argumente);
int sprintf(char * s, const char * format, argumente);
Funciile au ca rezultat numrul de octei scrii. In cazul unei erori intrare / ieire
funciile au ca rezultat un numr negativ.
Pentru citire, funciile sunt
int fscanf(FILE * stream, const char * format, argumente);
int scanf( const char * format, argumente);
int sscanf(char * s, const char * format, argumente);
Funciile au ca rezultat numrul de valori citite. In cazul unei erori intrare / ieire
funciile au ca rezultat un numr negativ.
funciile de scriere / citire pentru fiiere pe disc sunt fprintf i fscanf,
funciile de scriere / citire pentru fiierele standard stdout / stdin sunt printf i
scanf,
funciile de scriere / citire pentru fiierele tip ir de caractere sunt sprintf i
sscanf. In cazul fiierelor ir de caractere, parametrul s este un ir tip C n care
se scriu sau din care se citesc valorile variabilelor.
Menionm c, n cazul instruciunilor scanf, argumentele sunt parametri de
ieire, deci de tip pointer.
Parametrul format este un ir de caractere compus din specificatori de conversie
definii de % i alte caractere. Specificatorii de conversie sunt :
%d ntreg cu semn n baza 10
%i ntreg cu semn
%o ntreg n baza 8
%u ntreg n baza 10 far semn
%x ntreg n baza 16 cu semn
%c caracter
%s ir de caractere
%f numr real tip float
%lf numr real tip double
%e numr real cu exponent
%le- numr real tip double cu exponent
%p pointer
Dup % poate urma un numr ntreg care d lungimea minim a cmpului n cazul
instruciunii printf i lungimea maxim a cmpului n cazul instruciunii scanf. La
scrierea numerelor cu specificatorul %f putem specifica i numrul de cifre ale prii
subunitare. De exemplu specificatorul %7f descrie un numr real ce ocup 7 poziii,
iar specificatorul %7.3f descrie un numr real ce ocup 7 poziii din care 3 sunt pentru
partea subunitar.
108
Exemplu. S citim un numr hexazecimal de la tastatur i s-l scriem n format
zecimal i hexazecimal pe ecran.
#include<stdio.h>
int main()
{
int i;
printf(introduceti un numar hexazecimal\n);
scanf(%x, &i);
printf(\n numarul zecimal : %d\n numarul hexazecimal : %x\n, i, i);
}
Rezultatul programului este cel de mai jos.
Reamintim c, parametrii funciei scanf sunt de tip pointer (sunt parametri de ieire).
In consecin, la apelarea funciei scanf n program utilizm adresa variabilei i
scanf(%x, &i);
Exemplu. S citim dou numere reale de tip float de la tastatur i s afim suma lor.
Programul este urmtorul
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
float a, b, c;
printf("Dati doua numere reale\n");
scanf("%f %f", &a, &b);
c = a + b;
printf("a = %f, b = %f, a + b = %f\n", a, b, c);
return 0;
}
Rezultatul programului este cel de mai jos.
109
In cazul instruciunilor printf specificatorii de conversie dau formatul de scriere a
variabilelor, n timp ce restul caracterelor dn format sunt scrise n fiier. De exemplu,
instruciunea
printf(\n numarul zecimal : %d\n numarul hexazecimal : %x\n, i, i);
are ca rezultat scrierea caracterului \n, (trecerea la un nou linie), a irului numarul
zecimal i a valorii variabilei i, etc.
Exemplu. S definim o funcie ce calculeaz suma unui ir de numere reale tip double
citite de la tastatur. Numrul termenilor din ir va fi citit tot de la tastatur.
Programul este dat n continuare. Numerele reale tip double se citesc i se afiaz cu
specificatorul de conversie tip %lf.
#include <stdio.h>
#include <stdlib.h>
double sumnum()
{
int i, n;
double s, x;
printf("cate numere ?\n");
scanf("%d", &n);
s = 0;
for(i = 1; i <=n; i++)
{
scanf("%lf", &x);
s = s + x;
}
return s;
}

int main(int argc, char *argv[])
{
double rez;
rez = sumnum();
printf(" suma numerelor = %lf\n", rez);
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Exemplu. Vom scrie un program care calculeaz valoarea unei expresii
x x
x x
ln
) sin( 2 ) 2 cos( 1
+
+ +
110
pentru valori ale lui x cuprinse ntre 1 i 2 cu pasul 0.2. Valorile expresiei vor fi scrise
pe ecran i ntr-un fiier cu numele rez.txt. Specificatorii de format vor fi %4.1f pentru
variabila x i %6.2f pentru expresie. Valoarea lui x va ocupa 4 caractere, cu un
caracter pentru partea subunitar, cea a lui z ocup 6 caractere, dou pentru partea
subunitar. Valorile vor fi separate de un caracter tab, \t. Formatul se va ncheia cu
un caracter \n.
#include <stdio.h>
#include <math.h>
int main ()
{
FILE * fp;
fp = fopen("rez.txt", "w");
int i;
float x, z;
// scrie antetul pe ecran si in fisier
printf(" x \t f(x) \n");
fprintf(fp, " x \t f(x) \n");
x = 1.0;
// calculeaza valorile expresiei si scrie aceste valori in fisiere
for(i = 0; i < 6; i++)
{
z = (1 + cos(2 * x) + 2 * sin(x)) / (x + log(fabs(x)));
printf(" %4.1f \t %6.2f \n", x, z);
fprintf(fp, " %4.1f \t %6.2f \n", x, z);
x = x + 0.2;
}
fclose(fp);
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
In programul anterior puteam defini un ir de caractere cu specificatorii de format
char * format = %4.1f \t %6.2f \n;
sau
char format[] = %4.1f \t %6.2f \n;
i instruciunile de scriere devin
printf(format, x, z);
fprintf(fp, format, x, z);
Exemplu. Consideram un vector cu 5 componente numere reale. Vom afia
componentele vectorului sub forma
111
Element Valoare
x[0] ..
x[1] ..
# include <stdio.h>
int main()
{
double x[5] = {1.23, -23, 0.98, 4.12, 5.1};
int i;
printf(Element\tValoare);
// scrie componentele vectorului
for(i = 0; i < 5; i++)
printf(x[%d] \t\t%d\t, i, x[i]);
return 0;
}
5.1.2 Funcii intrare / ieire tip caracter
Fiierele text sunt formate din linii ce conin zero sau mai multe caractere, urmate de
caracterul \n. Pentru prelucrarea acestor fiiere este util s putem citi sau scrie
caractere i linii, adic iruri de caractere terminate cu caracterul \n.
Fiierele text au indicator de poziionare ce conine numrul urmtorului caracter de
scris sau de citit. Acest indicator are valoarea zero la deschiderea fiierului i apoi este
modificat de instruciunile de citire sau scriere a fiierului.
funcia
int fgetc(FILE * stream);
citete un caracter din fiierul de intrare i avanseaz indicatorul de poziionare al
fiierului cu valoarea unu. Dac se ntlnete sfritul de fiier, funcia returneaz
valoarea EOF care este o constant predefinit n biblioteca <stdio.h>.
funcia
int getchar();
citete un caracter din streamul stdin. Ea este echivalent cu funcia fgetc(stdin);
funcia
int fputc(int c, FILE * stream);
scrie caracterul c n fiierul specificat de stream. Funcia returneaz caracterul scris
sau constanta EOF n caz de eroare.
funcia
int putchar(int c);
scrie caracterul c in streamul stdout.
funcia
int ungetc(int c, FILE * stream);
pune caracterul c n streamul de intrare.
Urmtoarele funcii citesc sau scriu linii din fiiere tip text.
funcia
char * fgets(char * s, int n, FILE * stream);
citete cel mult n 1 caractere n vectorul s. Ea se oprete dac ntlnete caracterul
\n sau sfritul de fiier. Caracterul \n este introdus n irul citit. Dup ultimul
caracter citit se adaug caracterul \0 n vectorul s. Funcia returneaz adresa
vectorului s. La ntlnirea sfritului de fiier sau la o eroare funcia returneaz
valoarea NULL.
112
funcia
int fputs(const char * s, FILE * stream);
scrie irul de caractere s (terminat cu \0) n fluxul stream. Caracterul \0 nu este
scris.
funcia
int puts(char * s);
scrie irul de caractere s (terminat cu \0) n fluxul stdout. Caracterul \0 nu este
scris. Funcia scrie i caracterul \n dup ce a scris irul s.
Reamintim c funcia fputs() nu scrie caracterul \n n fiier, n timp ce funcia puts()
scrie caracterul \n n fiierul stdout dup ir. In consecin, pentru a afia cte un ir
de caractere pe rnd, la scrierea n fiierul stdout cu funcia fputs vom scrie :
fputs(x, stdout);
fputs(\n, stdout);
Schema principial de citire a unui fiier secvenial este urmtoarea. Presupunem c
utilizm o funcie read(file) ce citete cte un bloc de date din fiier. Operaia de citire
poziioneaz indicatorul de sfrit de fiier la o valoare diferit de zero dac n cursul
operaiei de citire s-a detectat sfrtul fiierului i acest lucru este detectat cu funcia
feof().
read(file)
while(! feof(file))
{
// prelucreaza blocul citit
read(file)
}
Exemplu. Vom exemplifica utilizarea acestor funcii cu un program care s calculeze
dimensiunea n octei a unui fiier. Programul va citi numele unui fiier de la tastatur,
i va deschide fiierul n citire. Funcia fopen are ca rezultat valoarea NULL dac
operaia nu a avut succes. Apoi se va citi cte un caracter pan la ntlnirea sfritului
de fiier i se vor numra octeii citii. Citirea secvenial a fiierului se va face cu
instruciunea while.
/* calculul dimensiunii unui fisier */
#include <stdio.h>
int main()
{
char name[64];
FILE* file;
char car;
int nb = 0;
printf("introduceti numele fisierului\n");
scanf("%s", name);
// deschide fisierul in citire
file = fopen(name, "r");
// test daca fisierul exista
if(file == NULL)
{
printf("nume de fisier eronat\n");
113
return EXIT_FAILURE;
}
// citeste cate un caracter pn la sfarsitul fisierului
// citeste un caracter
car = fgetc(file);
while(car != EOF)
{
// numara caracterul citit
nb = nb + 1;
// citeste un caracter
car = fgetc(file);
}
fclose(file);
printf("fisierul %s contine %d octeti\n", name, nb);
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos.
Un alt mod de a rezolva aceast problem va fi artat ulterior.
Exemplu. Vom face un program care s copieze un fiier existent n altul. Fie un fiier
n directorul curent. Vom copia acest fiier ntr-un alt fiier tot n directorul curent.
Numele celor dou fiiere se vor citi de la tastatur. Vom deschide cele dou fiiere i
vom testa dac operaia a avut success, utiliznd faptul c funcia fopen are ca rezultat
valoarea NULL dac operaia nu a avut succes. Apoi se citete repetat cte un caracter
din primul fiier i se scrie n al doilea, pn la ntlnirea sfritului primului fiier.
Citirea secvenial a primului fiier se va face cu instruciunea while.
# include <stdio.h>
int main ()
{
FILE * fin, * fout;
char nume1[64], nume2[64];
// citeste numele primului fisier
printf(introduceti numele fisierului ce va fi copiat\n);
scanf(%s, nume1);
fin = fopen(nume1, "r");
if(fin == NULL)
{
printf("fisierul %s nu exista\n", nume1);
return EXIT_FAILURE;
}
// citeste numele noului fisier
printf(introduceti numele noului fisier\n);
114
scanf(%s, nume2);
fout = fopen(nume2, "w");
if(fout == NULL)
{
printf("fisierul %s nu se poate crea", nume2);
return EXIT_FAILURE;
}
// copiaza fisierul
int c;
c = fgetc(fin);
while(c != EOF)
{
fputc(c, fout);
c = fgetc(fin);
}
fclose(fin);
fclose(fout);
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos.
Instruciunile ce citesc i scriu din fiiere pot fi rescrise ca
int c;
while((c = fgetc(fin)) != EOF)
fputc(c, fout);
5.2 Fiiere text tip ir de caractere
Limbajul ofer posibilitatea de a lucra cu fiiere tip text memorate ntr-un ir tip C.
Instruciunile de scriere / citire sunt:
int sprintf(char * s, const char * format, argumente);
int sscanf(char * s, const char * format, argumente);
Parametrul s este irul de caractere tip C n care se scriu sau din care se citesc datele.
Parametrul format este un ir de caractere compus din specificatori de conversie
definii de % i alte caractere. Asemenea fiiere sunt utile la conversii ale valorilor
numerice n iruri de caractere i invers.
Exemplu. Fie doi vectori tip char ce conin irurile 104 i 1.23e-1. Vom converti
aceste iruri n dou variabile tip int i double, vom face suma lor i o vom scrie pe
ecran.
#include <stdio.h>
int main()
{
115
char x[] = "104";
char y[] = "1.23e-1";
int i;
double z, r;
sscanf(x, "%d", &i);
sscanf(y, "%lf", &z);
printf("i = %d \n", i);
printf("y = %lf \n", z);
r = z + i;
printf("i + y = %lf\n", r);
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Reamintim c la citirea unei variabile tip double se utilizeaz specificatorul de
conversie %lf.
Exemplu. Fie variabilele
int x = 10;
double d = -2.34;
char c[] = "abc";
Vom scrie valorile acestor variabile, separate de spaii, ntr-un fiier tip ir de
caractere
char s[100] ;
Vom apoi citi valorile din vectorul s i vom iniializa alte variabile
int x1;
double d1;
char c1[10];
i vom scrie valorile lor pe ecran.
# include <stdio.h>
int main()
{
char s[100];
int x = 10;
double d = -2.34;
char c[] = "abc";
// scrie pe ecran variabilele initiale
printf("x = %d d = %f c = %s\n", x, d, c);
// scrie variabilele initiale in fisierul tip sir
sprintf(s, "%d %f %s", x, d, c);
// scrie pe ecran fisierul tip sir
printf("%s\n", s);
int x1;
double d1;
116
char c1[10];
// citeste valorile variabilelor din fisierul tip sir
sscanf(s, "%d %lf %s", &x1, &d1, c1);
// scrie valorile variabilelor pe ecran
printf("x = %d d = %f c = %s\n", x1, d1, c1);
return 0;
}
Pentru citirea variabilei d1 de tip double am utilizat specificatorul de conversie %lf.
Menionm c parametrii funciei sscanf sunt de tip pointer (sunt parametri de ieire).
In consecin, la apelarea funciei sscanf n program utilizm adresele variabilelor x1
i d1
sscanf(s, "%d %lf %s", &x1, &d1, c1);
Valorile scrise pe ecran cu cele trei instruciuni printf() sunt cele de mai jos.
5.3 Fiiere binare
Pentru scrierea i citirea de date din fiiere binare exist funciile fread i fwrite.
Aceste funcii pot citi sau scrie unul sau mai multe blocuri de date.
funcia
size_t fread(void * ptr, size_t size, size_t nmb, FILE * stream);
citete n vectorul ptr cel mult nmb blocuri de dimensiune size din fiierul stream.
Funcia returneaz numrul efectiv de blocuri citite. Indicatorul de poziie al fiierului
este avansat cu numrul de octei citii. In cazul ntlnirii sfaritului de fiier, funcia
returneaz valoarea 0.
funcia
size_t fwrite(const void * ptr, size_t size, size_t nmb, FILE * stream);
scrie n vectorul ptr cel mult nmb blocuri de dimensiune size n fiierul stream.
Funcia returneaz numrul efectiv de blocuri scrise. Indicatorul de poziie al
fiierului este avansat cu numrul de octei scrii.
pentru citirea elementelor n ordine aleatoare exist posibilitatea de a modifica
indicatorul de poziie al fiierului cu funcia
int fseek (FILE * stream, long int offset, int whence);
Noua valoare a indicatorului fiierului este obinut adugnd valoarea offset la
poziia specificat de whence care poate fi
SEEK_SET nceputul fiierului
SEEK_CUR poziia curent a indicatorului
SEEK_END sfritul fiierului
Dup o instruciune fseek, urmtoarea instruciune poate fi o operaie de citire sau
scriere.
funcia
long ftell(FILE* stream);
are ca rezultat valoarea indicatorului de poziie al fiierului.
117
Exemplu. Vom calcula lungimea unui fiier text modificnd indicatorul fiierului
astfel nct noua lui poziie s fie sfritul fiierului. Lungimea fiierului va fi
valoarea indicatorului de poziie.
#include <stdio.h>
// fisiere text C. Calculul lungimii fiserului
// prin modificarea indicatorului de pozitie.
int main()
{
char name[256];
FILE* file;
int nb = 0;
printf("introduceti numele fisierului\n");
scanf("%s", name);
// deschide fisierul in citire
file = fopen(name, "r");
if(file == NULL)
{
printf("nume de fisier eronat\n");
return EXIT_FAILURE;
}
// modifica indicatorul de pozitie la sfarsitul fisierului
fseek(file, 0, SEEK_END);
// citeste valoarea indicatorului de pozitie
nb = ftell(file);
fclose(file);
printf("fisierul %s contine %d octeti\n", name, nb);
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos.
Fiierul citit este chiar fiierul surs ce conine programul, main.c.
Exemplu. Vom crea un fiier binar pe care l citim apoi secvenial. Vom scrie n fiier
10 blocuri de cte 15 octei fiecare. Blocurile vor consta din iruri de cte 14 caractere
plus \0, primul bloc caractere 0, al doilea bloc caractere 1, etc. Citirea secvenial
se va face cu instruciunea while. Dup fiecare citire testm rezultatul funciei fread.
Un rezultat zero al acestei funcii semnific ntlnirea sfritului de fiier. Menionm
c putem genera caracterele 0, 1, etc., cu o expresie de forma
0 + i
unde i ia valorile 0, 1, 2,
Programul este urmtorul.
118
#include <stdio.h>
int main()
{
FILE * fil;
int i, j;
char x[15];
// deschide fisierul in creare
fil = fopen(fil.txt, wb);
if(fil == NULL)
{
printf(fisierul nu se poate crea\n);
return EXIT_FAILURE;
}
for(i = 0; i < 10; i++)
{
// creaza un bloc
for(j = 0; j < 14; ++j)
x[j] = 0 + i ;
x[14] = 0;
// scrie blocul in fisier
fwrite(x, 15, 1, fil);
}
// inchide fisierul
fclose(fil);
// deschide fisierul in citire
fil = fopen(fil.txt, rb);
if(fil == NULL)
{
printf(fisierul nu se poate deschide\n);
return EXIT_FAILURE;
}
int xct;
// citeste un sir
xct = fread(x, 15, 1, fil);
while(xct != 0)
{
// scrie sirul pe ecran (in streamul stdout)
printf(%s\n, x);
// citeste un sir
xct = fread(x, 15, 1, fil);
}
// inchide fisierul
fclose(fil);
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos.
119
Exemplu. Vom crea un fiier binar n care vom scrie 15 blocuri de cte 10 caractere i
apoi vom citi blocurile pare. Blocurile vor conine iruri de caractere de forma
abcde
bcdef
cdefg
generate cu o expresie de forma
a + i + j
unde i i j iau valorile 0, 1, 2,
#include <stdio.h>
int main()
{
FILE * fil;
int i, j;
char x[10];
// deschide fisierul in creare
fil = fopen(fil, wb);
if(fil == NULL)
{
printf("fisierul nu se poate crea\n");
return EXIT_FAILURE;
}
for(i = 0; i < 15; i++)
{
// creaza un bloc
for(j = 0; j < 9; ++j)
x[j] = a + i + j;
x[9] = 0;
// scrie blocul in fisier
fwrite(x, 10, 1, fil);
// scrie sirul pe ecran (in fluxul stdout)
puts(x);
}
// inchide fisierul
fclose(fil);
// deschide fisierul in citire
fil = fopen(fil, rb);
if(fil == NULL)
{
printf("fisierul nu se poate citi\n");
120
return EXIT_FAILURE;
}
printf("fisierul citit\n");
// citeste sirurile pare
for(i = 0; i < 15; i += 2)
{
// pozitioneaza indicatorul fisierului in raport cu inceputul fisierului
fseek(fil, (long)(i * 10), SEEK_SET);
// citeste un sir
fread(x, 10, 1, fil);
// scrie sirul pe ecran (in fluxul stdout)
fputs(x, stdout);
fputs(\n, stdout);
}
// inchide fisierul
fclose(fil);
return EXIT_SUCCESS;
}
Menionm c puteam avansa indicatorul fiierului la sfritul ciclului for cu 10 octei
n raport cu poziia curent ca mai jos
// avanseaza indicatorul fisierului cu 10 in raport cu pozitia curenta
fseek(fil, (long)(10), SEEK_CUR);
n loc de a-l poziiona n raport cu nceputul fiierului.
Rezultatul rulrii programului este cel de mai jos.
121
6 Structuri tip C i uniuni
6.1 Structuri
O structur este un tip de date definit de utilizator. O structur este o colecie de
variabile de tipuri diferite. Variabilele dintr-o structur vor fi numite componente sau
cmpuri. Definirea unei structuri se face cu instruciunea
struct nume {lista de declaraii de variabile };
unde nume este noul tip de date.
De exemplu, urmtoarea structur poate defini tipul numere complexe
struct complex {
float real;
float imag;
};
Menionm c o structur poate conine componente de orice tip, chiar i alte
structuri.
Putem defini apoi variabile corespunznd acestui nou tip cu o instruciune de forma
struct nume list de variabile;
De exemplu, ntr-un program putem defini numerele complexe a i b astfel
struct complex a, b;
Este posibil s combinm cele dou instruciuni n una singur
struct complex {
float real;
float imag;
} a, b;
Instruciunea de definire a unei structuri i a variabilelor corespunznd acestui tip are
forma general
struct nume {liste de declaraii de tip } list de variabile;
O structur poate fi iniializat la declarare. Putem scrie
struct complex n = {1.1, -2.34};
Adresarea unui element al unei structuri se face cu operatorul de selecie .
(punct), cu forma
nume.membru
Operatorul . are aceeai prioritate ca i operatorii () i [], vezi Anexa 2.
Putem da valori variabilei a definite anterior astfel :
a.real = 1.0;
a.imag = -1.2 + sin(0.3);
Putem utiliza valorile componentelor unei structuri n expresii :
float x;
x = a.real * a.imag;
Este posibil s atribuim o structur alteia, de exemplu :
struct complex a = {0.0, 1.0}, b;
b = a;
Exemplu. Vom defini o structur ce conine date despre o persoan, numele,
prenumele i vrsta. iniializm structura i scriem datele pe ecran.
122
#include <stdio.h>
#include <string.h>
struct person {
char nume[64];
char prenume[64];
int varsta;
};
int main()
{
struct person p1, p2;
strcpy(p1.nume, "popescu");
strcpy(p1.prenume, "ioan");
p1.varsta = 50;
p2 = p1;
printf("nume : %s\n", p2.nume);
printf("prenume : %s\n", p2.prenume);
printf("varsta : %d\n", p2.varsta);
return 0;
}
Rezultatul rulrii programului este prezentat mai jos.
Pentru a defini funcii ce au ca argumente sau rezultat structuri n limbajul C,
definim un nou tip de date corespunznd structurii cu instruciunea typedef.
Reamintim c, forma instruciunii typedef este
typedef tip-existent tip-nou
Pentru exemplificare vom defini (cu instruciunea typedef) un tip numit vector ce va fi
o structur ce conine dou numere reale.
# include <stdio.h>
typedef struct
{
double cmpa;
double cmpb;
} vector;
Vom construi o funcie care s adune doi vectori. Funcia va avea ca parametri dou
structuri i ca rezultat o structur. Vom declara variabile de acest tip n funcia main i
n funcia ce adun vectorii.
vector addcmp(vector x, vector y)
{
123
vector c;
c.cmpa = x.cmpa + y.cmpa;
c.cmpb = x.cmpb + y.cmpb;
return c;
}
int main()
{
vector cx, cy, cz;
cx.cmpa = 1;
cx.cmpb = 1;
cy.cmpa = 0;
cy.cmpb = 1;
cz = addcmp(cx, cy);
printf(suma este (%f , %f) \n, cz.cmpa ,cz.cmpb);
return 0;
}
La fel ca i n cazul variabilelor putem defini variabile tip pointer la o structur, ce vor
conine adresa unei structuri. Instruciunea de definire a unui pointer la o structur are
formele
struct nume * identificator;
unde nume este numele structurii sau
numetip * identificator;
dac am definit un tip de structur numetip cu instruciunea typedef. In ambele cazuri
identificator este numele variabilei tip pointer.
De exemplu,
struct complex * pc;
definete un pointer de tip struct complex. Variabila tip pointer poate fi iniializat ca
orice variabil tip pointer, folosind operatorul de calcul al adresei &
pc = &a;
In cazul unui pointer p la o structur, un cmp al structurii este adresat astfel
(*p).membru
deoarece operatorul . are preceden mai mare dect operatorul *.
Prin definiie, aceast scriere se prescurteaz ca
p->membru
Operatorii . i -> au aceeai prioritate ca operatorii () i [], vezi Anexa 2.
Exemplu. Fie instruciunile
complex y;
struct complex * py;
py = &y;
Putem iniializa structura y, utiliznd pointerul py
py->real = -23.4;
py->imag = 1.2;
Putem iniializa direct componentele structurii
y.imag = 1.24;
y.real = 0.23;
Putem utiliza valorile variabileleor definite de structura y.
float fm = pc->imag;
Sunt corecte i scrierile
124
(*pc).real = cos(1.23);
double d = (*pc).imag + (*pc).real;
Exemplu. Vom defini un tip de structur numcmp ce descrie numerele complexe cu
instruciunea typedef. Vom iniializa o variabil de acest tip i vom scrie valorile ei pe
ecran folosind un pointer la structur. Rezultatul va fi scris sub forma (real, imag).
# include <stdio.h>
typedef struct
{
float real;
float imag;
} numcmp;
int main ()
{
numcmp cr = {1.1, -1.2};
numcmp * pv;
pv = &cr;
printf("(%f , %f)\n", pv->real, pv->imag);
}
Vom prezenta un exemplu de utilizare a operatorilor de incrementare ++ n cazul
pointerilor la structuri. Fie structura
struct strx {
int c;
float d;
};
Fie o variabil tip strx i o variabil tip pointer la structura strx;
struct strx a;
struct strx * b;
Vom iniializa pe b cu adresa lui a.
b = &a;
i elementul c al structurii la valoarea zero.
b->c = 0;
Putem incrementa pe c astfel:
b->c = b-> + 1;
b->c += 1;
(*b).c += 1;
++(*b).c;
++b->c;
Operatorul ++ are o prioritate mai mic dect -> i . astfel nct ultimele expresii sunt
echivalente cu
++((*b).c);
++(b->c);
6.2 Uniuni
Uniunile conin membri ale cror tipuri individuale pot diferi unul de altul dar toi
membrii unei uniuni ocup aceeai zon de memorie. Reamintim c, n cazul
structurilor, fiecare membru ocup o zon proprie de memorie. Uniunile sunt utile n
125
aplicaii cu multe variabile ale cror valori nu sunt necesare simultan. Instruciunea de
definire a unei uniuni este
union nume {lista de declaraii de tip };
unde nume este noul tip de date. Menionm c o structur poate conine componente
de orice tip, chiar i alte structuri.
Putem defini apoi variabile corespunznd acestui nou tip cu o instruciune
union nume list de variabile;
Exemplu.
union id {
int marime;
char culoare[21];
};
Variabilele tip id se declar ca
union id v1, v2;
Putem scrie direct ca i n cazul structurilor
union id {
int marime;
char culoare[21];
} v1, v2;
O varibil tip id poate conine o mrime sau o culoare, dar nu ambele simultan.
Un membru individual al uniunii se adreseaz prin operatorii . i -> ca i n cazul
structurilor. Exemplu.
// atribuie o valoare marimii
v1.marime = 12;
printf(marime %d \n, v1.marime);
// atribuie o valoare culorii
strcpy(v1.culoare, alb);
printf(culoare %s \n, v1.culoare);
// afisaza dimensiunea uniunii
printf(dimensiunea uniunii id : %d \n, sizeof(id));
126
Partea II. Programarea orientat obiect
7 Clase
Pentru a rezolva o problem trebuie s identificm datele problemei i operaiile ce
trebuie efectuate asupra acestor date. Tipurile fundamentale de date, int, double, char,
etc., i cele derivate, tablouri, fiiere, permit rezolvarea oricrei probleme. In cazul
proiectelor complexe este avantajos s putem defini propriile tipuri de date.
Pentru a rezolva o problem trebuie s crem un model al problemei. Procesul de
modelare se numeste abstractizare. In acest proces trebuie s definim :
datele afectate,
operaiile ce trebuie efectuate pentru a rezolva problema.
S definim, de exemplu, un tip de date ce descrie funcia liniar
f(x) = m x + n
Datele corespunznd acestui tip sunt m i n, de tip real. Operaiile corespunztoare
sunt: calculul valorii funciei ntr-un punct, integrala definit, calculul funciei inverse,
etc. Alte operaii ce se pot defini suma a dou funcii liniare, compunerea a dou
funcii liniare, etc.
Abstractizarea este structurarea problemei n entiti prin definirea datelor i
operaiilor asociate acestor entiti. Tipurile de date abstracte pe care le definim au
urmtoarele proprieti :
definesc o structura a datelor
datele sunt accesibile prin operaii bine definite. Setul de operaii definite se
numete interfa. Operaiile interfeei sunt singurul mecanism de acces la
structura de date.
Definiiile pe care le vom utiliza sunt urmtoarele :
Clasa este o reprezentare a unui tip de date abstracte. Ea asigur detaliile de
implementare pentru structura de date i pentru operaii. Clasa definete
variabile (numite i cmpuri sau atribute sau proprieti) i funcii (numite
i metode sau operaii) care implementeaz structura de date i operaiile
tipului abstract. Instanele clasei se numesc obiecte. Clasa definete deci
proprietile i comportarea unei mulimi de obiecte. Intr-o clas exist o parte
de definire a datelor i operaiilor i o parte de implementare. Partea de
definire se numete interfa.
Obiect. Un obiect este o instan a unei clase (a unui tip abstract). El este unic
identificat prin nume i definete o stare reprezentat de valorile atributelor
sale la un moment dat. Comportarea unui obiect este definit de metodele ce
se pot aplica asupra lui.
Mesaje. Un program este o multime de obiecte create, distruse i care
interacioneaz. Interaciunea este bazat pe mesaje trimise de la un obiect la
altul, cernd destinatarului s aplice o metod asupra lui (s apeleze o functie).
Un mesaj este o cerere ctre un obiect ca el s invoce una din metodele lui.
Mesajul conine :
- numele metodei,
- argumentele metodei.
Metoda. O metoda este asociat unei clase. Un obiect invoca o metod ca
reacie la primirea unui mesaj.
Clasa este un tip de date definit de utilizator. Clasa definete variabile i funcii ce
prelucreaz aceste variabile.
127
Reprezentarea tipurilor de date abstracte pe care le definim este cea de mai jos.
Nume clas
Atribute
Operaii
In programe crem variabile de tipul unor clase care se numesc obiecte. Vom spune
c obiectele sunt instane sau realizri ale unor clase. Obiectele unei clase sunt create
i iniializate de funcii membre ale clasei special definite pentru acest scop numite
constructori. Obiectele sunt distruse cnd nu mai sunt necesare de funcii membre ale
clasei numite destructori.
Valorile proprietilor dau starea obiectului la un moment dat.
Datele i funciile membre ale clasei pot fi publice, private sau protejate. Membri
publici ai clasei pot fi utilizai de orice funcie din program. Membri privai ai clasei
pot fi utilizai doar de funciile membre ale clasei. Membri protejai vor fi prezentai
ulterior. Implicit, toate datele i funciile unei clase sunt private.
Principiile programrii orientate obiect sunt urmtoarele :
Incapsularea datelor este primul principiu al programrii orientate obiect.
Acesta cere combinarea datelor i metodelor ntr-o singur structur de date.
Acest principiu asigurar ascunderea structurii de date cu ajutorul unei
interfee pentru adresarea datelor. Exemplu. Numere complexe. Un numr
complex este o pereche ordonat de numere reale. Pentru a nmuli dou
numere complexe trebuie s adresm structura i s adunm produse ale prile
reale si cele imaginare. Definind o operaie pentru nmulirea numerelor
complexe ncapsulm detaliile i nmulim dou numere complexe fr s ne
intereseze cum se face operaia n detaliu.
Motenirea. Acest principiu cere definirea unei clase generale ce conine
caracteristicile comune ale mai multor elemente. Apoi aceast clas este
motenit de alte clase particulare, fiecare clas particular adaug doar
elementele proprii. Clasa care este motenit se numete clas de baz sau
superclas, iar clasele care motenesc se numesc clase derivate sau subclase.
Polimorfismul. Avem funcii cu acelai nume n clasa de baz i n clasele
derivate. Funciile din clasele derivate redefinesc operaiile necesare claselor
derivate. Cnd apelm funcia respectiv, se apeleaz versiunea
corespunztoare tipului obiectului.
7.1 Definirea unei clase
7.1.1 Definirea unei clase
Diagrama sintactic a definiiei unei clase este
Clasele sunt definite utiliznd cuvntul cheie class. identificator este numele clasei.
Specificatorii de acces sunt public, protected i private, iar membru este o declaraie
de funcie membru sau dat a clasei. Implicit toi membri clasei sunt privai.
128
In exemplele urmtoare vom adopta urmtoarea regul : numele claselor vor ncepe
cu litere mari, iar numele cmpurilor i metodelor cu litere mici.
Ca exemplu vom defini o clas care reprezint funcia liniar
f(x) = mx + n
pentru care vom calcula valoarea funciei ntr-un punct, funcia invers i integrala
definit. Clasa va avea o funcie print() ce afiaz parametrii m i n ai obiectului.
Reprezentarea acestei clase este cea de mai jos.
Line
double m;
double n;
Line (double, double);
void assign();
void value(double);
double intgrl(double, double);
void print();
void invert();
Definiia clasei este urmtoarea
/*
clasa Line descrie functia f(x) = m * x + n
*/
class Line
{
private:
double m, n;
public:
void assign(double, double);
double value(double);
double intgrl(double, double);
void print();
Line(double, double);
void invert();
};
Numele clasei este Line. Definiia clasei este inclus ntre acolade { i }, urmate de ;.
Variabilele, (cmpurile, atributele) membre ale clasei sunt m i n i ele vor memora
valorile m i n ale funciei descris de clas. Ele sunt de tip double i sunt declarate
private. In acest fel ele vor fi accesibile doar funciilor membre ale clasei.
Interzicerea accesului din afara clasei este un principiu al programrii orientate
obiect numit ncapsularea datelor.
Funciile membre ale clasei, (metodele, operaiile) sunt assign(), ce atribuie valori
variabilelor m i n, value(), ce d valoarea funciei ntr-un punct, invert(), ce
inverseaz funcia i print(), ce scrie variabilele m i n i intgrl() ce calculeaz
integrala definit a funciei.
Funcia Line() este constructor. Ea creaz obiecte i iniializeaz variabilele acestora.
Toate funciile sunt declarate publice i vor putea fi utilizate de obiecte n afara clasei.
Funciile membre, (metodele), pot fi definite n interiorul clasei sau n afara ei.
129
Funciile definite n interiorul clasei sunt funcii inline; compilatorul genereaz direct
textul lor n program, i nu un apel la funcie.
In limbajul C++ avem posibilitatea de a grupa anumite nume n spaii de nume.
Prin definiie, datele i funciile unei clase constituie un spaiu de nume ce are
numele clasei. Operatorul de rezoluie :: arat c un nume de dat sau de funcie
aparine unui spaiu de nume. Operatorul de rezoluie :: se utilizeaz pentru a
defini o funcie membru a clasei n afara clasei.
Vom defini metodele clasei Line n afara definiiei clasei, ca mai jos :
// atribuirea de valori variabilelor m si n
void Line::assign(double a, double b)
{
m = a;
n = b;
}
// calculul valorii functiei in punctul x
double Line::value(double x)
{
return m * x + n;
}
// calculul integralei definite
double Line::intgrl(double a, double b)
{
return m * (b * b - a * a) / 2 + n * (b - a);
}
// afisarea parametrilor pe ecran
void Line::print()
{
cout << "functia f(x) = m * x + n, "
<< " m = " << m << " n = " << n << endl;
}
// constructor cu parametri
Line::Line(double a, double b)
{
m = a;
n = b;
}
// calculul functiei inverse
void Line::invert()
{
double temp;
temp = 1.0 / m;
m = temp;
n = - n * temp;
}
130
Apelul unei funcii a clasei de ctre un obiect se face cu operatorul de selecie .
(punct), conform urmtoarei diagrame sintactice
nume_obiect.nume_funcie (lista de parametri)
Utilizarea unui cmp al un obiect se face cu operatorul de selecie . (punct),
conform urmtoarei diagrame sintactice
nume_obiect.nume_camp
Declararea unui obiect se face n acelai fel cu declararea tipurilor standard.
Reamintim c, un obiect este o instan a unei clase. La declararea unui obiect se
apeleaz un constructor al clasei. Diagrama sintactic pentru definirea obiectelor
este cea de mai jos
nume_clasa nume_obiect(lista de parametri), , ;
Orice obiect are propriile variabile.
Vom exemplifica utilizarea clasei definind funcia liniar x + 1i calculnd valoarea ei
n punctul 0.5, integrala de la 0 la 1 i inversa ei.
int main()
{
// creaza un obiect de tipul Line
Line a(1, 1);
a.print();
cout << "f(0.5) = " << a.value(0.5) << endl;
cout << "intgrala f(x) de la 0 la 1 = " << a.intgrl(0, 1) << endl;
cout << "functia inversa \n";
a.invert();
a.print();
return 0;
}
Pe ecran se vor afia urmtoarele rezultate
In funcia main(), x este declarat ca un obiect de tipul Line cu numele a (o instan a
clasei Line) cu instruciunea
Line a(1, 1);
El are cele dou data membre, m i n. Obiectul este creat prin apelarea constructorului
Line(double a, double b)
care iniializeaz cmpurile m i n la valorile 1 i 1.
131
Obiectul poate apela funciile membre assign(), invert() i print(), etc., care au fost
declarate publice n definiia clasei.
Acesta este un principiu al programrii orientate obiect. Nu utilizm variabile i
funcii globale, ci fiecare obiect are propriile lui date.
Menionm c n funcia main() nu putem scrie instruciunea
cout << a.m << << a.n << endl;
deoarece variabilele clasei, m i n, sunt declarate private. Ele pot fi utilizate doar de
funciile clasei i nu de obiecte de tipul clasei. Pentru a putea utiliza datele clasei de
ctre obiecte n funcia main(), ca mai sus, ele ar trebui declarate publice.
Menionm c n limbajul C++ putem defini clase i cu cuvntul cheie struct (spre
deosebire de limbajul C, unde cuvntul cheie struct definete structuri). In cazul unei
clase definite cu struct toi membrii clasei sunt doar variabile i sunt declarai implicit
ca publici.
Menionm c putem scrie definiia unei clase ntr-un fiier antet (header) i definiia
funciei main() ce utilizeaz aceast clas ntr-un fiier separat care s includ fiierele
antet cu definiiile claselor. De exemplu, putem scrie definiia clasei Line n fiierul
header Line.h ca mai jos.
/*
clasa Line descrie functia f(x) = m * x + n
*/
class Line
{
private:
double m, n;
public:
void print();
Line(double a, double b) { m = a; n = b; };
Line() { m = 0; n = 0; };
};
void Line::print()
{
cout << "functia f(x) = m * x + n, "
<< " m = " << m << " n = " << n << endl;
}
Scriem apoi definiia funciei main() n alt fiier surs n care includem fiierul Line.h.
In cazul fiierelor header definite de utilizator, n directiva include numele fiierului
header se scrie ntre ghilimele, ca exemplul de mai jos.
#include <iostream>
using namespace std;
#include "Line.h"
int main()
{
// creaza un vector cu obiecte de tipul Line
Line v[2];
132
v[0] = Line(1, -1);
v[1] = Line(2, -2);
int i;
for(i = 0; i < 2; i++)
v[i].print();
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
7.1.2 Pointerul this
Fie o instruciune de definire a unor obiecte de tipul Line
Line x(1, 1), y(2.5, -1) ;
Considerm instruciunile n care un obiect apeleaz o funcie membru a clasei, de
exemplu
x.assign(3, 4);
y.assign(-1, 5);
Adresarea funciilor clasei de ctre obiecte se face n felul urmtor. Fiecare obiect are
o variabil implicit numit this care este un pointer de tipul clasei ce conine adresa
obiectului. Funciile clasei au un parametru implicit, un pointer de tipul clasei. In
acest fel aceste funcii pot adresa variabilele obiectului (n cazul nostru ale obiectelor
x i y). Atunci cnd un obiect apeleaz o funcie a clasei, parametrul implicit al
funciei primete ca valoare pointerul this. Pointerul this poate fi utilizat n orice
funcie membr a clasei pentru a apela variabile sau alte funcii membre. Ca exemplu
vom rescrie funcia invert() ca s utilizeze pointerul this la apelarea variabilelor m i
n.
void Line::invert()
{
double temp;
temp = 1.0 / this->m;
m = temp;
n = - this->n * temp;
}
7.1.3 Spaii de nume
In limbajul C++ putem defini spaii de nume. Un spaiu de nume ne permite s
grupm o mulime de clase, obiecte i funcii globale sub un nume. Definiia unei
clase creaz automat un spaiu cu numele clasei ce cuprinde toate variabilele i
funciile membre ale clasei. Declararea unui spaiu de nume se face astfel
namespace identificator
{
// declaraii de clase, funcii, obiecte
}
133
De exemplu, putem defini urmtorul spaiu de nume
namespace general
{
int a, b;
}
Pentru a adresa variabilele i funciile definite ntr-un spaiu n funciile din
afara spaiului se utilizeaz operatorul de rezoluie :: Operatorul de rezoluie
are cea mai mare prioritate, vezi Anexa 2.
De exemplu, pentru a utiliza variabila a din spaiul anterior scriem
general::a
Directiva using namespace asociaz un spaiu de nume cu un anumit nivel al
programului, astfel nct obiectele i funciile din acel spaiu sunt accesibile direct ca
i cnd ar fi fost definite ca globale. Aceast directiv are forma
using namespace identificator;
Toate clasele, obiectele i funciile din bibliotecile C++ standard sunt definite n
spaiul std.
De exemplu, obiectul cout corespunztor streamului de ieire standard poate fi utilizat
astfel:
Folosind directiva using namespace
# include <iostream>
using namespace std;

cout << endl;


Utiliznd numele spaiului
# include <iostream>

std::cout << std::endl;


Putem utiliza directiva using pentru a declara doar anumite simboluri din
spaiul de nume
#include <iostream>
using std::cout;
using std::endl;
..
cout << endl;
7.2 Constructori i destructori
7.2.1 Constructori
In general, obiectele trebuie s iniializeze variabilele lor la declararea obiectului.
Pentru aceasta se definesc funcii constructor ale clasei. Un constructor este o
funcie membru a clasei apelat automat atunci cnd obiectul este declarat.
Constructorul are acelai nume cu al clasei i nu are nici un tip.
Clasa precedent avea un constructor
Line (int, int);
Deoarece constructorii au, n general, rolul de a iniializa variabilele obiectului,
limbajul are o sintax special numit list de iniializatori. Constructorul anterior
se poate scrie
134
Line::Line(double a, double b)
: m(a), n(b)
{
}
Clasele au n general urmtoarele tipuri de constructori, identificai de definiia lor :
X(); // constructorul implicit
X(const X&); // constructorul copiere
unde X numele clasei.
Constructorul implicit nu are parametri. El este apelat ori de cte ori declarm un
obiect cu instruciunea
X obiect;
Dac nu definim niciun constructor, compilatorul definiete un constructor implicit.
Constructorul copiere este apelat atunci cnd vrem s crem un obiect care s aibe
aceleai atribute ca un alt obiect. De exemplu instruciunea
X a(b);
declar un obiect a de tipul X i i atribuie ca valori ale atributelor, pe cele ale obiectul
b. Dac nu definim un constructor copiere, el este definit de compilator. Menionm
cuvntul cheie const din definiia constructorului copiere. El interzice modificarea
argumentului n timpul copierii.
In consecin, orice clas are cel puin doi constructori.
Orice clas include un operator implicit de atribuire = care are ca parametru un
obiect de tipul clasei.
Exemplu. Fie din nou clasa Line n care definim un constructor copiere, un
constructor fr parametri, i un constructor cu parametri.
# include <iostream>
using namespace std;
/*
clasa Line descrie functia f(x) = m * x + n
*/
class Line
{
private:
double m, n;
public:
void assign(double, double);
double value(double);
double intgrl(double, double);
void print();
Line(double, double);
void invert();
Line();
Line(const Line&);
};
Vom prezenta doar definiiile constructorilor, definiiile celorlalte funcii sunt cele
anterioare.
135
// constructor tip copiere
Line::Line(const Line& x)
{
m = x.m;
n = x.n;
}
// constructor implicit
Line::Line()
{
m = 0;
n = 0;
}
Vom rescrie definiia clasei utiliznd lista de iniializatori n definiiile constructorilor.
/*
clasa Line descrie functia f(x) = m * x + n
*/
class Line
{
private:
double m, n;
public:
void assign(double, double);
double value(double);
double intgrl(double, double);
void print();
Line(double a, double b) : m(a), n(b) {};
void invert();
Line() : m(0), n(0) {};
Line(const Line& x) : m(x.m), n(x.n) {};
};
Un program care testeaz definiia clasei Line este cel de mai jos n care crem un
obiect x, apelm constructorul copiere pentru a crea un alt obiect y, i iniializm un
obiect z cu operatorul =.
int main()
{
Line x(12, 25);
Line z;
Line y(x); // se apeleaza constructorul copiere
x.print();
y.print();
z = x;
z.print();
}
136
Constructorul copiere este apelat ori de cte ori :
un obiect este copiat,
un obiect este transmis prin valoare unei funcii (obiectul este copiat n stiv),
un obiect este returnat ca valoare de o funcie.
Exemplu. Presupunem clasa Line definit anterior. Fie o funcie global f care are ca
parametru un obiect de tip Line i ca rezultat un obiect de tip Line, care este inversul
obiectului ce este parametrul funciei. Definitia funciei f() este urmtoarea
Line f(Line r)
{
Line s = r;
s.invert();
return s; // se apeleaza constructorul copiere
}
int main()
{
Line x(11, 22);
Line y(x); // se apeleaza constructorul copiere
Line z;
// scrie numarul x
x.print();
z = f(y); // se apeleaza constructorul copiere
// scrie numrul z
z.print();
return 0;
}
Constructorul copiere este apelat
1) atunci cnd se declar obiectul
Line y(x);
2) cnd se apeleaz funcia
z = f(y);
Parametrul funciei f (r n definiia funciei) este transmis prin valoare, deci n stiv se
pune o copie a obiectului creat cu constructorul copiere.
3) cnd se execut instruciunea
return s;
obiectul s se copiaz n stiv utiliznd constructorul copiere.
Menionm c, n cazul n care o funcie nu trebuie s modifice un parametru,
acest parametru trebuie declarat ca fiind constant. Acest lucru se face cu
cuvntul cheie const. Un exemplu este constructorul copiere al clasei Line. In cazul
funciei f, parametrul r nu trebuie modificat, deci definiia funciei f poate fi
Line f(const Line r);
In acest caz, dac funcia modific parametrul r, compilatorul detecteaz o eroare.
Un parametru obiect al unei funcii poate fi de tip valoare sau de tip referin.
Cnd parametrul este de tip valoare, n stiv se poate pune valoarea obiectului,
cnd este de tip referin n stiv se pune adresa (referina) lui.
De exemplu, parametrul
Line r
este transmis prin valoare. Dac parametrul este declarat ca
137
Line& r
el este transmis prin adres. Parametrii tip pointer vor fi prezentai ulterior.
Putem defini tablouri de obiecte n mod obinuit, de exemplu
Line x[20];
Pentru aceasta, clasa trebuie s defineasc un constructor fr parametri.
Tabloul definit poate fi prelucrat, de exemplu putem avea instruciunile
Line a(1, 2);
x[5] = a;
Exemplu. Vom crea un vector cu dou componente de tipul Line i vom afia
parametri m i n ai obiectelor.
# include <iostream>
using namespace std;
class Line
{
private:
double m, n;
public:
void print();
Line(double a, double b) : m(a), n(b) {};
Line() : m(0), n(0) {};
};
void Line::print()
{
cout << "functia f(x) = m * x + n, "
<< " m = " << m << " n = " << n << endl;
}
int main()
{
// creaza un vector cu obiecte de tipul Line
Line v[2];
v[0] = Line(1, -1);
v[1] = Line(2, -2);
int i;
for(i = 0; i < 2; i++)
v[i].print();
return 0;
}
Menionm c am definit un constructor fr parametri, Line(), care este apelat la
definirea vectorului de obiecte tip Line
Line v[2];
n funcia main().
7.2.2 Destructori
Destructorul unei clase este apelat automat cnd un obiect este distrus. Fiecare clas
are un singur destructor. Destructorul nu are tip i nici parametri. Diagrama sintactic
a destructorului este urmtoarea
138
~ NumeClasa(){/* corpul destructorului*/}
Dac el nu este definit explicit, compilatorul genereaz unul. Un obiect este creat ntr-
o funcie sau ntr-un bloc dintr-o funcie i este distrus la ieirea din funcie sau din
bloc. Blocul este orice ir de instruciuni ntre acolade, { i } din interiorul unei
funcii.
Pentru a vedea cum sunt apelai constructorii i destructorul, fie urmtoarea clas n
care constructorul i destructorul scriu un mesaj.
class Test
{
public:
Test() {cout << obiectul este creat << endl;}
~Test() {cout << obiectul este distrus << endl;}
};
Fie dou blocuri de instruciuni n funcia main. In fiecare bloc crem cte un obiect.
int main()
{
{
cout << intrare blocul 1 << endl;
Test x;
cout << iesire blocul 1 << endl;
}
{
cout << intrare blocul 2 << endl;
Test y;
cout << iesire blocul 2 << endl;
}
}
Mesajele afiate vor fi:
Menionm c toate variabilele locale unei funcii sau unui bloc, inclusive
obiectele, sunt create n stiv la intrarea n funcie sau bloc i sunt terse din stiv
la ieirea din funcie sau bloc.
139
7.3 Funcii prietene
In general avem nevoie s utilizm i s modificm datele (cmpurile) unui obiect n
timpul execuiei programului. Dac datele sunt declarate private, nu putem face acest
lucru direct. Exist dou moduri de a rezolva aceast problem.
In prima metod se definesc funcii member ale clasei care s modifice i s
furnizeze valorile datelor unui obiect.
In cazul clasei Line putem defini funcii membre ale clasei care furnizeaz parametrii
m i n :
double getm();
double getn();
i funcii care modific aceti parametric :
void setm(double);
void setn(double);
Aceste funcii se numesc funcii de acces i sunt de regul publice.
Definiiile funciilor getm() i setm() pot fi:
double Line::getm()
{
return m;
}
void Line::setm(double x)
{
m = x;
}
Exemplu. Vom defini o funcie global care s adune dou funcii liniare. Prototipul
funciei va fi
Line sum(const Line x, const Line y);
Parametrii x i y sunt cele dou funcii liniare ce sunt adunate. Deoarece obiectele nu
vor fi modificate, ele au fost declarate const.
Considerm dou funcii liniare,
1 1 1
) ( n x m x f + i
2 2 2
) ( n x m x f + . Suma lor va fi
funcia ) ( ) ( ) (
2 1 2 1
n n x m m x f + + + . In implementarea funciei nu putem folosi
direct variabilele m i n, deoarece ele sunt declarate private n definiia funciei. Vom
utiliza funciile getm() i getn() pentru a obine valoarea lor. Implementarea funciei
este urmtoarea
Line sum(Line x, Line y)
{
double a = x.getm();
double b = x.getn();
double c = y.getm();
double d = y.getn();
Line temp(a + c, b + d);
return temp;
}
140
O alt soluie este urmtoarea. Pentru ca o funcie extern s aibe acces la membri
privai ai clasei, ea trebuie declarat n definiia clasei ca funcie prieten, friend.
Diagrama sintactic a definiiei unei funcii prietene este
friend tip nume_funcie ( parametri);
Noua definiie a clasei Line este urmtoarea.
class Line
{
public:
Line (double, double);
Line (const Line & x);
Line ();
void assign(double, double);
double value(double);
double intgrl(double, double);
void print();
void invert();
friend Line sum(const Line x, const Line y);
private:
double m;
double n;
};
In aceast definiie funcia sum a fost declarat prieten, deci poate utiliza variabilele
private ale clasei. Implementarea funciei sum este urmtoarea
Line sum(const Line x, const Line y)
{
Line temp(x.m + y.m, x.n + y.n);
return temp;
}
Un program care testeaz funcia sum este cel de mai jos.
int main()
{
Line a(1.5, 2.3);
a.print();
Line b(2.2, -1.5);
b.print();
Line c;
c = sum(a, b);
c.print();
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
141
7.4 Determinarea tipului unei expresii
Limbajul C++ are operatorul typeid ce permite determinarea tipului unei expresii.
Forma operatorului este
typeid(expresie)
Operatorul typeid are ca rezultat o referin la un obiect constant de tipul type_info.
Clasa type_info este o clas predefinit a limbajului, definit n fiierul antet
<typeinfo>. Clasa definete metoda
const char * name();
care are ca rezultat un ir de caractere cu numele tipului expresiei i operatorii == i !
= cu care putem compara tipurile a dou expresii.
Exemplu. Vom determina tipul expresiilor aritmetice i booleene i tipul unui pointer
i al unei referine cu programul de mai jos.
#include <iostream>
#include <typeinfo>
using namespace std;
int main(int argc, char *argv[])
{
int a, b;
char c;
double x, y;
bool bl;
float f1, f2;
int * ptra;
int& ra = a;

cout << typeid(a + b).name() << endl;
cout << typeid(x + y).name() << endl;
cout << typeid(f1 + f2).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(bl).name() << endl;
cout << typeid(ptra).name() << endl;
cout << typeid(ra).name() << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
142
In program se include biblioteca <typeinfo> ce definete clasa type_info . Expresia
typeid(a + b)
are ca rezultat o referin la un obiect constant de tipul type_info. In consecin,
expresia
typeid(a + b).name()
apeleaz metoda name() a acestui obiect, care are ca rezultat un ir de caractere cu
numele tipului expresiei, i pentru int, d pentru double, etc.
Operatorul typeid poate avea ca parametru un obiect de un tip oarecare, definit de
programator, de exemplu o clas.
Exemplu. Fie clasa Line definit mai sus. Programul urmtor determin tipul unui
obiect de tipul Line i al unui poiner la un obiect de tip Line. Definiia clasei Line nu
este artat.
#include <iostream>
#include <typeinfo>
using namespace std;
int main(int argc, char *argv[])
{
Line a(1, 2);
Line * ptrln = new Line(-1, 2);
cout << typeid(a).name() << endl;
cout << typeid(ptrln).name() << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Programul afiaz tipul unui obiect Line i tipul unei variabile pointer de tip Line.
143
8 Siruri tip C++
8.1 Clasa string
Limbajul C++ definete clasa string pentru lucrul cu iruri. Spre deosebire de irurile
tip C care sunt terminate printr-un octet cu valoarea zero, obiectele acestei clase au un
cmp ce conine lungimea irului. Clasa string este definit n fiierul header
<string>. Clasa are urmtorii constructori:
constructorul implicit care creaz un ir vid
string();
constructor copiere cu argument ir tip C sau ir tip string
string(const string&);
string(const char *);
Operatorul = are ca argument drept un ir tip C sau ir tip string.
Exemple de definiri de obiecte tip string
string x = "abcd";
string y(x);
string z = x;
string x1("abc");
Clasa implementeaz operatorii + i += (concatenarea irurilor).
Exemple.
string a = x + xyz;
a += abc;
Clasa definete operatori de comparare ai irurilor, <, <=, >, >= , = = i != ce au ca
rezultat valorile true sau false.
Operatorii << i >> scriu i citesc iruri tip string.
Exemple.
if(x < x1)
cout << x1;
else
cout << x;
Funcii membre importante ale clasei sunt :
int length(); d lungimea irului tip string,
int size(); d lungimea irului tip string,
const char * c_str(); - convertete irul ntr-un ir tip C,
bool empty(); - are valoarea true dac irul este vid.
Exemplu. S copiem un ir C++ ntr-un ir tip C.
char c[100];
string str(abcd);
strcpy(c, str.c_str());
Clasa are operatorul de selecie [] ce poate selecta un caracter din ir sau poate atribui
o valoare unui caracter din ir. Indicele primului caracter din ir este 0.
Exemplu. S scriem un ir, caracter cu caracter.
string s = abcd;
for(j = 0; j < s.length(); j++)
cout << s[j];
Vom prezenta acum funcii de cutare de subiruri i de modificare a irurilor.
Fie un obiect tip string.
funcia find() cu prototipul
144
int find(char * substring);
d indicele primei apariii a irului substring n irul respectiv. Dac irul substring nu
exist n ir funcia are ca rezultat lungimea irului.
Exemple. Fie irul
string str = abcdefg;
Instruciunea
cout << str.find(cd) << endl;
afiaz valoarea 2. (indicele caracterului c este 2). Instruciunea
cout << str.find(xyz) << endl;
afiaz valoarea 7 (lungimea irului).
funcia erase() cu prototipul
erase(int index, int size)
terge size caractere ncepnd cu caracterul cu indicele index.
Exemplu. Fie irul
string str = ABCD*FGHIJK;
Instruciunea
str.erase(4, 2);
terge dou caractere ncepnd cu caracterul * ce are indicele 4. Noul ir este
ABCDGHIJK
funcia replace() nlocuiete un subir cu altul. Ea are prototipurile :
replace(int index, int size, char * sir);
replace(int index, int size, string sir);
i nlocuiete subirul de lungime size ce ncepe cu caracterul index cu subirul sir.
Exemple. Fie irul
string str = ABCDGHIJK;
Instruciunea
str.replace(5, 2, xyz);
nlocuiete subirul HI cu subirul xyz. Noul ir este
ABCDGxyzJK
Fie irul
string s2 = abc;
Instruciunea
str.replace(5, 3, s2);
nlocuiete subirul xyz cu irul abc. Noul ir este
ABCDGabcJK
funcia substr() cu prototipul
string substr(int index, int size);
creaz un ir tip string din subirul de lungime size ce ncepe cu caracterul index.
Exemplu. Fie instruciunile
string x = abcxyz;
string z = x.substr(3, 2);
A doua instruciune creaz irul tip string z
xy
funcia insert() cu prototipul
insert( int index, string str) ;
insereaz irul str ncepnd cu indexul index.
funcia append() adaug un ir la sfritul irului curent. Ea are prototipurile:
append(const string& str);
append(const char* str);
append(const string& str, int index, int size);
145
Primele dou funcii adaug irul str la sfritul irului coninut n obiect. A treia
funcie adaug la sfritul irului din obiect caracterele din irul str de lungime size
ncepnd cu indicele index. Trebuie s avem ndeplinit condiia
index <= size
Exemplu. Fie un obiect tip string ce conine irul "abc". Se va aduga la sfritul lui
irul "xyz". Fie apoi un obiect tip string ce conine irul "xyz". Se va aduga la
sfritul lui irul "abc", ncepnd odat de la indicele 1 i alt dat de la indicele 2.
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
string a("abc");
a.append("xyz");
cout << a << endl;
string b = "xyz";
b.append("abc", 1, 3);
cout << b << endl;
b = "xyz";
b.append("abc", 2, 3);
cout << b << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Adugnd irul abc ncepnd cu indicele 1 se obine irul xyzbc, adugnd irul
abc ncepnd cu indicele 2 se obine irul xyzc.
Exemplu. Fir irurile
string x = abcd, y = xyz;
Instruciunea
x.insert(1, y) ;
modific irul x la axyzbcd ;
Pentru citirea unui ir de caractere dintr-un fiier ntr-un obiect tip string, este definit
funcia global getline cu prototipurile :
getline(stream, string& str, char delim);
getline(stream, string& str);
Funcia citete caractere din stream n obiectul str pn la ntlnirea caracterului delim
(prima variant) sau pn la ntlnirea caracterului \n (a doua variant). In acest fel
este posibil citirea unei linii dintr-un fiier tip text.
Exemplu. Instruciunile
string s;
getline(cin, str);
146
produc citirea unei linii de caractere din fiierul de intrare cin.
Exist n plus, funcii getline() ale obiectului cin cu prototipurile:
getline(char * s, int size, char delim);
getline(char * s, int size);
care citesc caractere n vectorul de caractere s, cel mult size 1 caractere, sau pn la
ntlnirea caracterului delimitator, delim (prima variant), sau caracterului \n (a doua
variant).
Un program ce exemplific utilizarea funciei insert este cel de mai jos care insereaz
repetat un ir inaintea unui ir dat.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str1 = "abcdg";
string str2 = "xy";
cout << str1 << endl;
str1.insert(0, str2);
cout << str1 << endl;
str1.insert(2, str2);
cout << str1 << endl;
str1.insert(2, "123");
cout << str1 << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
147
9 Suprancrcarea operatorilor
Operatorii limbajului C++ sunt predefinii pentru tipurile fundamentale: int, double,
char, etc. Cnd definim o clas nou, crem un nou tip. Operatorii limbajului pot fi
definii i pentru tipurile nou create. Aceast operaie se numete suprancrcarea
operatorilor (operator overloading). Pentru a suprancarca un operator definim o
funcie de forma
tip operator semn (parametri) {/* corpul funciei */}
unde semn este operatorul dorit +,-, *, / , [], (), <<, >>, =, etc., iar tip este un tip de
date definit de programator (o clas). Operatorii suprancrcai au aceeai prioritate ca
cei originali i acelai numr de parametri. Menionm c, orice operator
suprancrcat poate fi definit ca funcie membr a clasei sau ca funcie global.
9.1 Suprancrcarea operatorilor aritmetici
S suprancrcm operatorul + pentru clasa Line, care s adune dou funcii liniare.
Fie funciile:
f
1
(x) = m
1
x + n
1
f
2
(x) = m
2
x + n
2
Suma lor este funcia
f(x) = (m
1
+ m
2
) x + (n
1
+ n
2
)
Definiia clasei Line este urmtoarea
class Line
{
private:
double m, n;
public:
void print();
Line();
Line(double, double);
double getm();
double getn();
void setm(double);
void setn(double);
};
Definiiile funciilor menbre ale clasei sunt cele anterioare i nu sunt repetate.
Operatorul +, definit ca o funcie global, este urmtorul.
Line operator+( Line x, Line y)
{
double a = x.getm();
double b = x.getn();
double c = y.getm();
double d = y.getn();
Line temp(a + c, b + d);
return temp;
}
148
A se compara aceast funcie cu funcia sum definit anterior. Funcia are doi
parametri, obiectele ce vor fi adunate, deoarece este o funcie global. Putem utiliza
funcia definit astfel
int main()
{
Line x(2, 3), y(3, -5), z;
z = x + y;
z.print();
return 0;
}
Rezultatul programului este cel de mai jos. Constructorul implicit este apelat la
definirea obiectului z.
Putem defini operatorul ca funcie prieten a clasei. In definiia clasei vom scrie n
definiia clasei instruciunea
friend Line operator+ (Line, Line);
In acest caz definiia funciei este urmtoarea.
Line operator+( Line x, Line y)
{
Line temp(x.m + y.m, x.n + y.n);
return temp;
}
Al doilea mod de a defini operatorul + este ca funcie membr a clasei. O funcie
membr a clasei este apelat de un obiect. Fie a, b, c obiecte de tip Line. Putem scrie
c = a.operator+(b);
sau, echivalent
c = a + b;
De accea, atunci cnd operatorul este o funcie membr a clasei, funcia
corespunztoare are un singur parametru, operandul drept, i este apelat de operandul
stng. Definiia clasei Line cu operatorul + este
class Line
{
public:
Line (double, double);
Line ();
void print();
// functia operator+ are un singur parametru
Line operator+( Line);
private:
double m;
double n;
149
};
Implementarea funciei operator+ este urmtoarea
Line Line::operator+( Line x)
{
Line temp(m + x.m, n + x.n);
return temp;
}
Definiiile celorlalte funcii nu sunt repetate.
Putem utiliza funcia definit astfel
int main()
{
Line x(2, 3), y(3, -5), z;
z = x + y;
z.print();
return 0;
}
Rezultatul este cel anterior.
Exemplu. Fie instruciunea
Line a(1, 3), b(4, -3), c;
Instruciunile
c = a + b;
i
c = a.operator+(b);
sunt echivalente.
Exerciiu. S se scrie implementarea operatorilor de scdere a dou funcii liniare, -, i
compunere a dou funcii liniare, *.
9.2 Suprancrcarea operatorului de atribuire
Considerm clasa definit anterior n care definim constructorul implicit,
constructorul copiere i operatorul de atribuire. Menionm c operatorul de atribuire
este automat definit pentru orice clas. Suprancrcarea lui aici este doar un exerciiu.
O definiie incorect a operatorului de atribuire este urmtoarea
# include <iostream>
using namespace std;
class Line
{
private:
double m, n;
public:
Line ();
Line (const Line &);
void operator = (const Line &);
};
150
O implementare a definiiei incorecte a operatorului de atribuire suprancrcat este
urmtoarea
void Line::operator = (const Line & r)
{
m = r.m;
n = r.n;
}
Ea copiaz obiectul r n obiectul ce apeleaz operatorul. Fie de exemplu instruciunea
Line x, y, z;
Cu aceast definiie putem scrie
x = z;
y = z;
dar nu putem scrie
x = y = z;
deoarece rezultatul funciei este void.
Pentru a vedea care este definiia corect a operatorului de atribuire considerm
instruciunea
int x, y, z = 2;
Limbajul permite o instruciune de atribuire multipl de forma
x = y = z = 2;
Operatorul = este asociativ la dreapta i are ca rezultat valoarea atribuit operandului
stng. Prima dat se execut instruciunea
z = 2;
apoi
y = z;
etc. Simboliznd operatorul = cu o funcie f cu dou argumente, putem scrie
instruciunea de atribuire multipl de mai sus
f(x, f(y, f(z, 2)));
In consecin, operatorul de atribuire = trebuie s aibe ca rezultat o referin de acelai
tip cu valoarea pe care o atribuie operandului stng (tipul operandului stng). In acest
fel operatorul este asociativ la dreapta.
Definiia corect a operatorului de atribuire al clasei Line este
Line & operator = (const Line & );
Definiia clasei va fi
class Line
{
private:
double m, n;
public:
Line ();
Line (const Line &);
Line & operator = (const Line &);
};
Prototipul operatorului de atribuire al unei clase T este
T& operator = (const T&);
151
Operatorul are ca rezultat o referin la obiectul ce apeleaz operatorul. Pentru aceasta
se utilizeaz pointerul this. Implementarea corect a operatorului de atribuire
suprancrcat este urmtoarea.
Line & Line::operator = (const Line & r)
{
// obiectul ce a apelat operatorul primeste valoarea obiectului r
m = r.m;
n = r.n;
// rezultatul funciei este referinta obiectului ce a apelat operatorul
return *this;
}
Putem scrie acum instruciunile
Line x, y, z(2, 3);
x = y = z;
9.3 Suprancrcarea operatorilor << i >>
Operatorul << este numit operator de inserie, el insereaza caractere ntr-un stream.
Operatorul >> este numit operator de extracie, el extrage caractere dintr-un stream.
Toate funciile de inserie au forma
ostream& operator << (ostream& stream, tip obiect)
{
// corpul functiei
return stream;
}
Primul parametru este o referin la streamul de ieire. Al doilea este obiectul ce
trebuie inserat. Ultima instruciune este
return stream;
Exemplu. Suprancrcarea operatorului de inserie << pentru clasa Line. Operatorul va
insera n streamul de ieire irul (m, n).
In definiia clasei definim funcia
friend ostream& operator << (ostream& stream, Line x);
Implementarea funciei este
ostream& operator << (ostream& stream, Line x)
{
stream << "(" << x.m << "," << x.n << ")";
return stream;
}
Exemplu. Operatorul de extracie >> pentru clasa Line este suprancrcat astfel
friend istream& operator >> (istream& stream, Line& x);
Implementarea lui este urmtoarea
istream& operator >> (istream& stream, Line& x)
{
stream >> x.m >> x.n;
152
return stream;
}
Cnd un operator este membru al unei clase, operandul stng (transmis prin this) este
cel care apeleaza operatorul. Operatorii << i >> pe care i-am definit, nu pot fi
membri ai clasei, deoarece operandul stng ostream, respectiv istream, nu sunt un
membri ai clasei. In consecin, aceti operatori vor fi funcii externe.
Reamintim c, pentru ca o funcie extern s aibe acces la membrii privai ai
clasei, ea trebuie declarat n definiia clasei ca funcie prieten friend. Definiia
unei funcii prietene este, cum s-a arat mai nainte
friend tip nume_funcie ( parametri);
Cu aceasta, definiia clasei Line este
class Line
{
private:
double m, n;
public:
Line ();
Line (double, double);
Line operator+( Line);
friend ostream& operator << (ostream& stream, Line x);
friend istream& operator >> (istream& stream, Line& x);
};
Definiia funciilor membre ale clasei este cea de mai sus.
Putem utiliza operatorii definii astfel
int main()
{
Line a, b, c;
cout << "suma a doua functii liniare" << endl;
cout << "parametrii primei functii " << endl;
cin >> a;
cout << "parametrii functiei a doua" << endl;
cin >> b;
c = a + b;
cout << "functia suma" << endl << c << endl;
return 0;
}
Rezultatul programului este cel de mai jos.
153
Reamintim c, trebuie ca n definiia clasei s includem un constructor implicit, fr
parametri, de exemplu cu definiia
Line() {m = 0; n = 0;}
Constructorul implicit nu mai este generat automat de compilator deoarece am definit
un constructor. Constructorul implicit este apelat la declararea obiectelor
Line a, b, c;
Reamintim c, orice clas include un operator implicit de atribuire =, care are ca
parametru un obiect de tipul clasei.
154
10 Motenirea
10.1 Pointeri la obiecte. Operatorii new i delete
O clas, odat definit, este un tip de date valid. Putem defini variabile pointer de
tipul clasei la fel cu pointeri la orice tip. O variabil tip pointer la o clas poate fi
iniializat cu adresa unui obiect de tipul clasei, iar datele i funciile acelui obiect pot
apelate utiliznd pointerul.
Instruciunea de definire a unui pointer de tipul unei clase este
tip * identificator;
unde tip este numele clasei, iar identificator este numele variabilei tip pointer.
Variabila tip pointer poate fi iniializat, ca orice variabil tip pointer, folosind
operatorul de calcul al adresei &.
Fie p un pointer la un obiect. Un cmp al obiectului este adresat astfel
(*p).membru
Expresia *p se inchide n paranteze deoarece operatorul . are o prioritate mai mare ca
operatorul *. Prin definiie, aceast scriere se prescurteaz
p->membru
O funcie membr a clasei se apeleaz astfel
(*p).funcie(parametri);
Prin definiie, aceast scriere se prescurteaz astfel
p->funcie(parametri);
Operatorii . i -> au aceeai prioritate ca operatorii () i [].
Exemplu. Vom considera din nou definiia clasei Line care reprezint funcia liniar
f(x) = mx + n
Clasa definete variabilele m i n, un constructor ce iniializeaz variabilele i dou
funcii, value(), ce calculeaz valoarea funciei ntr-un punct i print(), ce scrie pe
ecran valorile variabilelor.
Line
double m;
double n;
Line (double, double);
void value(double);
void print();
Definiia clasei Line este urmtoarea
/*
clasa Line descrie functia f(x) = m * x + n
*/
class Line
{
private:
double m, n;
public:
double value(double x) {return (m * x + n) ;}
void print(){
cout << "functia f(x) = m * x + n, " <<
155
" m = " << m << " n = " << n << endl;
}
Line(double x, double y) {m = x; n = y;};
};
Vom apela funciile clasei Line direct i prin pointer.
int main()
{
Line d(1, 0);
double a = 1.5;
// apeleaza functiile direct
d.print();
cout << "f(" << a << ")= " << d.value(a) << endl;
// defineste un pointer la obiect
Line * pl;
pl = &d;
// apeleaza functiile prin pointer
pl->print();
cout << "f(" << a << ")= " << pl->value(a) << endl;
return 0;
}
Datele afiate pe ecran sunt cele de mai jos.
Exist trei moduri de a crea obiecte:
obiecte globale declarate la nivelul programului, n afara oricrei funcii. Ele
sunt create la nceputul execuiei programului i distruse la sfritul execuiei,
obiecte locale, declarate n funcii sau n blocuri din funcii, ntre { i } Ele
sunt create la intrarea n bloc i distruse la ieirea din bloc,
obiecte create n memorie cu operatorul new. Ele trebuie distruse cu
operatorul delete cnd nu mai sunt necesare. Ele sunt create ntr-o zon
special de memorie denumit heap.
Operatorul new creaz un obiect n memorie apelnd constructorul i furnizeaz
adresa obiectului nou creat, adic un pointer. Forma operatorului new este
ptr = new tip (list de parametri);
tip este numele unei clase, iar ptr este variabil tip pointer de tipul clasei. Operatorul
delete are forma
delete ptr;
unde ptr este variabila tip pointer cu adresa obiectului.
Exemplu. Vom considera aceeai clas Line, vom crea un obiect corespunznd
funciei
f(x) = x
156
cu operatorul new i vom calcula f(1.5). Operatorul new are ca rezultat un pointer de
tipul variabilei create. Funcia main() corespunztoare este cea de mai jos.
int main()
{
// defineste un pointer la obiect
Line * pl;
// creaza un obiect cu operatorul new
pl = new Line (1, 0);
double a = 1.5;
// apeleaza functiile prin pointer
pl->print();
cout << "f(" << a << ")= " << pl->value(a) << endl;
// distruge obiectul
delete pl;
return 0;
}
In programul de mai sus puteam scrie
Line *pl = new Line(1, 0) ;
Putem crea vectori de obiecte. Pentru aceasta, clasa trebuie s aib un constructor fr
parametri. Fie, din nou, definiia clasei Line cu un constructor implicit.
# include <iostream>
using namespace std;
class Line
{
private:
double m, n;
public:
void print();
Line(double a, double b) : m(a), n(b) {};
Line() : m(0), n(0) {};
};
void Line::print()
{
cout << "functia f(x) = m * x + n, "
<< " m = " << m << " n = " << n << endl;
}
Exemplu. Vom crea vectori cu dou componente, obiecte tip Line i vom afia pe
ecran parametrii obiectelor create.
In prima variant, crearea vectorului cu dou obiecte de tip Line se face astfel
Line vx[2] ;
La crearea acestui vector, este apelat de ctre sistemul de operare constructorul fr
parametri Line(). Dup definirea vectorului, putem iniializa elementele sale cu
obiecte definite de constructorul cu parametri, de exemplu
Line vx[1] = Line(2.3, -1.2) ;
157
Programul este cel de mai jos.
int main()
{
Line vx[2];
// vx[0] = Line();
vx[1] = Line(2.3, -1.2);
// afisaza obiectele
vx[0].print();
vx[1].print() ;
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos
La crearea vectorului, toate componentele sale sunt iniializate cu constructorul fr
parametri. In consecin, instruciunea
vx[0] = Line() ;
este inutil.
In a doua variant, rescriem programul de mai sus astfel. Definim o variabil pointer
de tip Line i o iniializm cu adresa unei zone de memorie pentru dou obiecte,
alocat cu operatorul new. Dup cum am spus ntr-un capitol anterior, instruciunile :
Line vx[2] ;
i
Line * pvx = new Line[2] ;
sunt echivalente.
De exemplu, putem crea un vector cu dou obiecte de tip Line astfel
Line * vect;
vect = new Line[2];
Programul anterior este acum urmtorul.
int main()
{
// vectori de obiecte
Line * pvx = new Line[2];
pvx[0] = Line(-1, 1);
pvx[1] = Line(-2, 2);
// afisaza obiectele
int i;
for(i = 0; i < 2; i++) {
pvx[i].print();
// (*(pvx + i)).print();
// (pvx + i)->print();
}
delete [] pvx;
158
return EXIT_SUCCESS;
}
Ca exerciiu, obiectele au fost afiate cu instruciunea for. S-au artat forme
echivalente ale apelrii funciei print(). Conform celor spuse ntr-un capitol anterior,
expresiile
pvx[i]
i
*(pvx + i)
sunt echivalente. S se explice de ce expresia *(pvx + i) este scris n paranteze n
instruciunea
(*(pvx + i)).print();
Conform celor spuse mai sus, expresia
(*(pvx + i)).print();
este echivalent cu expresia
(pvx + i) -> print();
memoria alocat cu operatorul new este tears cu instruciunea
delete [] pvx;
Exemplu. Vom rezolva problema de mai sus cu vectori de pointeri. Pentru nceput
definim un vector de pointeri, pv[2] de tipul Line :
Line * pv[2] ;
Componentele vectorului pv sunt iniializate cu adresele unor obiecte create cu
operatorul new
pv[0] = new Line(-1, 1) ;
pv[1] = new Line(-2, 2) ;
Apelarea funciei print() se face conform definiiei de mai sus. De exemplu, adresa
primului obiect, indicat de componentul pv[0] al vectorului pv, este *pv[0], deci
funcia print() se apeleaz
(*pv[0]).print();
sau, echivalent
pv[0]->print();
Programul este urmtorul
int main()
{
int i;
Line * pv[2];
pv[0] = new Line(-1, 1);
pv[1] = new Line(-2, 2);
// scrie obiectele
for(i = 0; i < 2; i++) {
(*pv[i]).print();
// pv[i]->print();
}
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
159
10.2 Motenirea
Motenirea este un concept al programrii cu obiecte. Motenirea permite s
crem clase care sunt derivate din alte clase existente. Clasa care este motenit
se numete clas de baz sau superclas, iar clasele care motenesc se numesc
clase derivate sau subclase. Clasa derivat motenete membrii clasei de baz,
funcii i variabile. In acest fel, putem construi programe complexe din obiecte
simple. Clasa de baz conine caracteristicile comune ale mai multor elemente.
Clasele care motenesc sunt clase particulare ce adaug doar elementele proprii.
Cu ajutorul motenirii reutilizm clase deja construite.
10.2.1 Definirea unei clase derivate
Forma definiiei unei clase derivate este
class nume_clasa_derivata : acces nume_clasa_baza
{
// definitia clasei derivate
};
In aceast definiie, cuvntul cheie acces poate fi public, protected sau private. In
primele exemple, vom utiliza acces public, care are semnificaia c, membrii publici
ai clasei de baz, funcii i variabile, sunt membri publici i n clasa derivat.
Exemplu. Vom defini o clas numit Baza ce definete dou variabile tip double a i
b, un constructor cu parametri ce iniializeaz variabilele a i b i o funcie print() ce
afiaz valorile variabilelor a i b sub forma (a, b).
Baza
double a;
double b;
Baza (double, double);
void print();
Definiia clasei Baza este urmtoarea
# include <iostream>
# include <cmath>
using namespace std;
class Baza
{
protected:
double a, b;
public:
Baza(double x, double y) {a = x; b = y;}
void print() {cout << "(" << a << "," << b << ")" << endl;}
};
160
Variabilele clasei Baza au specificatorul de acces protected. Semnificaia lui este
aceea c variabilele a i b pot fi utilizate n clasele derivate.
Vom defini dou clase, ce descriu triunghiuri i respectiv numere complexe, ce
motenesc clasa Baza, conform diagramei de mai jos.
Baza
double a;
double b;
Baza (double,
double);
void print();
Triunghi
double aria() ;
void print() ;
Triunghi(double, double) ;
In clasa Triunghi, a va fi baza, iar b nlimea. Clasa Triunghi definete o funcie
aria() ce calculeaz aria triunghiului, o funcie print() ce afiaz valorile bazei i
nlimii i un constructor cu parametri. Definiia acestei clase este cea de mai jos.
class Triunghi : public Baza
{
public:
double aria() {return a * b / 2;}
void print(){cout << "(baza, inaltimea) = "; Baza::print(); }
Triunghi(double x, double y) : Baza(x, y) {}
};
Funcia print() a clasei Triunghi apeleaz funcia print() a clasei de baz pentru a scrie
valorile a i b. Apelarea funciei print() din clasa de baz se face cu operatorul de
rezoluie ::
Baza ::print() ;
deoarece simpla apelare cu instruciune print() ar fi nsemnat apelare recursiv a
funciei print() din clasa Triunghi. Constructorul clasei Triunghi apeleaz
constructorul clasei Baza pentru a iniializa variabilele a i b.
In clasa Complex, a i b vor fi partea real i partea imaginar a numrului complex.
Clasa Complex definete o funcie abs() ce calculeaz modulul numrului complex, o
funcie print() ce afiaz numrul complex i un constructor cu parametri.
class Complex : public Baza
{
Complex
double abs() ;
void print() ;
Complex(double, double) ;
161
public:
double abs() {return sqrt(a * a + b * b);}
void print() {cout << "(u, v) = "; Baza::print();}
Complex(double x, double y) : Baza(x, y) {}
};
Pentru a scrie numrul complex, funcia print() a clasei Complex apeleaz funcia
print() a clasei de baz cu operatorul de rezoluie ca mai sus. Constructorul clasei
Complex apeleaz constructorul clasei Baza pentru a iniializa variabilele a i b.
Constructorul clasei derivate apeleaz totdeauna constructorul clasei de baz. Implicit,
se apeleaz constructorul fr parametri. Dac vrem s apelm alt constructor, el este
apelat n lista de iniializatori, ca n exemplele de mai sus.
Pentru a utiliza clasele derivate de mai sus, crem un triunghi i i calculm aria, apoi
definim un numr complex i i calculm modulul.
int main()
{
Triunghi trg(2.5, 1);
Complex c(1, 1);
trg.print();
cout << "aria = " << trg.aria() << endl;
c.print();
cout << "|(u, v)| = " <<c.abs() << endl;
return 0;
}
Rezultate afiate sunt cele de mai jos.
Reamintim c am definit variabilele clasei de baz h i b de tipul protected.
Datele i funciile declarate protected n clasa de baz pot fi utilizate n clasele
derivate, dar nu pot fi utilizate de obiecte.
De exemplu, nu putem scrie n funcia main() instruciunea
cout << trg.a << endl;
Din acest motiv, pentru a afia valorile variabilelor a i b, am definit funcia print().
Datele i funciile declarate private n clasa de baz nu pot fi utilizate n clasele
derivate sau de obiecte de tipul claselor derivate.
10.2.2 Specificatorii de acces
Vom prezenta acum semnificaia cuvntului cheie acces din definiia clasei derivate.
In definiia unei clase, specificatorii de acces sunt : public, private i protected.
Semnificaia lor este urmtoarea :
membrii privai ai clasei pot fi utilizai doar n interiorul clasei,
menbrii protejai ai clasei pot fi utilizai i n clasele derivate,
162
membrii publici ai clasei pot fi utilizai i n afara clasei, n clasele derivate i
n obiectele de tipul clasei sau al claselor derivate.
Clasa derivat motenete membrii publici i pretejai ai clasei de baz.
Membrii publici i protejai ai clasei de baz apar ca i cum ar fi declarai n
clasa derivat.
Fie definiia unei clase numit Baza
class Baza
{
public:
int x;
protected:
int y;
private :
int v ;
} ;
Variabila x poate fi utilizat n clasa Baza, n clasele derivate i n obiecte de tipul
clasei Baza sau clase derivate din Baza. Variabila y poate fi utilizat n clasa Baza i
in clasele derivate, dar nu n obiecte de tipul clasei Baza sau clase derivate. Variabila
v poate fi utilizat doar n interiorul clasei Baza.
Considerm acum specificatorul de acces din definiia clasei derivate :
public spune c toi membrii publici i protejai ai clasei de baz sunt motenii
n clasa derivat ca membrii publici sau protejai,
protected spune c toi membrii publici i protejai ai clasei de baz sunt
motenii n clasa derivat ca membrii protejai,
private spune c toi membrii publici i protejai ai clasei de baz sunt
motenii n clasa derivat ca membrii privai.
Specificatorul de acces din definiia clasei derivate d nivelul minim de acces pentru
membrii motenii din clasa de baz. La utilizarea specificatorului de acces public,
clasa derivat motenete toi membrii clasei de baz cu nivelul de acces avut n clasa
de baz.
Putem sumariza acum specificatorul de acces al variabilelor unui obiect n funcie de
specificatorii lor n clasa de baz i specificatorul din definiia clasei derivate.
specificatorul de acces n
definiia clasei derivate
specificatorul de acces al variabilei n
clasa de baz
public protected private
public public protected acces interzis
protected protected protected acces interzis
private private private acces interzis
Putem sumariza acum accesul la variabilele unui obiect n funcie de specificatorii lor.
specificatorul de acces al variabilei
din tabelul de mai sus
public protected private
membri ai aceleiai clase da da da
membri claselor derivate da da nu
163
nemembri da nu nu
Vom defini mai jos trei clase derivate din Baza cu specificatorii de acces public,
protected i private i vom analiza specificatorii de acces n aceste clase ai variabilelor
x i y definite n clasa Baza..
Fie o clas derivat din Baza cu specificatorul de acces public
class DerivPublic : public Baza
{
/* declaratii din clasa Deriv */
public:
int w;
};
In acest caz variabilele x i y au n clasa DerivPublic specificatorul de acces definit n
clasa Baza, respectiv public pentru x i protected pentru y. Ele pot fi utilizate de
funciile definite n clasa Deriv, dar numai variabila x poate fi utilizat de obiectele de
tipul Deriv, deoarece doar variabilele cu specificatorul de acces public pot fi utilizate
de obiectele de tipul clasei. Variabila y nu poate fi utilizat de obiecte de tipul
DerivPublic deoarece este declarat protected n clasa de baz.
De exemplu, n funcia main() putem avea instruciunile
int main()
{
DerivPublic b;
cout << b.x;
}
dar nu putem avea instruciunea
cout << b.y;
In tabelul de mai jos se arat variabilele obiectului b, cu tipul lor.
Considerm clasa derivat din Baza definit cu specificatorul de acces protected
class DerivProtected : protected Baza
{
/* declaratii din clasa Deriv */
public:
int w;
} ;
In acest caz variabilele x i y au n clasa DerivProtected specificatorul de acces
protected. Ele pot fi utilizate de funciile definite n clasa DerivProtected, dar nu pot fi
utilizat de obiecte de tipul DerivProtected. Fie obiectul
DerivProtected c ;
Variabilele obiectului, cu tipurile lor, sunt cele din tabelul de mai jos.
Considerm acum clasa derivat din Baza definit cu specificatorul de acces private
class DerivPrivate : private Baza
{
/* declaratii din clasa Deriv */
164
public:
int w;
} ;
In acest caz toate variabilele x si y din clasa Baza au n clasa DerivPrivate
specificatorul de acces private. Fie obiectul
DerivPrivate d ;
Variabilele obiectului, cu tipurile lor, sunt cele din tabelul de mai jos.
x, w :public
y : protected
w : public
x, y : protected
w : public
x, y : private
DerivPublic b; DerivProtected c; DerivProtected d;
O clas derivat motenete membri clasei de baz, innd cont de specificatorii de
acces, exceptnd constructorii, destructorul i operatorul =. Dei constructorii i
destructorii nu sunt motenii, constructorul implicit i destructorul clasei de baz sunt
ntotdeauna apelai cnd un obiect de tipul clasei derivate este creat sau distrus.
In constructorul clasei derivate se apeleaz la nceput constructorul implicit al clasei
de baz. Dac este nevoie, putem apela explicit un constructor al clasei de baz pentru
iniializarea variabilelor. Apelarea sa se face n lista de iniializatori, care are forma
constructor_clasa_derivata (lista de parametri)
: constructor_clasa_de_baza (lista de parametri)
{
/* definitia constructorului clasei derivate */
}
Menionm c, la distrugerea unui obiect de tipul unei clase derivate, se apeleaz
destructorul clasei i apoi destructorul clasei de baz.
Exemplu. S definim o clas numit Point care descrie un punct pe ecran prin
coordonatele sale i o clas Pixel ce descrie un pixel i motenete din clasa Point
coordonatele pixelului. Clasa Point va avea dou cmpuri de tip ntreg, x i y, ce sunt
coordonatele punctului pe ecran, doi constructori, o funcie clear() ce pune la valoarea
zero cele dou coordinate i o funcie print() ce scrie pe ecran coordonatele punctului.
Definiia clasei Point este cea de mai jos
165
class Point
{
public:
int x, y;
void clear();
Point(int, int);
Point(){x = 0; y = 0;}
void print();
};
void Point::clear()
{
x = 0;
y = 0;
}
Point::Point(int p1, int p2)
{
x = p1;
y = p2;
}
void Point::print()
{
cout << x = << x << , << y = << y << endl;
}
Definim acum o clas Pixel care s descrie un pixel pe ecran. Un pixel este
caracterizat de coordonatele sale i de culoare. Culoarea va fi un cmp de tip ntreg.
Clasa Pixel va moteni coordonatele punctului din clasa Point. Clasa Pixel va defini
funcia clear() ce va pune la valoarea zero coordonatele i culoarea obiectului tip Pixel
i funcia print() ce va scrie pe ecran coordonatele i culoarea pixelului.
166
class Pixel : public Point
{
public:
int color;
void clear();
Pixel(int, int, int);
void print();
};
In definiia constructorului clasei Pixel vom apela constructorul cu parametri al clasei
Point, care va iniializa coordonatele pixelului.
Pixel::Pixel(int a, int b, int c) : Point(a, b)
{
color = c;
}
In constructorul clasei derivate de mai sus am apelat constructorul clasei de baz n
seciunea :
Pixel :: Pixel(int a, int b, int c) : Point(a, b)
void Pixel::clear()
{
Point::clear();
color = 0;
}
void Pixel::print()
{
Point::print();
cout << culoarea : << color << endl;
}
167
Menionm c nu puteam defini funcia clear() a clasei Pixel astfel
void Pixel::clear()
{
clear();
color = 0;
}
deoarece ar fi nsemnat o apelare recursiv a funciei clear().
Un exemplu de utilizare a claselor Point i Pixel este prezentat mai jos. Vom crea un
obiect pt tip Point i vom afia coordonatele lui, apelnd funcia print(), direct de ctre
obiect, i printr-o variabil tip pointer. Vom crea apoi un obiect p tip Pixel i vom
afia coordonatele i culoarea apelnd funcia print(), direct de ctre obiect, i printr-o
variabil tip pointer.
int main()
{
// creaza un obiect de tipul Point si afisaza coordonatele lui
Point pt(124, 200);
cout << "coordonatele unui punct" << endl;
pt.print();
// afisaza coordonatele punctului utilizand un pointer
Point * ptrp = &pt;
ptrp->print();
// creaza un obiect de tipul Pixel si afisaza coordonatele si culoarea
Pixel p(1, 2, 15);
cout << "coordonatele si culoarea unui pixel" << endl;
p.print();
// afisaza coordonatele si culoarea utilizand un pointer
Pixel * ptrx = & p;
ptrx->print();
return 0;
}
Rezultatul programului este cel de mai jos.
10.3 Funcii virtuale. Polimorfism
Considerm urmtorul exemplu. Vrem s definim o list care s conin informaii
despre angajaii unei intreprinderi, numele i departamentul n cazul tuturor
168
angajailor, iar n cazul managerilor i poziia. Aceste informaii vor fi iruri de
caractere.
O soluie este aceea de a defini dou clase diferite, una pentru angajaii simpli, ce va
defini cmpuri cu numele angajatului i departamentul i alt clas pentru manageri ce
va defini cmpuri cu numele angajatului, departamentul i poziia. Un astfel de
program este complicat deoarece trebuie s prelucreze separat cele dou tipuri de
obiecte.
O alt soluie, aplicat n continuare, este de a defini o clas de baz ce conine
cmpuri cu numele angajatului i departamentul, i o clas derivat ce va defini n
plus un cmp pentru poziia managerului.
Vom defini o clas de baz numit Angajat, ce conine :
dou variabile tip string, nume i dept, cu numele persoanei i al
departamentului,
un constructor cu doi parametri pentru nume i departament,
o funcie print() ce afiaz aceste variabile.
Vom defini apoi o clas numit Manager, ce motenete clasa Angajat, ce conine :
variabil tip string pozitie cu poziia managerului,
un constructor cu trei parametri pentru nume, departament i poziie,
o funcie print() ce afiaz variabilele nume, dept i poziie.
Reprezentarea acestor clase este cea de mai jos.
Definiia clasei Angajat este urmtoarea :
class Angajat
{
protected:
string nume;
string dept;
public:
Angajat(string s, string d) : nume(s), dept(d) {} ;
void print()
{cout << "Nume : " << nume << "\n";
cout << "Departament : " << dept << "\n";
}
};
169
Definiia clasei Manager este urmtoarea.
class Manager : public Angajat
{
protected:
string pozitie;
public:
Manager(string s, string d, string p) : Angajat(s, d), pozitie(p) {};
void print()
{Angajat::print();
cout << " Pozitie : " << pozitie << "\n";
}
};
Presupunem c vrem s afim numele i departamentul pentru toi angajaii i, n
plus pentru manageri, poziia. O soluie este de a crea doi vectori cu pointeri, unul de
tipul Angajat i altul de tip Manager cu adresele obiectelor respective i de a le
prelucra separat. Aceast soluie duce la programe complexe. Limbajul ofer
urmtoarea posibilitate. Un pointer de tipul clasei de baz conine adresa unui
obiect de tipul clasei de baz sau de tipul unei clase derivate. Putem deci memora
adresele obiectelor de tip Angajat i Manager ntr-un vector cu pointeri de tip Angajat
i s apelm funcia print() a fiecrui obiect ntr-un ciclu. Fie urmtoarea funcie
main() pentru rezolvarea problemei (necorect). Definim dou obiecte, unul de tipul
Angajat, altul de tipul Manager i un vector de doi pointeri de tipul Angajat
Angajat x1("Alex", "proiectare");
Manager m1("George", "proiectare", "sef");
Angajat * vect[2];
Memorm adresele celor dou obiecte n componentele vectorului
vect[0] = &x1;
vect[1] = &m1;
i ntr-o instruciune for apelm funcia print() a obiectelor
for(int i = 0; i < 2; i++)
vect[i]->print();
Programul este cel de mai jos
int main()
{
Angajat x1("Alex", "proiectare");
Manager m1("George", "proiectare", "sef");
Angajat * vect[2];
// memoreaza adresele obiectelor in vector
vect[0] = &x1;
vect[1] = &m1;
// apeleaza functia print() a obiectelor din vector
for(int i = 0; i < 2; i++)
vect[i]->print();
return 0;
}
Rezultatele sunt urmtoarele
170
Remarcm c, n cazul celui de-al doilea angajat, nu se afiaz poziia. Problema
acestei soluii este urmtoarea. Funcia apelat este determinat de tipul variabilei
pointer, nu de tipul obiectului. In consecin, este apelat totdeauna funcia print() a
clasei de baz, Angajat, dei, n cazul obiectelor de tipul Manager, ar trebui s se
apeleze funcia clasei Manager.
Putem face ca funcia apelat s fie determinat de tipul obiectului i nu de tipul
variabilei pointer definind funcia respectiv virtual. O funcie virtual are
acelai nume n clasa de baz i n toate clasele ce o motenesc. O funcie este
definit ca virtual scriind cuvntul cheie virtual n faa definiiei funciei.
Vom defini funcia print() din exemplul anterior virtual i n acest caz funcia apelat
este determinat de tipul obiectului. Programul complet este urmtorul.
# include <iostream>
# include <string>
using namespace std;
class Angajat
{
protected:
string nume;
string dept;
public:
Angajat(string s, string d)
{
nume = s;
dept = d;
}
virtual void print()
{
cout << "nume : " << nume << "\n";
cout << " departament : " << dept << "\n";
}
};
class Manager : public Angajat
{
protected:
string pozitie;
public:
Manager(string s, string d, string p) : Angajat(s, d)
{
171
pozitie = p;
}
virtual void print()
{
Angajat::print();
cout << " pozitie : " << pozitie << "\n";
}
};
int main()
{
Angajat x1("Alex", "proiectare");
Manager m1("Bob", "proiectare", "sef");
Angajat * vect[2];
// memoreaza adresele obiectelor in vector
vect[0] = &x1;
vect[1] = &m1;
// apeleaza functia print() a obiectelor din vector
for(int i = 0; i < 2; i++)
vect[i]->print();
return 0;
}
Reamintim c funcia print() este virtual, astfel nct apelul
vect[i]->print();
apeleaz funcia print() din clasa corespunztoare tipului obiectului, producnd
rezultatul corect. Menionm de asemenea c instruciunea
Angajat x1(Alex, proiectare);
este corect deoarece constructorul copiere al clasei string are ca argument un ir de
tip C sau de tip C++.
Rezultatul rulrii programului este cel de mai jos.
Vom spune c apelul de funcie
vect[i]->print();
este polimorfic, deoarece se modific dup natura obiectului indicat de pointer. Orice
clas ce definete sau motenete funcii virtuale se numete polimorf.
Exerciiu. S se nlocuiasc irurile de tip string din clasele Angajat i Manager cu
iruri tip C.
Indicaie. Definiia clasei Angajat poate fi urmtoarea
class Angajat
{
protected:
172
char * nume;
char * dept;
public:
Angajat(char *, char *);
virtual void print();
};
Definiia constructorului Angajat poate fi urmtoarea
Angajat::Angajat(char * s, char * d)
{
nume = new char[strlen(s) + 1]; strcpy(nume, s);
dept = new char[strlen(d) + 1]; strcpy(dept, d);
}
Exemplu. Fie clasele Point i Pixel definite anterior. Vom redefini clasele Point i
Pixel astfel ca, funcia clear(), ce pune la valoarea zero variabilele, i funcia print(),
ce afiaz valoarea variabilelor, vor fi declarate virtuale. Noua definiie a clasei Point
este urmtoarea
class Point
{
private:
int x, y;
public:
virtual void clear();
virtual void print();
Point(int, int);
Point(){x = 0; y = 0;}
};
Definiiile funciilor sunt cele anterioare. Ele vor fi repetate aici.
void Point::clear()
{
x = 0;
y = 0;
}
Point::Point(int p1, int p2)
{
x = p1;
y = p2;
}
void Point::print()
{
cout << x = << x << , << y= << y << endl;
}
173
Clasa Pixel motenete clasa Point. Constructorul ei apeleaz constructorul cu
parametri al clasei Point, iar funciile clear() i print() apeleaz funciile cu acelai
nume din clasa Point. Noua definiie a clasei Pixel este urmtoarea
class Pixel : public Point
{
private:
int color;
public:
virtual void print();
virtual void clear();
Pixel(int, int, int);
};
Definiiile funciilor sunt urmtoarele.
Pixel::Pixel(int a, int b, int c) : Point(a, b)
{
color = c;
}
void Pixel::clear()
{
Point::clear();
color = 0;
}
void Pixel::print()
{
Point::print();
cout << culoare = << color << endl;
}
In funcia main() vom crea un obiect p de tip Point i apoi un obiect px de tipul Pixel.
Vom scrie datele din aceste obiecte apelnd funcia print() printr-un pointer la clasa de
baz.
int main()
{
Point * ptr;
// creaza un obiect de tipul Point
Point p(2, 7);
// atribuie variabilei ptr adresa obiectului p de tip Point
ptr = &p;
// afisaza coordonatele punctului
cout << "coordonatele unui punct" << endl;
ptr->print();
// creaza un obiect de tipul Pixel
Pixel px(1, 2, 15);
// atribuie variabilei ptr adresa obiectului px de tip Pixel
174
ptr = &px;
// afisaza coordonatele si culoarea pixelului
cout << "coordonatele si culoarea unui pixel" << endl;
ptr->print();
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Prima instruciune
ptr->print();
apeleaz funcia print() a clasei Point. A doua instruciune
ptr->print();
apeleaz funcia print() a clasei Pixel.
In consecin, apelul de funcie
ptr->print();
este polimorfic, deoarece se modific dup natura obiectului indicat de pointer.
10.4 Destructori virtuali
La distrugerea unui obiect de tipul unei clase derivate este apelat destructorul clasei
derivate i apoi destructorul clasei de baz.
Fie programul de mai jos n care definim o clas de baz numit Baza i o clas
derivat numit Deriv. In ambele clase, constructorul i desctructorul afiaz un
mesaj. In funcia main() definim o variabil pointer de tipul Baza i i dm ca valoare
adresa unui obiect de tipul Deriv creat cu operatorul new, dup care tergem obiectul
creat cu instruciunea delete
Baza * var = new Deriv();
delete var;
Programul este urmtorul
#include <iostream>
using namespace std;
class Baza
{
public:
Baza(){cout << "Constructor Baza" << endl;}
~Baza(){cout << "Destructor Baza" << endl;}
};
175
class Deriv : public Baza
{
public:
Deriv(){cout << "Costructor Deriv" << endl;}
~Deriv(){cout << "Destructor Deriv" << endl;}

};

int main(int argc, char *argv[])
{
Baza * var = new Deriv();
delete var;
return 0;
}
Rezultatul rulrii programului este urmtorul
Dup cum se observ, este apelat destructorul clasei Baza i nu al clasei Deriv,
deoarece, n cazul funciilor ce nu sunt virtuale, funcia ce se apeleaz este dat de
tipul variabilei, care este Baza, i nu de tipul obiectului memorat. Pentru a apela
destructorul clasei Deriv trebuie s declarm destructorul clasei de baz de tip virtual,
ca mai jos
class Baza
{
public:
Baza(){cout << "Constructor Baza" << endl;}
virtual ~Baza(){cout << "Destructor Baza" << endl;}
};
Rezultatul rulrii programului este acum urmtorul
Acum este apelat destructorul clasei derivate, ce apeleaz i destructorul clasei de
baz.
176
10.5 Date i funcii statice
10.5.1 Date statice
Obiectele de tipul unei clase au propriile variabile ce conin valori ce dau starea
fiecrui obiect. Uneori, este necesar s existe o variabil a clasei, ntr-un singur
exemplar, pentru toate obiectele. Acest lucru se poate realiza simplu, definind acea
variabil de tip static.
Definirea se face scriind cuvntul cheie static la nceputul declaraiei variabilei.
Datele de tip static se iniializeaz n afara clasei. Datele statice sunt la fel ca i
variabilele globale, dar n spaiul de nume al clasei.
Exemplu.
class A
{
public:
static int x;
};
int A::x = 0;
Datele statice exist, chiar dac nu exist nici un obiect de tipul clasei. Pentru
exemplul clasei definite mai sus, putem utiliza variabila static x ca A::x, sau A.x sau,
dac exist obiecte de tipul clasei, ca nume_obiect.x Datele statice sunt iniializate
automat la zero.
O aplicaie a datelor statice este aceea de a numra obiectele existente de tipul clasei.
Exemplu. Vom defini o clas cu o variabil static ce va numra obiectele existente de
tipul clasei. Constructorul adun o unitate, iar destructorul scade o unitate din
variabila static. Valoarea iniial a variabilei statice este zero. Reamintim c
variabilele statice se iniializeaz n afara clasei. Definiia clasei este urmtoarea.
# include <iostream>
using namespace std;
class X
{
public:
static int nb;
X(){nb ++;}
~X(){nb--;}
};
int X::nb = 0;
In funcia main() vom crea obiecte i vom afia numrul lor.
int main()
{
X a, b;
cout << exista << X::nb << obiecte << endl;
{
X c, d;
177
cout << exista << X::nb << obiecte << endl;
}
cout << exista << X::nb << obiecte << endl;
return 0;
}
Rezultate afiate vor fi
Dup instruciunea
X a, b;
exist dou obiecte, a i b. Dup instruciunea
X c, d;
exist patru obiecte, a, b, c i d. Dup ieirea din blocul interior exist dou obiecte,
obiectele c i d fiind distruse la ieirea din bloc. Menionm c, putem adresa variabila
nb ca X.nb sau X::nb.
10.5.2 Funcii statice
O clas poate defini funcii statice. Ele pot prelucra doar datele statice ale clasei.
Funciile statice pot fi apelate utiliznd numele clasei sau al unui obiect de tipul clasei.
Definirea unei funcii statice se face scriind cuvntul cheie static naintea definiiei
funciei. Funciile statice sunt la fel ca i funciile globale, dar n spaiul de nume al
clasei.
Un exemplu de utilizare a funciilor statice este tehnica de programare
ClassFactory. Ea const n urmtoarele. Fie o clas de baz numit Base i dou
clase derivate din ea, Deriv1 i Deriv2. Vrem s definim o funcie care s aibe ca
rezultat un obiect de tipul Deriv1 sau Deriv2 n funcie de valoarea unui
parametru. Acest lucru se face definind o clas cu o funcie static, ce creaz un
obiect de tipul Deriv1 sau Deriv2, n funcie de valoarea parametrului i
furnizeaz adresa obiectului creat ca pointer de tipul clasei de baz Base.
Exemplu. Vrem s definim urmtoarele dou clase :
o clas cu o metod ce calculeaz suma a dou numere,
o clas cu o metod ce calculeaz produsul a dou numere.
In ambele cazuri metoda se va numi oper(). Vrem ca n funcie de valoarea unui
parametru s obinem un obiect care s fac produsul sau suma a dou numere.
Vom defini o clas de baz ce definete dou variabile a i b, constructorii
corespunztori i o funcie virtual oper() ce nu efectueaz nici un calcul.
# include <iostream>
using namespace std;
class Base
{
protected:
float a, b;
178
public:
Base() {a = 0; b = 0;}
Base(float, float);
virtual float oper(){return 0;}
};
Base::Base(float x, float y)
{
a = x;
b = y;
}
Clasele derivate definesc funcia virtual oper() ce efectueaz adunarea, In cazul
clasei Add, respectiv produsul a dou numere, n cazul clasei Mul. Ele au urmtoarele
definiii
class Add : public Base
{
public:
Add(float x, float y) : Base(x, y) {}
virtual float oper(){return a + b;}
};
class Mul : public Base
{
public:
Mul(float x, float y) : Base(x, y) {}
virtual float oper() {return a * b;}
};
Vom defini o clas cu o metod static numit operfactory ce creaz un obiect de tip
Add sau Mul n funcie de valoarea unui parametru i are ca rezultat un pointer de
tipul clasei de baz (Base) cu adresa obiectului creat.
class ClassFactory
{
public:
static Base * operfactory(float, float, char);
};
Implementarea funciei operfactory este urmtoarea
Base * ClassFactory::operfactory(float x, float y, char c)
{
if(c == '+')
return new Add(x, y);
else
return new Mul(x, y);
};
179
Presupunem c vrem s calculm suma a dou numere. In funcia main() se creaz un
obiect de tipul Add apelnd funcia operfactory i se memoreaz adresa obiectului
ntr-un pointer de tipul clasei de baz. Apoi utilizeaz metoda oper() a obiectului la
calculul sumei a dou numere.
int main()
{
float z;
Base * base;
ClassFactory cf;
float x = 1.2, y = -2.35;
cout << "suma numerelor " << x << " si " << y << endl;
// creaza un obiect care sa calculeze suma a doua numere
base = cf.operfactory(x, y, '+');
// calculeaza suma numerelor
z = base->oper();
cout << z << endl;
cout << "produsul numerelor " << x << " si " << y << endl;
// creaza un obiect care sa calculeze produsul a doua numere
base = cf.operfactory(x, y, '*');
// calculeaza produsul numerelor
z = base->oper();
cout << z << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
180
11 Fiiere tip C++
Limbajul C++ definete clasele istream i ostream pentru operaii intrare i ieire cu
fiierele standard, tastatura i ecranul. Obiectul cin este o instan a clasei istream, iar
obiectul cout o instan a clasei ostream. In afar de aceste dou obiecte, mai sunt
predefinite: obiectul cerr, de tipul ostream, care este fiierul de ieire standard pentru
erori i obiectul clog, de tipul ostream, pentru nregistrarea diverselor mesaje. De
obicei, aceste dou fiiere sunt tot ecranul.
Pentru lucrul cu fiiere de date limbajul C++ definete urmtoarele clase:
ofstream pentru operaii de scriere de fiiere,
ifstream pentru operaii de citire din fiiere,
fstream pentru operaii de citire i scriere a fiierelor.
Aceste clase motenesc clasele istream i ostream. Definiiile acestor clase se gsesc
n bibliotecile <ifstream>, <ofstream> i <fstream>. O serie de constante, utilizate de
aceste clase, sunt definite n clasa ios. Pentru prelucrarea unui fiier se creaz un
obiect, (un stream), instan a uneia din clasele de mai sus. Clasele istream i ostream
sunt definite n biblioteca <iostream>. Diagrama motenirii acestor clase este cea de
mai jos.
Funciile membre importante ale claselor sunt:
constructor fr parametri,
funcia membr open() cu prototipul
open(char * filename, int mode);
asociaz obiectul creat cu fiierul ce va fi prelucrat. Parametrul filename este un ir de
caractere cu numele fiierului. Al doilea parametru este opional i d modul de
deschidere. El poate avea valorile :
ios::in - deschidere n citire
ios::out deschidere n scriere
ios::binary fiier binary
Aceti parametri se pot combina folosind operatorul | . De exemplu, pentru un fiier
binar deschis n scriere, vom avea
ios::binary | ios:out
iar pentru deschiderea unui fiier binar n citire
ios::binary | ios::in
Acest al doilea parametru este opional pentru obiecte de tipul ifstream i ofstream
care sunt automat deschise n citire i respectiv scriere.
constructor cu parametrii funciei open(). Acest constructor creaz un obiect i
apoi deschide fiierul,
funcia
void close();
181
nchide un fiier,
funcia
bool eof();
are valoarea adevrat dac s-a detectat sfritul fiierului,
funcia
bool is_open();
are valoarea adevrat dac fiierul este deschis.
In cazul citirii datelor de la tastatur, (din fiierul cin), sfritul de fiier este
indicat prin Ctrl+Z. Dup aceasta se apas tasta return.
11.1 Fiiere text
Fiierele text sunt compuse din linii, care sunt iruri formate din 0 sau mai multe
caractere, separate de caracterul \n. Exist dou tipuri de operaii intrare/ ieire
pentru fiiere tip text :
funcii pentru intrri / ieiri cu format,
funcii ce scriu / citesc caractere.
11.1.1 Funcii intrare / ieire cu format
Fiierele tip text se prelucreaz n acelai mod ca fiierele standard de intrare i ieire,
cin i cout. Operatorii de citire i scriere sunt >> i <<.
Exemplu. Vom calcula valoarea expresiei
) sin(
1
) ( cos
2
x
x
x x
e +
+
+

pentru x cuprins n intervalul [0, 2] cu pasul 0.2. Vom scrie valorile calculate ntr-un
fiier text cu numele rez.txt. In fiecare linie vom scrie o pereche de valori x i e,
separate de caracterul \t. Apoi, vom citi fiierul creat i vom afia rezulatate pe
ecran. Programul este urmtorul.
# include <fstream>
# include <iostream>
# include <cmath>
using namespace std;
int main()
{
char * filename = rez.txt;
ofstream fil1;
fil1.open(filename);
if(!fil1.is_open())
{
cout << Nu se poate crea fisierul << filename << endl;
return EXIT_FAILURE;
}
// creaza fisierul
double x, e;
for(int i = 0; i < 11; i++)
{
// calculeaza expresia si scrie in fisier
182
x = i * 0.2;
e = (cos(x) * cos(x) + x) / (1.0 + fabs(x)) + sin(x);
fil1 << x << \t << e << endl;
}
fil1.close();
// citeste fisierul creat si afisaza rezulatele
ifstream fil2;
fil2.open(filename);
if(!fil2.is_open())
{
cout << nu se poate deschide fisierul << filename << endl;
return EXIT_FAILURE;
}
// scrie antetul
cout << "x" << '\t' << "e" << endl;
fil2 >> x >> e;
while(!fil2.eof())
{
cout << x << \t << e << endl;
fil2 >> x >> e;
}
fil2.close();
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos.
Exerciiu. S se rescrie programul de mai sus definind un singur obiect de tipul
fstream
fstream fil;
Se va deschide la nceput streamul n scriere pentru crearea fiierului cu instruciunea
fil.open(filename, ios::out);
i apoi n citire pentru afiarea rezultatelor cu instruciunea
fil.open(filename, ios::in);
11.1.2 Funcii intrare / ieire tip caracter
Fiierele text sunt formate din linii ce conin zero sau mai multe caractere, urmate de
caracterul \n. Pentru prelucrarea acestor fiiere sunt definite funcii ce citesc sau
scriu caractere i linii, adic iruri de caractere terminate cu caracterul \n.
183
Fiierele text au indicator de poziionare ce conine numrul urmtorului caracter de
scris sau de citit. Acest indicator are valoarea zero la deschiderea fiierului i apoi este
modificat de instruciunile de citire sau scriere a fiierului.
Clasa ifstream are urmtoarele funcii membre pentru citirea caracterelor. Funciile :
int get();
ifstream& get(char&);
citesc un caracter dintr-un fiier.
Clasa ofstream are funcia membr
ofstream& put(char);
ce scrie un caracter ntr-un fiier.
Reamintim c, clasele istream i ostream, definesc aceleai funcii.
Exemplu. Fie fis un obiect tip ifstream i c o variabil tip caracter. Instruciunile
c = fis.get() ;
i
fis.get(c) ;
citesc un caracter din streamul fis n variabila c.
Reamintim c operatorul >> este un operator binar. Operandul stng este un obiect tip
ifstream, operandul drept este o variabil, rezultatul este o referin la un obiect tip
ifstream. innd cont de prototipul funciei get()
ifstream& get(char&);
este posibil s citim un caracter i un ntreg
char c;
int x;
cu instruciunea
fis.get(c) >> x;
unde fis este un obiect tip fstream, deoarece operandul stng este o referin tip
ifstream, rezultatul evalurii funciei get(char&). Acelai lucru este valabil pentru
operatorul << i funcia put();
Exemplu. Vom copia un fiier n altul, citind cte un caracter din primul fiier i
copiindu-l n al doilea i vom calcula lungimea fiierului.
# include <iostream>
# include <fstream>
using namespace std;
int main()
{
char filename [24];
cout << Introduceti numele fisierului de copiat << endl;
cin >> filename;
ifstream fila(filename);
if(!fila.is_open())
{
cout << endl << Fisierul << filename << nu exista <<
endl;
return EXIT_FAILURE;
}
cout << endl << Introduceti numele noului fisier << endl;
184
cin >> filename;
ofstream filb(filename);
if(!filb.is_open())
{
cout << Fisierul << filename << nu se poate crea << endl;
return EXIT_FAILURE;
}
char car;
int nl = 0;
fila.get(car);
while(!fila.eof())
{
filb.put(car);
nl++;
fila.get(car);
}
fila.close();
filb.close();
cout << "fisierul " << filename << are << nl << " caractere" <<
endl;
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos. In acest exemplu fiierul surs,
main.cpp este copiat n fiierul main.txt.
Menionm c expresia
fila.get(car)
are valoarea fals la ntlnirea sfritului de fiier. In consecin, puteam copia fiierul
astfel
while(fila.get(car))
{
filb.put(car);
nl++;
}
Pentru citirea unui ir de caractere se utilizeaz funcia getline() a clasei ifstream.
Funcia
ifstream& getline(char * bloc, int size, char ch);
citete cel mult size 1 caractere n vectorul bloc. Citirea se oprete dup citirea a
size-1 caractere sau la ntlnirea caracterului ch. Valoarea implicit a caracterului ch
este \n. In acest caz funcia are forma
ifstream& getline(char * bloc, int size);
185
Citirea unui ir de caractere dintr-un fiier tip text ntr-un obiect tip string se poate
face i cu funcia global
istream& getline(istream& file, string str, char ch) ;
unde file este un obiect tip istream. Funcia citete caractere n obiectul str de tip
string pn la ntlnirea caracterului ch. Valoarea implicit a caracterului ch este \n,
n care caz funcia are forma
istream& getline(istream& file, string str);
Vom ilustra utilizarea funciei getline la afiarea unui fiier pe ecran. Programul
citete cte o linie i o scrie pe ecran;
# include <iostream>
# include <fstream>
# include <string>
using namespace std;
int main()
{
string str;
ifstream fin("test.txt");
if(fin.is_open())
{
while(!fin.eof())
{
getline(fin, str);
cout << str << endl;
}
fin.close();
}
return 0;
}
In acest exemplu, se creaz un obiect (stream) cu constructorul clasei ifstream cu
parametrii funciei open(). Constructorul creaz obiectul i deschide fiierul.
11.2 Fiiere binare
Scrierea i citirea datelor din fiierele binare se face cu funciile:
write(char * block, int size);
read(char * block, int size);
Primul parametru este adresa unui vector de caractere de unde sunt scrise datele sau
unde sunt citite datele. Al doilea parametru d numrul de caractere de citit sau de
scris. Fiierele au indicatori interni care dau adresa urmtorului octet de citit sau de
scris. Voloarea acestor indicatori este dat de funciile
tellg();
pentru indicatorul urmtorului octet de citit i
tellp();
pentru indicatorul urmtorului octet de scris.
Indicatorii de poziie sunt modificai de funciile
seekg(int offset, int direction);
pentru indicatorul de citire i respectiv
seekp(int offset, int direction);
186
pentru indicatorul de scriere. Parametrul offset d valoarea cu care se modific
indicatorul. Parametrul direction are valorile :
ios::beg relativ la nceputul fiierului
ios::end relativ la sfaritul fiierului
ios::cur relativ la poziia curent
Exemplu. Vom crea un fiier binar cu 10 blocuri de cte 10 caractere fiecare, dup
care citim fiierul i l afim pe ecran. Primul bloc va conine cractere 0, al doilea
bloc caractere 1, etc. Citirea fiierului se face dup schema cunoscut, testnd
sfaritul de fiier dup fiecare citire.
# include <iostream>
# include <fstream>
using namespace std;
int main()
{
char filename[] = fis.txt;
char x[11];
ofstream fila;
// deschide fisierul in creare
fila.open(filename, ios::out | ios::binary);
if(!fila.is_open())
{
cout << Nu se poate crea fisierul << filename << endl;
return EXIT_FAILURE;
}
// creaza fisierul
for(int i = 0; i < 10; i++)
{
// creaza un bloc
for(int j = 0; j < 10; j++)
x[j] = 0 + i;
x[10] = 0;
// scrie blocul in fisier
fila.write(x, 11);
}
fila.close();
ifstream filb;
// deschide fisierul
filb.open(filename, ios::binary | ios::in);
if(!filb.is_open())
{
cout << Nu se poate citi fisierul << filename << endl;
return EXIT_FAILURE;
}
// citeste si afisaza fisierul
filb.read(x, 11);
while(!filb.eof())
{
187
cout << x << endl;
filb.read(x, 11);
}
filb.close();
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos.
Exemplu. Vom crea un fiier binar format din 26 de blocuri, coninnd iruri de 10
caractere fiecare. Primul bloc va conine caractere a, al doilea bloc va conine
caractere b, etc. Vom citi apoi fiecare al patrulea bloc, modificnd indicatorul de
citire.
# include <iostream>
# include <fstream>
using namespace std;
int main()
{
char filename[] = fil.txt;
char x[11];
ofstream filx;
// creaza fisierul
filx.open(filename, ios::out | ios::binary);
if(!filx.is_open())
{
cout << Nu se poate crea fisierul << filename << endl;
return EXIT_FAILURE;
}
int i;
for(i = 0; i < 26; i++)
{
// creaza un bloc
for(int j = 0; j < 10; j++)
x[j] = a + i;
x[10] = 0;
// scrie blocul in fisier
filx.write(x, 11);
}
188
filx.close();
ifstream fily;
// citeste si afisaza fisierul
fily.open(filename, ios::binary | ios::in);
if(!fily.is_open())
{
cout << Nu se poate citi fisierul << filename << endl;
return EXIT_FAILURE;
}
// citeste si afisaza fisierul
for(i = 0; i < 26; i = i + 4)
{
// modifica indicatorul fisierului
fily.seekg(i * 11, ios::beg);
fily.read(x, 11);
cout << x << endl;
}
fily.close();
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos.
11.3 Fiiere text tip string
Limbajul C++ definete, n afar de fiierele standard i cele pe disc, i fiiere tip
string. In cazul acestor fiiere scrierea / citirea datelor se face n / din iruri tip string.
Limbajul definete clasele
istringstream
ostringstream
stringstream
pentru fiiere de intrare, ieire i respective intrare / ieire tip string. Definiiile acestor
clase se gsesc n biblioteca <sstream>. Clasele definesc constructori far parametri i
constructori cu un parametru tip string. Scrierea i citirea se fac cu operatorii << i >>.
Clasele definesc funcia
string str();
care furnizeaz o copie a irului din obiectul stream tip i/o/stringstream.
Exemplu. Vom scrie valoarea unor variabile de tip int, respectiv double, separate de
un spaiu ntr-un fiier tip string, vom citi din acest fiier o variabil tip string i vom
scrie valoarea ei pe ecran. Dup fiecare operaie vom afia pe ecran irul de caractere
din fiierul tip string.
189
# include <string>
# include <iostream>
# include <sstream>
using namespace std;
int main()
{
int x = 12;
double d = -3.42;
string t;
// creaza un obiect stringstream ce contine un sir vid
stringstream ss;
// scrie valoarea variabilelor x si d separate de un spatiu in obiectul ss
ss << x << << d;
// scrie valoarea sirului continut in obiectul ss
cout << ss = << ss.str() << endl;
// citeste valoarea variabilei tip string t din obiectul ss
ss >> t;
// scrie valoarea variabilei t pe ecran
cout << t = << t << endl;
// scrie valoarea sirului continut in obiectul ss
cout << ss = << ss.str() << endl;
return 0;
}
Valorile afiate pe ecran vor fi
Variabilele tip int i float sunt convertite n ir de caractere la scrierea n obiectul ss.
Variabila t va primi ca valoare prin citire irul de caractere 12 . Reamintim c, la
citirea unui ir de caractere, operatorul << citete caractere pn primul caracter
spaiu, \t sau \n ntlnit. Menionm c valoarea irului coninut ntr-un fiier de tip
string nu este modificat de operatorul de citire >>.
190
12 Tratarea excepiilor
12.1 Excepii
In timpul execuiei programului pot aprea diverse erori, numite n continuare
excepii. Un tip de erori sunt cele asociate operaiilor intrare/ ieire : ncercm s citim
un fiier inexistent, vrem s crem un fiier pe disc dar discul este plin, etc. Alt tip de
erori sunt cele legate de operaiile aritmetice : mprirea prin zero, indici eronai,
depirea gamei de reprezentare a numerelor, etc. Limbajul C++ are un mecanism de
tratare a excepiilor ce permite ca, atunci cnd apare o eroare, (o excepie), programul
s apeleze automat o funcie de tratare a erorii. Aceast funcie are ca parametru un
obiect cu informaii despre eroare. Tratarea excepiilor se face cu instruciunile try,
catch i throw. Instruciunea pe care o urmrim pentru apariia unei excepii trebuie
inclus ntr-un bloc try. Dac apare o excepie (o eroare), ea este lansat cu
instruciunea throw, care genereaz un obiect cu informaii despre eroare. Eroarea
este prins (tratat) de instruciunea catch care are ca parametru obiectul cu informaii
despre eroare, generat de instruciunea throw. Forma general a instruciunilor try i
catch este urmtoarea
try
{
// bloc cu instruciuni
}
catch(tip1 arg)
{
// prelucreaza exceptia
}
catch(tip2 arg)
{
// prelucreaza exceptia
}

catch(tipn arg)
{
// prelucreaza exceptia
}
Atunci cnd apare o excepie, programul trebuie s execute instruciunea throw cu
forma
throw obiect;
Excepia este obiectul cu informaii despre eroare generat de instruciunea throw. Ea
poate fi un obiect simplu de tip int, char, double, etc., sau un obiect instan a unei
clase definit de programator. Intr-un bloc pot aparea mai multe tipuri de excepii.
Fiecare tip de excepie este tratat de o anumit instruciune catch. Instruciunea catch
executat este cea pentru care tipul argumentului coincide cu tipul obiectului generat
de instruciunea throw. In consecin, o instruciune try poate fi urmat de mai multe
instruciuni catch. Tipurile argumentelor din instruciunile catch trebuie s fie diferite.
191
Exemplu. Tratarea unei excepii mprire prin zero. Instruciunea throw va genera un
obiect de tip int ce conine numrul liniei unde a aprut eroarea. Limbajul definete
dou constante utile n cazul excepiilor :
constanta __LINE__ conine numrul liniei curente compilate,
constanta __FILE__ conine numele fiierului curent compilat.
Vom utiliza constanta __LINE__ n instruciunea throw pentru a semnala linia n care
a aprut eroarea. Instruciunea throw va aprea ntr-un bloc try. Programul este
urmtorul.
#include<cstdlib>
# include <iostream>
using namespace std;
int main()
{
int x = 2, y = 0, z;
try
{
if(y == 0)
throw __LINE__;
z = x / y;
cout << "x = " << x << " y = " << y << " x / y = " << z << endl;
}
catch(int a)
{
cout << "impartire prin zero linia : " << a << endl;
}
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Blocul catch ncearc repararea erorii aprute. Dac nu este posibil acest lucru, se pot
utiliza funciile :
void exit(int);
sau
void abort();
pentru a termina programul. Funcia exit() termin programul dintr-o funcie diferit
de main(). Prototipurile acestor funcii se gsesc n biblioteca <cstdlib>.
Pentru a prinde o excepie, trebuie ca tipul obiectului lansat de instruciunea throw s
coincid cu tipul obiectului specificat ntr-o instruciune catch, astfel excepia nu este
prins.
Preluarea tuturor excepiilor
192
Dac dorim ca un singur bloc catch s preia toate excepiile unui bloc try vom scrie
blocul catch astfel
catch()
{
// trateaza exceptiile
}
12.2 Excepii lansate de funcii
Intr-un bloc try pot exista apeluri la multe funcii, i orice funcie poate lansa excepii.
Excepiile lansate de o funcie sunt precizate la definirea funciei n felul urmtor
tip nume_functie(lista de parametri) throw (lista de tipuri)
{
// corpul functiei
}
Lista de tipuri d tipurile de excepii ce vor fi lansate de instruciunea throw.
Exemplu. Vom defini o funcie ce calculeaz ctul a dou numere ntregi i lanseaz o
excepie, un obiect de tip string, dac numitorul este zero. Tipul excepiei lansate de
funcie este precizat n linia de definiie a funciei i este, n cazul nostru, un ir tip
string ce va conine mesajul
impartire prin zero linia :

+ numrul liniei + fisierul : + numele fisierului sursa.
Numrul liniei unde apare eroarea este constanta __LINE__ , vezi exemplul anterior.
Numele fiierului surs este coninut n constanta tip caracter __FILE__. Obiectul de
tip string ce reprezint mesajul este creat cu secvena de instruciuni :
stringstream ss;
ss << "linia : " << __LINE__ << " fisierul : " << __FILE__ ;
Programul este urmtorul
# include <iostream>
# include <string>
#include <sstream>
using namespace std;
int fun(int x, int y) throw(string)
{
if(y == 0)
{
stringstream ss;
ss << "linia : " << __LINE__ << " fisierul : " << __FILE__;
throw string(impartire prin zero

+ ss.str());
}
else
return x / y;
}
193
int main()
{
int a = 10, b = 0, c;
try
{
// functie ce poate lansa o exceptie
c = fun(a, b);
cout << a << / << b << = << c << endl;
}
catch(string mesaj)
{
cout << mesaj << endl;
}
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Menionm c funcia ce lanseaz o excepie este apelat ntr-un bloc try, iar excepia
este prins n blocul catch corespunztor.
Exerciiu. S se modifice funcia fun() astfel ca irul ss s fie de tip C.
Indicaie. Se va defini un ir ss de tip C i se vor nscrie n el constantele __LINE__ i
__FILE__ cu funcia sprintf() :
char ss[50];
sprintf(ss, "linia : %i fisierul : %s", __LINE__, __FILE__);
throw string(impartire prin zero

+ string(ss));
Prototipul funciei sprintf() este definit n biblioteca <cstdio>.
12.3 Excepii standard
Limbajul C++ definete o clas de baz special proiectat pentru a declara obiecte
care s fie lansate de instruciunea throw. Clasa se numete exception iar prototipul ei
se afl n biblioteca <exeception>. Clasa definete
un constructor implicit i un constructor copiere :
exception();
exception(exception&);
operatorul = ,
funcia virtual what(), cu prototipul
virtual char * what();
care are ca rezultat un mesaj despre excepie.
Funciile din clasele standard ale limbajului C++ genereaz excepii de tipul exception
sau de tipul unei clase standard derivate din clasa exception, dup cum se va arta n
continuare.
Vom prezenta un exemplu n care se prinde o eroare generat de o funcie a clasei
string: funcia append, apelat cu un parametru eronat: indicele primului caracter din
irul adugat depete indicele ultimului element din irul adugat.
194
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
int main()
{
try
{
string str1("err");
string str2("test");
str1.append(str2, 4, 3);
cout<<str1<<endl;
}
catch (exception &e)
{
cout << "Exception: " << e.what() <<endl;
cout << "Type: " << typeid(e).name() <<endl;
}
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Excepiile lansate de funciile din bibliotecile standard ale limbajului C++ sunt
grupate n patru categorii:
excepii lansate de operatorul new cnd nu se poate aloca memoria cerut,
excepii lansate cnd o excepie nu corespunde nici unei clauze catch, etc.;
aceste excepii sunt descrise de clase ce motenesc direct din clasa excepion,
excepii logice, care pot fi determinate nainte de execuia programului, de
exemplu ncercarea de a apela o funcie cu un argument invalid; aceste
excepii sunt descrise de clase ce motenesc din clasa logic_error,
excepii detectabile la execuia programului, de exemplu depirea gamei
inferioare sau superioare de reprezentare a numerelor n timpul unei operaii
aritmetice; aceste excepii sunt descrise de clase ce motenesc din clasa
runtime_error,
excepii intrare / ieire ce apar la operaii cu fiiere; aceste excepii sunt
descrise de clasa ios::failure; ele vor fi tratate ntr-un paragraf special.
Clasele logic_error, runtime_error i ios::failure extind direct clasa exception i sunt
extinse de alte clase pentru a descrie diverse tipuri de erori. Clasele logic_error i
runtime_error i clasele ce le extind sunt definite n biblioteca standard <stdexcept>.
Exemplu. S rescriem funcia care ce calculeaz ctul a dou numere ntregi i
lanseaz o excepie, un obiect de tip runtime_error, dac numitorul este zero. Tipul
excepiei lansate de funcie este precizat n linia de definiie a funciei i este, n cazul
195
nostru, este un obiect de tipul runtime_error ce conine un mesaj de eroare. Programul
este urmtorul.
#include <iostream>
# include <stdexcept>
#include <sstream>
using namespace std;
int fun(int x, int y) throw(runtime_error)
{
if(y == 0)
{
stringstream ss;
ss << __LINE__;
throw runtime_error("impartire prin zero linia : " + ss.str());
}
else
return x / y;
}
int main(int argc, char *argv[])
{
int a = 10, b = 0, c;
try
{
// functie ce poate lansa o exceptie
c = fun(a, b);
cout << a << "/" << b << "=" << c << endl;
}
catch(runtime_error& exm)
{
cout << exm.what() << endl;
}
return 0;
}
Rezultatul rulrii ptogramului este cel de mai jos.
12.4 Excepii intrare / ieire
Orice obiect de tip stream conine urmtorii bii, definii n clasa ios, ce dau starea
curent a streamului :
badbit poziionat la unu la apariia unei erori nereparabile (eroare intrare /
ieire pe disc),
196
failbit poziionat la unu la apariia unei erori reparabile (de exemplu citim o
variabil numeric, dar streamul de intrare conine litere, ncercm s
deschidem n citire un fiier inexistent, etc),
eofbit poziionat la unu cnd s-a detectat sfritul de fiier la citire,
goodbit poziionat la unu atunci cnd nu exist eroare.
Aceti bii sunt definii n clasa ios i sunt poziionai dup fiecare operaie de scriere
sau citire. Ei pot fi testai cu urmtoarele funcii definite n clasa ios :
bool bad(); are valoarea true dac bitul badbit are valoarea unu,
bool fail(); are valoarea true dac cel puin unul din biii badbit i failbit are
valoarea unu,
bool eof(); are valoarea true dac bitul eofbit are valoarea unu,
bool good(); are valoarea true dac ceilali bii au valoarea zero.
Menionm c funcia eof() a fost deja folosit la citirea fiierelor. Implicit, erorile
streamurilor nu lanseaz excepii. Putem permite lansarea excepiilor cu funcia
int exceptions(int except);
definit n clasa ios, unde except este o combinaie a biilor pentru care dorim s
urmrim excepii:
ios::badbit
ios::failbit
Aceti bii se pot combina cu operatorul |. De exemplu
exceptions(ios::badbit | ios::failbit);
permite lansarea excepiilor cnd biii badbit i failbit sunt poziionai la unu, n urma
unei operaii intrare / ieire.
In cazul n care excepiile sunt permise, instruciunile de prelucrare a fiierului vor fi
incluse ntr-un bloc try i excepiile sunt prinse n blocul catch corespunztor. Clasa
de baz pentru excepiile lansate de streamuri este clasa failure, care motenete din
clasa exception. Ea este o clas intern a clasei ios i definete constructorul
failure(string);
i funcia virtual
const char * what();
vezi definiia clasei exception.
Exemplu. Vom citi un fiier tip text i vom afia coninutul lui pe ecran. Se vor lansa
excepii n cazul apariiei unei erori n care sunt poziionai la unu biii badbit i
failbit.
#include <iostream>
#include <fstream>
#include <exception>
using namespace std;
int main()
{
ifstream file;
// permite lansarea unei exceptii
file.exceptions(ios::badbit | ios::failbit);
try
{
197
char car;
file.open(rez.txt);
car = file.get();
while(! file.eof())
{
cout << car;
car = file.get();
}
file.close();
}
catch(ios::failure e)
{
cout << exceptie : << e.what() << endl;
}
return 0;
}
Blocul catch are ca parametru un obiect de tipul failure
catch(ios::failure e)
care este tipul excepiilor lansate de streamuri.
198
13 Aplicaii
13.1 Funcii de timp
Limbajele C i C++ au funcii predefinite pentru a obine timpul curent. Prototipurile
acestor funcii se gsesc n bibliotecile <time.h>, pentru programele C, respectiv
<ctime>, pentru programele C++.
Timpul este msurat n secunde, de la data de 1 Ianuarie 1970 GMT ora 0. Tipul
variabilelor ce memoreaz timpul este long int i este redefinit ca time_t.
typedef long time_t; /* time value */
Funcia standard ce completeaz o variabil de acest tip este
time_t time(time_t * timeptr);
Funcia time are ca rezultat timpul n parametrul timeptr i ca valoare.
Exemplu. Fie instruciunile :
time_t t1, t2;
t1 = time(&t2);
Variabilele t1 i t2 primesc ca valoare timpul curent.
Funcia ctime convertete timpul unei variabile de tip time_t ntr-un ir de caractere de
forma
luna ziua hh:mm:ss an
Prototipul funciei este
char* ctime(time_t * timeptr);
Exemplu. Instruciunile
cout << ctime(&t1) << endl;
sau
printf(%s\n, ctime(&t1));
afiaz timpul din variabila t1 ca mai jos.
Structura predefinit tm conine urmtoarele informaii
struct tm {
int tm_sec; /* seconds after the minute - [0,59] */
int tm_min; /* minutes after the hour - [0,59] */
int tm_hour; /* hours since midnight - [0,23] */
int tm_mday; /* day of the month - [1,31] */
int tm_mon; /* months since January - [0,11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday - [0,6] */
int tm_yday; /* days since January 1 - [0,365] */
int tm_isdst; /* daylight savings time flag */
};
Funcia
struct tm * localtime(time_t * timeptr);
convertete timpul coninut ntr-o variabil time_t n structura tm.
Exemplu. Fie secvena de instruciuni
199
time_t t1;
struct tm t2;
struct tm * ptrt;
t1 = time(NULL);
ptrt = localtime(&t1);
t2 = * ptrt;
cout << t2.tm_hour << ":" << t2.tm_min << ":" << t2.tm_sec << endl;
Timpul afiat pe ecran este
Vom rescrie secvena de program astfel
time_t t1;
struct tm t2;
t1 = time(NULL);
t2 = localtime(&t1);
printf(%d:%d:%d\n, t2.tm_hour, t2.tm_min, t2.tm_sec);
Funcia
time_t mktime(struct tm * t);
convertete timpul dintr-o structur tip tm ntr-o variabil time_t.
Funcia
struct tm * gmtime(const time_t * timeptr);
are ca rezultat un pointer la o structur tm ce conine timpul GMT.
Exist dou macroinstruciuni ce pot fi utilizate :
__DATE__ , ir de caractere, ce d data compilrii fiierului surs curent,
__TIME__ , ir de caractere, ce d timpul compilrii fiierului surs curent.
Instruciunile :
printf("DATE : %s \n", __DATE__ );
printf("TIME : %s \n", __TIME__ );
afiaz cele dou iruri de caractere ca mai jos.

13.2 Fire de execuie
Programele de pn acum au fost procese cu un singur fir de execuie (thread). Este
posibil s cream programe cu mai multe fire de execuie simultan. Sistemul de operare
aloc cuante de timp ale unitii centrale pe rnd fiecrui fir. Fiecare fir execut o
anumit funcie cu prototipul
void f(void *);
Parametrul funciei este un pointer de un tip nespecificat.
Prototipurile funciilor pentru lucrul cu fire de execuie se afl n biblioteca
<process.h>. Limbajul C definete urmtoarele funcii pentru lucrul cu fire de
execuie :
funcia
unsigned long _beginthread(void (*f) (void *), unsigned int stack, void *
arglist);
200
lanseaz n execuie un fir. Primul parametru este un pointer la funcia ce va fi
executat de fir. Al doilea parametru este dimensiunea unei stive ce poate fi utilizat
de fir. Ultimul parametru este o list de argumente transmis firului.
Parametrul
void (*f) (void *)
este interpretat astfel
void (*f) (void *) f este
void (*) (void *) pointer la
void (void *) funcie cu un parametru de tip void*
void de tip void
funcia
_sleep(int ms);
suspend execuia firului de pentru un numr de milisecunde dat de parametrul ms.
Un fir se termin atunci cnd funcia executat de fir se termin.
Exemplu. Vom face un program care s lanseze n execuie dou fire. Funciile
executate de fire vor consta dintr-un ciclu n care se scrie un mesaj i apoi se pune
firul n ateptare un numr de milisecunde. In exemplul nostru dimensiunea stivei
firelor de execuie va fi zero iar lista de parametri pasai firelor va fi NULL.
# include <stdio.h>
# include <stdlib.h>
# include <process.h>
// functie executata de primul fir
void fthr1(void * arg)
{
int i;
for(i = 0; i < 5; i++)
{
printf("thread1\n");
_sleep(1000);
}
return;
}
// functie executata de al doilea fir
void fthr2(void * arg)
{
int i;
for(i = 0; i < 5; i++)
{
printf("thread2\n");
_sleep(500);
}
return;
}
201
int main()
{
_beginthread(fthr1, 0, NULL);
_beginthread(&fthr2, 0, NULL);
return 0;
}
Execuia primului fir se termin cnd se ajunge la instruciunea return a funciei fthr1.
Reamintim c primul parametru al funciei _beginthread() este adresa funciei ce va fi
executat de fir. Aceasta poate fi specificat ca
&fthr1
sau
fthr1
deoarece numele funciei este chiar adresa punctului de intrare n funcie.
Rezultatul rulrii programului este prezentat mai jos. Dup cum se vede fiecare fir
este executat de cinci ori.
13.3 Funcia system
Funcia system() execut o comand a sistemului de operare. Prototipul funciei este
int system(const char * command) ;
Parametrul funciei este un ir de caractere ce conine comanda.
Prototipul funciei system() este definit n biblioteca <stdlib.h>, pentru programele C
i <cstdlib> pentru programele C++.
O comand util este pause, ce oprete execuia programului pn cnd se apas tasta
Return. O alt comand util este dir, ce afiaz coninutul unui director.
Vom utiliza comanda dir pentru a afia coninutul directorului curent al aplicaiei.
Programul este urmtorul.
#include <cstdlib>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
system("dir");
system("PAUSE");
return EXIT_SUCCESS;
}
202
Rezultatul rulrii programului este cel de mai jos. Se pot observa n directorul curent :
fiierul surs, main.cpp, fiierul obiect rezultat al compilrii, main.o i programul
executabil, main.exe.
203
14 Biblioteca de abloane standard
In activitatea de programare apar frecvent anumite structuri de date: liste simplu sau
dublu nlnuite, cozi, stive, mulimi, vectori, etc. Limbajul C++ are o bibliotec cu
clase special proiectate (Biblioteca de abloane standard, Standart Template Library,
STL) pentru crearea i manipularea acestor structuri. Clasele bibliotecii de abloane
standard sunt clase generice care au ca parametri tipul obiectelor manipulate.
Biblioteca de abloane standard are trei componente :
containere. Un container este o structur de date ce conine alte obiecte,
numite elementele containerului. Exemple de containere sunt vectorii, listele,
irurile tip string, etc.,
iteratori. Iteratorii permit parcurgerea elementelor unui container,
algoritme. Algoritmele sunt funcii ce se aplic asupra elementelor unui
container, de exemplu sortarea elementelor unei liste sau ale unui vector.
Un exemplu de container sunt listele dublu nlnuite reprezentate grafic ca mai jos.
Listele dublu nlnuite pot fi parcurse n ambele sensuri, folosind iteratori. Putem
aduga / terge elemente n / din list. Elementele listelor pot fi sortate n ordine
cresctoare sau descresctoare, cu funciile existente n biblioteca de algoritme.
14.1 Clase generice
Clasele din biblioteca de abloane standard sunt clase generice, n care tipurile datelor
i funciilor sunt parametri. O clas generic se definete cu instruciunea template cu
forma
template <class T1, class T2, , class Tn>
class nume_clasa
{
// definitia clasei
};
In aceast definiie T1, T2, , Tn sunt tipuri parametri, ce se pot utiliza la declararea
de variabile i funcii membre ale clasei generice. Un obiect de tipul unei clase
generice se declar cu constructorul clasei cu urmtoarele forme:
nume_clasa <tip1, tip2, , tipn> nume_obiect;
nume_clasa <tip1, tip2, , tipn> nume_obiect (parametrii);
Aici tip1, tip2, , tipn sunt tipurile actuale cu care este apelat constructorul clasei
generice. Ele pot fi tipuri simple, int, double, etc., sau clase definite de programator.
Exemplu. S definim o clas generic X ce poate calcula ptratul unui numr ntreg
sau real. O reprezentare a clasei este cea de mai jos. In aceast reprezentare tipul T
este parametrul clasei X. Clasa definete un cmp de tipul T ce va conine numrul, un
constructor tip copiere, o funcie de acces, geta(), i o funcie ce calculeaz ptratul
numrului, square().
204
Definiia clasei este urmtoarea
# include <iostream>
using namespace std;
template <class T>
class X
{
private:
T a;
public:
X(T b) {a = b;}
T square() {return a * a;}
T geta() {return a;}
};
S definim obiecte de tipul X, ce conin elemente de tip int sau double, utiliznd clasa
generic X.
int main()
{
// creaz un obiect cu ablonul <int>
X<int> n(2);
cout << "patratul valorii " << n.geta() << " este " << n.square() <<
endl;
// creaz un obiect cu ablonul <double>
X<double> d(3.14);
cout << "patratul valorii " << d.geta() << " este " << d.square() <<
endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Ca exerciiu, vom repeta definiia clasei generice X, cu funciile membre definite n
afara clasei. In acest caz, definiia
template <class T>
205
trebuie repetat naintea fiecrei definiii de funcie.
#include <iostream>
using namespace std;
template <class T>
class X
{
private:
T a;
public:
X(T b);
T square();
T geta();
};
template <class T>
X<T>::X(T b)
{
a = b;
}
template <class T>
T X<T>::square()
{
return a * a;
}
template <class T>
T X<T>::geta()
{
return a;
}
Compilatorul utilizeaz definiia clasei generice pentru a genera clasele necesare. In
exemplul de mai sus, compilatorul genereaz dou clase, una X_int i una X_double.
14.2 Containere, iteratori i algoritme generice
Un container este un obiect ce poate conine alte obiecte, numite n continuare
elemente ale containerului. Cel mai cunoscut tip de container este vectorul. Obiectele
din container au acelai tip. Tipul obiectelor poate fi unul simplu, int, double, etc., sau
unul structurat, iruri tip string, sau obiecte ale unei clase definite de utilizator.
Containerele permit inserarea i tergerea de obiecte. Ele au funcii membre ce
efectueaz aceste operaii. De regul, funciile ce insereaz obiecte n container, aloc
i memoria necesar.
Menionm c, clasa obiectelor memorate ntr-un container, trebuie s aib un
constructor implicit (fr parametri), un constructor tip copiere i operatorul =.
O operaie obinuit efectuat asupra unui container este parcurgerea obiectelor din
container (traversarea containerului). Parcurgerea elementelor containerului se face cu
un iterator. Iteratorul este similar unui pointer, el indic un anumit obiect din
206
container. Operaiile efectate asupra iteratorilor, indiferent de tipul containerului, sunt
urmtoarele :
iniializarea iteratorului la un anumit obiect din container,
furnizarea obiectului de la poziia curent a iteratorului,
modificarea obiectului de la poziia curent a iteratorului,
avansarea iteratorului la poziia urmtoare din container.
Clasele ce definesc iteratori au urmtorii operatori, ce realizeaz operaiile de mai sus
asupra iteratorilor :
operatorii ++ i -- modific iteratorul la urmtoarea / precedenta poziie din
container,
operatorii relaionali, = =, !=, <, <=, >, >=, permit compararea a doi iteratori,
operatorul de adresare indirect, *, permite adresarea sau modificarea
obiectului de la poziia curent a containerului indicat de iterator.
In cele ce urmeaz vom prezenta vectorii, listele i numerele complexe.
Biblioteca de abloane standard definete funcii ce se pot aplica asupra
containerelor. Cele mai importante funcii sunt sort(), ce sorteaz elementele
containerului, i find(), care caut un element n container. Aceste funcii vor fi
prezentate pentru fiecare tip de container.
14.3 Vectori
Clasa generic vector implementeaz vectori cu elemente de un anumit tip. Un obiect
de tip vector are un numr iniial de componente i dimensiunea lui crete dac este
nevoie. Clasa vector are ca parametru tipul componentelor vectorului. Ea este definit
n biblioteca <vector>.
Clasa definete urmtorii constructori :
constructorul implicit (fr parametri)
vector();
creaz un vector vid,
constructorul copiere
vector(const vector& p);
creaz un vector n care copiaz elementele vectorului p care este parametru,
constructor ce creaz un vector cu n elemente
vector(int n);
Clasa definete destructorul
~vector() ;
Fie T tipul componentelor vectorului (parametrul clasei generice). Clasa definete
urmtoarele funcii :
void push_back(const T&); adaug un element la sfritul vectorului,
void pop_back(); terge ultimul element al vectorului,
int size(); d numrul de componente ale vectorului,
bool empty(); are valoarea true dac vectorul este vid,
operatorul [] i funcia T& at(int) selecteaz un element al vectorului,
T& front(); are ca rezultat primul component al vectorului,
T& back();are ca rezultat ultimul component al vectorului,
void clear(); terge toate componentele vectorului,
operatorul = este operatorul de atribuire,
operatori de comparare a doi vectori , <, <=, = =, >, >=, != , element cu
element.
207
Menionm c un vector poate avea dou sau mai multe elemente egale.
Elementele unui vector pot fi accesate n trei moduri : cu operatorul [] i funcia at(),
artate mai sus, i cu iteratori. Aceste moduri vor fi exemplificate n continuare.
Exemplu. S crem i s listm un vector cu componente ntregi.
# include <iostream>
# include <vector>
using namespace std;
#include <string>
int main()
{
// definim un vector cu componente siruri
vector <string> v;
// adauga 4 elemente
v.push_back("abcd");
v.push_back("x+y");
v.push_back("1x2ycz");
v.push_back("29dx");
// afisaza componentele vectorului
cout << elementele vectorului << endl ;
for(int k = 0; k < v.size(); k++)
cout << v[k] << ;
cout << endl;
return EXIT_SUCCESS;
}
Rezultatul rulrii programului este cel de mai jos.
Menionm c, expresia v[k], putea fi nlocuit cu expresia v.at(k).
La definirea obiectului v, se creaz un vector vid. Dup execuia instruciunilor
push_back(), vectorul este urmtorul
i are dimensiunea 4. Menionm c, vectorul are un element fictiv naintea
primului element i alt element fictiv dup ultimul element.
In exemplul urmtor comparm doi vectori, vec1 i vec2.
#include <cstdlib>
#include <iostream>
#include <vector>
using namespace std;
208
int main(int argc, char *argv[])
{
vector<int> vec1;
vec1.push_back(-2);
vec1.push_back(7);
vector<int> vec2;
vec2.push_back(-3);
vec2.push_back(5);
vec2.push_back(8);
if(vec1 < vec2)
cout << "vec1 < vec2" << endl;
else if(vec1 == vec2)
cout << "vec1 = vec2" << endl;
else
cout << "vec1 > vec2" << endl;
return EXIT_SUCCESS;
}
Primul element al vectorului vec1 este -2, primul element al vectorului vec2 este -3.
In consecin, vec1 > vec2. rezultatul programului este cel de mai jos.
14.3.1 Parcurgerea vectorilor cu iteratori
Pentru parcurgerea componentelor unui container (vector, list, etc.) se pot utiliza
iteratori. Un iterator este asemenea unui pointer la un element al containerului.
Clasa iterator definete urmtorii operatori :
operatorii ++ i -- modific iteratorul la urmtoarea / precedenta poziie din
container,
operatorii relaionali, = =, !=, <, <=, >, >=, permit compararea a doi iteratori,
operatorul de adresare indirect, *, permite adresarea sau modificarea valorii
de la poziia curent a containerului indicat de iterator.
Un iterator poate fi iniializat la orice poziie din container. Vom meniona c exist
dou tipuri de iteratori ce permit parcurgerea vectorilor, n sens direct i respectiv n
sens invers. Dac parcurgem un container fr s modificm elementele componente,
putem defini iteratori constani.
Parcugerea unui vector n sens direct
Clasa iterator este o clas intern a containerului (n cazul nostru a clasei vector). Un
obiect de tipul acestei clase se definete astfel
vector<tip>::iterator nume_obiect;
Clasa vector definete funciile :
begin();
end();
209
ce au ca rezultat un iterator, ce indic primul element al vectorului i respectiv
elementul fictiv ce urmeaz dup ultimul element al vectorului. Pentru vectorul
anterior avem situaia din figura de mai jos
Parcurgerea unui vector n sens invers
Clasa reverse_iterator este o clas intern a containerului (n cazul nostru a clasei
vector). Un obiect de tipul acestei clase se definete astfel
vector<tip>::reverse_iterator nume_obiect;
Clasa vector definete funciile :
rbegin();
rend();
ce au ca rezultat un iterator, ce indic ultimul element al vectorului i respectiv
elementul fictiv ce precede primul element al vectorului. Pentru vectorul anterior
avem situaia de mai jos
Exemplu. S parcurgem vectorul anterior utiliznd iteratori.
# include <iostream>
# include <vector>
using namespace std;
int main()
{
// definim un vector cu componente intregi
vector <int> v;
// se adauga 4 elemente
v.push_back(12);
v.push_back(-5);
v.push_back(7);
v.push_back(13);
// afisaza componentele vectorului in ordine directa utilizand iteratori
cout << "componentele in ordine directa" << endl;
vector <int>::iterator it;
for(it = v.begin(); it != v.end(); it++)
cout << *it << ;
210
cout << endl;
// afisaza conponentele vectorului in ordine inversa utilizand iteratori
cout << "componentele in ordine inversa" << endl;
vector<int>::reverse_iterator iter1;
for(iter1 = v.rbegin(); iter1 != v.rend(); iter1++)
cout << *iter1 << ;
cout << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Putem reprezenta parcurgerea vectorului n sens direct ca n figura de mai jos.
14.3.2 Stergerea i inserarea de elemente
In afar de funcia pop_back(), clasa vector definete i funciile :
erase(iterator pos);
erase(iterator first, iterator last);
Prima funcie terge elementul din poziia specificat de iteratorul pos, a doua funcie
terge elementele ntre poziiile first i last.
In afar de funcia push_back(), clasa vector definete funcia
insert(iterator pos, const T& val);
ce insereaz elementul val naintea poziiei specificate de iteratorul pos.
Dup tergerea sau inserarea unui element n vector, toi iteratorii trebuie
reiniializai.
Exemplu. Fie vectorul din exemplul precedent. Programul de mai jos terge primul
element din vector i apoi insereaz valoarea 25 ca prim element al vectorului.
Programul este urmtorul. Crem un vector v
vector <int> v;
care este apoi iniializat ca n exemplul anterior. Fie it un iterator
vector <int>::iterator it;
Stergerea primului element al vectorului se face astfel
it = v.begin();
v.erase(it);
Inserarea valorii 25 ca prim element se face astfel
it = v.begin();
v.insert(it, 25);
Afiarea elementelor vectorului se face cu o instruciune for
211
for(i = 0; i < v.size(); i++)
cout << v.at(i) << " ";
cout << endl;
Programul este urmtorul
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char *argv[])
{
// definim un vector cu componente intregi
vector <int> v;
// se adauga 4 elemente
int i;
for(i = 1; i < 5; i++)
v.push_back(i * i);
// afisaza componentele vectorului
cout << "vectorul initial" << endl;
for(i = 0; i < v.size(); i++)
cout << v.at(i) << " ";
cout << endl;
vector <int>::iterator it;
// sterge primul element din vector
cout << sterge primul element din vector << endl;
it = v.begin();
v.erase(it);
cout << "vectorul modificat"
<< endl;
for(i = 0; i < v.size(); i++)
cout << v.at(i) << " ";
cout << endl;
// insereaza un element in prima pozitie
cout << insereaza un element in prima pozitie << endl;
it = v.begin();
v.insert(it, 25);
cout << vectorul modificat" << endl;
for(i = 0; i < v.size(); i++)
cout << v.at(i) << " ";
cout << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
212
14.3.3 Sortarea componentelor unui vector
Biblioteca de abloane standard definete funcii de sortare a componentelor unui
vector. Aceste funcii sunt definite n biblioteca <algorithm>. Prima dintre acestea,
funcia sort() sorteaz elementele vectorului n ordine cresctoare i are urmtorul
prototip
sort(iterator iter1, iterator iter2);
unde iteratorii iter1 i iter2 dau primul element din vector i ultimul element din
vector ce vor fi sortai. Prototipul funciei sort() este definit n biblioteca <algorithm>.
Exemplu. Vom crea un vector cu componente tip string i l vom sorta.
# include <iostream>
# include <vector>
# include <algorithm>
# include <string>
using namespace std;
int main()
{
int i;
vector<string> vst;
vst.push_back("abc");
vst.push_back("rst");
vst.push_back("ccd");
vst.push_back("mnp");
// afisaza vectorul initial
cout << "vectorul initial" << endl;
for(i = 0; i < vst.size(); i++)
cout << vst.at(i) << endl;
// sorteaza vectorul
sort(vst.begin(), vst.end());
// afisaza vectorul sortat
cout << "vectorul sortat" << endl;
for(i = 0; i < vst.size(); i++)
cout << vst.at(i) << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
213
A doua funcie sort() are prototipul
sort(iter1, iter2, comp);
unde iter1 i iter2 sunt iteratori, ce dau primul element din vector i ultimul element
din vector ce vor fi sortai, iar comp este o funcie cu prototipul
bool comp(T x, T y);
Parametri x i y sunt dou elemente de tipul T al componentelor vectorului. Funcia
comp() are rezultatul true dac x < y i false n caz contrar. Funcia sort permut
elementele x i y dac funcia comp are rezultatul false. Definind diferite funcii comp
putem sorta vectorul n ordine cresctoare sau descresctoare, vezi exemplul de mai
jos. Prototipul funciei sort() este definit n biblioteca <algorithm>.
Exemplu. Vom crea un vector cu componente ntregi i l vom sorta n ordine
cresctoare i apoi descresctoare. Vom defini dou funcii de comparare pentru cele
dou cazuri.
# include <iostream>
# include <vector>
# include <algorithm>
using namespace std;
/*
funcie de comparare pentru sortarea elementelor
vectorului in ordine crescatoare
*/
bool comp(int k, int n)
{
return k < n;
}
/*
funcie de comparare pentru sortarea elementelor
vectorului in ordine descrescatoare
*/
bool comp2(int k, int n)
{
return k > n;
}
214
int main()
{
vector<int> v;
// creaza vectorul
v.push_back(12);
v.push_back(-3);
v.push_back(4);
v.push_back(12);
// afisaza vectorul initial
cout << Vectorul initial parcurs in sens direct << endl;
int i;
for(i = 0; i < v.size(); i++)
// cout << v[i] << endl;
cout << v.at(i) << ;
cout << endl;
// sorteaza vectorul in ordine crescatoare
cout << "Se sorteaza vectorul in ordine crescatoare" << endl;
sort(v.begin(), v.end(), comp);
// afisaza vectorul sortat
cout << "Vectorul sortat parcurs in sens direct \n";
for(i = 0; i < v.size(); i++)
cout << v[i] << ;
cout << endl;
// sorteaza vectorul in ordine descrescatoare
cout << "Se sorteaza vectorul in ordine descrescatoare" <<
endl;
sort(v.begin(), v.end(), comp2);
// afisaza vectorul sortat
cout << "Vectorul sortat parcurs in sens direct \n";
for(i = 0; i < v.size(); i++)
cout << v[i] << ;
cout << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
14.3.4 Cutarea unui element ntr-un vector
Biblioteca de abloane standard definete funcia global find() cu prototipul
find(iterator first, iterator last, const T& val);
care caut elementul val printre elementele containerului, n intervalul dat de iteratorii
first i last. Dac elementul a fost gsit, funcia are ca rezultat iteratorul ce indic
215
prima apariie a elementului, n caz contrar funcia are ca rezultat sfritul
intervalului.
Vom exemplifica utilizarea funciei find() cu programul de mai jos. Definim vectorul
vector <int> v;
care este apoi iniializat. Definim un iterator ce va conine rezultatul cutrii
vector <int>::iterator it;
Funcia find() va cauta valoarea 4 printre elementele vectorului
it = find(v.begin(), v.end(), 4);
Pentru a vedea dac elementul a fost gsit, iteratorul it se compar cu sfritul
intervalului
if(it != v.end())
{
// componentul a fost gsit
}
Indicele elementului gsit se calculeaz ca
int i = it v.begin();
Programul este urmtorul
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char *argv[])
{
// definim un vector cu componente intregi
vector <int> v;
// se adauga 4 elemente
int i;
for(i = 1; i < 5; i++)
v.push_back(i * i);
// afisaza componentele vectorului
cout << "componentele vectorului" << endl;
for(i = 0; i < v.size(); i++)
cout << v.at(i) << " ";
cout << endl;
// cauta un element
vector <int>::iterator it;
it = find(v.begin(), v.end(), 4);
if(it != v.end())
{
cout << "elementul " << *it << " a fost gasit" << endl;
i = it - v.begin();
cout << "indicele elemnetului este " << i << endl;
}
return 0;
216
}
Rezultatul rulrii programului este cel de mai jos.
14.3.5 Copierea unui container. Iteratori pentru streamuri
Iteratorii pot itera pe streamuri, pentru a citi sau scrie date. Acest lucru este util pentru
a citi / scrie elementele unui container dintr-un / pe un fiier. In biblioteca de abloane
standard exist adaptori pentru interfaa cu streamuri, definii n biblioteca <iterator>.
Adaptorul pentru interfaa cu streamuri de intrare este
istream_iterator<T>
unde T este streamul de intrare.
Adaptorul pentru interfaa cu streamuri de ieire este
ostream_iterator<T, C>
unde T este streamul de ieire, iar C este un ir de caractere ce este scris pe stream
dup scrierea unui element.
Vom exemplifica utilizarea acestor iteratori la copierea de numere ntregi din streamul
cin ntr-un vector de tip int, i scrierea componentelor vectorului n streamul cout.
Biblioteca de abloane standard definete funcia copy ce copiaz un container n
altul. Prototipul funcie este
copy(iterator first, iterator last, iterator prim);
Funcia are ca parametrii trei iteratori, first i last dau primul i ultimul element din
containerul surs, iar prim d primul element din containerul destinaie.
Fie vectorul cu zece componente de tip int
vector<int> vec (10);
Vom copia elemente din streamul de intrare cin n vectorul vec astfel
copy(istream_iterator<int>(cin), istream_iterator<int>(), vec.begin());
Poziia iteratorului ce corespunde sfritului de fiier al streamului de intrare este
istream_iterator<int>()
Vom copia elementele vectorului vec pe streamul de ieire cout astfel
copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, " "));
Programul este urmtorul
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
int main(int argc, char *argv[])
{
vector<int> vec(10);

copy(istream_iterator<int>(cin), istream_iterator<int>(), vec.begin());
copy(vec.begin(), vec.end(), ostream_iterator<int>(cout, " "));
217
cout << endl;

return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Reamintim c, sfritul streamului de intrare cin, este indicat prin Ctrl+Z, dup care se
apas tasta Enter, vezi figura cu rezultatul programului.
Exemplu. Vom utiliza funcia copy la copierea unui vector cu elemente de tip string n
altul. Vom iniializa vectorul str cu elemente tip string i vom copia elementele pe
ecran cu un iterator pentru streamul cout. Vom copia vectorul str n vectorul strcp cu
funcia copy() i vom copia elementele vectorului strcp pe ecran, tot cu un iterator
pentru streamul cout. Programul este urmtorul.
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
using namespace std;
int main(int argc, char *argv[])
{
vector<string> str;
str.push_back("a+b");
str.push_back("3*2");
str.push_back("m*n+p");
cout << "vectorul initial" << endl;
copy(str.begin(), str.end(), ostream_iterator<string>(cout, " "));
cout << endl;
// se defineste un vector strcp de aceeasi lungime cu str
vector<string> strcp(str.size());
// se copiaza str in strcp
copy(str.begin(), str.end(), strcp.begin());
// se afisaza strcp
cout << "vectorul copiat" << endl;
copy(strcp.begin(), strcp.end(), ostream_iterator<string>(cout, " "));
cout << endl;
return 0;
}
218
Rezultatul rularii programului este cel de mai jos.
14.4 Liste
Clasa generic list implementeaz o list dublu nlnuit, adic o list ce poate fi
parcurs n ambele sensuri. Lista are elemente de acelai tip. Prototipul clasei este
definit n biblioteca <list>.
Clasa definete urmtorii constructori :
constructorul implicit
list();
creaz o list vid,
constructorul copiere
list(const list& p);
creaz o list n care copiaz elementele listei p.
Clasa definete destructorul
~list() ;
Fie T tipul elementelor listei. Funciile definite de clasa list sunt urmtoarele :
void push_back(T&); adaug un element la sfritul listei,
void push_front(T&); adaug un element la nceputul listei,
void pop_front(); terge primul element din list,
void pop_back();terge ultimul element din list,
int size(); d numrul de elemente din list,
bool empty() ; are valoarea true dac lista este vid,
T& front(); are ca rezultat primul element din list,
T& back();are ca rezultat ultimul element din list,
void clear(); trerge toate elementele din list,
operatorul = este operatorul de atribuire,
operatori de comparare = =, !=, <, <=, >, >=, compar dou liste, element cu
element.
14.4.1 Parcurgerea listelor
Pentru parcurgerea componetelor unei liste se utilizeaz iteratori. Un iterator este
asemenea unui pointer la un element al listei. O list poate fi parcurs n sens direct
sau n sens invers.
Parcurgerea listelor n ordine direct

Pentru parcurgerea unei liste n ordine direct se utilizeaz clasa iterator care este o
clas intern a clasei list. Un obiect de tipul acestei clase se definete astfel
list<tip>::iterator nume_obiect;
Operaiile implementate de clasa iterator sunt cele prezentate n paragraful despre
vectori. Pentru parcurgerea direct a listelor clasa list definete funciile :
begin();
end();
219
ce au ca rezultat un iterator ce indic primul element al listei i respectiv ultimul
element al listei.
Parcurgerea listelor n ordine invers
Pentru parcurgerea invers a listelor se utilizeaz clasa reverse_iterator care este tot o
clas intern a clasei list. Un obiect de tipul acestei clase se definete ca
list<tip>::reverse_iterator nume_obiect;
Operaiile implementate de clasa iterator sunt cele prezentate n paragraful despre
vectori.
Clasa list definete funciile :
rbegin();
rend();
ce au ca rezultat un iterator pentru parcurgerea n sens invers a listei, ce indic ultimul
element al listei i respectiv primul element al listei.
14.4.2 Sortarea listelor
Pentru sortarea n ordine direct a listelor, clasa list definete funcia
void sort();
ce sorteaz elementele listei n ordine cresctoare.
Exemplu. Vom crea o list cu elemente ntregi, o vom sorta ascendent i vom afia
elementele listei sortate parcurgnd-o n sens direct i invers.
# include <iostream>
# include <list>
using namespace std;
int main()
{
list <int> ls;
// adauga elemente la sfarsitul si inceputul listei
ls.push_back(11);
ls.push_back(7);
ls.push_front(4);
ls.push_front(12);
// parcurgerea listei initiale in sens direct
cout << "-- lista initiala --\n";
list<int>::iterator iter;
for(iter = ls.begin(); iter != ls.end(); iter++)
cout << *iter << " ";
cout << endl;
// sortarea elementelor listei in ordine crescatoare
ls.sort();
cout << "-- sorteaza lista in ordine creascatoare --\n";
// parcurgerea listei sortate in sens direct
cout << "-- lista sortata parcursa in sens direct --\n";
for(iter = ls.begin(); iter != ls.end(); iter++)
cout << *iter << " ";
cout << endl;
// parcurgerea listei sortate in sens invers
220
cout << "-- lista sortata parcursa in sens invers --\n";
list<int>::reverse_iterator iter1;
for(iter1 = ls.rbegin(); iter1 != ls.rend(); iter1++)
cout << *iter1 << " ";
cout << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
14.4.3 Inversarea ordinii elementelor listelor
Pentru inversarea ordinii elementelor unei liste clasa list definete funcia
void reverse();
Vom exemplifica utilizarea acestei funcii, inversnd elementele unei liste i
parcurgnd-o n sens direct i apoi invers.
# include <iostream>
# include <list>
using namespace std;
int main()
{
list <int> ls;
ls.push_back(11);
ls.push_back(7);
ls.push_front(4);
ls.push_front(12);
// parcurgerea listei in sens direct
cout << "-- lista initiala --\n";
list<int>::iterator iter;
for(iter = ls.begin(); iter != ls.end(); iter++)
cout << *iter << endl;
// inversarea ordinii elementelor listei
cout << "-- inversarea ordinii elementelor listei --\n";
ls.reverse();
// parcurgerea listei in ordine inversa
cout << "lista parcursa in sens direct\n";
for(iter = ls.begin(); iter != ls.end(); iter++)
cout << *iter << endl;
// parcurgerea listei in ordine inversa
cout << "lista parcursa in sens invers\n";
221
list<int>::reverse_iterator iter1;
for(iter1 = ls.rbegin(); iter1 != ls.rend(); iter1++)
cout << *iter1 << endl;
return 0;
}
Rezultatul programului este cel de mai jos.
14.4.4 Inserarea i tergerea elementelor din liste
Clasa list definete i funciile :
erase(iterator pos);
erase(iterator first, iterator last);
Prima funcie terge elementul din poziia specificat de iteratorul pos, a doua funcie
terge elementele ntre poziiile first i last.
Clasa list definete funcia
insert(iterator pos, const T& val);
ce insereaz elementul val naintea poziiei specificate de iteratorul pos.
Dup tergerea sau inserarea unui element n vector, toi iteratorii trebuie
iniializai.
Exemplu. Vom crea o list cu iruri de caractere, vom terge primul element din list,
i vom insera un element n prima poziie. Definim lista cu iruri de caractere astfel
list<string> lst;
Iniializm apoi lista ca n exemplele anterioare. Definim un ierator ce va fi utilizat n
funciile erase() i insert()
list<string>::iterator it;
Stergerea primului element din list se face astfel
it = lst.begin();
lst.erase(it);
Inserarea unui element n prima poziie din list se face astfel
it = lst.begin();
lst.insert(it, "xns");
Reamintim c, dup fiecare tergere sau inserare de elemente, iteratorii trebuie
iniializai.
Vom afia lista iniial i dup fiecare modificare. Pentru aceasta, definim o funcie
afisarelista(), ce are ca parametri o list cu iruri de caractere i doi iteratori, ce dau
prima poziie i ultima poziie din list. Prototipul funciei este
void afisarelista(list<string>, list<string>::iterator, list<string>::iterator);
222
Programul este urmtorul

#include <iostream>
#include <list>
#include <string>
using namespace std;
/*
functie ce afisaza lista
*/
void afisarelista(list<string> lst, list<string>::iterator beg,
list<string>::iterator den)
{
list<string>::iterator it;
for(it = beg; it != den; it++)
cout << *it << " ";
cout << endl;
}
int main(int argc, char *argv[])
{
list<string> lst;
// initializeaza lista
lst.push_back("abc");
lst.push_back("xyzp");
lst.push_front("a*b");
lst.push_front("zma");
lst.push_back("mzp");
// afisaza lista initiala
cout << "-- lista initiala --" << endl;
afisarelista(lst, lst.begin(), lst.end());

list<string>::iterator it;
// sterge primul element din lista
cout << "sterge primul element din lista" << endl;
it = lst.begin();
lst.erase(it);
cout << "-- lista modificata --" << endl;
afisarelista(lst, lst.begin(), lst.end());

// insereaza un element la inceputul listei
it = lst.begin();
lst.insert(it, "xns");
cout << "insereaza un element la inceputul listei" << endl;
cout << "-- lista modificata --" << endl;
afisarelista(lst, lst.begin(), lst.end());

223
return 0;
}
Rezultatul programului este cel de mai jos.
14.4.5 Copierea listelor
Copierea elementelor unei liste n alt list, sau n alt container, se poate face cu
funcia copy() cu prototipul
copy(iterator first, iterator last, iterator prim);
Funcia are ca parametrii trei iteratori, first i last dau primul i ultimul element din
containerul surs, iar prim d primul element din containerul destinaie.
Vom exemplifica utilizarea funciei copy n exemplul urmtor n care crem o list cu
numele list1, o copiem n lista list2 i apoi copim lista list2 pe ecran. Cele dou
operaii de copiere se fac cu funcia copy(). Pentru a copia list ape ecran utilizm
adaptorul ostream_iterator ca mai sus. Programul este urmtorul.
#include <cstdlib>
#include <iostream>
#include <list>
#include <string>
#include <iterator>
using namespace std;
int main(int argc, char *argv[])
{
list<string> list1, list2(10);
list1.push_back("abv");
list1.push_back("a12c");
list1.push_back("n*m+p");
list1.push_back("x2y3");
copy(list1.begin(), list1.end(), list2.begin());
copy(list2.begin(), list2.end(), ostream_iterator<string>(cout, " "));
cout << endl;
return 0;
}
Rezultatul rulrii programului este artat mai jos.
224
Vom prezenta un exemplu n care copiem elementele unui vector ntr-o list i apoi
elementele listei pe ecran cu funcia copy(). Programul este cel de mai jos.
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
vector<string> vec;
vec.push_back("vector");
vec.push_back("copiat");
vec.push_back("in");
vec.push_back("lista");
list<string> lis(4);
copy(vec.begin(), vec.end(), lis.begin());
copy(lis.begin(), lis.end(), ostream_iterator<string>(cout, " "));
cout << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
14.5 Parcurgerea irurilor tip string cu iteratori
Sirurile tip string pot fi parcurse cu iteratori la fel ca orice container. Clasa string
definete o clas intern iterator i funciile :
begin();
end() ;
ce au ca rezultat un iterator pentru parcurgerea irului n sens direct.
Clasa string definete clasa intern reverse_iterator i funciile :
rbegin();
rend() ;
ce au ca rezultat un iterator pentru parcurgerea irului n sens invers.
Exemplu. Vom defini un ir tip string i l vom parcurge n ambele sensuri folosind
iteratori.
# include <string>
# include <iostream>
using namespace std;
int main()
{
string str1(abcdegh);
225
// parcurge sirul in sens direct
string::iterator ii;
cout << "-- sirul parcurs in sens direct --" << endl;
for(ii = str1.begin(); ii != str1.end(); ii++)
cout << *ii << " ";
cout << endl;
// parcurge sirul in sens invers
string::reverse_iterator rii;
cout << "-- sirul parcurs in sens invers --" << endl;
for(rii = str1.rbegin(); rii != str1.rend(); rii++)
cout << *rii << " ";
cout << endl;
return 0;
}
Menionm c deoarece nu modificm elementele irului am putea utiliza iteratori
constani. Am putea deci defini iteratorul
string::const_iterator ii;
14.6 Numere complexe
Biblioteca de abloane standard definete clasa complex pentru lucrul cu numere
complexe. Prototipul acestei clase este definit n biblioteca <complex>. Clasa are ca
parametru T, tipul double sau float al perechii de numere reale ce definesc numrul
complex. Clasa definete urmtorii constructori:
Constructorul implicit fr parametri
complex();
ce creaz numrul complex 0 + i 0,
Constructorul cu parametri
Complex(T u, T v);
ce creaz un numr complex u + iv,
Constructorul copiere
complex(const complex<T>&);
Biblioteca definete urmtorii operatori:
cei patru operatori aritmetici +, -, * , / ,
operatorii relaionali = = i !=,
operatorul de scriere <<. Numrul complex u + i v este scris de operatorul <<
ca
(u, v)
operatorul de citire >>. Pentru citirea cu operatorul >> numrul complex u + i
v este introdus ca
(u, v)
Clasa definete urmtorii operatori de atribuire :
=, +=, -=, *= , /=
Biblioteca definete urmtoarele funcii matematice standard care au ca argumente
numere complexe :
asin sin exp pow
acos cos log sqrt
atan tan log10
Biblioteca definete urmtoarele funcii:
226
T real();
T real(const complex<T>&);
dau partea real a numrului complex, prima este funcie membr a clasei,
T imag();
T imag(const complex<T>&);
dau partea imaginar a numrului complex, prima este funcie membr a
clasei,
complex <T> conj(complex<T>&);
are ca rezultat conjugatul numrului complex,
T abs(complex<T>&);
d valoarea absolut (norma) a numrului complex,
T norm(complex<T>&);
d ptratul valoarii absolute (normei) a numrului complex.
Exemplu. Fie un program ce definete numerele complexe a = 1 + i, b = 2.0 3.5i,
calculeaz suma lor c = a + b, apoi c = c + a i valoarea absolut a lui c. Se citete
apoi numrul complex 2.7 3.5i de la tastatur care este scris pe ecran. Numrul este
introdus sub forma
(2.7, -3.5)
In final se scrie partea real a numrului complex cu cele dou funcii real()
Programul este urmtorul.
#include <iostream>
#include <complex>
using namespace std;
int main()
{
complex <double> a(1, 1), b(2.0, -3.5), c;
c = a + b;
cout << c = << c << endl;
c += a;
cout << "c + a = " << c << endl;
cout << "abs(c) = " << abs(c) << endl;
// citeste si scrie un numar complex
cin >> a;
cout << "a = " << a << endl;
// scrie partea reala a numarului complex
cout << "real a = " << a.real() << endl;
cout << "real a = " << real(a) << endl;
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
227
228
Anexa 1. Reprezentarea informaiilor
A.1 Reprezentarea numerelor ntregi n sistemul binar
Un numr natural se reprezint ca o colecie de cifre. In sistemul poziional, poziia
unei cifre determin ponderea cifrei n mrimea numrului. Fie numrul
r n n
d d d N ) ... (
0 2 1

Mrimea numrului natural corespunztor este:

+ + +
1
0
0
0
2
2
1
1
...
n
i
i
i
n
n
n
n
r d r d r d r d N
unde: r>1 este baza, n este numrul de cifre, iar
i
d
este cifra de pe poziia i. Avem
totdeauna
r d
i
< 0
. In sistemul zecimal cifrele utilizate sunt 0, 1, 2, , 9. In
sistemul binar cifrele utilizate sunt 0 i 1, n sistemul octal 0, 1, 2, , 7, iar n
sistemul hexazecimal cifrele utilizate sunt: 0, 1, 2, , 9, A, B, C, D, E, F unde: A=10,
B=11, C=12, D=13, E=14 i F=15. Cifrele sistemului binar se numesc bii.
Conversia zecimal-binar
Fie un numr natural reprezentat n sistemul binar
0
1
1
1
1
1
0
2 ... 2 2 b b b b N
n
n
n
i
i
i
+ + +

In partea dreapt avem un polinom de puteri ale lui 2. Coeficientul


i
b
este 0 sau 1. Din
expresia de mai sus a numrului se observ c cifrele
o
b
,
1
b , etc se pot obine ca
resturile impririlor repetate ale numrului N cu 2. Vom nota:
0
q N
i vom scrie:
0 1 0
2 r q q +
de unde deducem:
0 0
b r
1
2
1 1
... 2 b b q
n
n
+ +

Vom scrie
1 2 1
2 r q q +
de unde deducem:
1 1
b r
2
3
1 2
... 2 b b q
n
n
+ +

Dup n astfel de operaii vom avea:


1 1
2

+
n n n
r q q
unde:
1 1

n n
b r
0
n
q
Dup cum se observ, resturile obinute reprezint chiar cifrele numrului binar.
Exemplu. S convertim numarul 14 din baza 10 n baza 2.
0 2 * 7 2 14
0 1
+ + r q
229
Avem deci:
0
0 0
r b
1 2 * 3 2 7
1 2
+ + r q
Avem: 1
1 1
r b
1 2 * 1 2 3
2 3
+ + r q
de unde obinem:
1
2 2
r b
In final:
1 2 * 0 2 1
3 4
+ + r q
de unde obinem:
1
3 3
r b
deci reprezentarea numrului 14 n binar este:
( ) ( )
2 10
1110 14
Algoritmul de conversie a unui numr din baza 10 n baza 2 este urmtorul:
1.
N q
0
2. 0 i
3. while
0
i
q
{
2
1
i
i
q
q
+
2 %
i i
q r
1 + i i
}
Resturile obinute sunt cifrele numrului binar, primul rest fiind cifra cea mai puin
semnificativ.
Conversia din baza 10 n baza 8 sau 16 se face prin mpriri repetate cu 8 i respectiv
16.
Unitatea de baz a informaiei n calculator este un octet sau byte, ce cuprinde 8 cifre
binare (bii). Numerele ntregi se reprezint n calculator pe 8, 16, 32 sau 64 de bii.
Avnd un numr n baza 2, pentru reprezentarea sa n baza 16 se grupeaz cte 4 cifre
binare.
Exemplu.
( ) ( ) ( )
16 2 10
1100 12 C
( ) ( ) ( ) ( )
10 10 16 2
158 14 16 * 9 9 10011110 + E
Reprezentarea n baza 16 este important deoarece un octet poate fi reprezentat prin
dou cifre hexazecimale.
Avnd un numr n baza 2, pentru reprezentarea n baza 8 se grupeaz cte 3 cifre
binare.
Exemplu.
( ) ( ) ( )
8 2 10
34 11100 28
Pentru verificare
( ) ( ) ( )
10 10
1
8
28 4 8 * 3 34 +
Conversia unui numr din baza 16 n baza 2 se face reprezentnd fiecare cifr
hexazecimal prin 4 cifre binare.
Conversia unui numr din baza 8 n baza 2 se face convertind fiecare cifr octal prin
3 cifre binare. Pentru conversii de numere ntre bazele 2, 8, 10 i 16 i operaii cu
230
numere n aceste baze se poate folosi aplicaia Calculator a sistemului de operare
Windows.
In final prezentm n tabela de mai jos numerele de la 0 la 15 reprezentate n bazele
10, 2, 8 i 16.
Baza 10 Baza 2 Baza 8 Baza 16
0 0000 0 0
1 0001 1 1
2 0010 2 2
3 0011 3 3
4 0100 4 4
5 0101 5 5
6 0110 6 6
7 0111 7 7
8 1000 10 8
9 1001 11 9
10 1010 12 a
11 1011 13 b
12 1100 14 c
13 1101 15 d
14 1110 16 e
15 1111 17 f
Reprezentarea numerelor binare cu semn
In cazul numerelor binare cu semn, bitul cel mai semnificativ este bitul de semn. El
este 0 pentru numere pozitive i 1 pentru numere negative. Exist trei reprezentri ale
numerelor binare cu semn.
Reprezentarea n mrime i semn
Numrul pozitiv X se reprezint ca:

+
2
0
1
2 2 * 0
n
i
i
i
n
a X
Numrul negativ X se reprezint ca:

+
2
0
1
2 2 * 1
n
i
i
i
n
a X
Exemple. Vom considera numere ntregi reprezentate pe 8 bii, un bit de semn i 7 bii
ai numrului:
Numr zecimal Reprezentare binar Reprezentare hexazecimal
13 0000 1101 0D
-13 1000 1101 8D
25 0001 1001 19
-7 1000 0111 87
127 0111 1111 7F
-127 1111 1111 FF
231
Gama numerelor ntregi reprezentabile pe un octet n mrime i semn este [-127, 127].
Putem scrie formula de reprezentare a numerelor binare n mrime i semn ca:

+
2
0
1
1
2 2 ) 1 (
n
i
i
i
n
n
a a X
unde coeficientul
1 n
a
are valoarea 0 sau 1. Primul bit va fi interpretat ca i
coeficientul lui
1
2

n
.
Reprezentarea n complement fa de 1
Numrul pozitiv X se reprezint n complement fa de 1 ca:

+
2
0
1
2 2 * 0
n
i
i
i
n
a X
Numrul negativ X se reprezint ca:

+
2
0
1
2 2 * 1
n
i
i
i
n
a X
unde:
i i
a a 1 . Pentru a reprezenta un numr negativ n complement fa de 1
complementm toate cifrele lui.
Exemple de numere reprezentate n complement fa de 1 pe un octet.
Numr zecimal Reprezentare binar Reprezentare hexazecimal
15 0000 1111 0F
-15 1111 0000 F0
-19 1000 1100 8C
19 0001 0011 13
Reprezentarea numerelor binare n complement fa de 2
Numerele pozitive se reprezint n complement fa de 2 ca:

+
2
0
1
2 2 * 0
n
i
i
i
n
a X
Numerele negative se reprezint n complement fa de 2 ca:

+ +
2
0
1
1 2 2 * 1
n
i
i
i
n
a X
unde:
i
i a a 1 .
Exemple de numere reprezentate n complement fa de 2 pe un octet.
Numr zecimal Reprezentare binar Reprezentare hexazecimal
13 0000 1101 0D
-13 1111 0011 F3
-7 1111 1001 F9
127 0111 1111 7F
-127 1000 0001 81
Menionm c n calculatoare numerele ntregi se reprezint n general n complement
fa de 2. Considerm formula ce reprezint un numr negativ n complement fa de
2. Avem relaia:
232
1
2
0
2 1 2

n
n
i
i
In consecin, putem scrie:

+ + +
2
0
2
0
1 1
2
0
1
2 2 2 2 * 1 1 2 ) 1 ( 2 * 1
n
i
i
i
n
i
i
i
n n
n
i
i
i
n
a a a
A.2 Deplasarea numerelor binare cu semn
Inmulirea unui numr binar cu semn cu
1
2
sau
1
2

este echivalent cu deplasarea


numrului binar la stnga sau la dreapta cu o cifra. La deplasarea numrului binar
bitul de semn rmane neschimbat.
Cazul numerelor pozitive.
Se deplaseaz toate cifrele numrului, iar cifrele adaugate sunt zerouri. Regula este
evident din formula de reprezentare a numerelor pozitive. Fie numrul pozitiv

+
2
0
1
2 2 * 0
n
i
i
i
n
a X
i fie numrul nmulit cu 2

+
2
0
1
2 2 * 0 * 2
n
i
i
i
n
b X Y
de unde se deduce:
i i
a b
+1
0
0
b
Am presupus c prin nmulirea cu 2 a numrului X nu apare depsire. In acelai fel se
arat c regula este valabil pentru mprirea cu 2.
Exemple. S deplasm numrul 28 cu o cifr binar la dreapta i la stnga.
Numr zecimal Numr binar Numr hexazecimal
28 0001 1100 1C
14 0000 1110 0E
56 0011 1000 38
Numere negative reprezentate n complement fa de 2
Regula de nmulire a acestor numere cu
1
2
sau
1
2

este urmtoarea:
la deplasarea la stnga cifrele adugate sunt zerouri,
la deplasarea la dreapta cifrele adaugate sunt unuri,
reamintim ca bitul de semn rmne neschimbat.
Exemplu.
Numr zecimal Numr binar Numr hexazecimal
-28 1110 0100 E4
-14 1111 0010 F2
-56 1100 1000 C8
233
A.3 Reprezentarea numerelor reale
Numerele reale se reprezint n calculator n virgul mobil. Numarul real R este pus
sub forma
e
b f R *
unde: f este un numr subunitar (mantisa), b este baza iar e este exponent sau
caracteristic. Pentru exemplificare vom considera baza zece.
Exemple de reprezentare a numerelor reale.
2
10 * 3245 . 0 45 . 32
1
10 * 35 . 0 035 . 0


Mantisa f are totdeauna un numr finit de cifre i n plus:
1 < f
In continuare, pentru exemplificare vom considera c mantisa are apte cifre
semnificative. Deoarece mantisa are un numr finit de cifre, n unele cazuri se pierd
cifrele mai puin semnificative din numr.
Exemplu. Numrul
... 3333333333 . 0 ) 3 .( 0
3
1

se reprezint ca:
0
10 * 3333333 . 0
Pentru mrirea preciziei calculelor se caut ca prima cifr din mantis s fie diferit
de zero (vezi exemplul de mai sus). Numerele n aceast form se numesc
normalizate.
Efectuarea operaiilor cu numere n virgul mobil
Pentru adunare numerele se aduc la acelai exponent (numrul mai mic la exponentul
celui mai mare) i apoi se adun mantisele. Aducerea numrului mai mic la
exponentul celui mai mare poate duce la pierderea cifrelor cel mai puin semnificative
din mantis.
Exemplu. S efectum adunarea:
12.48+0.35
Primul numr este reprezentat ca:
2
10 * 1248 . 0
iar al doilea
0
10 * 35 . 0
Pentru adunare se aduc numerele la acelai exponent
0.1248 2
0.035 2
i se adun mantisele.
0.1283 2
Vom prezenta un exemplu n care se pierd cifrele cele mai puin semnificative din
rezultat. S adunm numerele:
237.55 + 0.000425
Numerele sunt reprezentate astfel:
3
10 * 23755 . 0
0.3245 2
-0.35 -1
0.1248 2
0.35 0
0.23755 3
234
3
10 * 425 . 0

La adunare, mantisa celui de-al doilea numr este deplasat la dreapta cu ase cifre.
Considernd lungimea mantisei de apte cifre, se pierd doua cifre semnificative din al
doilea numr la aducerea la acelai exponent.
0.23755 3
0.0000004 3
Rezultatul adunrii este:
0.2375504 3
Observm c s-au pierdut dou cifre, cele mai puin semnificative.
Operaia de scdere se face aducnd numerele la acelai exponent i scznd
mantisele.
La nmulire se adun exponenii i se nmulesc mantisele. La mprire se scad
exponenii i se mpart mantisele. Dup efectuarea unei operaii aritmetice se caut ca
prima cifra din mantis s fie diferit de zero, pentru o precizie maxim a rezultatului
(normalizare).
Pentru creterea preciziei operaiilor, n unitatea de calcul mantisa mai are nc o cifr.
Operaiile se fac deci cu o mantis mai lung, dup care se rein apte cifre din
rezultat.
Exemplu. Fie de efectuat operaia de scdere:
103.4567 23.45678
Numerele se reprezint astfel:
0.1034567 3
0.2345678 2
Dac mantisa are doar apte cifre semnificative, cnd deplasm la dreapta cu o cifr
mantisa celui de-al doilea numr, pierdem o cifr semnificativ.
0.0234567 3
Rezultatul scderii celor dou numere este:
0.08 3
i dup normalizare obtinem rezultatul 80. Presupunem acum c n unitatea de calcul
se lucreaz cu o mantis cu opt cifre semnificative. In acest caz cele dou numere sunt
0.10345670 3
0.02345678 3
Dup scderea celor dou numere obinem:
0.07999992 3
iar dup normalizare obinem rezultatul corect:
0.7999992 2
Mentionm c exponentul poate fi pozitiv sau negativ. Pentru a nu avea dou semne,
unul pentru numr i altul pentru exponent, se adaug un numr constant la exponent
astfel ca s fie totdeauna pozitiv. De exemplu, dac gama exponentului este [-63, 63]
se adaug valoarea 63 astfel nct gama exponentului va fi [0, 126].
In calculatoarele personale numerele reale se reprezint n virgul mobil n felul
urmtor
R = semn * 1.mantisa * 2
exponent
In reprezentarea n virgul mobil scurt (simpla precizie) numrul se reprezinta pe 32
bii. Domeniul exponentului este [-127, 127]. La exponent se adaug totdeauna
0.425 -3
235
valoarea 127 astfel ncat exponentul are domeniul [0,254]. Bitul de semn are valoarea
0 pentru numere pozitive i 1 pentru cele negative. Bitul de semn ocup bitul 31,
exponentul ocup biii 23-30 iar mantisa biii 0-22. Gama numerelor reale ce se pot
reprezenta n acest format este
) 10 , 10 (
37 37

iar mantisa corespunde la opt cifre zecimale.


Exemple. Fie numrul
0
2 * 0 . 1 0 . 1 R . Bitul de semn este zero iar exponentul
0+127. In consecin, primii nou biti din numr sunt
001111111
Numrul R va fi reprezentat n hexazecimal ca 3F800000.
Fir numrul
1
2 * 0 . 1 5 . 0

R . Bitul de semn este zero iar exponentul este
-1+127=126. Primii nou bii din numr sunt
001111110
Numrul se reprezint n hexazecimal ca 3F000000.
Menionm c dac exponentul este 255 iar mantisa este 0, numrul este t Dac
exponentul este 255 i mantisa este diferit de zero numrul este NaN (not a number).
Valoarea NaN apare atunci cnd efectum urmatoarele operaii


/
0 / 0
* 0
sau cnd un operand este NaN.
In reprezentarea n virgul mobil lung (dubl precizie) numrul se reprezint pe 64
bii. Domeniul exponentului este [-1023, 1023]. La exponent se adaug cantitatea
1023, deci exponentul are domeniul [0, 2046]. Bitul de semn este bitul 63, exponentul
ocup biii 52-62 iar mantisa ocup biii 0-51. Gama numerelor reale ce se pot
reprezenta este
) 10 , 10 (
307 307

A.4 Reprezentarea caracterelor


Unul dintre sistemele de reprezentare a caracterelor este codul ASCII n care fiecare
caracter este reprezentat pe un octet. De exemplu caracterul A are codul hexazecimal
41, caracterul a are codul hexazecimal 61, cifra 0 are codul hexazecimal 30, etc.
Codurile ASCII ale caracterelor sunt prezentate n tabela de mai jos.
y\x 0 1 2 3 4 5 6 7
0 Nul Dle 0 P p
1 Soh Dc1 ! 1 A Q a q
2 Stx Dc2 2 B R b r
3 Etx Dc3 # 3 C S c s
4 Eot Dc4 $ 4 D T d t
5 Enq Nak % 5 E U e u
6 Ack Syn & 6 F V f v
7 Bel Elb 7 G W g w
8 Bs Can ( 8 H X h x
9 Ht Em ) 9 I Y i y
10 Lf Sub * : J Z j z
236
11 Vt Esc + : K [ k {
12 Ff Fs , < L \ l |
13 Cr Gs - = M ] m }
14 So Rs . > N ^ n ~
15 Si Us / ? O _ o del
Tabelul 1.1 Codul ASCII
Codul zecimal unui caracter este 16*x+y. De exemplu caracterul A are codul zecimal
65 sau, echivalent, codul hexazecimal 41.
Un alt sistem de reprezentare a caracterelor este UNICODE. n varianta UNICODE-
16 fiecare caracter este reprezentat pe 2 octei.
237
Anexa 2. Prioritile i asociativitatea operatorilor
Operator Asociativitate
1 :: stnga
2 [ ] ( ) -> . stnga
3 ++ -- + - ! ~ sizeof (tip) new
delete * &
dreapta
4 (tip) dreapta
5 * / % stnga
6 + - stnga
7 << >> stnga
8 < <= > >= stnga
9 = = != stnga
10 & stnga
11 ^ stnga
12 | stnga
13 && stnga
14 | | stnga
15 ?: dreapta
16 = += -= *= /= %= <<= >>= &= |
= ^=
dreapta
17 , stnga
238
Anexa 3. Tipul complex n limbajul C
Limbajul C definete tipul complex pentru lucrul cu numere complexe. Un numr
complex este o pereche ordonat de dou numere reale. In limbajul C vom avea
numere complexe constituite din dou numere reale tip float i separat, numere
complexe constituite din dou numere reale tip double.
Un numr complex (o constant complex) are forma
a + b * I
unde a i b sunt numere reale sau ntregi ce definesc numrul complex.
Forma instruciunilor de declarare a variabilelor de tip complex este
complex double list de variabile;
complex float list de variabile;
Lista de variabile este format din nume de variabile separate de virgule. Putem
defini, de exemplu, variabilele de tip complex urmtoare
complex float x, y, z ;
complex double m, n, p ;
Variabilele de tip complex pot fi iniializate la definirea lor. La iniializare se
utilizeaz constante complexe cu forma de mai sus sau cu forma
{a + b * I}
unde a i b sunt cele dou numere reale ce definesc constanta complex.
Exemplu. S definim variabilele complexe x = 2.35 7.4i i y = -12.5 + 4.3i.
Instruciunea de definire a acestor variabile este
complex double x = 2.35 7.4 * I, y = -12.5 + 4.3 * I ;
sau
complex double x = {2.35 7.4 * I}, y = {-12.5 + 4.3 * I} ;
Operaiile cu numere complexe sunt cele cunoscute, +, -, * i /. Prioritile i
asociativitatea operatorilor sunt tot cele cunoscute. Expresiile se scriu dup regulile
cunoscute. In scrierea expresiilor se pot utiliza ca termeni variabile i numere ntregi,
reale i complexe.
Scrierea numerelor complexe se face cu instruciunea printf, n care vom defini doi
specificatori de conversie pentru fiecare numr complex, primul pentru partea real, al
doilea pentru partea imaginar.
Pentru lucrul cu numere complexe trebuie s includem biblioteca antet <complex.h>.
Ca prim exemplu, definim dou variabile complexe, d1 i d2, i calculm expresiile
d1 * d2 i d1 / d2 + 1. Variabila d1 este iniializat la definirea di, n instruciunea
complex, la valoarea 2.35 3.45i, variabila d2 este iniilizat n program la valoarea
4.3 - 2.5i -3.57 + 2.95i. Programul este urmtorul.
#include <stdio.h>
#include <stdlib.h>
#include <complex.h>
int main(int argc, char *argv[])
{
// definirea de variabile complexe
complex double d1 = 2.35 - 3.45 * I, d2, d3;
d2 = 4.3 - 2.5 * I -3.57 + 2.95 * I;
d3 = d1 * d2;
239
printf(" d1 = %6.2f %6.2f\n d2 = %6.2f %6.2f\n d1 + d2 = %6.2f %6.2f\n",
d1, d2, d3);
d3 = d1 / d2 + 1;
printf(" d1 = %6.2f %6.2f\n d2 = %6.2f %6.2f\n d1 / d2 + 1 = %6.2f %6.2f\n",
d1, d2, d3);
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Ca un alt exemplu, definim dou variabile complexe, a1 i a2, calculm produsul lor,
a3=a1*a2 i afim rezultatul pe ecran. Programul este urmtorul.
#include <stdio.h>
#include <stdlib.h>
#include <complex.h>
int main(int argc, char *argv[])
{

complex double a1 = {2.75 + 3.2 * I}, a2 = {-1 + 3.42 * I}, a3;
printf("a1 = (%6.2f %6.2f)\n", a1);
printf("a2 = (%6.2f %6.2f)\n", a2);
a3 = a1 * a2;
printf("a1 * a2 = (%6.2f %6.2f)\n", a3);
return 0;
}
Rezultatul rulrii programului este cel de mai jos.
Biblioteca <complex.h> include funciile uzuale pentru lucrul cu numere complexe.
Exist funcii separate pentru numere complexe ce constau din dou numere de tip
double i respective din numere de tip float.
Funciile pentru operaiile cu numere complexe formate dintr-o pereche de dou
numere tip double sunt urmtoarele:
240
creal csin cexp
cimag ccos clog
carg casin csqrt
cabs cacos cpow
conj catan
Toate funciile de mai sus au un argument numr complex, cu excepia funciei cpow
ce are dou argumente, numere complexe formate dintr-o pereche de dou numere tip
double.
Funciile pentru operaiile cu numere complexe cu sunt formate dintr-o pereche de
dou numere tip float au numele de mai sus urmate de litera f.
Vom exemplifica utilizarea acestor funcii calculnd 1 , apoi partea real, partea
imaginar, argumentul i modulul numrului complex 2.46 -6.25i. Programul este cel
de mai jos.
#include <stdio.h>
#include <stdlib.h>
#include <complex.h>
int main(int argc, char *argv[])
{
// complex double a = {-1 + 0 * I}, b, c = {2.46 - 6.25 * I};
complex double a = -1 + 0 * I, b, c = 2.46 - 6.25 * I;
double cr, ci;

// calcul sqrt(-1)
b = csqrt(a);
// b = csqrt(-1);
// b = csqrt(-1 + 0 * I);
printf("a = (%6.2f, %6.2f)\n", a);
printf("b = (%6.2f, %6.2f)\n", b);
// calcul partea reala si imaginara a unui numar complex
cr = creal(c);
ci = cimag(c);
printf("c = (%6.2f, %6.2f)\n", c);
printf("real(c) = %6.2f, imag(c) = %6.2f\n", cr, ci);
// calcul argumentul si modulul unui numar complex
cr = carg(c);
ci = cabs(c);
printf("arg(c) = %6.2f, abs(c) = %6.2f\n", cr, ci);
return 0;
}
In program am iniializat variabilele folosind cele dou forme artate mai sus.
Calculul valorii 1 se poate face cu una din formele
b = csqrt(a);
b = csqrt(-1);
b = csqrt(-1 + 0 * I);
241
unde variabila complex a este iniializat la valoarea -1.
Rezultatul rulrii programului este cel de mai jos.
In exemplul urmtor vom citi un numr complex x de la tastatur i vom calcula x
2
i
x
i
. Vom utiliza numere complexe formate din dou numere reale tip float. Vom citi
pertea real i cea imaginar cu instruciunea scanf. Pentru ridicarea la putere vom
utiliza funcia cpow. Programul este urmtorul.
#include <stdio.h>
#include <stdlib.h>
#include <complex.h>
int main(int argc, char *argv[])
{
complex float x, y;
float xr, xi;

scanf("%f %f", &xr, &xi);
x = xr + xi * I;
printf("x = %f %f\n", crealf(x), cimagf(x));
y = cpow(x, 2);
printf("x ^ 2 = %f %f\n", crealf(y), cimagf(y));
y = cpow(x, 1 * I);
printf("x ^ I = %f %f\n", crealf(y), cimagf(y));
return 0;
}
Rezultatul rulrii programului este cea de mai jos.
Vom exemplifica acum calculul valorii, modulului i argumentului funciei de o
variabil complex
( )
( )
( )( )( ) 1 01 . 0 01 . 0 1 01 . 0 1
1 05 . 0 10
2 2
+ + + +
+

s s s s
s
s f
pentru s lund valori n intervalul [0, 14i] cu pasul 0.5i. Programul este cel de mai jos.
242
#include <stdio.h>
#include <stdlib.h>
#include <complex.h>
int main(int argc, char *argv[])
{
complex float ft;
complex float s, pas = {0.5 * I};
float realft, imagft, absft, argft, imags;
int i;
printf(" omega real imag abs arg\n");
for(i = 1; i <= 28; i++)
{
s = i * pas;
// s = i * (0.5 * I);
// s = i * 0.5 * I;
ft = 10 * (0.05 * s + 1) / (s + 1) / (0.01 * s + 1) /
(0.01 * 0.01 * s * s + 0.01 * s + 1);
imags = cimag(s);
realft = creal(ft); imagft = cimag(ft);
argft = carg(ft); absft = cabs(ft);
printf("%6.2f %6.2f %6.2f %6.2f %6.2f \n", imags, realft, imagft, absft, argft);
}
return 0;
}
Se va remarca modul de calcul al variabilei complexe s.
Rezultatul rulrii programului este cel de mai jos.
243
244