Sunteți pe pagina 1din 223

3

Cuprins
Cuprins .......................................................................................................................... 4
1. Structura general a unui program C.................................................................. 9
1.1. Istoric, concepie , evoluie ........................................................................................... 9
1.2. Conceptul de funcie ................................................................................................... 10
1.2.1. Definiia unei funcii............................................................................................. 11
1.2.2. Antet i prototip ..................................................................................................... 11
1.2.3. Corpul unei funcii................................................................................................ 11
1.3. Construciile de baz ale limbajului.......................................................................... 12
1.3.1. Caractere................................................................................................................. 12
1.3.2. Nume ...................................................................................................................... 12
1.3.3. Cuvinte cheie ......................................................................................................... 12
1.3.4. Tipuri de baz ....................................................................................................... 13
1.3.5. Constante ................................................................................................................ 13
1.3.6. Variabile simple..................................................................................................... 15
1.3.7. Tablouri ................................................................................................................... 15
1.3.8. Comentariu .............................................................................................................. 16
1.4. Preprocesare .................................................................................................................. 17
1.4.1. Includerea unui fiier surs.................................................................................. 17
1.4.2. Constante simbolice............................................................................................... 18
2. Clase de variabile (de memorie)........................................................................ 19
2.1. Variabile globale .......................................................................................................... 19
2.2. Variabile locale ............................................................................................................ 20
2.3. Variabile registru ........................................................................................................ 21
2.4. Iniializare ..................................................................................................................... 22
3. Expresii, operanzi, operatori ................................................................................. 24
3.1. Expresii ......................................................................................................................... 24
3.2. Operanzi ......................................................................................................................... 24
3.3. Operatori ....................................................................................................................... 24
3.3.1. Operatori aritmetici................................................................................................ 25
3.3.2. Operatori relaionali............................................................................................... 25
3.3.3. Operatori de egalitate............................................................................................ 26
3.3.4. Operatori logici...................................................................................................... 26
3.3.5. Operatori logici pe bii......................................................................................... 27
3.3.6. Operatori de atribuire............................................................................................ 28
3.3.7. Operatori de incrementare i decrementare ........................................................ 29
3.3.8. Operatorul de conversie explicit (expresie cast)................................................ 30
3.3.9. Operatorul dimensiune (sizeof) ............................................................................ 30
3.3.10. Regula conversiilor implicite............................................................................... 31
3.3.11. Operatori condiionali.......................................................................................... 32
3.3.12. Operatorul virgul................................................................................................. 32
4. Intrri / ieiri standard ............................................................................................ 33
4.1. Funcia standard printf ................................................................................................ 33
4.2. Funcia standard scanf................................................................................................ 35
4.3. Funcia standard putchar ............................................................................................. 38
4.4. Funcia standard getchar ............................................................................................. 38
4.5. Funciile standard getche i getch ............................................................................. 39
5. Instruciuni .............................................................................................................. 40
5.1. Scurt istoric al metodelor de programare................................................................. 40
5.1.1. Programare artizanal ............................................................................................ 40
4

5.1.2. Programare procedural......................................................................................... 40


5.1.3. Programare modular ........................................................................................... 41
5.1.4. Programare structurat.......................................................................................... 41
5.1.5. Programare prin abstractizarea datelor ................................................................ 42
5.1.6. Programarea orientat spre obiecte...................................................................... 43
5.2. Instruciunea vid.......................................................................................................... 44
5.3. Instruciunea expresie .................................................................................................. 44
5.4. Instruciunea compus ................................................................................................. 44
5.5. Instruciunea if ............................................................................................................. 45
5.6. Instruciunea while....................................................................................................... 46
5.7. Instruciunea for ........................................................................................................... 47
5.8. Instruciunea do while................................................................................................ 48
5.9. Instructiunea switch .................................................................................................... 49
5.10. Instruciunea break.................................................................................................... 51
5.11. Instruciunea continue ................................................................................................ 51
5.12. Instruciunea goto....................................................................................................... 51
5.13. Apelul i revenirea dintr-o funcie .......................................................................... 52
5.13.1. Apelul unei funcii .............................................................................................. 52
5.13.2. Prototipul unei funcii ......................................................................................... 52
5.13.3. Apel prin valoare i apel prin referin ........................................................... 53
5.13.4. Revenirea dintr-o funcie .................................................................................... 53
6. Pointeri .................................................................................................................... 55
6.1. Declaraia de pointer ................................................................................................... 55
6.2. Legtura dintre pointeri i tablouri ........................................................................... 56
6.3. Operaii cu pointeri ..................................................................................................... 57
6.3.1. Incrementare i decrementare ............................................................................... 57
6.3.2. Adunarea i scderea unui ntreg dintr-un pointer............................................ 57
6.3.3. Compararea a doi pointeri ................................................................................... 58
6.3.4. Diferena a doi pointeri........................................................................................ 58
6.3.5. Exemple .................................................................................................................. 58
6.4. Alocarea dinamic a memoriei .................................................................................. 59
6.5. Pointeri spre funcii..................................................................................................... 60
6.6. Tratarea parametrilor din linia de comand ............................................................. 63
6.7. Modificatorul const ...................................................................................................... 63
6.8. Stiva .............................................................................................................................. 65
7. Recursivitate............................................................................................................ 66
8. Structuri, tipuri utilizator....................................................................................... 71
8.1. Declaraia de structur ................................................................................................ 71
8.2. Accesul la elementele unei structuri ......................................................................... 73
8.3. Atribuiri de nume pentru tipuri de date.................................................................... 74
8.4. Uniune........................................................................................................................... 75
8.5. Cmp .............................................................................................................................. 80
8.6. Tipul enumerat ............................................................................................................. 81
9. Liste......................................................................................................................... 84
9.1. Date structurate definite recursiv ............................................................................ 84
9.2. Liste nlnuite ............................................................................................................. 84
9.3. Lista liniar simplu nlnuit .................................................................................... 85
9.4. Crearea i afiarea unei liste ...................................................................................... 86
10. Prelucrarea fiierelor ............................................................................................ 88
10.1. Fiiere.......................................................................................................................... 88
10.2. Nivelul inferior de prelucrare al fiierelor ............................................................. 88
5

10.2.1. Deschiderea unui fiier ....................................................................................... 89


10.2.2. Citirea dintr-un fiier (consultare) ..................................................................... 89
10.2.3. Scrierea ntr-un fiier (creare, actualizare, adugare)...................................... 90
10.2.4. Poziionarea ntr-un fiier ................................................................................... 90
10.2.5. nchiderea unui fiier .......................................................................................... 91
10.3. Nivelul superior de prelucrare a fiierelor ............................................................. 92
10.3.1. Deschiderea unui fiier ........................................................................................ 92
10.3.2. Prelucrarea pe caractere a unui fiier............................................................... 93
10.3.3. nchiderea unui fiier .......................................................................................... 93
10.3.4. Operaiile de intrare-ieire cu format................................................................. 94
10.3.5. Intrri-ieiri de iruri de caractere .................................................................... 95
10.3.6. Poziionarea ntr-un fiier ................................................................................... 96
10.3.7. Prelucrarea fiierelor binare................................................................................ 96
10.4. tergerea unui fiier .................................................................................................. 98
11. Funcii standard .................................................................................................... 99
11.1. Macrouri de clasificare ............................................................................................ 99
11.2. Macrouri de transformare a simbolurilor .............................................................. 100
11.3. Conversii ................................................................................................................... 100
11.4. Funcii de prelucrare a irurilor de caractere ...................................................... 102
11.5. Funcii de calcul ...................................................................................................... 103
11.6. Funcii pentru controlul proceselor ......................................................................... 104
11.7. Funcii pentru gestiunea datei i orei .................................................................. 104
11.8. Alte funcii diverse de uz general ........................................................................ 105
12. Gestiunea ecranului n mod text...................................................................... 106
12.1. Setarea ecranului n mod text................................................................................ 107
12.2. Definirea unei ferestre............................................................................................ 107
12.3. tergerea unei ferestre............................................................................................. 108
12.4. Gestiunea cursorului ................................................................................................ 108
12.5. Determinarea parametrilor ecranului ...................................................................... 109
12.6. Modurile video alb/negru........................................................................................ 109
12.7. Setarea culorilor ....................................................................................................... 110
12.8. Gestiunea textelor .................................................................................................... 110
13. Gestiunea ecranului n mod grafic .................................................................. 113
13.1. Setarea modului grafic ............................................................................................ 113
13.2. Gestiunea culorilor.................................................................................................... 115
13.3. Starea ecranului.......................................................................................................... 117
14. Probleme diverse ................................................................................................ 119
14.1. Generarea combinrilor ............................................................................................ 119
14.2. Metoda greedy ........................................................................................................ 120
14.3. Metoda backtracking (cutare cu revenire)............................................................. 122
14.4. Metoda divide et impera (divide i stpnete)................................................... 127
14.5. Metoda programrii dinamice.................................................................................. 129
15. Faciliti noi n limbajul C++ ............................................................................. 134
15.1. Extinderea limbajului C......................................................................................... 134
15.2. Operatori ................................................................................................................ 137
15.2.1. Operatorul de rezoluie ........................................................................................ 137
15.2.2. Folosirea operatorului adres pentru definirea tipului referin .......................... 137
15.2.3. Apelul prin referin ............................................................................................ 139
15.2.4. Operatori pentru alocare i dezalocare dinamic a memoriei.............................. 140
15.3. Structuri, reuniuni i tipuri enumerare................................................................... 141
15.4. Funcii.................................................................................................................... 143
6

15.4.1. Iniializarea parametrilor formali ai funciilor..................................................... 143


15.4.2. Funcii care returneaz tipuri referin ................................................................ 145
15.4.3. Suprancrcarea funciilor.................................................................................... 146
15.4.4. Funcii inline........................................................................................................ 148
16. Noiunea de clas................................................................................................ 152
16.1. Realizarea proteciei datelor prin metoda programrii modulare .......................... 152
16.2. Tipuri abstracte de date.......................................................................................... 153
16.3. Declararea claselor ................................................................................................ 154
16.4. Referirea la elementele claselor. Pointerul this ..................................................... 155
16.5. Constructorul ......................................................................................................... 157
16.5.1. Iniializarea obiectelor prin constructor............................................................... 157
16.5.2 Apelarea constructorilor n cazul n care datele membru sunt obiecte .............. 159
16.6. Destructorul ........................................................................................................... 160
16.7. Suprancrcarea operatorilor.................................................................................. 161
16.7.1. Metoda general de suprancrcare ..................................................................... 161
16.7.2. Suprancrcarea operatorilor de atribuire ............................................................ 162
16.7.3. Suprancrcarea operatorilor de incrementare i decrementare........................... 169
16.8. Conversii definite de programator ......................................................................... 171
16.8.1. Efectuarea conversiilor ........................................................................................ 171
16.8.2. Conversii implicite definite de programator........................................................ 172
16.8.3. Suprancrcarea operatorului de conversie explicit ........................................... 177
17. Metoda programrii orientate obiect .................................................................. 181
17.1. Bazele teoretice ale metodei programrii orientate obiect ................................... 181
17.2. Declararea claselor derivate................................................................................... 182
17.3. Funcii membru virtuale ........................................................................................ 183
17.4. Clase virtuale ......................................................................................................... 186
17.5. Clase abstracte. Funcia membru virtual pur ..................................................... 191
18. Ierarhii de clase pentru operaii de intrare/ieire ................................................ 194
18.1. Streamuri ............................................................................................................... 194
18.2. Ieiri formatate....................................................................................................... 195
18.2.1. Operatorul de inserare ......................................................................................... 195
18.2.2. Funcia membru setf ............................................................................................ 197
18.2.3. Funciile membru width, fill i precision............................................................. 201
18.2.4. Manipulatori ........................................................................................................ 204
18.2.5. Suprancrcarea operatorului de inserare ............................................................ 205
18.3. Intrri formatate ..................................................................................................... 208
18.3.1. Operatorul de extragere ....................................................................................... 208
18.3.2. Starea de eroare ................................................................................................... 211
18.3.3. Suprancrarea operatorului de extragere ............................................................ 217
19. Anex.................................................................................................................. 219
19.1. Un memento al sintaxei limbajului C ................................................................... 219
19.2. Lista programelor C++ .......................................................................................... 224
20. Bibliografie:........................................................................................................ 225

PARTEA I
Limbajul C

1. Structura general a unui program C


1.1. Istoric, concepie , evoluie
Limbajul C a fost finalizat n 1972 de Dennis M. Ritchie i Brian W. Kernighan de
la firma american Bell Laboratories. Prima versiune a limbajului se numete BCPL apoi
urmtoarele poart numele de A, B i C. Cei doi autori au dezvoltat aceste prime versiuni n
jurul sistemului de operare UNIX. La vremea respectiv din aproximativ 13000 linii surs ale
UNIX-ului doar 200 de linii surs nu erau scrise n limbajul C. De acest fapt se leag
detractorii limbajului care spun c limbajul C nu este un limbaj deosebit ci doar un fel de
limbaj oficial al sistemului de operare UNIX.
n anul 1978 apare manualul The C Programming Language care este de fapt i
prima standardizare a limbajului. Cei doi autori intr astfel n istorie...
Dup anul 1980 odat cu dezvoltarea hardware apar i primele PC-uri iar acestea
implic i produse software adecvate. Principalele firme productoare de sofware MICROSOFT i BORLAND - au dezvoltat unelte adecvate pentru programarea i
utilizarea limbajului C. Deocamdat firma BORLAND deine supremaia prin versiunile
mediului BORLAND C. Cele mai folosite sunt versiunile 2.0, 3.1, 4.0. n ultimii doi ani au
aprut aa numitele medii visuale: VISUAL C versiunile 4.5 i 5.0, C++ BUILDER care
sunt de fapt extensii ale mediului BORLAND C adaptate programrii orientate obiect i
interfeei grafice WINDOWS 95. Mediile de programare BORLAND C pot compila 3
tipuri de programe surs C:
- fiiere cu extensia .C
(fiiere cu programe standard C);
- fiiere cu extensia .CP (fiiere cu programe C+, un C extins);
- fiiere cu extensia .CPP (fiiere cu programe C++).
Menionm c limbajul C++ a fost elaborat de Bjarne Stroustrup de la AT&T. El
este un superset al limbajului C i permite principalele concepte ale programrii prin
abstractizarea datelor i programrii orientate spre obiecte.
Limbajul C este un limbaj hibrid avnd faciliti caracteristice limbajelor de asamblare
ct i faciliti ale limbajelor de nalt nivel.
Cteva dintre principalele caracteristici ale limbajului C sunt:
a) portabilitate:

chiar dac acest concept nu-i definit foarte riguros spunem c


c un program este portabil dac el poate fi transferat uor de la
un tip de calculator la altul; limbajul C este un astfel de limbaj;
b) flexibilitate:
compilatorul face un numr mai redus de controale (face multe
conversii implicite);
c) programare structurat: limbajul are principalele structuri ale programrii structurate:
structura secvenial, structura iterativ i structura de selecie;
d) compactizare:
unele instruciuni sunt scrise foarte compact; de exemplu i:=i+1
se poate scrie mai scurt ca i++;
e) lucrul pe bii i calcule cu adrese.

1.2. Conceptul de funcie


Un program C se compune din una sau mai multe funcii. Funcia este o unitate lexical
de program compilabil independent. Una dintre funcii este funcie principal, numele ei este
predefinit i anume main. Execuia programului ncepe cu prima instruciune din funcia
principal. Dm n continuare 2 exemple de structuri de program (fiiere surs):
1)

directive de preprocesare

2)

directive de preprocesare

declaraii de date globale

declaraii de date globale

declaraie prototip funcia f1


...
declaraie prototip funcia fn

implementare funcia f1
...
implementare funcia fn

void main(void)
{ declaraii
instruciuni
}

void main(void)
{ declaraii
instruciuni
}

implementare funcia f1
...
implementare funcia fn
Funcia principal main este obligatorie pentru orice program celelalte elemente fiind
optionale. Pentru ca o anumit funcie s poat fi apelat e necesar ca ea s aib declarat
prototipul dac implementarea (definiia) ei se afl dup funcia main (exemplul 1). Dac
funcia principal se afl la sfritul fiierului atunci nu mai e necesar prototipul funciei
apelate ci doar implementarea ei (exemplul 2). Comparnd structura unui program C cu
structura unui program PASCAL se observ nivelul de imbricare diferit. n PASCAL apare o
imbricare a procedurilor i funciilor pe cnd n C nu exist o astfel de imbricare.
PASCAL

...

10

1.2.1. Definiia unei funcii


Definiia unei funcii n limbajul C se compune din antet i corp. O funcie poate fi apelat
dac este precedat de definiia sau de prototipul ei. Aceste lucruri care sunt valabile n
limbajul C se regsesc i n limbajul C++.

1.2.2. Antet i prototip


Antetul simplificat al unei funcii n C are formatul:
tip nume_funcie (lista_parametrilor_formali)
unde: tip -

reprezint tipul valorii returnate de funcie sau dac funcia nu returneaz nici
o valoare se pune cuvntul cheie void;
nume_funcie reprezint un identificator clasic format dintr-un mixaj de litere
i cifre, primul caracter fiind obligatoriu liter;
printre litere se numr i liniua de subliniere(_);
lista_parametrilor_formali nume de variabile sau expresii separate prin virgule.

Exemple:
1) double radical (double x)
// calculeaza radacina patrata din x si returneaza valoarea gasita
2) double radical_n (double x, int n)
// calculeaza radacina de ordinul n din x

Prototipul unei funcii este asemntor antetului dar la sfrit se pune caracterul ;

1.2.3. Corpul unei funcii


Corpul unei funcii C se compune din declaraii de variabile locale i instruciuni
scrise ntre acolade conform figurii urmtoare:
{ declaraii
instruciuni
}
i pentru c autorii limbajului C consider c un limbaj de programare se nva mai
repede scriind i executnd programe ct mai timpuriu vom da un mic exemplu de funcie.
int modul (int i)
// determina valoarea absoluta a intregului i si returneaza aceasta valoare
{ if (i < 0)
return i;
if (i = = 0)
return 0;
else
return i;
}

sau scris i altfel


int modul (int I)
// determina valoarea absoluta a intregului i si returneaza aceasta valoare
{ if(i)
if (i > 0)
return i;
else
return i;
else
return 0;
}

11

1.3. Construciile de baz ale limbajului


1.3.1. Caractere
Limbajul C folosete setul de caractere al codului ASCII care se codific prin numere
ntregi din intervalul [0,127], adic 128 de coduri. Un astfel de ntreg se pstreaz pe un
BYTE (OCTET) adic pe 8 bii.
Mulimea caracterelor se mparte n trei grupe:
- caractere negrafice (coduri cuprinse ntre 00=NUL i 31 precum i 127=DEL);
- spaiu (codul 32);
- caractere grafice (coduri cuprinse ntre 33 i 126).
Caracterele grafice se mpart la rndul lor n:
- litere mari (coduri ntre 65 i 90);
- litere mici (coduri ntre 97 i 122);
- cifre (coduri ntre 48 i 59);
- caractere speciale (celelalte coduri).
Caracterele negrafice au diferite funcii. Astfel codul 10 semnific LF (Line Feed),
adic deplaseaz cursorul pe linia urmtoare n coloana 1, iar codul 13 semnific CR
(Carriage Return) adic deplaseaz cursorul n coloana 1 aceeai linie. n limbajul C
combinaia celor dou caractere se noteaz prin:
\n
i are semnificaia trecerii cursorului la linie nou i coloana 1 (newline).
Tabulatorul orizontal (deplasarea cursorului peste un anumit numr de poziii) se precizeaz
prin notaia:
\t
S mai precizm c urmtoarele 3 caractere: spaiu, newline i tabulator orizontal se mai
numesc spaii albe (white spaces).

1.3.2. Nume
n limbajul C, un nume este o succesiune de litere i eventual cifre, care ncepe cu o
liter, deci un nume este un identificator. Ca lungime un nume poate fi orict de lung dar
numai primele 32 de caractere se iau n considerare. Folosind notaia BNF (vezi anexa) un
nume se poate defini recursiv, astfel:
<nume> :: = <litera> | <nume><litera> | <nume><cifra>
<litera> :: = A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Z|
a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|z|_
<cifra> :: = 0|1|2|3|4|5|6|7|8|9|
Numele se folosesc pentru denumirea variabilelor, tablourilor, funciilor, etc.
Exemple:
A, _start, a_, matrice, matrice_patratica.
Dm i cteva contraxemple:
&B - conine caracterul &;
x+y - conine caracterul +;

1.3.3. Cuvinte cheie


12

Un cuvnt cheie este un cuvnt mprumutat din limba englez, care are un neles
predefinit. Aceste cuvinte se scriu cu litere mici. Un cuvnt cheie nu poate avea alt utilizare
ntr-un program C dect cea care i-a fost predefinit. Fiind o succesiune de litere, un cuvnt
cheie este un nume. Lista cuvintelor cheie se d n anexa de la sfritul crii. Sensul fiecrui
cuvnt cheie va rezulta la definirea construciei n care se utilizeaz.
Exemple:
for, do, if, while, else, break, return, int, long, double, static, extern.

1.3.4. Tipuri de baz


n limbajul C distingem cteva tipuri predefinte de date care se mai numesc tipuri
de baz. n general un tip de dat are trei atribute bine conturate:
- mulimea valorilor;
- reprezentarea (intern i extern);
- comportamentul (adic operaiile ce se pot face cu acel tip).
n tabelul de mai jos dm cuvntul cheie, mulimea valorilor, reprezentarea intern.

Cuvnt cheie
int
short
long
unsigned
char
float
double

Mulimea valorilor

Reprezentarea intern
Lungime (bii)
Formatul intern

ntregii din [-215; 215-1]


ntregii din [-215; 215-1]
ntregii din [-231; 231 - 1]
ntregii din [0; 216-1]

16
16
32
16
8
32
64

[-3.4*10-38; 3.4*1038]
[-1.7*10-308; 1.7*10308]

reprezentare binar
reprezentare binar
reprezentare binar
reprezentare binar
cod ASCII
virgul flotant simpl precizie
virgul flotant dubl precizie

Facem precizarea c se poate folosi i combinaia unsigned long pentru ntregii fr


semn n dubl precizie.
Cu tipurile de baz se pot defini i tipuri utilizator folosind instruciunea struct.

1.3.5. Constante
O constant are un tip i o valoare. Att tipul ct i valoarea unei constante se
definesc prin caracterele care compun constanta respectiv.
Constant ntreag zecimal
O constant ntreag este un ir de cifre care eventual este prefixat de un semn (+ sau -) i
postfixat de litera L sau l. O constant ntreag se reprezint n binar pe 16 sau 32 de bii.
Sufixul L sau l foreaz reprezentarea pe 32 de bii.
Constant ntreag octal
O constant ntreag octal este un ir de cifre octale (cifre cuprinse ntre 0 i 7) precedat de
un zero nesemnificativ.

Constant ntreag hexazecimal


O constant ntreag hexazecimal este un ir de cifre hexazecimale prefixat cu 0x sau 0X.
13

Exemple:
Reprezentare
12345
-12345
12345L
012345
0xabcd
12345678

Interpretare
ntreg zecimal reprezentat n binar pe 16 bii
ntreg zecimal reprezentat n binar pe 16 bii
ntreg zecimal reprezentat n binar pe 32 bii
ntreg octal reprezentat n binar pe 16 bii (o cifr octal pe 3 bii)
ntreg hexa reprezentat n binar pe 16 bii (o cifr pe 4 bii)
ntreg zecimal reprezentat pe 32 de bii

Constant flotant
O constant flotant este un numr raional care se compune din urmtoarele elemente:
- un semn (+ sau -) care poate lipsi pentru numerele pozitive;
- o parte ntreag care poate fi i vid;
- o parte fracionar care poate fi i vid;
- un exponent care poate fi i vid.
Prezena prii fracionare este suficient pentru ca numrul respectiv s reprezinte o
constant flotant. Absena prii fracionare implic prezena prii ntregi i a exponentului.
Partea ntreag reprezint o succesiune de cifre zecimale. Partea fracionar se
compune din caracterul punct urmat de o succesiune de cifre zecimale, care poate fi i vid.
Exponentul se compune din litera e sau E urmat de un + sau -, opional, i un ir de cifre
zecimale.
Constantele flotante se pstreaz n format flotant dubl precizie.
Exemple:
3.14; 0.314e1; 3.1415926; 2.71828; 2.; 271828E-5.
Constant caracter
O constant caracter reprezint un caracter i are ca valoare codul ASCII al
caracterului respectiv. O constant caracter grafic se poate scrie incluznd caracterul
respectiv ntre caractere apostrof.
Exemple:
Constant caracter
A
B
a
0
9
*

valoare cod ASCII


65
66
97
48
58
77

Anumite caractere negrafice au notaii speciale la scrierea crora se utilizeaz


caracterul backslash (\). Dm n continuare un tabel cu cele mai folosite caractere negrafice:

Caracter

Reprezentare ca i constant caracter


14

Valoare cod ASCII

Backspace
Retur de car
Newline
Apostrof
Backslash
Tabulator vertical
Salt pagin imprimant
Carcterul NUL

\b
\r
\n
\
\\
\v
\f
\0

8
13
10
39
92
11
12
0

Constant ir de caractere
O constant ir de caractere este o succesiune de zero sau mai multe caractere delimitate
prin ghilimele. Ghilimelele nu fac parte din irul de caractere.
Exemple:
123; Limbajul C; sir de caractere; sir\; (irul vid).
Constanta ir de caractere se reprezint n memoria calculatorului printr-o
succesiune de octei n care se pstreaz codurile ASCII ale caracterelor irului, iar ultimul
octet conine caracterul NUL care marcheaz sfritul irului de caractere.
Dup cum se observ din exemplele date putem s folosim i convenia cu backslash.
De reinut c X i X reprezint construcii diferite.

1.3.6. Variabile simple


Prin variabil nelegem o dat a crei valoare se poate schimba n timpul execuiei
programului care o conine. Unei variabile i se ataeaz un nume prin intermediul cruia o
putem referi sau modifica. Totodat valorile pe care le poate lua o variabil trebuie s aparin
aceluiai tip. Corespondena dintre numele i tipul unei variabile se realizeaz printr-o
construcie special numit declaraie.
Dup modul de grupare datele sunt:
- date izolate denumite i variabile simple;
- date grupate:
- mulimi ordonate de date de acelai tip la care ne referim cu indici,
- structuri n care date de tipuri diferite se grupeaz.
Declaraia de variabil simpl are formatul general:
tip list_de_nume;
unde list_de_nume este format fie dintr-un singur nume fie din mai multe, separate prin
virgule.
n limbajul C trebuie declarate toate variabilele nainte de a fi utilizate.
Exemple:
int a,b,c;
float x,y,z;
char car;
long i,j,k;

//
//
//
//

a, b, c sunt variabile de tip int reprezentate in binar pe 2 octei;


x, y, z sunt variabile de tip float in format flotant simpla precizie pe 32 de bii;
car este o variabila de tip char pe 1 octet, poate conine coduri ASCII;
i, j, k sunt variabile de tip long in format binar pe 32 de bii.

1.3.7. Tablouri
Un tablou ca orice variabil simpl trebuie declarat nainte de a fi utilizat. Dac
referirea la elementele tabloului se face cu un singur indice se spune c tabloul este
15

unidimensional (se mai numete vector); dac referirea se face cu doi indici tabloul se
numete bidimensional (matrice); iar cu n indici tabloul este n-dimensional.
Declaraia unui tablou n forma cea mai simpl este:
tip nume[l1][l2]...[ln];
unde:
l1, l2, ... ln sunt expresii constante care au valori ntregi ce pot fi evaluate de compilator
la ntlnirea lor.
Evident c se pot declara mai multe tablouri deodat i atunci numele de tablouri se
pot nirui ntr-o list, fiecare separat prin virgul.
Exemple:
int t[5];
float a[5][3];

// s-a declarat un tablou unidimensional de 5 componente;


// s-a declarat un tablou bidimensional de 5*3=15 componente;

La elementele unui tablou ne referim prin variabile cu indici. O variabil cu indici se


compune din numele tabloului urmat de unul sau mai muli indici, fiecare indice fiind inclus
n paranteze drepte. Numrul indicilor definete dimensiunea tabloului. Indicii sunt expresii
care au valori ntregi. Limita inferioar a indicilor este zero. Astfel dac t este tabloul de tip
int n exemplul de mai sus, elementele lui sunt:
t[0], t[1], t[2], t[3], t[4].
n cazul matricii a declarate mai sus elementele ei vor fi:
prima linie:
a[0][0], a[0][1], a[0][2];
a doua linie: a[1][0], a[1][1], a[1][2];
...
a cincea linie: a[4][0], a[4][1], a[4][2];
Deci pentru dimensiunea k indicele variaz ntre 0 i lk-1.
Repartizarea memoriei pentru un tablou se face astfel: pentru fiecare element al
tabloului se repartizeaz o zon de memorie necesar n conformitate cu tipul tabloului
respectiv (pentru tipul int cte 2 octei, pentru tipul float cte 4 octei, etc). Numele unui
tablou este un pointer, el poate fi utilizat n diferite construcii i el are ca valoare adresa
primului su element.
n desenul urmtor se indic repartizarea memoriei pentru tabloul t din exemplul de
mai nainte:
t
adresa lui
t[0]
t[0]

t[1]

t[2]

t[3]

t[4]

1.3.8. Comentariu
n limbajul C un comentariu se scrie ntre /* i */ pe oricte rnduri. ntre /* i */ se
poate scrie o succesiune arbitrar de caractere, care ns nu poate s conin secvena de
terminare (adic */). Comentariul poate fi inserat ntr-un program n orice poziie n care este
legal s apar un caracter alb. El constituie o explicaie pentru programator sau pentru
16

utilizator. Compilatorul nu interpreteaz comentariul n nici un mod. Comentariul se


recomand a fi inserat n punctele n care programatorul poate lmuri prin explicaii, anumite
aspecte ale procesului de calcul sau ale datelor utilizate. n mediile BORLAND C exist i o
alt convenie de a marca un comentariu la nivelul unui rnd cu ajutorul a dou caractere slash
(//), convenie pe care am folosit-o de altfel n exemplele anterioare.

1.4. Preprocesare
Un program surs C poate fi prelucrat nainte de a fi compilat. O astfel de prelucrare
se numete preprocesare sau precompilare. Acest lucru se realizeaz printr-un program
special numit preprocesor. Acesta este apelat automat nainte de a ncepe compilarea.
Preprocesorul limbajului C realizeaz urmtoarele:
- includeri de alte fiiere (de obicei fiiere surs);
- definiii i apeluri de macrouri simple;
- compilare condiionat.

1.4.1. Includerea unui fiier surs


Fiierele surs pot fi incluse cu ajutorul directivei include.
Dou formate se folosesc pentru a include un fiier surs n locul n care apare directiva (de
obicei se pune la nceputul programului):
#include specificator_de_fisier
sau
#include <specificator_de_fisier>
unde: specificator_de_fiier trebuie s fie un nume de fiier valid din punct de vedere al
sistemului de operare DOS i de obicei are extensia .h sau .c.
Prima variant este folosit de obicei cnd fiierele de inclus sunt furnizate de
utilizator; dac nu este indicat calea atunci fiierele sunt cutate n directorul curent i apoi n
directoarele setate pentru directiva include.
A doua variant este folosit pentru ncorporarea unui fiier standard care se caut n
directoarele setate pentru directiva include (de obicei astfel de fiiere standard sunt livrate n
biblioteci ataate mediului de programare C).
Odat localizat fiierul dintr-o directiv include se nlocuiete aceasta prin textul
fiierului surs. Deci compilatorul nu va mai ntlni directiva include ci textul fiierului inclus
de preprocesor.
Includerile de fiiere se fac de obicei la nceput pentru ca textele fiierelor surs (date
i funcii) s poat fi utilizate n tot fiierul surs de lucru. De aceea, la nceputul fiierelor
surs vom ntlni mai multe includeri de fiiere standard: stdio.h, stdlib.h, etc. Textul unui
fiier inclus poate la rndul su s conin directiva include. Fiierul stdio.h (prescurtarea de
la standard input output header) conine printre altele i funciile standard de intrare-ieire
printf i scanf. Fiierele cu extensia .h se mai numesc i fiiere header (fiiere care se pun
la nceputul fiierului surs). Un alt exemplu de fiier header este iostream.h folosit n mediul
BORLAND C++ care conine funciile cin (console input) i cout (console output).

17

1.4.2. Constante simbolice


Cu directiva define se pot defini constante simbolice i macrouri. Constantele
simbolice se definesc astfel:
#define nume succesiune_de_caractere
Preprocesorul substituie nume cu succesiune_de_caractere peste tot n fiierul surs
care urmeaz poziiei directivei define. Dac succesiune_de_caractere nu ncape pe un rnd
atunci se poate continua pe rndul urmtor scriind caracterul \ la sfritul primului rnd.
Numele nume definit ca mai sus se spune c este o constant simbolic. Se
recomand ca nume s fie scris cu litere majuscule pentru a scoate n eviden c este o
constant simbolic.
Construcia succesiune_de_caractere folosit pentru a defini o constant simbolic
poate la rndul ei s conin alte constante simbolice.
O constant simbolic poate fi redefinit (tot cu define) sau poate fi anihilat cu
undef (#undef nume).
Exemple:
1)

#define PROCENT 10
...
#define PROCENT 15
...
#undef PROCENT

// din acest punct al fisierului sursa se substituie


// PROCENT cu 10
// de aici PROCENT se substituie cu 15
// din acest punct constanta simbolica PROCENT
// isi inceteaza existenta

...
2)

#define DIM 100


#define DOI_PI (2*3.1415926)
...
int vector[DIM];
...
x=DOI_PI;
...

// s-au definit doua constante simbolice DIM


// si DOI_PI
// DIM va fi inlocuit cu 100

18

2. Clase de variabile (de memorie)


Compilatorul C aloc memorie variabilelor din program de dimensiune
corespunztoare tipului fiecreia.
Memoria se aloc n 2 moduri:
- static, repartizat ntr-o zon special asociat programului;
- dinamic, repartizat ntr-o zon special numit stiv (se comport ca o list LIFO).
n funcie de modul cum se aloc memorie, vom distinge mai multe clase de variabile.
O prim clas de variabile este aceea a variabilelor globale crora li se aloc memorie pe
toat durata execuiei programului i ele pot fi utilizate n orice funcie a programului. Alt
clas de variabile este clasa variabilelor locale, aceste variabile au o utilizare local la nivelul
unei funcii.

2.1. Variabile globale


O variabil global are o definiie i attea declaraii de variabil extern cte sunt
necesare.
Definiia unei variabile globale coincide sintactic cu o declaraie obinuit, dar care
este scris n afara oricrei funcii a programului. Implicit, definiia unei variabile globale
determin ca variabila respectiv s fie definit ncepnd din punctul scrierii ei i pn la
sfritul fiierului surs respectiv. De aceea se recomand ca definiiile variabilelor globale s
fie scrise la nceputul fiierului surs, pentru a fi accesate n orice funcie a fiierului.
Pentru a utiliza variabilele globale i n alte funcii situate n alte fiiere surs dect n
cel n care sunt definite, ele trebuie declarate ca externe n funciile respective.
O declaraie de variabil extern coincide cu o declaraie obinuit doar c ncepe cu cuvntul
cheie extern.
Exemplu:
fiierul n care sunt declarate ca variabile globale

fiierul n care sunt folosite ca variabile externe

int i;
float f;
void main(void)
{ i = 10;
...
f = 3.14;
...
}

void functie(. . .)
{ extern int i;
extern double f;
...
f = f*i;
...
}

Variabilele i i f sunt declarate n afara funciei main i n afara oricrei funcii, deci
sunt variabile globale. Ele pot fi folosite n toate funciile din fiierul surs care conine
definiiile lor. Pentru a le utiliza n funcii situate n alte fiiere surs dect n cel n care sunt
definite ele sunt declarate ca externe. Deci variabilele i i f sunt declarate ca externe n funcia
functie din al doilea fiier surs. Cele dou fiiere surs care pot fi scrise n momente i de
persoane diferite se pot asambla ntr-un singur program cu ajutorul directivei de preprocesare
include.

19

2.2. Variabile locale


Variabilele locale nu sunt valabile n tot programul. Ele au o utilizare local n dou
feluri:
- ca i variabile automatice (alocate pe stiv) la nivelul unei funcii;
- ca i variabile statice (alocate n zona programului) la nivel de fiier (eventual i la
nivelul unei funcii).
Variabilele declarate n interiorul unei funcii i care nu sunt declarate ca externe sunt
variabile locale. Lor li se aloc memorie pe stiv la intrarea n funcia n care sunt declarate.
La ieirea din funcie, stiva se reface la coninutul dinaintea apelului i variabilele locale pierd
alocarea. Deci ori de cte ori se apeleaz o funcie, variabilele locale acesteia (denumite i
variabile automatice) se aloc (primesc memorie) pe stiv i la ieirea din funcie variabilele
locale sunt terse din stiv.
Variabilele locale pot s nu fie alocate pe stiv, ci ntr-o zon de memorie destinat
acestui scop. O astfel de variabil local se numete variabil static. O variabil local
static se declar printr-o declaraie obinuit, precedat de cuvntul cheie static. O variabil
static poate fi declarat att n corpul unei funcii ct i n afara oricrei funcii. n cazul unei
variabile statice declarat n interiorul unei funcii alocarea nu se face pe stiv ci ntr-o zon
de memorie destinat acestui scop, aceasta fiind deosebirea esenial fa de variabilele
automatice. n cazul n care o variabil static este declarat n afara funciilor, ea este definit
din punctul n care a fost declarat i pn la sfritul fiierului surs care conine declaraia
ei. Spre deosebire de variabilele globale, o variabil static nu poate fi declarat ca extern i
deci nu poate fi utilizat n funciile din alte fiiere surs diferite de cel n care a fost declarat.
Se recomand ca tablourile mari s fie declarate statice, deoarece dac sunt automatice pot
depi capacitatea stivei (care are implicit o valoare de civa Koctei).
Exemple:
1) Fiierul fisier1.c este un fiier surs care conine 2 variabile globale i i d , o variabil
static x i dou funcii f i main. Funcia main conine variabila static a iar funcia f
conine variabila static b.
int i;
double d;
static int x;

// definiia variabilei globale i


// definiia variabilei globale d
// definiia variabilei statice x, locala fisierului fisier1.c

void main (void)


{ static char a;
// definiia variabilei statice a, locala funciei main
float c;
// definiia variabilei automatice c, locala funciei main
/* in acest moment se pot folosi variabilele i,d,x,a si c */
...
}
{ int p;
// definiia variabilei automatice p, locala funciei f
static float b;
// definiia variabilei statice b, locala funciei f
/* in acest moment se pot folosi variabilele i,d,x, p si b */
...
}

Variabilele a i c fiind locale funciei main nu pot fi utilizate n funcia f. Analog,


variabilele p i b sunt locale n funcia f, nu pot fi utilizate n funcia main.
20

2) Fiierul fisier2.c conine funciile f1 i f2 care intr n componena aceluiai


program ca i funciile main i f din fiierul fisier1.c
static unsigned t;
// definitia variabilei statice t, locala fisierului fisier2.c
void f1(...)
{ extern int i;
// declaratie externa pentru i
extern double d;
// declaratie externa pentru d
static int k;
// definitia variabilei statice k, locala functiei f1
/* in acest moment se pot folosi varibilele i,d, k si t */
...
}
void f2(...)
{ extern int i;
// declaratie externa pentru i
static double s;
// definitia variabilei statice s, locala functiei f2
/* se pot folosi variabilele i, s si t */
...
}

Variabila static x definit n fiierul fisier1.c nu poate fi utilizat n fiierul fisier2.c.


De asemenea, variabila static t nu poate fi utilizat n fiierul fisier1.c. Variabila global d nu
poate fi utilizat n funcia f2, ea nefiind declarat ca i extern.
Observaii:
1o. Variabilele globale constituie un mijloc simplu de interfa ntre funciile unui
program. Se recomand a fi folosite cnd dorim transferuri de valori ntre dou sau mai multe
funcii n aa fel nct modificrile realizate de o funcie s fie accesibile pentru toate funciile
programului. Nu trebuie s se fac abuz n utilizarea variabilelor globale deoarece constituie
i o surs potenial de erori, pentru c accesul unui mare numr de funcii la anumite date
globale conduce deseori la modificri nedorite i greu evideniabile.
2o. Funciile sunt considerate cu atributul implicit extern. Dac ntr-un program exist mai
multe fiiere surs atunci o funcie poate fi apelat oriunde, bine neles respectnd convenia
definirii ei sau a prototipului ei nainte de a fi folosit. Putem s limitm definind funciile cu
atributul static precednd antetul funciei prin cuvntul cheie static. Astfel funcia respectiv
devine local i deci apelabil doar n fiierul n care este definit.

2.3. Variabile registru


Limbajul C ofer posibilitatea de a aloca anumite variabile n regitri
microprocesorului. Deoarece regitri constituie un tip de memorie ultrarapid n anumite
cazuri se poate economisi att timp de execuie ct i memorie. Se pot aloca n regitri numai
parametrii funciilor i variabilele automatice de tip int, char i de tip pointer. O variabil
registru se declar n mod obinuit, precednd declaraia ei prin cuvntul rezervat register.
Alocarea ntr-un registru a unei variabile se face numai dac exist un registru
disponibil. n caz contrar, variabila registru se aloc pe stiv exact ca o variabil automatic.
Alocarea se face n ordinea declarrii variabilelor registru.
Exemplu:
void f (register int i)
{ register char c;
register int j;
...
}

Mai nti se aloc parametrul i ntr-un registru, apoi se aloc c i j n ali doi regitri.
21

Se recomand alocarea n regitri a variabilelor care au o utilizare mare, de exemplu a


indicilor de tablouri.

2.4. Iniializare
Variabilelor li se pot atribui valori iniiale. Pentru variabilele globale valorile iniiale
se atribuie prin definiiile lor, iar n cazul celorlalte variabile se pot atribui valori prin
declaraiile lor. Pentru c de multe ori am folosit cuvintele definiia variabilelor sau
declaraiile varibilelor precizm c ele au neles distinct n anumite contexte. O variabil
global se definete o singur dat i se poate declara ori de cte ori e necesar utilizarea ei
n alte fiiere (evident declarat extern). n cazul celorlalte tipuri de variabile definiiile i
declaraiile coincid. Totodat definiia i declaraia (prototipul) unei funcii nu coincid.
O variabil simpl se poate iniializa printr-o declaraie de forma:
tip nume=expresie;
Variabilelor globale i statice li se atribuie valori iniiale la lansarea programului.
Expresia utilizat n cazul acestor variabile trebuie s fie o expresie constant care s poat fi
evaluat de compilator la ntlnirea ei. Aceasta, deoarece variabilele globale i statice se
iniializeaz prin valori definite la compilare.
Variabilele automatice se iniializeaz la execuie, de fiecare dat cnd se activeaz
funcia n care sunt declarate. Din aceast cauz, nu mai este necesar ca expresia s fie o
expresie constant. Totui, la ntlnirea ei, trebuie s fie definii operanzii expresiei de
iniializare.
Exemplu:
void f(int n)
{ int i=10;
int k=i+n;
...
}

La ntlnirea expresiei i+n sunt deja definii ambii operanzi:


- i a fost n prealabil iniializat cu valoarea 10;
- n are o valoare care e transferat la apel.
Variabilele globale i statice neiniializate au implicit valoarea egal cu zero, iar
varibilele automatice neiniializate au o valoare iniial imprevizibil.
Tablourile se pot iniializa printr-o list de expresii incluse ntre acolade. Astfel un
tablou unidimensional se poate iniializa folosind urmtorul format:
tip nume[n] = { exp1, exp2, . . . expn }
La iniializarea tablourilor se pot utiliza numai expresii constante. Numrul expresiilor
poate fi mai mic dect numrul elementelor tabloului. Elementele neiniializate au valoarea
zero n cazul tablourilor globale i statice. Dac se iniializeaz fiecare element al tabloului
atunci numrul n al elementelor acestuia nu mai este obligatoriu n declaraia tabloului
respectiv. Deci se poate scrie astfel:
tip nume[ ] = { exp1, exp2, . . . expn}

22

Numrul elementelor tabloului se consider c este egal cu cel al expresiilor care


realizeaz iniializarea lor.
Pentru un tablou bidimensional vom folosi urmtoarea structur:
tip nume [n][m] = { {exp11, exp12, . . . exp1m}, // pentru linia nti
{exp21, exp22, . . . exp2m}, // pentru linia a doua
...
{expn1, expn2, . . . expnm}, // pentru ultima linie
}
Numrul expresiilor poate fi mai mic dect m n oricare din acoladele corespunztoare
celor n linii ale tabloului bidimensional. n acest caz se poate omite doar numrul n (al liniilor
tablourilor), m fiind obligatoriu. Formatul de iniializare a tablourilor bidimensionale se
extinde imediat pentru tablouri cu mai multe dimensiuni.
Tablourile de tip caracter pot fi iniializate folosind un format mai simplu dect cel
indicat anterior. Astfel, un tablou de tip caracter se poate iniializa printr-o declaraie de
forma:
char sir[n] = ir_de_caractere;
Evident, marginea superioar n poate fi omis i n acest caz. Totodat compilatorul
pune automat caracterul NUL dup ultimul caracter al irului utilizat n iniializare.
Exemple:
1) int itab[] = {1,2,3,4,5}

// tabloul de tip intreg are 5 elemente itab[0] = 1,. . . itab[4] = 5

2) int m[3][3] = {{-1,0,1},{-1},{0,1}};

// tabloul are 3 linii si 3 coloane.

Elementele primei linii sunt iniializate astfel:


m[0][0] = 1;
m[0][1] = 0;
m[0][2] = 1;
n linia a doua se iniializeaz numai m[1][0] = -1; Dac tabloul ar fi declarat global sau static
atunci m[1][1] i m[1][2] ar avea valoarea zero. Altfel ele au o valoare imprevizibil.
Elementele ultimei linii se iniializeaz astfel:
m[2][0] = 0;
m[2][1] = 1;
3) declaraiile de mai jos sunt identice:
char sir [ ] = {L,I,M,B,A,J,U,L, ,C};
char sir [ ] = {LIMBAJUL C};

23

3. Expresii, operanzi, operatori


3.1. Expresii
O expresie n limbajul C este format fie dintr-un operand fie din mai muli operanzi
legai ntre ei prin operatori. O expresie are o valoare i un tip care se determin aplicnd
operatorii conform prioritilor i asociativitii acestora.
n limbajul C operatorii se asociaz de la stnga la dreapta, exceptnd operatorii unari
i de atribuire, care se asociaz de la dreapta la stnga.. Totodat pot fi folosite parantezele
rotunde pentru a impune o anumit ordine n executarea operaiilor.

3.2. Operanzi
Un operand n limbajul C poate fi una din urmtoarele elemente:
- o constant;
- o constant simbolic;
- numele unei variabile simple;
- numele unui tablou;
- numele unei structuri;
- numele unei funcii;
- referirea la elementul unui tablou (variabil cu indici);
- referirea la elementul unei structuri;
- apelul unei funcii;
- o expresie inclus n paranteze rotunde.
Unele dintre elementele de mai sus nu au fost nc definite, ele se vor prezenta n leciile
viitoare.
Exemple:
9876
- constant ntreag;
x
- variabil simpl;
t[i][3]
- variabil cu indici;
0xabcd
- constant hexazecimal;
t
- nume de tablou;
(expresie)
- expresie inclus n paranteze rotunde;
f1
- numele unei funcii.

3.3. Operatori
Operatorii limbajului C pot fi grupai n mai multe clase, dar oricum ei pot fi folosii
mpreun ntr-o aceeai expresie. Operatorii au ariti diferite: unari, binari, ternari i totodat
o anumit prioritate implicit care e redat n tabelul de mai jos. Operatorii de aceeai
prioritate se afl trecui n aceeai linie. Liniile tabelulul conin operatorii limbajului C n
ordinea descresctoare a prioritilor. Astfel n prima linie se afl operatorii de prioritate
maxim, iar n ultima linie operatorul virgul cu prioritatea cea mai mic. Cu excepia
operatorilor ., ->,&,*, a parantezelor rotunde (folosite la definiia i apelul funciilor)
i a parantezelor drepte (folosite la variabilele cu indici) ceilali operatori vor fi explicai n
aceast lecie.
( ) [ ] . ->
24

- (unar) +(unar) *(unar) &(unar) ! ~ ++ -- (tip) sizeof


* / %
+ << >>
< <= >= >
= = !=
&
^
|
&&
||
? : (ternar)
= op= op poate fi: *(binar) / % +(binar) (binar) << >> & ^ |
,

3.3.1. Operatori aritmetici


Lista operatorilor aritmetici este redat mai jos:
- (minus unar);
+ (plus unar);
* / % operatori binari multiplicativi; (nmulire, mprire, restul mpririi ntregi);
+ - operatori binari aditivi (adunare i scdere).
Operatorii de pe aceeai linie au aceeai prioritate. Cei unari au prioritate mai mare
dect cei binari. Operatorii multiplicativi au prioritate mai mare dect cei aditivi.
Exemple:
int i,j,k;
float x,y;
double t[10];
// se dau cateva exemple de expresii folosind operatorii aritmetici
i*x+t[5];
-y+k;
i%j;
i/j;
x*-y;

// daca i=9 si j=4 atunci i%j are valoarea 1


// daca i=9 si j=4 atunci i/j are valoarea 2
// - este operatorul unar deci avem x*(-y)

3.3.2. Operatori relaionali


Lista operatorilor relaionali este redat astfel:
<
(mai mic)
<=
(mai mic sau egal; cele dou caractere ce compun operatorul sunt concatenate)
>
(mai mare)
>=
(mai mare sau egal; cele dou caractere ce compun operatorul sunt
concatenate)
Toi operatorii relaionali au aceeai prioritate. Ea este mai mic dect prioritatea
operatorilor aditivi. Rezultatul aplicrii unui operator relaional este 1 sau 0, dup cum
operanzii se afl n relaia definit de operatorul respectiv sau nu.
Exemple:
25

a= 4 i b= -5
atunci
a>0
a<=0
a+b>0
a>=b
a<0
a+b>=b-a
a+b>=(b-a)*(b-a)

are valoarea 1;
are valoarea 0;
are valoarea 0;
are valoarea 1;
are valoarea 0;
are valoarea 1;
are valoarea 0.

3.3.3. Operatori de egalitate


Lista operatorilor de egalitate este redat mai jos:
= = (egal; dou semne = concatenate)
!= (diferit; semnele sunt concatenate).
Operatorii de egalitate au ambii aceeai prioritate i este imediat mai mic dect a
operatorilor relaionali. Operatorul = = testeaz egalitatea a doi operanzi. Dac operanzii
sunt egali atunci rezultatul operaiei = = este 1, n caz contrar este 0. Operatorul !=
furnizeaz rezultatul 1 cnd cei doi operanzi sunt diferii i 0 cnd sunt egali.
Exemple:
a= 2 i b=-1
atunci
a= =b
a!=b
a*b!=a+b

are valoarea 0;
are valoarea 1;
are valoarea 1.

3.3.4. Operatori logici


Lista operatorilor logici este redat mai jos:
!
(negaia logic - operator unar);
&& (I logic);
||
(SAU logic).
Operatorul ! are aceeai prioritate cu operatorii unari + i -. Operatorul &&
este mai prioritar dect operatorul ||, dar are o prioritate mai mic dect operatorii de
egalitate.
n limbajul C nu exist valori logice speciale. Valoarea fals se reprezint prin zero.
Orice valoare diferit de zero reprezint valoarea adevrat.
Dac operatorul ! se aplic la un operand a crui valoare este zero, atunci rezultatul
este 1. Dac acelai operator se aplic la un operand a crui valoare este diferit de zero,
atunci rezultatul este 0.
Dm n continuare tabelele operatorilor logici binari aplicate valorilor 0 i 1.

&& 0 1
0
0 0
1
0 1

|| 0 1
0 0 1
1 1 1

sau exclusiv 0 1
0
0 1
1
1 0
26

Chiar dac pentru sau exclusiv nu exist operator el se poate realiza prin expresia
urmtoare aplicat operanzilor a i b: !a&&b||!b&&a sau folosind parantezele rotunde ((!a)
&&b)||((!b)&&a).
Operatorii logici se evalueaz de la stnga la dreapta. Dac la evaluarea unei expresii
se ajunge ntr-un punct n care se cunoate valoarea ntregii expresii, atunci restul expresiei nu
se mai evalueaz.
Dac a=0 i b=1 atunci expresia ! a||b are valoarea 1 pentru c !a are deja valoarea 1.

3.3.5. Operatori logici pe bii


Lista operatorilor logici pe bii este redat mai jos n ordinea descrectoare a prioritilor:
~
(operator unar; complement fa de 1)
>> <<
(deplasri la dreapta, respectiv la stnga)
&
(I pe bii)
^
(SAU-EXCLUSIV pe bii)
|
(SAU pe bii)
Operatorul ~, fiind unar, are aceeai prioritate ca i ceilali operatori unari ai
limbajului C. El schimb fiecare bit 1 al operandului n 0 i invers.
Operatorul >> realizeaz deplasarea la dreapta care este echivalent cu o mprire
ntreag cu puteri a lui 2; a >> 3 este echivalent cu [a/23].
Operatorul << realizeaz deplasarea la stnga care este echivalent cu o nmulire
cu puteri a lui 2; a << 3 este echivalent cu a*8.
Pentru operatorii &, |, ^ dm n continuare tabelele operaiilor:
&
0
1

0 1
0 0
0 1

| 0 1
0 0 1
1 1 1

^
0
1

0 1
0 1
1 0

Observaii:
1o. Operanzii care nu ocup un cuvnt (16 bii) se extind la un cuvnt. De exemplu expresia
~0 are ca rezultat un cuvnt cu toi bii egali cu 1.
2o. Operatorii logici pe bii se execut bit cu bit spre deosebire de operatorii logici care se
evalueaz global. De exemplu dac x=2 i y=1 sunt variabile de tipul int atunci:
x&&y
are valoarea 1 pentru c ambii operanzi sunt diferii de 0.
x&y
are valoarea 0 conform schemei de mai jos
x=
0000 0000 0000 0010
y=
0000 0000 0000 0001
x&y= 0000 0000 0000 0000
3o. Operatorul & se folosete frecvent pentru a anula bii din configuraia unui cuvnt, iar
operatorul | pentru a seta (pune) bii ntr-un anumit mod.
4o. Operanzii trebuie s fie ntregi (de tipul int sau long).
5o. Atenie la deplasri nu se modific valoarea operandului; deci trebuie s facem o atribuire;
de exemplu a = a << 3 va modifica valoarea lui a pe cnd a << 3 nu modific valoarea lui a.
Exemple:
1) Fie declaraia:
27

int i;
atunci expresia i >> 8 & 255 are ca rezultat valoarea celui mai semnificativ octet a lui i; i
>> 8 deplaseaz octetul mai semnificativ al lui i n poziia mai puin semnificativ; se face
apoi un I logic pe bii cu masca 255 care pstreaz octetul mai puin semnificativ.
2) Fie expresia: (x >> 6) & ~(~ 0 << 3)
S presupunem c x are valoarea n bii: 1010 1011 1000 1101.
Atunci x>>6 are valoarea:
1111 1110 1010 1110
Al doilea operand pregtete o masc astfel:
~0
~0<<3
~(~0<<3)

1111 1111 1111 1111


1111 1111 1111 1000
0000 0000 0000 0110

Rezultatul final este dat de:


0000 0000 0000 0111
1111 1110 1010 1110
0000 0000 0000 0110
Practic s-a obinut valoarea biilor 8,7,6 a lui x (numerotai de la dreapta ncepnd cu 0).

3.3.6. Operatori de atribuire


n forma cea mai simpl operatorul de atribuire se noteaz cu = i se utilizeaz n
construcii de forma:
v=expresie;
(v este fie o variabil simpl, fie variabil cu indici sau un element de structur).
Aceast construcie se mai numete expresie de atribuire. Ea este considerat ca
fiind un caz particular de expresie. Tipul ei coincide cu tipul lui v, iar valoarea ntregii
expresii este chiar valoarea atribuit lui v.
O expresie de forma:
v1=(v=expresie);
este i ea legal i se efectueaz n felul urmtor :
- se evalueaz expresia expresie i valoarea ei se atribuie lui v;
- valoarea lui v se atribuie apoi i lui v1.
Deoarece operatorii de atribuire se asociaz de la dreapta la stnga, expresia de mai
sus se poate scrie i fr paranteze:
v1=v=expresie;
n general, putem realiza atribuiri multiple printr-o expresie de forma:
vn =. . . =v1=v=expresie
Dac expresia din dreapta semnului egal are un tip diferit de cel al variabilei v, atunci nti
valoarea ei se convertete spre tipul variabilei v i pe urm se realizeaz atribuirea.
Pentru operaia de atribuire, n afara semnului egal se mai poate folosi i succesiunea :
28

op=
unde prin op se nelege unul din operatorii binari aritmetici sau logici pe bii, adic unul din
urmtorii:
% / * - + & ^ | << >>
Acest mod de construcie se folosete pentru a compacta un anumit tip de atribuire. Astfel
expresia:
v op = expresie;
este identic cu expresia de atribuire:
v = v op expresie;
Exemple:
int i, j;
double x, y;
int v[10];
i=5;
j=10;
x=y=10.01;
i +=1;
x*=3;
j<<=10;
v[i]*=i
x /= x-y

//
//
//
//
//

echivalenta
echivalenta
echivalenta
echivalenta
echivalenta

cu i=i+1 si cu i++
cu x=x*3
cu j=j<<10
cu v[i]=v[i]*i
cu x = x/(x-y)

3.3.7. Operatori de incrementare i decrementare


Aceti operatori sunt unari i au aceeai prioritate cu ceilali operatori unari ai
limbajului C. Operatorul de incrementare se noteaz prin ++ i mrete valoarea
operandului cu unu, iar operatorul de decrementare se noteaz prin - - i micoreaz
valoarea operandului cu unu. Operatorii sunt folosii prefixat i postfixat. Astfel operatorii
prefixai au notaia:
++operand;
- - operand;
Ei se aplic mai nti i apoi se folosete valoarea operanzilor.
Astfel operatorii postfixai au notaia:
operand++;
operand - -;

Se folosete valoarea operanzilor i apoi se aplic incrementarea sau decrementarea.


Menionm c aceti operatori se pot aplica numai la urmtorii operanzi:
- variabil simpl;
- variabil cu indici;
- referire la elementul unei structuri.
Exemple:
int i,j;

29

double x,y;
int vector [5];
j=i++;
y=--x;
i=++vector[j]

// este echivalent cu j=i si i=i+1;


// este echivalent cu x=x-1 si y=x;
// este echivalent cu vector[j]=vector[j]+1 si i=vector[j]

3.3.8. Operatorul de conversie explicit (expresie cast)


Pentru forarea tipului unui operand se folosete o construcie de forma:
(tip) operand
Prin aceasta valoarea operandului se convertete spre tipul indicat n paranteze.
Exemplu:
int i,j;
double y;
i=8; j=5;
y=i/j;

// y are valoarea 1, pentru ca se face impartirea intreaga i/j

Dac vom converti operanzii i i j spre tipul double se va obine rezultatul corect adic 1.6.
Deci:
int i,j;
double y;
i=8; j=5;
y=(double) i / (double) j;

// y are valoarea 1.6,

Construcia (tip) este un operator unar prin care se expliciteaz conversia dorit. Are aceeai
prioritate ca restul operatorilor unari.

3.3.9. Operatorul dimensiune (sizeof)


Pentru a determina lungimea n octei a unei date se poate folosi construcia:
sizeof (data)
unde data poate fi:
- numele unei variabile simple;
- numele unui tablou;
- numele unei structuri;
- numele unui tip;
- referirea la elementul unui tablou sau structur.

Exemple:
int i;
long l;
float f;
double d;
char c;
int itablou[5];
double dtablou[5];
sizeof (i)
sizeof (l)

// are valoarea 2;
// are valoarea 4;

30

sizeof (f)
sizeof (d)
sizeof (c)
sizeof (itablou[1])
sizeof (dtablou[1])
sizeof (itablou)
sizeof (dtablou)

//
//
//
//
//
//
//

are valoarea 4;
are valoarea 8;
are valoarea 1;
are valoarea 2;
are valoarea 8;
are valoarea 10;
are valoarea 40.

3.3.10. Regula conversiilor implicite


n general o expresie C conine operanzi de tipuri diferite. Pentru operatorii binari
exist situaii cnd operanzii nu sunt de acelai tip i trebuie executate conversii astfel nct
operatorii s se aplice pentru operanzi de acelai tip. Aceste conversii le face automat
compilatorul. Exist o regul a conversiilor implicite care are urmtorii pai:
- fiecare operand de tip char se convertete spre tipul int i fiecare operand de tipul float
se convertete spre double;
- dac unul dintre operanzi este de tip double atunci i cellalt se convertete spre tipul
double i rezultatul va avea tipul double;
- dac unul dintre operanzi este de tip long, atunci i cellalt se convertete spre tipul long
i rezultatul va avea tipul long;
- dac unul dintre operanzi este de tip unsigned, atunci i cellalt se convertete spre tipul
unsigned i rezultatul va fi de tipul unsigned;
- la acest pas se ajunge numai dac ambii operanzi sunt de tip int i deci operaia se execut
cu operanzii respectivi, iar rezultatul va fi de tip int.
Aplicnd regula de mai sus pas cu pas (la fiecare operator n momentul efecturii lui), se
ajunge n final la evaluarea ntregii expresii i prin acesta se determin tipul expresiei. Regula
conversiilor implicite nu se aplic pentru operatorul de atribuire (valoarea expresiei din partea
dreapt a semnului de atribuire se convertete spre tipul variabilei din stnga semnului egal).
Exemple:
int i, j, k;
float a, b;
double x, y;
unsigned p;
long r;
char c;

expresii

conversii

i-j/k
a/b
x+y
i+a
i-3.14

nu
a spre double; b spre double
nu
a spre double; i spre double
i spre double

expresii

conversii

i+3
i+x
i-c
x+10
p-10
r*5
(double)(i/j)

nu
i spre double
c spre int
10 spre double
10 spre unsigned
5 spre long
se realizeaz mprirea ntreag ntre
i i j i rezultatul se convertete spre double

31

tipul expresiei
int
double
double
double
double
tipul expresiei
int
double
int
double
unsigned
long
double

Dac rezultatul unei operaii depete domeniul de valori ce corespunde tipului


rezultatului, valoarea respectiv se trunchiaz i rezultatul este eronat.

3.3.11. Operatori condiionali


Operatorii condiionali sunt ? i : i se folosesc mpreun n construcii de forma:
exp1 ? exp2 : exp3
Evaluarea se face astfel:
se evalueaz expresia exp1;
- dac exp1 este diferit de zero, atunci valoarea i tipul expresiei condiionale sunt egale cu
valoarea i tipul expresiei exp2; altfel cu expresia exp3.
Exemplu: procesul de determinare a maximului a dou numere a i b este:
dac a>b
atunci max=a
altfel max=b
sfdac
n limbajul C se poate realiza acest proces cu ajutorul operatorilor condiionali astfel:
max= a>b ? a : b
Dac a>b atunci expresia condiional are valoarea i tipul lui a altfel expresia condiional
are valoarea i tipul lui b.

3.3.12. Operatorul virgul


Operatorul , este folosit pentru gruparea mai multor expresii ntr-una singur.
Cu ajutorul acestui operator (care are prioritatea cea mai mic) se construiesc expresii de
forma:
exp1, exp2,. . ., expn
Aceast expresie are valoarea i tipul ultimei expresii (deci a lui expn).
Exemplu:

k= (i=10, j=j+5; i+j)

Se execut pe rnd cele dou atribuiri de la stnga la dreapta din parantezele rotunde apoi se
face suma i+j i n final se atribuie aceast sum lui k.

32

4. Intrri / ieiri standard


Limbajul C nu posed instruciuni de intrare / ieire. Aceste operaii se realizeaz prin
apeluri de funcii din bibliotecile standard ale mediului de programare. De obicei astfel de
funcii asigur interfaa programului cu terminalul de la care s-a lansat, cu imprimanta, etc. Se
numesc intrri standard i ieiri standard intrrile respectiv ieirile de la terminalul de la
care s-a lansat programul. Totodat se presupune c datele de intrare / ieire sunt organizate
n fiiere.
Unui program C i se ataeaz n mod implicit urmtoarele fiiere:
- stdin intrare standard;
- stdout
ieire standard;
- stderr
ieire standard pentru erori;
- stdprn
ieire pe imprimant;
- stdaux
intrare / ieire serial.

4.1. Funcia standard printf


Funcia printf realizeaz ieiri cu format la ieirea standard stdout, deci afiare la
terminalul la care care s-a lansat programul. Funcia printf se apeleaz printr-o instruciune cu
formatul:
int printf (control, lista_expresii);
unde

control este un ir de caractere care conine:


- texte de scris;
- specificatori de format pentru datele care se scriu din lista_expresii.
lista_expresii conine expresii; valoarea fiecrei expresii se scrie conform unui
specificator de format corespondent din parametrul control.
Parametrul control este inclus ntre ghilimele, iar numrul specificatorilor de format
coincide cu numrul expresiilor din lista_expresii. Dac dorim s scriem numai un text atunci
parametrul de control nu conine nici un specificator de format iar lista_expresii este vid
(practic este absent).
Un specificator de format are formatul BNF urmtor:
%[-][d..d][.d..d][l1]l2
Dup cum se observ un specificator de format ncepe ntotdeauna cu caracterul
procent (%). Dup acest caracter poate urma una din construciile urmtoare:
- un caracter - opional; prezena acestui caracter indic cadrarea la stnga a datei n
cmpul n care se scrie (implicit data se scrie cadrat la dreapta);
-un ir de cifre zecimale opional, care definete lungimea minim a cmpului n care se scrie
data corespunztoare din lista_expresii; data se scrie astfel:
- n cazul n care necesit o lungime mai mic se scrie cadrat la dreapta sau
stnga (n funcie de absena sau prezena semnului -)
- n cazul n care necesit o lungime mai mare se va scrie pe attea poziii
cte i sunt necesare;
-un punct urmat de un ir de cifre zecimale (dup cum se observ opional); acest element
indic precizia datei care se scrie:
33

dac data se scrie n virgul flotant, precizia definete numrul de cifre


aflate dup marca zecimal (deci cte zecimale);
- dac data este un ir de caractere atunci indic cte caractere se scriu.
-una sau dou litere, care definesc tipul de conversie din formatul intern n formatul extern:
- prima litera poate fi l, ceea ce semnific conversia din formatul intern
long n formatul extern definit de specificator;
- a doua liter este obligatorie ntotdeauna i are semnificaiile de mai jos:
litera
d
o
x
X
u
c
s
f
e

g
G

tipul de conversie realizat


- din int intern n zecimal extern
- din int intern n octal extern
- din int intern n hexazecimal extern (litere mici pentru
cifrele mai mari ca 9 deci a,b,c,d,e,f,)
- din int intern n hexazecimal extern (litere mici pentru
cifrele mai mari ca 9 deci A,B,C,D,E,F)
- din unsigned intern n zecimal extern fr semn
- din binar intern (cod ASCII) n caracterul corespunztor
- din ir de coduri ASCII ntr-un ir de caractere (atenie
ultimul cod este NUL (adic \0)
- din float sau double intern n d...d.d...d (implicit 6 cifre
zecimale la partea fracionar dac nu e prezent precizia)
- din float sau double intern n d.d...deddd (implicit 6
cifre zecimale la partea fracionar dac nu e prezent
precizia)
- din float sau double intern n d.d...dEddd (implicit 6
cifre zecimale la partea fracionar dac nu e prezent
precizia)
- se aplic una din conversiile definite de literele f i e
alegndu-se aceea care are un numr minim de poziii
- se aplic una din conversiile definite de literele f i E
alegndu-se aceea care are un numr minim de poziii

Literele d, o, x, u pot fi precedate de litera l conversia realizndu-se din formatul


intern long n loc de int.
Observaie.
1o. Dac caracterul % nu este urmat de una din construciile de mai sus atunci nu reprezint
un specificator de format.
Funcia printf ntoarce lungimea total n octei a datelor scrise la terminal sau
valoarea simbolic EOF n caz de eroare. Precizm c EOF este definit n fiierul header
stdio.h astfel:
#define EOF 1.
Totodat funcia printf poate fi testat i astfel:
EOF = = printf (control, lista_expresii)
Dac are valoarea adevrat atunci la scrierea datelor s-a produs o eroare.
Exemple:
1)
#include<stdio.h>
#include<math.h>

34

void main(void)
{ int i=10; long j=11;
float a=1.2, b=1.3;
double A=1.4; B=1.5;
clrscr();
// inceputul instructiunilor executabile; se sterge ecranul
printf ("\ni*j = %d",i*j);
// incep afisarile
printf ("\ni*j = %5d",i*j);
printf ("\ni*j = %-5d",i*j);
printf ("\ni*j = %5.5d",i*j);
printf ("\ni*j = %05d",i*j);
printf ("\na*b = %10.1f",a*b);
printf ("\nA*B = %10.5lf",A*B);
printf ("\nradical(a*b) = %lf",sqrt((double) a*b));
printf ("\nradical(A*B) = %15.10lf",sqrt(A*B));
printf ("\nradical(A*B) = %25.17lf",sqrt(A*B));
printf ("\nradical(A*B) = %25.19lf",sqrt(A*B));
getche();
// asteapta citirea unui caracter de la terminal
}
Rezultatele execuiei programului sunt:
i*j = 110
i*j = 110
i*j = 110
i*j = 00110
i*j = 00110
a*b =
1.6
A*B = 2.10000
radical(a*b) = 1.249000
radical(A*B) = 1.4491376746
radical(A*B) =
1.44913767461894372
radical(A*B) = 1.4491376746189437200
2)
#define sir PC WORLD
void main (void)
{ printf(*%10s*,sir);
printf(*%-10s*,sir);
printf(*%10.5s*,sir);
printf(*%-10.5s*,sir);
}

Rezultatele execuiei programului sunt:


* PC WORLD*
*PC WORLD *
*
PC WO*
*PC WO
*

4.2. Funcia standard scanf


Funcia de bibliotec scanf realizeaz intrri cu format de la intrarea standard stdin
(intrare de la terminalul de la care s-a lansat programul) i poate fi apelat printr-o
instruciune de apel de forma:
scanf (control, lista_adrese);
Ca i n cazul funciei printf, parametrul control este delimitat de ghilimele i poate
conine texte i specificatori de format. Caracterele albe din parametrul control sunt neglijate.
Celelalte caractere care nu fac parte dintr-un specificator de format, trebuie s existe la intrare
n poziii corespunztoare. Ele se folosesc n scopul realizrii unor verificri asupra datelor
care se citesc.
35

Un specificator de format ncepe i n acest caz prin caracterul procent, apoi:


- un caracter * opional;
- un ir de cifre, opional, care definete lungimea maxim a cmpului din
care se citete data de sub controlul specificatorului de format;
- una sau dou litere care definesc tipul conversiei din formatul extern n
formatul intern.
Cmpul controlat de un specificator de format ncepe cu primul caracter care nu este
alb i se termin:
a) fie la caracterul dup care urmeaz un caracter alb;
b) fie la caracterul dup care urmeaz un caracter care nu corespunde specificatorului
de format care controleaz acel cmp;
c) fie la caracterul prin care se ajunge la lungimea maxim indicat n specificatorul
de format.
Condiia c) este absent n definirea cmpului dac n specificatorul de format nu este indicat
lungimea maxim a cmpului.
Literele care termin un specificator de format sunt asemntoare cu cele utilizate la
funcia printf. n acest caz este realizat conversia invers fa de cea realizat de funcia
printf.
Litera
d
0
x
X
u
c

Tipul conversiei realizate

- data din cmpul de intrare este un ir de cifre zecimale, precedat eventual de un


semn i se convertete din zecimal extern n binar de tip int
- ca i n cazul literei d, dar din octal extern n binar de tip int.
- ca i n cazul literei d, dar din hexazecimal extern n binar de tip int; se utilizeaz
literele mici a-f sau mari A-F pentru cifrele mai mari ca 9.
- ca i n cazul literei x;
- data este un ir de cifre zecimale care formeaz un numr ntreg fr semn i se
convertete din zecimal extern n binar tipul unsigned.
- data se consider format din caracterul curent de la intrare i parametrului
corespunztor i se atribuie codul ASCII al acestui caracter; n acest caz nu se face
avans peste caracterele albe, ca i n cazul celorlali specificatori.
- data se consider c este ir de caractere; irul ncepe, conform regulii generale, cu
primul caracter care nu este alb i se termin la caracterul dup care urmeaz un
caracter alb sau cnd s-au citit attea caractere cte indic dimensiunea maxim din
specificatorul de format;
- data de intrare reprezint un numr flotant; deci conversie din flotant extern n virgul
flotant simpl precizie; data care se citete conine un punct sau un exponent, sau att
punct ct i exponent.

Literele d, o, x i u pot fi precedate de litera l i n acest caz conversia se realizeaz


spre long, n loc de int.
De asemenea, litera f va fi precedat de litera l pentru a face conversii spre formatul
virgul flotant dubl precizie.
Lista_adrese conine adresele zonelor receptoare ale datelor citite prin intermediul
funciei scanf. Fiecare dintre parametri reprezint adresa unei zone receptoare sub forma:
&nume
Ea determin adresa zonei de memorie rezervat variabilei nume. Caracterul & din
construcia de mai sus reprezint un operator unar, numit operatorul adres. El are aceeai
36

prioritate ca i ceilali operatori unari din limbajul C.


Exemplu:
void main (void)
{ int i;
long n;
float x;
scanf ( %d %ld %f ,&i,&n,&x);
scanf( %d %*ld %f , &i,&x);

// citeste datele din stdin si le atribuie lui i,n,x.


// caracterul * indica faptul ca valoarea pentru variabila n
// nu se citeste

Funcia scanf returneaz numrul de cmpuri citite corect din fiierul stdin. Dac
apare o eroare la citire (din diverse motive de exemplu neconcordan dintre specificatorul de
format i datele din fiierul stdin) atunci funcia nu va returna numrul total de cmpuri;
citirea se va ntrerupe n locul detectrii erorii i scanf va returna numrul de cmpuri citite
pn n acel moment. Deci de multe ori pentru a valida formal intrarea datelor (atenie nu e o
validare calitativ) se va folosi o construcie de forma:
nr=scanf(...)
Prin construcia de mai sus se poate pune n eviden sfritul de fiier, deoarece n
acest caz scanf returneaz valoarea EOF. Sfritul de fiier se poate genera de la tastatur
acionnd n acelai timp tastele CTRL i Z (deci CTRL / Z). Deorece funcia scanf citete
datele de la intrarea standard prin intermediul unei zone de memorie intermediare (numit
zon tampon sau buffer), ea are acces la caracterele din zona tampon numai dup acionarea
tastei ENTER (RETURN). De aceea dup acionarea combinaiei CTRL / Z se va tasta i
ENTER. Totodat aceast intermediere face posibil eventuale corecturi nainte de a aciona
tasta ENTER.
Exemplu:
Vom citi un ntreg de cinci cifre de la intrarea standard i vom afia cifrele respective
precedate fiecare de dou spaii.
void main (void)
{ int c1,c2,c3,c4,c5;
// c1,c2,c3,c4,c5 sunt cifrele intregului citit
scanf(%1d %1d %1d %1d %1d, &c1,&c2,&c3,&c4,&c5);
// se citesc cifrele intregului in cele 5 variabile cu %1d
printf(%3d%3d%3d%3d%3d,c1,c2,c3,c4,c5);
}

Pentru a citi iruri de caractere se va folosi funcia scanf fr a mai pune operatorul de
adres n faa numelui irului de caractere, deoarece numele unui tablou reprezint un pointer
(deci conine o adres i anume adresa primului element de tablou).
Exemplu:
Vom citi numele i prenumele unei persoane i le afim pe ecran.
#define MAX 20
void main(void)
{ char nume[MAX+1], prenume[MAX+1];
//declararea tablourilor de caractere
scanf (%20s %20s,nume, prenume);
//nu s-a mai pus operatorul de adresa
printf (\nnumele: %s, prenumele: %s,nume,prenume);
}

37

4.3. Funcia standard putchar


Funcia standard putchar se poate utiliza pentru a scrie un caracter n fiierul standard
de ieire stdout, n poziia curent a cursorului. Ea se apeleaz folosind o instruciune de apel
de forma:
putchar (expresie);
unde expresie este codul ASCII al caracterului care se scrie la ieirea standard.
Practic putchar nu este o funcie n sensul definiiei ce am dat-o n lecia 1, ci este un
macrou definit n fiierul header stdio.h, care folosete o funcie special destinat prelucrrii
fiierelor, funcia putc, astfel:
#define putchar(c) putc(c,stdout)
Exemplu:
void main (void)
{ putchar (A);
putchar (A+2)
putchar (\n);
}

// scrie caracterul A in fisierul de iesire in poziia curenta a cursorului


// scrie caracterul de cod ASCII A+2=65+2=67, adica litera C
// scrie caracterul newline; deci inceput de linie nou

4.4. Funcia standard getchar


Aceast funcie citete de la intrarea standard, stdin, caracterul curent i returneaz
codul ASCII al caracterului citit. Tipul valorii returnate este int. La ntlnirea sfritului de
fiier (CTRL/Z) se returneaz valoarea EOF (adic -1). Ca i putchar, getchar este un macrou
definit n fiierul header stdio.h, cu ajutorul unei funcii speciale, getc destinate prelucrrii
fiierelor, astfel:
#define getchar() getc(stdin)
De obicei getchar se folosete n expresii de atribuire de forma:
c=getchar();
Dup citirea caracterului curent de la intrarea standard se atribuie variabilei c codul ASCII al
caracterului citit sau EOF la ntlnirea sfritului de fiier.
Exemple:
1)
#include <stdio.h>
void main (void)
{ putchar(getchar() A + a);

// citeste o litera mare si o rescrie ca litera mica }

2) exemplul urmtor testeaz dac s-a citit o liter mare i numai n acest caz aplic
transformarea ei n liter mic. n cazul n care la intrare nu se afl o liter mare se rescrie
caracterul respectiv.
#include <stdio.h>
void main (void)
{ intc c;
putchar(((c = getchar() )>= A && c<= Z) ? c-A+a :c);
}

38

4.5. Funciile standard getche i getch


Funcia getche citete de la intrarea standard caracterul curent i returneaz codul
ASCII al caracterului citit. Aceast funcie are acces direct la caracter dup ce a fost tastat i
are forma:
getche();
Funcia getch este similar cu getche cu singura condiie c citirea se face fr ecou
(deci caracterul tastat nu se reafieaz pe terminal). De fapt prezena/absena sufixului e
precizeaz c funcia e sau nu cu ecou. Cele dou funcii fac citiri fr intermedierea zonei
tampon.

39

5. Instruciuni
5.1. Scurt istoric al metodelor de programare
Vom prezenta n continuare cteva metode de programare dar nu exhaustiv, nefiind
aici cadrul adecvat (eventual ntr-un curs de Software Engineering). O clasificare cronologic
a ceea ce vrem s prezentm ar fi urmtoarea:
a) programarea artizanal;
b) programarea procedural;
c) programarea modular;
d) programarea structurat;
e) programarea prin abstractizarea datelor;
f) programarea orientat spre obiecte.

5.1.1. Programare artizanal


Aceast metod de fapt nu este o metod propriu-zis ci este prima modalitate de programare
odat cu apariia calculatoarelor. Intuiia i experiena programatorului joac un rol important.
Fiecare programator i are propriile reguli de programare. Programele sunt monolitice (un
singur corp de instruciuni), lungi i greu de neles de alt programator. nsui cel ce a elaborat
un astfel de program ntmpin dificulti de nelegere a propriului program dup un timp
oarecare.

5.1.2. Programare procedural


Odat cu apariia primelor limbaje de nalt nivel se utilizeaz programarea
procedural. Necesitatea ca anumite secvene de program s fie folosite de mai multe ori duce
la organizarea acestora n uniti distincte de program numite n diverse limbaje
subprograme, subrutine, proceduri, etc. De multe ori procedurile trebuie s fie generale
deci procesarea s fac abstractizare de valorile datelor. De exemplu o procedur de calcul al
radicalului de ordinul 2 trebuie s calculeze acest lucru din orice numr real pozitiv iar pentru
cele negative s semnaleze eroare. Procedurile trebuie deci parametrizate cu anumite variabile
numite parametri formali. Valorile de la apel ale parametrilor formali se numesc parametri
efectivi. Programarea procedural are la baz deci utilizarea procedurilor, iar acestea
realizeaz o abstractizare prin parametri. La apelare o procedur funcioneaz dup
principiul cutiei negre (black box): se cunosc intrrile i ieirile rezultate din acestea dar nu i
modul de transformare care nu e important n acest moment.
n majoritatea limbajelor procedurale de programare se consider 2 categorii de proceduri:
- proceduri care definesc o valoare de revenire (denumite i funcii);
- proceduri care nu definesc o valoare de revenire.
n limbajele C i C++ procedurile de ambele categorii se numesc funcii.

40

5.1.3. Programare modular


Pe msur ce complexitatea aplicaiilor a crescut, a aprut ideea de a descompune problemele
n subprobleme mai simple care la rndul lor pot fi descompuse n altele mai simple i aa mai
departe. n felul acesta se ajunge la o descompunere arborescent a problemei date n
subprobleme mai simple. Programarea subproblemelor devine o problem mai simpl i
fiecare subproblem are o anumit independen fa de celelalte subprobleme. De asemenea,
interfaa ei cu celelalte subprobleme este limitat i bine precizat prin procesul de
descompunere a problemei iniiale. De obicei, programarea unei subprobleme, component a
descompunerii arborescente a problemei iniiale, conduce la realizarea unui numr relativ mic
de proceduri (funcii). Aceste funcii pot prelucra n comun anumite date. Unele dintre ele
sunt independente de funciile realizate pentru alte subprobleme componente ale
descompunerii arborescente. Altele realizeaz chiar interfaa cu subproblemele nvecinate.
Despre funciile obinute n urma programrii unei subprobleme se obinuiete s se spun c
sunt nrudite. De obicei, aceste funcii, mpreun cu datele pe care le prelucreaz, se
pstreaz ntr-un fiier i se compileaz independent.
O colecie de funcii nrudite, mpreun cu datele pe care le prelucreaz n comun formeaz un
modul. n felul acesta, problema iniial se realizeaz printr-un program alctuit din module.
Programarea modular are la baz elaborarea programelor pe module.
O parte din datele utilizate n comun de funciile modulului, sau chiar toate datele modulului,
nu sunt necesare i n alte module. Aceste date pot fi protejate sau cum se mai spune,
ascunse n modul.
Limbajul C i C++, permite ascunderea datelor n modul folosind date care au clasa de
memorie static. Mai mult, pot fi declarate i funciile ca statice i atunci ele vor fi ascunse n
modul (nu pot fi apelate din afara modului). Ascunderea funciilor n modul se face pentru
acele funcii care nu se utilizeaz la realizarea interfeei modulului cu celelalte module.
Ascunderea datelor i funciilor n module permite protejarea datelor i prentmpin
utilizarea eronat a funciilor.

5.1.4. Programare structurat


Descompunerea unei probleme n subprobleme mai simple se poate face succesiv n mai
multe etape, pn cnd subproblemele sunt direct programabile sub forma unor proceduri sau
module. Aceast descompunere succesiv se mai numete rafinare pas cu pas (stepwise
refinement).. Evident c se obine o descompunere arborescent. Procedurile se pot organiza
sau nu n module. n cadrul procedurilor se folosesc anumite structuri de control a execuiei.
Aceasta impune o anumit disciplin a programrii. Structurile de control de sunt:
a) secvena;
b) iteraia (pretestat, posttestat, cu numr prestabilit de ciclri);
c) alternativa (simpl, complet, generalizat).
Instruciunea de baz (primitiv) n cadrul acestor structuri de control este instruciunea
de atribuire.
Aceast abordare a programrii s-a nscut din necesitatea eliminrii instruciunii de
control GO TO care face saltul necondiionat la o instruciune precizat, alta dect
instruciunea urmtoare ei. Profesorul Dijsktra de la Universitatea din Eindhoven spunea, prin
anul 1965, calitatea unui programator este invers proporional cu numrul de instruciuni
GO TO folosite i a impus D-structurile de control:
a) secvena;
41

b) iteraia pretestat;
c) alternativa simpl.
D-structurile se regsesc n toate limbajele procedurale. Corespondena ar fi:
a) secvena este echivalent cu execuia instruciunilor n ordinea scrierii lor
programul surs;
b) iteraia pretestat echivalent cu WHILE ... DO;
c) alternativa simpl echivalent cu IF ... THEN.

O ilustrare grafic a celor trei D-structuri se d n continuare.

da
S1

C
nu

S2

da
S

C
S

nu

Sn
S-a demonstrat ulterior (Bohm i Jacopini) c orice algoritm se poate descrie doar cu
D-structurile dar pentru o mai bun lizibilitate i nelegere a programelor surs s-au adugat
i iteraia postestat (REPEAT ... UNTIL), iteraia cu numr prestabilit de ciclri (FOR ...
DO), alternativa complet (IF ... THEN ... ELSE) i alternativa generalizat (CASE ... OF).
n unele limbaje se folosesc i alte structuri pe lng cele de mai sus pentru o ct mai fidel
reflectare a algoritmului.

5.1.5. Programare prin abstractizarea datelor


n toate aceste tehnologii anterioare se urmrete mai mult organizarea programului i
mai puin rezolvarea ct mai natural a problemei. Programarea prin abstractizarea datelor i
programarea orientat spre obiecte propun metodologii n care conceptele deduse din analiza
problemei s poat fi reflectate ct mai fidel n program i s se poat manevra cu instanieri
ale acestor concepte ct mai natural. Se realizeaz o mai mare fidelitate a programului fa de
problem. De exemplu dac ntr-o problem n care se proceseaz numere complexe e nevoie
s se lucreze ntr-o form ct mai apropiat cu forma matematic se poate introduce tipul
COMPLEX (tip care nu exist n limbajele de programare) i apoi se pot declara variabile de
acest tip. Mai mult ar trebui s se poat face toate operaiile matematice asupra datelor de tip
COMPLEX. n general un TAD (Tip Abstract de Date) are dou componente fundamentale:
- datele membru (reflect reprezentarea tipului);
- funciile membru (reflect comportamentul tipului).

42

5.1.6. Programarea orientat spre obiecte


Un neajuns al programrii prin abstractizarea datelor este faptul c nu permite exprimarea
legturilor dintre diferite concepte (TAD-uri). Singura legtur dintre concepte care se poate
exprima, este aceea c datele membru ale unei clase pot fi obiecte ale unei alte clase. Acest
lucru nu este suficient n cazul n care conceptele sunt strns dependente ntre ele formnd
structuri ierarhice. Exprimarea ierarhiilor conduce la atribute suplimentare cum sunt cele de
motenire. Aceste atribute conduc la un nou model de programare pe care l numim
programare orientat obiect.
n vrful unei ierarhii se afl fenomenul sau forma de existen care are trsturi comune
pentru toate celelalte componente ale ierarhiei respective. Pe nivelul urmtor al ierarhiei se
afl componentele care pe lng trsturile comune de pe nivelul superior, mai au i trsturi
suplimentare, specifice. O ierarhie, de obicei, are mai multe nivele, iar situarea unui element
pe un nivel sau altul al ierarhiei este uneori o problem deosebit de complex. Dm n
exemplul urmtor o ierarhie arborescent pentru conceptele de paralelogram, dreptunghi,
romb i ptrat.
Paralelogram

Dreptunghi

Romb

Ptrat

Dac paralelogramul se afl n vrful ierarhiei atunci pe nivelul imediat inferior se aeaz
dreptunghiul (paralelogramul cu un unghi drept) dar i rombul (paralelgramul cu 2 laturi
alturate congruente). Apoi ptratul se poate defini fie ca un dreptunghi cu laturile
congruente fie ca un romb cu un unghi drept. Conceptul de pe fiecare nivel se observ c
motenete proprietile conceptului imediat superior din care este derivat.
La ora actual, toate ramurile cunoaterii tiinfice sunt pline de ierarhii rezultate n urma
clasificrii cunotinelor acumulate n perioada lung de observare a fenomenelor i formelor
de existen a lumii materiale i spirituale. Clasificrile ierarhice ale cunotinelor pot fi
ntlnite att n domeniile care pleac de la cele mai concrete forme ale lumii materiale, cum
sunt botanica, zoologia, biologia, etc ct i n domenii care studiaz concepte dintre cele mai
abstracte, cum ar fi matematica sau filozofia.
Aceste ierarhii sunt rezultatul definirii conceptelor dup regula includerii genul
proxim i diferena specific.
Limbajul C dispune de un set bogat de instruciuni care permit scrierea de:
- programe structurate,
- programe flexibile,
- programe compacte.

43

Totodat limbajul C permite aplicarea metodelor de programare procedural, programare


modular i programare structurat. Pe lng aceste metodologii limbajul C++ permite i
programarea prin abstractizarea datelor i programarea orientat spre obiecte.
Vom descrie n continuare instruciunile limbajului C. Ca o caracteristic sintactic
toate instruciunile limbajului se termin prin caracterul ;, excepie fcnd instruciunile
care se termin cu acolada nchis.

5.2. Instruciunea vid


Instruciunea vid se reduce la caracterul ;. Ea nu are nici un efect. Adesea este
nevoie de ea la construcii n care se cere prezena unei instruciuni, dar nu este necesar s se
execute nimic n punctul respectiv.

5.3. Instruciunea expresie


Instruciunea expresie se obine scriind punct i virgul dup o expresie, deci:
expresie;
Exist cazuri particulare ale instruciunii expresie:
1) expresia de atribuire, care de altfel este cel mai important caz particular al instruciunii
expresie:
v = expresie;
2) apelul unei funcii:
nume_funcie (par1, par2, . . . parn);
3) incrementrile i decrementrile pre i post fixate:
variabil++; ++variabil;
variabil- -; - - variabil;
Exemplu:
void main (void)

{ int i;
float f;
double d;
i=10;
i++;
f=i;
d=++f;
putchar(a);

//
//
//
//
//

instructiune de atribuire
i se mareste cu 1
instructiune de atribuire (valoarea lui i se converteste in float)
incrementare lui f si atribuirea valorii lui d
instructiune de apel

5.4. Instruciunea compus


Instruciunea compus este o succesiune de declaraii urmate de instruciuni,
succesiune inclus ntre acolade:
{
declaraii
instruciuni
}

44

Pot lipsi declaraiile sau instruciunle dar nu n acelai timp. Dac declaraiile sunt
prezente, atunci ele definesc variabile care sunt valabile numai n instruciunea compus
respectiv.
Exemplu:
...
{ int i=100;
i++;
printf (i=%d\n,i);
}

// variabila i este definita in aceasta instructiune compusa


// i are valabilitate doar intre acolade; dupa acolada inchisa i isi
// pierde valabilitatea

Observaii:
1o. Dup acolada inchis a unei instruciuni compuse nu se pune ;.
2o. Corpul unei funcii are aceeai structur ca i instruciunea compus, deci o funcie are
formatul:
antetul funciei
instruciune compus

5.5. Instruciunea if
Instruciunea if permite s realizm o ramificare a execuiei n funcie de valoarea unei
expresii. Ea are dou formate ce permit aplicarea structurii de alternativ simpl i compus.
Formatul 1:
if (expresie) instructiune;
Efectul:
1) se evalueaz expresia din paranteze;
2) dac valoarea expresiei este diferit de zero (deci conform conveniei are valoarea
adevrat), atunci se execut instructiune, altfel se trece la instruciunea urmtoare.
Formatul 2:
if (expresie)
else

instructiune_1;
instructiune_2;

Efectul:
1) se evalueaz expresia din paranteze;
2) dac valoarea expresiei este diferit de zero (deci conform conveniei are valoarea
adevrat), atunci se execut instructiune_1, altfel se execut instructiune_2; apoi n
ambele cazuri se trece la instruciunea urmtoare.
Observaii:
1o. Se pot folosi instruciuni if imbricate, nivelul de imbricare fiind oarecare (deci nu exist o
limitare a numrului de imbricri).
2o. Pentru mai multe imbricri se folosete regula asocierii if-lui cu else astfel:
un else se pune n coresponden cu primul if care se afl naintea lui n textul surs i nu este
inclus n instruciunea care l precede pe el i nici nu i corespunde deja un else.

45

Exemple
void main (void)
{ float x,y,a;
x=-5;
y=10;
if (x<0)
if (y<0)
else
else a=0;
}
void main (void)
{ float x,y,a;
x=-5;
y=10;
if (x<0)
{ if (y<0) a=1; }
else a=0;
}

a=1;
a=2;

// ultimul else se asociaza cu primul if iar


// penultimul else se asociaza cu cel de-al doilea if

// ultimul else se asociaza cu primul if deoarece cel de-al


// de-al doilea if este inclus in instructiunea compusa care
// il precede pe else

void main (void)


// citeste trei intregi si scrie minimul dintre ei
{int i,j,k,min;
scanf (\ndati i=%d, &i);
scanf (\ndati j=%d, &j);
scanf (\ndati k=%d, &k);
if (i>j)
min=j;
else
min=i;
if (k<min)
min=k;
printf (min(%d,%d,%d)= %d\n,i,j,k,,min);
}

5.6. Instruciunea while


Instruciunea while are urmtorul format:
while (expresie) instructiune;
Cu ajutorul instruciunii while se realizeaz structura repetitiv pretestat (condiionat
anterior).
Efectul:
1) se evalueaz valoarea expresiei din paranteze;
2) dac expresia are valoarea diferit de zero, atunci se execut instructiune i se reia
punctul 1), altfel se trece la instruciunea urmtoare instruciunii while.
Deci instructiune se execut repetat atta timp ct expresia din parantez este diferit de
zero. Se observ c dac expresia are valoarea zero de la nceput, atunci instructiune nu se
execut niciodat.
Antetul ciclului while este construcia while (expresie) iar instructiune formeaz corpul
ciclului. n cazul n care este necesar s se execute repetat mai multe instruciuni, se utilizeaz
o instruciune compus format din instruciunile respective.

46

Exemplu:
Vom crea un program care citete un ntreg n i scrie n!. Algoritmul n pseudocod:
Citeste n
f=1
i=2
CtTimp i<=n execut
f=f*i;
i=i+1
SfritCtTimp
Scrie n,f
Programul n C este:
#include<stdio.h>
void main (void)
{ int n,i;
double f;
f=1.0;
i=2;
printf(\n dati n= );
scanf(%d,&n);
while (i<=n)
{ f=f*i;
i++;
}
printf(\nn=%d, iar n!=%g\n,n,f);
}

Corpul ciclului while se poate scrie mai compact astfel:


while (i<=n) f*=i++;

5.7. Instruciunea for


Instruciunea for, ca i instruciunea while, se utilizeaz pentru a realiza o structur
repetitiv pretestat. Formatul instruciunii este:
for(exp1; exp2; exp3) instructiune;
Antetul ciclului este definit de for(exp1; exp2; exp3) iar instructiune formeaz corpul
ciclului. Prima expresie exp1 constituie partea de iniializare a ciclului, iar exp3 este partea de
reiniializare a ciclului. Condiia de continuare a ciclului este exp2. De obicei exp1 i exp3
reprezint atribuiri.
Efectul:
1) se execut secvena de iniializare definit de expresia exp1;
2) se evalueaz exp2; dac exp2 are valoarea zero, atunci se iese din ciclu, adic se trece la
instruciunea urmtoare instruciunii for, altfel se execut instruciunea din corpul ciclului;
3) se execut apoi secvena de reiniializare definit de exp3, apoi se reia secvena de la
punctul 2).
Observaii:
1o. Ca i n cazul instruciunii while, instruciunea din corpul ciclului for poate s nu se
execute niciodat dac exp2 are valoarea zero chiar la prima evaluare.
47

2o. Expresiile din antetul instruciunii for pot fi i vide; totui caracterele ; vor fi ntotdeauna
prezente.
3o. Comparnd instruciunile for i while observm c instruciunea for cu formatul anterior se
poate realiza cu secvena urmtoare folosind while:
exp1;
while (exp2)
{ instructiune;
exp3;
}
Invers, o instruciune while de forma: while (exp) instructiune este echivalent cu
urmtoarea instruciune for:
for(; exp; ) instructiune.
Autorii limbajului C propun ca instruciunea for s se foloseasc cu prioritate pentru
ciclurile care au pas.
Exemple:
Vom da o secven de instruciuni care nsumeaz elementele unui tablou:
s=0;
for(i=0; i<n; i++) s=s+tab[i];
sau scris mai compact:
for (s=0, i=0; i<n; i++) s+=tab[i];
n continuare vom da un mic program care afieaz numrul caracterelor citite de la
intrarea standard stdin.
#include <stdio.h>
void main(void)
{ long n;
for (n=0; getchar()!=EOF; n++);
printf (\nnumarul caracterelor citite =%ld\n,n);
}

sau scris cu instruciunea while


#include <stdio.h>
void main(void)
{ long n=0;
while (getchar()!=EOF) n++;
printf (\nnumarul caracterelor citite =%ld\n,n);
}

5.8. Instruciunea do while


Aceast instruciune realizeaz structura repetitiv condiionat posterior (posttestat)
dar modificat fa de REPEAT . . . UNTIL. Aceast instruciune s-a introdus pentru o mai
mare flexibilitate n scrierea programelor. Formatul ei este:
do
instructiune;
while (exp);
Efectul:
1) se execut instruciunea instructiune;
2) se evalueaz expresia exp din paranteze;
48

3) dac valoarea expresiei este zero se trece la instruciunea urmtoare instruciunii dowhile; altfel se revine i se execut din nou instructiune.
Observaii:
1o. Structura realizat de instruciunea do-while poate fi realizat printr-o secven n care
se folosete instruciunea while astfel:
instructiune;
while (exp) instructiune;
o
2 . Se observ c n cazul instruciunii do-while, corpul ciclului se execut cel puin odat,
spre deosebire de ciclurile while i for unde corpul ciclului poate s nu se execute niciodat.
Exemplu:
Vom da un program care calculeaz rdcina ptrat dintr-un numr real a>=0.
#include<stdio.h>
#define EPS 1e-10
void main (void)
{ double x1,x2,y,a;
clrscr();
// sterge ecranul
printf(\ndati un numar real pozitiv a=);
if (scanf(%lf,&a) !=1 || a<0) printf (numarul citit nu este pozitiv\n);
else {
x2 = 1.0;
do { x1 = x2;
x2 = 0.5 *(x1+a/x1);
// formula de iteratie
if ((y=x2-x1) < 0) y = -y;
}
while (y >= EPS);
printf (radacina patrata din:%g este: %.2lf\n,a,x2);
// 2 zecimale
} //sfirsit else
}

5.9. Instructiunea switch


Instruciunea switch permite realizarea structurii alternativa generalizat. Ea este
echivalent cu o imbricare de structuri de alternativ simple. Utilizarea instruciunii switch
face n schimb programul mult mai clar.
Formatul instruciunii switch este urmtorul:
switch (exp)
{ case c1:
case c2:
...
case cn:
default:

sir1
break;
sir2
break;
sirn
break;
sir

}
unde:

c1,. . . cn sunt constante sau constante simbolice;


sir1, . . . ,sirn, sir sunt iruri de instruciuni.

Efectul:
1) se evalueaz expresia din parantez;
49

2) se compar pe rnd valoarea expresiei cu valorile constantelor c1, . . . , cn;


3) dac valoarea expresiei coincide cu valoarea lui ck, se execut secvena de instruciuni
definit prin sirk; n cazul n care valoarea expresiei nu coincide cu nici una din
constantele c1, . . . , cn, se execut secvena de instruciuni definit prin sir;
4) dup execuia secvenei sirk sau sir se trece la instruciunea urmtoare instruciunii
switch, adic la prima instruciune aflat dup acolada nchis care termin instruciunea
switch respectiv; evident, acest lucru are loc dac irul care se execut nu impune, el
insui, un alt mod de continuare a execuiei, de exemplu o revenire din funcia respectiv,
un salt la o anumit instruciune, etc.
Observaii:
1o. Ramura default nu este obligatorie. n lipsa ei, dac valoarea expresiei nu coincide cu nici
una din constantele c1,. . . , cn, instruciunea switch respectiv nu are nici un efect.
2o.Construcia break reprezint o instruciune. Ea termin fiecare ramur de instruciuni sir1, .
. . , sirn, provocnd saltul la instruciunea urmtoare instruciunii switch sau, cum se mai
spune, realizeaz ieirea din instruciunea switch.
3o. Instruciunea break nu este obligatorie. n cazul n care este absent, se execut secvenial
urmtoarea ramur. De exemplu dac avem secvena:
switch (exp)
{ case c1: sir1
case c2: sir2
}
ea se execut n felul urmtor:
- dac valoarea expresiei este egal cu c1 se execut sir1 i apoi sir2;
- dac valoarea expresiei este egal cu c2 se execut sir2;
- dac valoarea expresiei difer de valorile c1 i c2 instruciunea switch de mai sus
nu este efectiv, se trece la instruciunea urmtoare care urmeaz dup switch.
- secvena de mai sus se putea realiza i astfel:
if (exp = = c1)
{ sir1
sir2
}else if (exp = = c2) sir2
Exemplu:
Vom citi din fiierul de intrare construcii de forma: op1 operator op2, unde op1 i op2 sunt
numere ntregi (operanzi ntregi) iar operator este un operator aritmetic {+, -, *, /}.
La ieire se va scrie valoarea expresiei citite. De exemplu dac se citete secvena 100/3 se
va afia rezultatul 33. Programul permite citirea i evaluarea mai multor astfel de expresii,
pn la ntlnirea sfritului de fiier.
#include <stdio.h>
void main (void)
{ int op1,op2,operator,rezultat,i;
while (( i=scanf(%d %c %d, &op1,&operator, &op2)) != EOF)
if (i = = 3 )
// ramura adevarat inseamna ca s-au citit 3 campuri corecte
{ switch (operator)
{ case +: rezultat = op1 + op2 ;
// avem adunare
break;
case - : rezultat = op1 op2;
// avem scadere
break;
case * : rezultat = op1 * op2;
// avem inmultire
break;
case / :
// avem impartire intreaga
if (op2 = = 0)
{ printf (divizor nul\n);

50

rezultat = 0;
} else rezultat = op1 / op2;
break;
default : printf (operator eronat\n);
rezultat = 0;
} // sfarsit switch
printf (%d %c %d %d\n, op1, operator, op2, rezultat);
} else
printf (expresie eronat\n);
// sfarsit if
// sfarsit while

5.10. Instruciunea break


Formatul instruciunii este urmtorul:
break;
De obicei instruciunea break se folosete pentru a iei dintr-un ciclu. Dac exist mai
multe cicluri imbricate instruciunea break va trece controlul la ciclul de nivel imediat
superior (deci imbricarea rmne, nu se iese din toate ciclurile). O alt utilizare este n
instruciunea switch, dup cum am observat n paragraful anterior.
Un alt exemplu de utilizare frecvent este ieirea dintr-un ciclu infinit de forma:
for ( ; ; )
{. . .
if (exp) break;
...
}

5.11. Instruciunea continue


Formatul instruciunii este urmtorul:
continue;
Efectul:
1) n ciclurile while i do-while ea realizeaz saltul la evaluarea expresiei care decide asupra
continurii ciclului;
2) n ciclul for ea realizeaz saltul la pasul de reiniializare.
Observaie:
1o. Instruciunea continue se utilizeaz numai n corpul unui ciclu, permind, dup caz, s se
treac la pasul urmtor al ciclului sau s se ias din ciclu.

5.12. Instruciunea goto

51

Conform principiilor programrii structurate instruciunea goto nu ar fi necesar. Dar


ea a fost introdus n limbaj, deoarece, n anumite cazuri, se dovedete a fi util, asigurnd o
flexibilitate mai mare n programare. De multe ori ieirea dintr-un ciclu imbricat n alte cicluri
se realizeaz mai simplu cu ajutorul instruciunii goto. n lipsa ei ar trebui s folosim mai
muli indicatori i teste asupra valorilor acestora pentru ieirea din ciclu. Saltul fcut de goto
se face la o instruciune care este prefixat de o etichet.
Prin etichet vom nelege un nume urmat de caracterul :. Etichetele sunt locale unei
funcii.
Instruciunea goto are urmtorul format:
goto eticheta;
Efectul:
1) se realizeaz saltul la instruciunea prefixat de eticheta al crei nume se afl scris dup
cuvntul cheie goto.

5.13. Apelul i revenirea dintr-o funcie


5.13.1. Apelul unei funcii
n limbajul C funciile sunt de dou tipuri:
- funcii care returneaz o valoare la revenirea din ele;
- funcii care nu returneaz nici o valoare la revenirea din ele.
O funcie care nu returneaz nici o valoare la revenirea din ea se apeleaz printr-o
instruciune de apel. Ea are urmtorul format:
nume (lista_parametrilor_efectivi);

(*)

unde:
-

nume este numele funciei;


lista_parametrilor_efectivi este fie vid, fie se compune din una sau mai multe
expresii separate prin virgul.
Instruciunea de apel este un caz particular al instruciunii expresie. Parametrii efectivi
(de la apel) trebuie s corespund cu cei formali (de la definirea funciei) prin ordine, tip i
numr.
n cazul n care o funcie returneaz o valoare, ea poate fi apelat fie printr-o
instruciune de apel, fie sub forma unui operand al unei expresii.
Observaii:
1o. Dac nu dorim s utilizm valoarea returnat de funcia respectiv, apelul se face printr-o
instruciune de apel.
2o. Dac dorim s utilizm valoarea returnat de funcie, vom folosi apelul funciei drept
operand ntr-o expresie, operandul avnd formatul (*).
Exemple de apeluri de funcii folosite pn acum sunt apelurile funciilor standard printf,
scanf, getchar i putchar. Funciile printf i putchar au fost apelate prin instruciuni de apel,
valorile returnate de ele nefiind utilizate. n schimb funciile scanf i getchar au fost apelate n
ambele moduri, att prin instruciuni de apel, ct i ca operanzi n diferite expresii.

5.13.2. Prototipul unei funcii

52

O funcie poate fi apelat dac ea este definit n fiierul surs nainte de a fi apelat.
Dup cum am prezentat n lecia 1 nu ntotdeauna este posibil acest lucru i n astfel de cazuri
apelul funciei trebuie s fie precedat de prototipul ei.
Prototipul unei funcii are ca scop s informeze compilatorul despre:
- tipul valorii returnate de funcie;
- tipurile parametrilor.
n felul acesta, la apelul unei funcii, compilatorul poate face teste cu privire la tipul
expresiilor care reprezint parametrii efectivi, precum i unele conversii necesare asupra
valorii returnate de funcie.
Observaii:
1o. Tipurile parametrilor pot s lipseasc. n acest caz, compilatorul nu controleaz tipurile
parametrilor efectivi, singura informaie coninut de prototip fiind tipul valorii returnate de
funcia respectiv.
2o. Absena att a prototipului unei funcii, ct i a definiiei funciei nainte de a fi apelat
este posibil; n acest caz se presupune c funcia returneaz o valoare de tip int.
3o. n practic se recomand utilizarea prototipurilor pentru toate funciile nainte de a fi
apelate. n acest scop, ele vor fi scrise la nceputul fiierelor surs.
Formatele posibile ale unui prototip sunt:
formatul 1:
formatul 2:
formatul 3:
formatul 4:

tip nume (lista_declaratiilor_de_parametri);


tip nume (lista_ tipurilor_parametrilor);
tip nume (void);
tip nume ();

Formatul 2 este cel mai utilizat. Formatul 3 se poate folosi pentru orice funcie care nu
are parametri. Formatul 4 se poate folosi pentru orice funcie la al crei apel nu se doresc teste
referitoare la tipul parametrilor efectivi.
Funciile din biblioteca standard a limbajului C au prototipurile definite n fiierele de
tip .h.

5.13.3. Apel prin valoare i apel prin referin


La apelul unei funcii, fiecrui parametru formal i se atribuie valoarea parametrului
efectiv care-i corespunde. Deci, la apelul unei funcii se transfer valorile parametrilor
efectivi. Din aceast cauz se spune c apelul este prin valoare (call by value). n anumite
limbaje de programare, la apel nu se transfer valorile parametrilor efectivi ci adresele
acestora. n acest caz se spune c apelul este prin referin (call by refference).
ntre cele dou tipuri de apeluri exist o diferen esenial i anume: n cazul apelului
prin valoare funcia apelat nu poate modifica parametrii efectivi din funcia apelant,
neavnd acces la ei. n cazul apelului prin referin, funcia apelat, dispunnd de adresele
parametrilor efectivi, i poate modifica.
n limbajul C apelul se realizeaz implicit prin valoare. n cazul c un parametru este
numele unui tablou atunci transferul se realizeaz prin referin deoarece numele unui tablou
este un pointer i conine adresa primului element al tabloului. Transferul prin referin se
realizeaz cu ajutorul variabilelor de tip pointer i cu ajutorul operatorului de adres (&).

5.13.4. Revenirea dintr-o funcie


Revenirea dintr-o funcie se poate face n dou moduri:
- la ntlnirea instruciunii return;
53

dup execuia ultimei sale instruciuni, adic a instruciunii care precede acolada
nchis ce termin corpul funciei respective.
Instruciunea return are dou formate:
return;

sau

return expresie;

Primul format se utilizeaz cnd funcia nu returneaz o valoare, iar cel de-al doilea
cnd funcia returneaz o valoare. n acest ultim caz, funcia returneaz valoarea expresiei
specificate.
Observaie:
1o. Cnd revenirea se face dup execuia ultimei instruciuni a funciei nu se returneaz o
valoare; revenirea n acest caz, se face ca i cum acolada nchis de la sfritul corpului
funciei ar fi precedat de instruciunea return.
Exemplu: vom da un exemplu de apel al funciei care determin rdacina ptratic dintr-un
numr nenegativ.
#include<stdio.h>
double radacina_2 (double);
void main (void)

// prototipul functiei
// functia principala care citeste d
// si afiseaza radacina patrata din d

{ double d;
clrscr();
// sterge ecranul
if (scanf (%lf,&d) != 1 || d<0)
printf (numarul dat este eronat\n);
else
printf (d=%f, radacina patrata = %.10g\n, d, radacina_2(d));
#define EPS 1e-10
double radacina_2 (double x)
{ double x1,x2,y;
x2 = 1.0;
do { x1 = x2;
x2 = 0.5 *(x1+x/x1);
if ((y=x2-x1) < 0) y = -y;
}
while (y >= EPS);
return x2;
}

// formula de iteratie

Observaie:
1o. Limbajul C dispune de o bibliotec matematic n care sunt incluse o serie de funcii
pentru calculul valorilor funciilor elementare. Exist o funcie numit sqrt (cu prototipul
double sqrt (double);). Fiierul care conine biblioteca matematic se numete math.h i
trebuie inclus n fiierul surs de lucru dac se dorete utilizarea funciilor definite n el.

54

6. Pointeri
Un pointer este o variabil care are ca valori adrese. Pointerii se folosesc pentru a face
referire la date cunoscute prin adresele lor. Astfel, dac p este o variabil de tip pointer care
are ca valoare adresa zonei de memorie alocat pentru variabila ntreag x atunci construcia
*p reprezint chiar valoarea variabilei x.
n construcia de mai sus, *p, caracterul * se consider ca fiind un operator unar care
furnizeaz valoarea din zona de memorie a crei adres este coninut n p. Operatorul unar *
are aceeai prioritate ca i ceilali operatori unari din limbajul C.
Dac p conine adresa zonei de memorie alocat variabilei x, vom spune c p
pointeaz spre x sau c p conine adresa lui x.
Pentru a atribui unui pointer adresa unei variabile, putem folosi operatorul unar &.
Astfel, dac dorim ca p s pointeze spre x, putem utiliza construcia:
p = &x;
n limba romn se utilizeaz i alte denumiri pentru noiunea de pointer: referin,
localizator; reper; indicator de adres.

6.1. Declaraia de pointer


Un pointer se declar ca orice variabil cu deosebirea c numele pointerului este
precedat de caracterul *. Astfel, dac, de exemplu, dorim s declarm variabila p utilizat
anterior pentru a pstra adresa variabilei ntregi x, vom folosi declaraia urmtoare:
int *p;
Tipul int stabilete n acest caz faptul c p conine adrese de zone de memorie alocate
datelor de tip int. Declaraia lui p se poate interpreta n felul urmtor: *p reprezint coninutul
zonei de memorie spre care pointeaz p, iar acest coninut are tipul int.
n general, un pointer se declar prin:
tip *nume;
ceea ce nseamn c nume este un pointer care pointeaz spre o zon de memorie ce conine o
dat de tipul tip.
Comparnd declaraia de pointer anterioar cu una obinuit:
tip nume;
putem considera c:
tip *
dintr-o declaraie de pointer reprezint tip dintr-o declaraie obinuit. De aceea, construcia
tip *
se spune c reprezint un tip nou, tipul pointer.
55

Exist cazuri n care dorim ca un pointer s fie utilizat cu mai multe tipuri de date. n
acest caz, la declararea lui nu dorim s specificm un tip anume. Aceasta se realizeaz
folosind cuvntul cheie void:
void *nume;
Exemple:
1)
void main (void)
{ int x,y;
int *p;
y=x+10;
// aceast atribuire este echivalenta cu secventa urmatoare
p=&x;
y=*p+100;
x=y;
p=&x;
*p=y;

// este echivalenta cu secventa

2) funcia permutare de mai jos realizeaz transferul parametrilor prin adres:


void permutare (int *x, int *y)
// x si y sunt pointeri
{ int temp;
temp = *x;
// temp ia valoarea ce se afla la adresa continuta in x
*x=*y;
// in zona a carei adresa se afla in x se transfera continutul
// zonei a carei adresa se afla in y
*y=temp;
// in zona a carei adresa se afla in y se transfera valoarea
// lui temp
}

Apelul funciei permutare se face astfel:


permutare (&a, &b);
pentru a schimba valorile lui a cu b.

6.2. Legtura dintre pointeri i tablouri


Numele unui tablou este un pointer deoarece el are ca valoare adresa primului su
element. Totui exist o diferen ntre numele unui tablou i o variabil de tip pointer, i
anume unui nume de tablou nu i se poate atribui alt adres. Deci numele unui tablou trebuie
considerat ca fiind un pointer constant.
Dac x este un parametru formal ce corespunde unui parametru efectiv care este un
nume de tablou, x poate fi declarat fie ca tablou fie ca pointer spre tipul tabloului.
Exemplu:
Fie funcia cu antetul urmtor:
unsigned lungime (char x[ ]);

S presupunem c aceast funcie determin lungimea unui ir de caractere i se poate apela


prin:
56

l=lungime(tablou);
unde tablou este de tip caracter.
Antetul funciei lungime poate fi schimbat n felul urmtor:
unsigned lungime (char *x);
Cele dou declaraii sunt identice deoarece declaraia:
char x[ ];
definete pe x ca numele unui tablou de tip caracter; dar atunci el este un pointer spre
caractere deci se poate declara prin:
char *x;

6.3. Operaii cu pointeri


Asupra pointerilor se pot face diferite operaii. Deoarece ei conin adrese atunci
operaiile se realizeaz cu adrese.

6.3.1. Incrementare i decrementare


Operatorii de incrementare i decrementare se pot aplica variabilelor de tip pointer.
Efectul:
- operatorul de incrementare aplicat asupra unui operand de tip pointer spre tipul tip
mrete adresa coninut de operand cu numrul de octei necesari pentru a pstra
o dat de tipul tip.
- operatorul de decrementare se execut n mod analog, cu singura diferen c n
loc s se mreasc adresa, ea se micoreaz cu numrul corespunztor de octei.
De obicei decrementrile i incrementrile adreselor sunt mai rapide ca execuie cnd
se au n vedere prelucrri de tablouri.
Exemplu:
int tab[10];
int *p;
int i=0;
p=&tab[i];
p++;

// p contine adresa lui tab[1]


// cu p se pot face referiri la orice element de tablou

6.3.2. Adunarea i scderea unui ntreg dintr-un pointer


Dac p este un pointer, sunt corecte expresiile de forma:
p+n i p-n
unde n este de tip ntreg.
Efectul:
-

expresia p+n mrete valoarea lui p cu n*nr_tip, unde nr_tip este numrul de
octei necesari pentru a memora o dat de tipul tip spre care pointeaz p;
analog expresia p-n micoreaz valoarea lui p cu n*nr_tip.
57

Dac x este un tablou de tipul tip, atunci x este pointer, deci o expresie de forma:
x+n;
este corect i deoarece x este un pointer spre primul su element x[0], x+n va fi un pointer
spre elementul x[n]. Rezult c valoarea elementului x[n] se poate reprezenta prin expresia:
*(x+n);
Astfel variabilele cu indici se pot nlocui prin expresii cu pointeri. Aceasta permite ca
la tratarea tablourilor s se foloseasc expresii cu pointeri n locul variabilelor cu indici.
Versiunile cu pointeri sunt de obicei optime n raport cu cele realizate prin intermediul
indicilor.

6.3.3. Compararea a doi pointeri


Doi pointeri care pointeaz spre elementele aceluiai tablou pot fi comparai folosind
operatorii de relaie i de egalitate. Astfel, dac p i q sunt doi pointeri care pointeaz spre
elementele tab[i], respectiv tab[j] ale tabloului tab, expresiile urmtoare au sens:
p<q

p!=j

p= =q.

Observaii:
1o. Pointerii nu pot fi comparai dect n condiiile amintite mai sus (deci dac sunt pointeri
spre elementele aceluiai tablou).
2o. Operatorii = = i != permit compararea unui pointer i cu o constant simbolic special
avnd numele NULL. Aceste comparaii permit s stabilim dac un pointer conine o adres
sau nu. Astfel, dac expresia:
p= = NULL
este adevrat, p nu conine o adres. Dac expresia respectiv are valoarea fals atunci p
conine o adres. Constanta simbolic NULL este definit n fiierul stdio.h

6.3.4. Diferena a doi pointeri


Doi pointeri care pointeaz spre elementele aceluiai tablou pot fi sczui. Rezultatul
diferenei a doi pointeri este definit astfel: fie t un tablou de un tip oarecare i p i q doi
pointeri, p conine adresa elementului t[i] iar q conine adresa elementului t[i+n]. Atunci
diferena q-p are valoarea n.

6.3.5. Exemple
Vom da cteva funcii asupra irurilor de caractere:
funcia lungime
58

unsigned lungime (char*x)


{ int i;
for (i=0; *x != \0; i++) x++;
return i;
}

// sau for (i=0; *x++; i++);

funcia copiaza
void copiaza(char *x, char *y)

// copiaza din zona de adresa y


// in zona de adresa x

{ while(*x++ = *y++); }

funcia concateneaza
void concateneaza (char *x, char *y)
// concateneaza sirul de adresa y la sfarsitul sirului
// de adresa x
{ while (*x) x++;
// avans de adresa pana la sfarsitul sirului x
while (*x++= *y++);
}

funcia compara
int compara (char *x, char *y)
{ while (*x= = *y)
{ if (*x= = \0) return 0;
x++;
y++;
return *x - *y;
// daca diferenta caracterelor este
// negativa atunci x<y altfel x>y
}
}

6.4. Alocarea dinamic a memoriei


Biblioteca standard a limbajului C pune la dispoziia utilizatorului funcii care permit
alocarea de zone de memorie n timpul execuiei programului. O astfel de zon de memorie
poate fi utilizat pentru a pstra date temporare. Zona respectiv poate fi eliberat n
momentul n care nu mai sunt necesare datele care au fost pstrate n ea. Alocarea de zone de
memorie i eliberarea lor n timpul execuiei programelor permite gestionarea optim a
memoriei de ctre programator. Un astfel de mijloc de gestionare a memoriei l vom numi
alocare dinamic a memoriei.
Vom indica dou funcii din bibloteca limbajului C utilizate frecvent n alocarea
dinamic a memoriei. Prototipurile lor se afl n fiierele standard alloc.h i stdlib.h, deci
pentru a le utiliza vom include unul din aceste fiiere.
Funcia malloc permite alocarea unui bloc de memorie a crui dimensiune se specific
n octei. Funcia returneaz un pointer spre nceputul zonei alocate. ntruct acest pointer
trebuie s permit memorarea oricrui tip de dat n zona alocat, el este de tip void *.
Prototipul funciei este:
void *malloc (unsigned n);
unde n este numrul de octei al zonei de memorie care se aloc. n cazul n care n este prea
mare, funcia returneaz pointerul NULL.
59

Funcia free elibereaz o zon de memorie alocat prin malloc. Prototipul ei este:
void free (void *p);
unde p este pointerul returnat de malloc la alocare, deci este pointerul spre nceputul zonei
care se elibereaz.
Exemplu:
Funcia memchar memoreaz un ir de caractere ntr-o zon de memorie alocat prin funcia
malloc. Ea returneaz adresa de nceput a zonei n care s-a salvat irul de caractere, deci
returneaz un pointer spre tipul char.
#include <stdio.h>
#include <alloc.h>
#include <string.h>
char *memchar (char *s)
{
char *p;
if ((p=(char *)malloc(strlen(s)+1) ) != NULL)
{
strcpy (p,s);
return p;
} else
return NULL;
}

Observaii:
1o. n fiierul stdio.h exist definiia constantei NULL.
2o. Fiierul alloc.h s-a inclus deoarece conine prototipul funciei malloc.
3o. Fiierul string.h conine prototipurile funciilor strlen i strcpy.
4o. Funcia malloc se apeleaz pentru a rezerva strlen(s)+1 octei; strlen returneaz numrul
de octei ocupai de caracterele proprii ale lui s (fr caracterul NUL). Cum n zona de
memorie rezervat prin malloc se pstreaz i caracterul NUL, lungimea returnat de funcia
strlen s-a mrit cu 1.
5o. Pointerul returnat de malloc a fost convertit spre char *, deoarece el este de tip void *.
Acest pointer se atribuie lui p, deci p pointeaz spre nceputul zonei de memorie alocate prin
apelul funciei malloc. Se testeaz dac acest pointer este diferit de NULL (deci dac s-a putut
aloca memoria de dimensiunea cerut). n caz afirmativ, se transfer irul prin apelul funciei
strcpy, returnndu-se apoi valoarea pointerului p.

6.5. Pointeri spre funcii


Numele unei funcii este un pointer spre funcia respectiv. El poate fi folosit ca
parametru efectiv la apeluri de funcii. n felul acesta, o funcie poate transfera funciei apelate
un pointer spre o funcie. Aceasta, la rndul ei, poate apela funcia care i-a fost transferat n
acest fel.
60

Exemplu:
Un exemplu matematic n care este nevoie de un astfel de transfer este cel cu privire la
calculul aproximativ al integralelor definite. S presupunem c dorim s calculm integrala
definit din funcia f(x), ntre limitele a i b, folosind formula trapezului:
I= h((f(a)+f(b))/2 +f(a+h)+f(a+2h)+. . . +f(a+(n-1)h)
unde
h=(b-a)/h.
n continuare construim o funcie care calculeaz partea dreapt a acestei relaii.
Numim aria_trapez aceast funcie.
Observaii:
1o. Deoarece funcia f(x) din relaia de mai sus nu este definit n acest moment, ea trebuie s
figureze printre parametrii funciei aria_trapez, mpreun cu limitele de integrare i valoarea
lui n.
2o. Funcia aria_trapez returneaz valoarea aproximativ a integralei i ea se va apela printr-o
expresie de atribuire, de exemplu:
aria=aria_trapez (a, b, n, f);
3o. Funcia aria_trapez returneaz o valoare flotant n dubl precizie. De asemenea, i funcia
f(x) returneaz o valoare flotant n dubl precizie. De aici rezult c prototipul funciei
aria_trapez este urmtorul:
double aria_trapez (double a, double b, int n, double (*f)());
sau
double aria_trapez (double, double, int , double (*)());
4o. Este necesar ca naintea apelului funciei aria_trapez funcia f(x) s fie definit sau s fie
prezent prototipul ei , de exemplu:
double f();
5o. Construcia double (*f) () se interpreteaz n felul urmtor:
- *f
nseamn c f este un pointer;
- (*f)()
nseamn c f este un pointer spre o funcie;
- double (*f) () nseamn c f este un pointer spre o funcie care returneaz o valoare
flotant n dubl precizie.
6o. Trebuie s se includ *f ntre paranteze, deoarece construcia double *f(); este corect,
dar nseamn altceva, parantezele rotunde fiind prioritare operatorului unar *. n acest caz, se
declar f ca o funcie ce returneaz un pointer spre o valoare flotant n dubl precizie.
7o. Ultimul parametru formal al funciei aria_trapez corespunde parametrului efectiv f i deci
el trebuie declarat ca i pointer spre o funcie ce returneaz o valoare flotant n dubl
precizie. Conform observaiei 5), dac p este numele parametrului formal ce corespunde
parametrului efectiv f, atunci p se declar astfel:
double (*p)();
8o. n corpul funciei aria_trapez va trebui s apelm funcia f(x) pentru a calcula valorile:
f(a), f(b), f(a+h), . . . , f(a+(n-1)h).
n momentul programrii funciei aria_trapez, nu se cunoate numele funciei concrete, ci
numai pointerul p spre ea. De aceea, vom nlocui numele funciei prin *p, deci vom folosi
apelurile:
61

(*p)(a), (*p)(b), (*p)(a+h), . . . ,(*p)(a+(n-1)h)


double aria_trapez(double x, double y, int m, double(*p)());
{
double h,s;
int i;
h=(y-x)/m;
for (i=1, s=0.0; i<m; i++) s+=(*p)(x+i*h);
s+=((*p)(x) + (*p)(y))/2;
s=h*s;
return s;
}

Vom utiliza funcia aria_trapez pentru a calcula integrala definit din funcia sin(x 2) pe
intervalul [0,1], cu o eroare mai mic dect 10-8. Vom nota cu In urmtoare sum:
In= h((f(a)+f(b))/2 +f(a+h)+f(a+2h)+. . . +f(a+(n-1)h)
Paii algoritmului sunt urmtorii:
Pasul 1. Se alege o valoare iniial pentru n, de exemplu 10.
Pasul 2. Se calculeaz In.
Pasul 3. Se calculeaz I2n prin dublarea lui n.
Pasul 4. Dac |In-I2n| < 10-8, algoritmul se ntrerupe i valoarea integralei, cu precizia admis,
este I2n; altfel se dubleaz n, se pune In=I2n; n, i se trece la pasul 3.
#define A 0.0
#define B 1.0
#define N 10
#define EPS 1e-8
#include <stdio.h>
#include <math.h>
double sinxp(double);
// prototipul functiei sin(x*x)
double aria_trapez(double, double, int, double (*)());
void main (void)
// functia principala
{ int n=N;
double in, i2n, vabs;
in=aria_trapez (A, B, n, sinxp);
do {
n=n*2;
i2n=aria_trapez(A, B, n, sinxp);
if ((vabs= in-i2n) < 0)
vabs = -vabs;
in=i2n;
} while (vabs >= EPS);
printf (valoarea integralei este : %g.10\n,i2n);
}
double aria_trapez(double x, double y, int m, double(*p)());
{
double h,s;
int i;
h=(y-x)/m;
for (i=1, s=0.0; i<m; i++) s+=(*p)(x+i*h);
s+=((*p)(x) + (*p)(y))/2;
s=h*s;
return s;
}
double sinxp (double x)
{ return sin (x*x); }

62

6.6. Tratarea parametrilor din linia de comand


n linia de comand folosit la apelul execuiei unui program se pot utiliza diferii
parametri. Aceti parametri pot fi utilizai folosind parametrii argc i argv ai funciei
principale.
Parametrul argc este de tip ntreg i indic numrul de parametri din linia de comand.
Parametrul argv este un tablou de pointeri spre zonele n care sunt pstrai parametrii
liniei de comand. Acetia se consider iruri de caractere.
Astfel antetul funciei principale va fi :
main (int argc, char *argv[ ])
Exemplu:
Considerm c la lansarea programului prog s-au furnizat parametrii:
31 MARTIE 1956
n acest caz argc=4, iar tabloul argv conine pointerii:
- argv[0]
- pointer spre numele programului (calea, numele i extensia .EXE
- argv[1]
- pointer spre irul 31;
- argv[2]
- pointer spre irul MARTIE;
- argv[3]
- pointer spre irul 1956.
Observaii:
1o. Lansarea unui program se face cu prima instruciune a funciei principale. Deci parametrii
argc i argv au deja n acest moment valorile indicate mai sus, putnd fi analizai chiar cu
prima instruciune executabil.
2o. n mod frecvent, aceti parametrii reprezint diferite opiuni ale programului, date
calendaristice, nume de fiiere, etc.
3o. argv[0] este ntotdeauna pointerul spre numele fiierului cu imaginea executabil a
programului.
void main ( int argc, char *argv[])
{ int i;
for (i=0; i<argc; i++) printf (%s\n,argv[i]);
}

// va afisa parametrii din linia de comanda

6.7. Modificatorul const


Am vzut anterior c o constant se definete prin caracterele care intr n
compunerea ei. De asemenea, n acelai capitol s-a artat c putem atribui un nume unei
constante printr-o construcie #define. Un astfel de nume se spune c este o constant
simbolic i el se substituie prin irul de caractere care i corespunde, n faza de preprocesare.
Un alt mod de a defini o constant este acela de a folosi modificatorul const ntr-o
declaraie. Printr-o astfel de declaraie, unui nume i se poate atribui o valoare constant. n
acest caz, numele respectiv nu mai este tratat de preprocesor i el poate fi folosit n program
n mod analog cu numele variabilelor. Unui astfel de nume declarat cu ajutorul
63

modificatorului const nu i se poate schimba valoarea printr-o expresie de atribuire, ca i unei


variabile obinuite.
Formatele declaraiei cu modificatorul const sunt urmtoarele:
tip const nume = valoare;
const tip nume = valoare;
tip const nume;
const tip nume;
const nume = valoare;
const nume;
Exemplu:
void main (void)
{ const i=10;
const pi = 3.1415926
char *const s=martie;

*s= 1;
*(s+1)=2
char const *s=aprilie;

const char *s=aprilie

// i devine egal cu 10; nu este posibila o atribuire i=0


// s este un pointer constant spre zona in care este
// pastrat sirul de caractere martie. Valoarea lui s
// nu poate fi schimbata dar continutul zonei spre
// care pointeaza s poate fi schimbat
// schimba litera m cu 1
// schimba litera a cu 2
// s este un pointer spre o zona constanta.
// valoare lui s poate schimbata dar sirul aprilie
// nu poate fi modificat
// este identica cu declaratia de mai sus.

Modificatorul const se folosete frecvent la declararea parametrilor formali de tip


pointer. O astfel de declaraie are formatul:
const tip *nume_parametru_formal;
Un parametru formal declarat prin construcia :
tip *nume_parametru_formal;
corespunde unui parametru efectiv a crui valoare este o adres. La apel, valoarea
parametrului formal devine egal cu aceast adres. Datorit acestui fapt, funcia apelat
poate s modifice data aflat la adresa respectiv. Dac se folosete modificatorul const
utilizat la declararea unui astfel de parametru formal atunci se interzice funciei apelate s
modifice data de la adresa recepionat la apel de ctre parametrul formal corespunztor.
Acest mecanism este folosit frecvent n cazul funciilor de tratare a irurilor de caractere.
De exemplu funcia strlen din biblioteca standard a limbajului C are prototipul:
unsigned strlen (const char *s);
Ea se apeleaz prin expresii de atribuire de forma:
i=strlen(x);
unde x este un pointer spre o zon de memorie n care se afl un ir de caractere.
64

Funcia strlen determin lungimea irului aflat la adresa recepionat de ctre


parametrul s. Ea nu are voie s modifice irul respectiv i din aceast cauz parametrul s se
declar folosind modificatorul const.

6.8. Stiva
Prin stiv (stack n englez) nelegem o mulime ordonat de elemente la care accesul
se realizeaz conform principiului ultimul venit primul servit. n englez stiva se mai
numete i list LIFO (Last In First Out).
O modalitate simpl de a implementa o stiv este pstrarea elementelor ei ntr-un tablou
unidimensional. n acest tablou se vor pstra elementele stivei unul dup altul. De asemenea,
ele se pot scoate din tablou n ordinea invers pstrrii lor. La un moment dat se poate scoate
ultimul element pus pe stiv i numai acesta.
Despre ultimul element pus n stiv se spune c este vrful stivei, iar despre primul
element c este baza stivei.
Accesul este permis doar la vrful stivei:
- un element se poate pune pe stiv numai dup elementul aflat n vrful stivei i
dup aceast operaie el ajunge vrful stivei;
- se poate scoate de pe stiv numai elementul aflat n vrful stivei i dup aceast
operaie n vrful stivei rmne elementul care a fost pus pe stiv naintea lui.
Vom numi stack tablou de tip int afectat stivei i next variabila care indic prima
poziie liber din stiv. Deci stack[0] este baza stivei iar stack[n] va fi vrful stivei. Vom
defini mai multe funcii asociate tabloului stack:
- push funcia care pune un element n stiv;
- pop funcia care scoate un element din stiv;
- clear funcia de iniializare a stivei; dup apelul ei stiva devine vid;
#define MAX 1000
static int stack[1000];
static next = 0;

// indicele pentru baza stivei

void push(int x)
// pune pe stiva valoarea lui x
{ if (next < MAX)
stack [next++]=x;
else
printf (stiva este depasita\n);
}

int pop()
{ if (next >0)
else

// scoate elementul din varful stivei si returneaza valoarea lui


return stack [--next];
{ printf (stiva este vida\n);
return 0;
}

}
void clear(void)
{
next=0;
}

// videaza stiva

65

7. Recursivitate
Spunem c o funcie C este recursiv dac ea se autoapeleaz nainte de a se reveni
din ea. Funcia se poate reapela fie direct, fie indirect (prin intermediul altor funcii).
La fiecare apel al unei funcii, parametrii i variabilele locale se aloc pe stiv ntr-o
zon independent. De asemenea, orice apel recursiv al unei funcii va conduce la o revenire
din funcie la instruciunea urmtoare apelului respectiv. La revenirea dintr-o funcie se
realizeaz curarea stivei, adic zona de pe stiv afectat la apel parametrilor i variabilelor
automatice se elibereaz.
Un exemplu simplu de funcie apelata recursiv este funcia de calcul al factorialului.
Putem defini recursiv funcia factorial astfel:
factorial(n)= 1,
dac n=0
factorial(n)=n*factorial(n-1), dac n>0
n limbajul C avem :
double factorial (int)
{ if (n= = 0)
else
}

return 1.0;
return n*factorial(n-1);

Observaii:
1o. n general, o funcie recursiv se poate realiza i nerecursiv, adic fr s se autoapeleze.
2o. De obicei, recursivitatea nu conduce nici la economie de memorie i nici la execuia mai
rapid a programelor. Ea permite ns o descriere mai compact i mai clar a funciilor.
Acest lucru rezult i din exemplul de mai sus de calcul al factorialului.
3o. n general, funciile recursive sunt de preferat pentru procese care se definesc recursiv.
Exist i excepii. De exemplu algoritmul de generare a permutrilor de n obiecte poate fi
descris recursiv astfel: avnd n memorie toate cele (n-1)! permutri, atunci permutrile de n
obiecte se genereaz nsernd pe n n toate poziiile posibile ale fiecrei permutri de n-1
obiecte. Dar ne aducem aminte c 10!=3628800 i capacitatea stivei se depete repede.
Exemple:
1) Programul determin recursiv cmmdc (algoritmul lui Euclid) a dou numere ntregi (de tip
long):
cmmdc (a,b) = b,
dac a%b =0 (restul mpririi lui a la b e zero)
cmmdc (a,b) = cmmdc (b,a%b),
n caz contrar.
#include <iostream.h>
#include <conio.h>
long cmmdc(long a, long b)
{ if (!(a % b))
return b;
else
return cmmdc(b, a % b);
}
void main(void)
{ long x,y;
clrscr();
cout << "dati un numar natural=";
cin >> x;
cout << "dati alt numar natural=";

66

cin >> y;
cout << "cmmdc(" << x << "," << y << ")=" << cmmdc (x,y);
}

Am folosit funciile de intrare / ieire cin i cout, imediat se observ modul lor de
utilizare.
2) Programul determin recursiv suma unor elemente de tablou unidimensional
a[1]+a[2]+ . . . + a[n]
#include <iostream.h>
#define MAX 100
int a[MAX];
// suma(n)= 0,
// suma(n)=suma(n-1) + a[n]
int suma(int n)
{ if (!n) return 0;
else return a[n]+suma(n-1);
}

daca n=0
daca n>0

void main(void)
{int n,i;
cout << "dati n= ";
cin >> n;
for (i=1; i<=n; i++)
{
cout << "a[" << i << "]= ";
cin >> a[i];
}
cout << "suma numerelor este " << suma(n);
}

3) Programul determin recursiv termenul al n-lea din irul lui Fibonacci definit dup cum
urmeaz:
fibonacci[0]=0
fibonacci[1]=1
fibonacci[n]=fibonacci[n-1]+fibonacci[n-2], dac n>1
#include<iostream.h>
long fibonacci (long n)
{if (!n)
return 0;
else if (n==1) return 1;
else
return fibonacci(n-1) + fibonacci(n-2);
}
void main (void)
{ long n;
cout << "dati n = ";
cin >> n;
cout << "fibo(" << n << ") =" << fibonacci (n);
}

4) Programul determina maximul dintr-un vector de numere astfel:


67

M(n)= a[1],
dac n=1
M(n)= max { M(n-1), a[n] }, dac n>1
#include<iostream.h>
#define MAX(x,y) (x > y ? x : y)
int a[100];
int M(int n)
{ if (n= =1)
else
}

return a[1];
return MAX (M(n-1), a[n]);

void main(void)
{int n,i;
cout << "dati n=";
cin >> n;
for (i=1; i<=n; i++)
{
cout << "a[" << i << "]= ";
cin >> a[i];
}
cout << "maximul este " << M(n);
}

5) Programul afiseaz un ir de caractere n mod recursiv, caracter cu caracter, considernd


c irul de caractere este format din primul caracter(capul) + restul irului de caractere
(coada).
#include <iostream.h>
#include <conio.h>
#define max 100
char sir [max];
int n;
void afis (int m)
{ if (m = = n+1) return;
else { cout << sir[m];
afis(m+1);
}
}
void main (void)
{int i;
do {
cout << "\ndati lungimea sirului =";
cin >> n;
}
while ( (n< 0) || (n > max));
for(i=1; i<=n; i++)
{
cout << "sir[" << i << "]=";
cin >> sir[i];
}
afis(1);
getch();
}

6) Programul ce urmeaz e oarecum asemntor cu exemplul anterior doar c afieaz irul de


caractere de la sfrit spre nceput.
#include <iostream.h>
#include <conio.h>
#define max 100

68

char sir [max];


void afis (int m)
{ if (m==0)
return;
else
{ cout << sir[m];
afis(m-1);
}
}
void main (void)
{int n,i;
do
{cout << "\ndati lungimea sirului ="), cin >> n;}
while ( (n< 0) || (n > max));
for(i=1; i<=n; i++)
{
cout << "sir[" << i << "]=";
cin >> sir[i];
}
afis(n);
getch();
}

7) Programul sorteaz prin metoda quicksort un vector de numere ntregi:


#define dim 50
#include <stdio.h>
#include <conio.h>
int x[dim+1],i,n;
void tipsir ()
{ for (i=1; i<=n; i++)
{
printf("%3d",x[i]);
if (!(i % 20)) printf ("\n");
}
}
void quik(int st, int dr)
{int i,j,y;
i=st; j=dr; y=x[i];
do {
while ((x[j] >= y) && (i<j)) j - -;
x[i]=x[j];
while ((x[i] <= y) && (i<j)) i++;
x[j]=x[i];
}
while (i != j);
x[i]=y;
if (st < i-1)
quik(st,i-1);
if (i+1 < dr)
quik(i+1,dr);
x[j]=x[i];
}
void citire (void)
{ int cod = 0;
n = dim+1;
while ( n <= 0 || n > dim || ! cod )
{
printf ("\ndati dim. sir:");
cod=scanf ("%d",&n);
}
i = 1;
while (i<=n)
{ printf ("x[%2d]=",i);
scanf ("%d", &x[i]);
i++;
}

69

}
void main(void)
{ clrscr();
citire();
clrscr();
printf ("\n\nsir initial\n");
tipsir();
quik(1,n);
printf ("\n\nsir sortat\n");
tipsir();
getche();
}

70

8. Structuri, tipuri utilizator


Dup cum am vzut datele de acelai tip se pot grupa n tablouri. Limbajul C permite
gruparea unor date de tipuri diferite sub alte forme de organizare numite structuri.
Tablourile au un tip i anume tipul comun elementelor lor. Astfel, distingem tablouri
de tip ntreg, de tip caracter, de tip flotant, etc. n cazul structurilor, nu mai avem un tip
comun. Fiecare structur reprezint un nou tip de date, tip care se introduce prin declaraia
structurii respective.
Un exemplu simplu de structur este data calendaristic, cu componentele urmtoare:
- ziua;
- luna;
- anul.
unde: ziua i anul sunt date de tip ntreg iar luna este un tablou de caractere.
Structura ca i tabloul, este o muline ordonat de elemente. n exemplul de mai sus se
consider c ziua este primul ei element, luna este al doilea iar anul este ultimul ei element.
Trebuie s precizm c referirea la componentele unei structuri nu se mai face cu ajutorul
indicilor ci prin calificare.

8.1. Declaraia de structur


O structur se poate declara n mai multe feluri, astfel:
Formatul 1:
struct nume_structura
{ lista_declaratii
};
Cu ajutorul acestui format se introduce un nou tip de dat cu numele nume_structur. Lista de
declaraii este format din declaraii obinuite. Tipul data_calendaristica l putem introduce
astfel:
struct data_calendaristica
{ int ziua;
char luna[11];
int anul;
};
O astfel de declaraie se numete declaraie de tip. S reinem c unui nou tip de date nu i se
aloc memorie, el este doar contabilizat ca un nou tip utilizator pe lng tipurile predefinite
ale limbajului C.
Formatul 2:
struct nume_structura
{ lista_declaratii
}lista_variabile;
Un astfel de format introduce tipul utilizator nume_structura i declar o list de variabile n
care fiecare element din list are tipul nume_structur. Prin exemplu urmtor se introduc
variabilele dc1 i dc2 ca date elementare de tipul data_calendaristica i tabloul dc de 13
componente.
struct data_calendaristica
71

{ int ziua;
char luna[11];
int anul;
} dc1, dc2, dc[13];
Formatul 3:
struct { lista_declaraii
} lista_variabile;
Acest format se foloseste dac nu vrem sa dm nume noului tip structurat i totodat dac nu
mai vrem s-l folosim. Deci nu vom mai pute declara alte date de tipul structurat nou introdus
pentru c tipul nu are nume.
Exemplu:
struct { int ziua;
char luna[11];
int anul;
} dc1, dc2, dc[13];
S-au declarat varibilele dc1, dc2 i tabloul dc avnd noul tip structurat utilizator dar nu se mai
dorete s declarm alte date de acest tip.
Observaii:
1o. Dac se folosete formatul 1 atunci pentru a declara date de tipul utilizator nou introdus se
folosete o construcie de forma:
struct nume_ structura lista_variabile;
Compilatorul aloc memorie varibilelor din lista de variabile, tratnd aceast construcie
ca i declaraiile obinuite.
2o. Componentele unei structuri pot fi ele nsele date structurate. O component care nu este
structurat se numete component elementar.
3o. Ca i n cazul celorlalte tipuri de variabile se pot defini structuri globale, statice sau
automatice. Structurile statice se declar precednd declaraiile lor prin cuvntul static, iar
cele externe prin cuvntul cheie extern.
4o. Elementele unei date de tip structur pot fi iniializate dup modelul iniializrii
variabilelor care au tipuri predefinite.
Exemple:
1) Introducem tipul utilizator data_calendaristica astfel:
struct data_calendaristica
{ int ziua;
char luna[11];
int anul;
};
pentru a iniializa o dat de tipul data_calendaristic vom scrie:
struct data_calendaristica dc1={31, martie, 1956};
2) Dac declarm un nou tip date_personale i n care vrem s folosim tipul
data_calendaristica, vom scrie:
72

struct date_personale
{ char nume[30];
char adresa[50];
struct data_calendaristica data_nasterii, data_angajarii;
};

8.2. Accesul la elementele unei structuri


Pentru a avea acces la componentele unei date structurate va trebui s folosim o
calificare de forma:
nume_data_structurata . nume_componenta
Astfel dac avem tipul structurat data_calendaristica introdus in exemplele anterioare i
declarm data dc astfel:
struct data_calendaristica dc;
atunci pentru a ne referi la componentele datei dc vom folosi construciile:
dc.ziua
dc.anul
dc.luna (atenie este pointer spre caractere)
Dac avem declarat un tablou astfel:
struct data_calendaristica tdc[10];
atunci pentru fiecare component i ne vom referi astfel:
tdc[i].ziua
tdc[i].anul
tdc[i].luna (este pointer)
tdc[i].luna[0], tdc[i].luna[1], . . . , tdc[i].luna[11]
Ca i tablourile structurile se pot transfera prin parametrii, transfernd un pointer spre
data structurat respectiv, adic adresa de nceput a zonei alocate structurii. Deci, printr-un
apel de forma:
functie(&data_structurata);
se transfer funciei functie adresa de nceput a zonei alocate structurii data_structurata.
Dac data_structurata este o structura de tipul tip, atunci antetul funciei functie este
urmtorul:
void functie(tip *p)
unde p este pointer spre tipul structurat tip.
Pentru data structurat dc de tipul data_calendaristica antetul funciei functie este:
void functie(struct data_calendaristica *p)
73

iar apelul pentru data dc se face


functie(&dc);
Printr-un astfel de apel, funcia apelat nu are acces la numele datei structurate
transferate, ci numai la pointerul spre ea. De aceea se pune problema accesului la
componentele datei structurate prin pointerul la ea. n acest caz numele datei structurate se va
nlocui prin *p. Deci, n cazul datei structurate dc, transferate ca i mai sus, n locul
construciei
dc.zi
vom scrie:
(*p).zi
nlocuind numele datei structurate dc prin *p, unde p este un pointer spre dc.
Observaie:
1o. Parantezele rotunde din construcia de mai sus sunt obligatorii, deoarece punctul este un
operator prioritar operatorului unar *.
2o. Construcia de mai sus poate fi nlocuit prin p->zi care este identic cu ea. Simbolul ->
se compune din caracterele - i > scrise unul dup cellalt fr spaiu ntre ele. El se
numete sgeat i este considerat a fi un operator cu aceeai prioritate ca i punctul, deci de
prioritate maxim.

8.3. Atribuiri de nume pentru tipuri de date


Dup cum tim tipurile de baz ale limbajului C, numite i tipuri predefinite se
identific printr-un cuvnt cheie (int, char, float, etc). Totodat prin instruciunea struct,
programatorul poate s introduc un tip nou. Programatorul poate s atribuie un nume unui
tip (predefinit sau utilizator) cu ajutorul construciei:
typedef tip nume_nou_tip;
unde:
- tip este numele unui tip predefinit sau al unui tip utilizator (introdus cu struct);
- nume_nou_tip este noul nume atribuit tipului respectiv.
Dup ce s-a atribuit un nou nume unui tip, numele respectiv poate fi utilizat pentru a
declara date de acel tip, la fel cum se utilizeaz n declaraii cuvintele cheie int, char, float,
etc.
Observaii:
1o. De obicei numele atribuit unui tip se scrie cu litere mari.
2o. Un exemplu de astfel de nume exist n fiierul stdio.h pentru tipul fiier, cruia i s-a
atribuit numele FILE.
Exemple:
1) Fie declaraiile:
typedef int INTREG;
typedef float REAL;
n continuare, denumirile INTREG i REAL se pot folosi la fel ca i cuvintele cheie
int i float. Cu alte cuvinte, declaraia:
INTREG i, j, tablou[10];
este identic cu declaraia urmtoare:
74

int i, j, tablou[10];
Analog:
REAL x, y, z;
este identic cu declaraia:
float x, y, z;
2) typedef struct data_calendaristica
{
int
ziua;
char luna[11];
int
anul;
} DC;
Prin aceast declaraie se atribuie denumirea DC tipului structurat data_calendaristica.
n continuare putem declara date de tip DC:
DC data_nasterii, data_angajarii;
DC data_curenta ={31,august,1998};
3) typedef int *PI;
Prin aceast declaraie se introduce un sinonim pentru tipul pointer spre ntregi: int *.
Putem s declarm n continuare pointeri spre ntregi astfel:
PI p;
care este echivalent cu:
int *p;
4) Declaraia

typdef struct
{

double real;
double imaginar;
} COMPLEX;
introduce numele COMPLEX pentru datele de tip complex.
Funcia urmtoare returneaz modulul unui numr complex:
typedef struct
{
double real;
double imaginar;
} COMPLEX;
#include <math.h>
double modul (COMPLEX *x)

// returneaza modulul numarului


// spre care pointeaza x
{ return sqrt (x->real * x->real + x->imaginar * x->imaginar);
}

8.4. Uniune
75

Limbajul C ofer utilizatorului posibilitatea de a folosi aceeai zon de memorie


pentru a pstra date de tipuri diferite n momente diferite ale execuiei programului. Astfel, de
exemplu, putem utiliza o zon de memorie pentru a pstra la un moment dat o dat flotant,
iar ulterior s reutilizm aceeai zon pentru o dat ntreag sau de tip pointer. Reutilizrile
zonelor de memorie conduc la utilizarea mai eficient a acesteia, uneori putndu-se obine o
economie substanial a spaiului de memorie alocat programului.
O uniune se declar printr-o construcie asemntoare declaraiei de structur.
Deosebirea const n nlocuirea cuvntului struct prin union:
union nume
{ tip_membru_1;
...
tip_membru_2;
};
Exemplu:
union u
{ int
i;
float f;
double d;
};
Prin aceast declaraie s-a definit tipul de date u. n continuare, putem declara date de
tipul u printr-o declaraie de forma:
union u u1;
unde u1 este o dat de tip u creia i se aloc o zon de memorie care poate fi utilizat pentru
a pstra date de tipurile int, float sau double. Deoarece tipul double necesit memoria cea mai
mare se aloc 8 octei. Astfel zona u1 poate pstra pe oricare din celelalte componente ale
uniunii dar n momente diferite ale execuiei programului.
Accesul la componentele unei uniuni se face la fel ca i n cazul structurilor. Astfel,
pentru a ne referi la componenta i a uniunii u1 definit n exemplul anterior folosim
construcia:
u1.i
sau dac p este pointer spre tipul u declarat prin
union u *p;
atunci construcia
p -> i
permite accesul la componenta i a uniunii spre care pointeaz p.
Pentru a evita erorile legate de evidena n fiecare moment a datei care se prelucreaz
se ataeaz unei uniuni o dat menit s indice componenta curent. Aceast dat este
specificat pentru fiecare uniune. De exemplu pentru uniunea u1 definit anterior este
important s se tie dac zona de memorie conine un ntreg, un flotant n simpl precizie sau
un flotant n dubl precizie. Se definesc trei constante simbolice:
#define INTREG 1
#define F_SIMPLU 2
#define F_DUBLU 3
76

Modificm tipul u atand data tip_curent de tip int astfel:


struct u
{int tip_curent;
union {
int
i;
float f;
double d;
} uu;
};
Declarm structura us astfel:
struct u us;
n acest caz, n momentul n care se pstreaz o dat n zona rezervat uniunii, se
atribuie componentei tip_curent una din constantele definite anterior:
- INTREG, dac se pstreaz un ntreg;
- F_SIMPLU dac se pstreaz un flotant n simpl precizie;
- F_DUBLU dac se pstreaz un flotant n dubl precizie.
Astfel cnd se folosete componenta de tip int se va asocia atribuirea:
us.tip_curent=INTREG;
Analog se vor folosi i atribuirile urmtoare cnd se vor folosi componentele de tip
float sau de tip double:
us.tip_curent=F_SIMPLU;
respectiv:
us.tip_curent=F_DUBLU;
n felul acesta, se poate testa, n fiecare moment, tipul de dat prezent n zona
rezervat. Aceasta se poate face printr-o secven de instruciuni if sau prin intermediul
instruciunii switch.
if (us.tip_curent = = INTREG)
else if (us.tip_curent = = FSIMPLU)
else if (us.tip_curent = = FDUBLU)
else eroare

// se foloseste us.uu.i
// se foloseste us.uu.f
// se foloseste us.uu.d

sau folosind switch avem o construcie mai clar de forma:


switch (us.tip_curent)
{ case INTREG:
// se foloseste us.uu.i
break;
case FSIMPLU:
// se foloseste us.uu.f
break;
case FDUBLU
// se foloseste us.uu.d
break;
default:
// eroare
}

Programul urmtor calculeaz ariile pentru urmtoarele figuri geometrice:


- cerc;
77

dreptunghi;
ptrat;
triunghi.

Programul citete datele pentru o figur geometric, calculeaz aria figurii respective
i scrie rezultatul:
La intrare se folosesc urmtoarele formate:
- pentru cerc
C raza;
- pentru dreptunghi D lungime laime;
- pentru ptrat
P latur;
- pentru triunghi
T latur latur latur;
- sfrit fiier
EOF.
n cazul triunghiului, se utilizeaz formula lui HERON pentru calculul ariei:
aria = sqrt (p*(p-a)(p-b)(p-b))
unde p este semiperimetrul, iar a, b, c sunt cele 3 laturi.
#include <stdio.h>
#include <math.h>
#define PI 3.14159265
#define EROARE -1
#define CERC 1
#define PATRAT 2
#define DREPT 3
#define TRIUNGHI 4
typedef struct
{
int tip;
union
{ double raza
double lp ;
double ld[2] ;
double lt[3] ;
} fig;
}FIG;

// tipul figurii
//
//
//
//

cerc
patrat
dreptunghi
triunghi

void main (void)


// calculeaza arii
{
double aria,p;
int i;
char car[2];
FIG zfig;
for(; ;)
// citeste primul caracter,el defineste tipul figurii geometrice
{ printf(se cere o litera mare\n);
if ((i = scanf("%1s",car)) == EOF)
break;
if (i != 1)
{ printf (" se cere o litera mare\n");
continue;
}
zfig.tip = EROARE;
switch(car[0])
{case 'C':
// cerc
printf("se cere raza cercului in flotanta\n");
i = scanf("%lf", &zfig.fig.raza);
if(i != 1)
{ printf("se cere raza cercului in flotanta\n");
break;

78

}
zfig.tip = CERC;
break;

// se pastreaza tipul figurii

case 'P':
// patrat
printf("se cere latura patratului in flotanta\n");
i = scanf("%lf",&zfig.fig.lp);
if( i !=1)
{ printf("se cere latura patratului in flotanta\n");
break;
}
zfig.tip = PATRAT;
break;
case 'D':
// dreptunghi
printf("se cer laturile dreptunghiului in flotanta\n");
i = scanf("%lf %lf",&zfig.fig.ld[0],&zfig.fig.ld[1]);
if(i != 2)
{ printf("se cer laturile dreptunghiului in flotanta\n");
break;
}
zfig.tip = DREPT;
break;
case 'T':
// triunghi
printf("se cer laturile triunghiului in flotanta\n");
i = scanf("%lf %lf %lf", &zfig.fig.lt[0], &zfig.fig.lt[1],&zfig.fig.lt[2]);
if(i != 3)
{ printf("se cer laturile triunghiului in flotanta\n");
break;
}
zfig.tip =TRI;
break;
printf("laturile nu formeaza un triunghi\n");
break;
default:
printf("se cere una din literele urmatoare\n");
printf("C pentru cerc\n");
printf("P pentru patrat\n");
printf("D pentru dreptunghi\n");
printf("T pentru triunghi\n");
} // sfarsit switch
switch (zfig.tip)
{case CERC:
// aria cercului
printf("raza=%g aria=%g\n", zfig.fig.raza, PI*zfig.fig.raza*zfig.fig.raza);
break;
case PATRAT:
// aria patratului
printf("latura =%g aria=%g\n",zfig.fig.lp, zfig.fig.lp*zfig.fig.lp);
break;
case DREPT:
// aria dreptunghiului
printf("lungimea =%g latimea =%g\n", zfig.fig.ld[0], zfig.fig.ld[1]);
printf("aria=%g\n", zfig.fig.ld[0]*zfig.fig.ld[1]);
break;
case TRIUNGHI:
// aria triunghiului
p=(zfig.fig.lt[0] + zfig.fig.lt[1] + zfig.fig.lt[2])/2;
if(p>zfig.fig.lt[0] && p>zfig.fig.lt[1] && p>zfig.fig.lt[2])
{p=p*(p-zfig.fig.lt[0])*(p-zfig.fig.lt[1])* (p-zfig.fig.lt[2]);
printf("a=%g b=%g c=%g\n", zfig.fig.lt[0], zfig.fig.lt[1], zfig.fig.lt[2]);
printf("aria = %g\n",sqrt(p));
}
else { printf ( laturile nu formeaza un triunghi);
break;

79

}
default :

// avans pana la newline sau EOF


while ((i = getchar()) != \n && i != EOF);
} // sfarsit switch
if (i = = EOF) break;
}
// sfarsit for
}
// sfarsit main

8.5. Cmp
Limbajul C permite utilizatorului definirea i prelucrarea datelor pe bii. Utilizarea
datelor pe bii este legat de folosirea indicatorilor care de obicei sunt date care iau numai
dou valori 0 sau 1.
Nu este justificat ca un astfel de indicator s fie pstrat ca un ntreg pe 16 bii i nici
mcar pe un octet. Indicatorul poate fi pstrat pe un singur bit. n acest scop, limbajul C ofer
posibilitatea de a declara date care s se aloce pe bii (unul sau mai muli bii). Acest lucru i
gsete aplicare n programele de sistem. Astfel, de exemplu, atributele variabilelor dintr-o
tabel de simboluri pot fi pstrate pe bii, ceea ce conduce la o economisire substanial a
memoriei ocupate de tabela respectiv.
Prin cmp nelegem un ir de bii adiaceni coninui ntr-un cuvnt calculator.
Cmpurile se grupeaz formnd o structur.
Un cmp se declar ca i o component a unei structuri i el are tipul unsigned (ntreg
fr semn). Totodat n declaraia cmpului se indic i dimensiunea lui n bii.
n general, o structur cu componente cmpuri are forma:
struct
{ camp1;
...
campn;
} nume;
unde campi (i=1,...,n) are unul din formatele de mai jos:
unsigned nume : lungime_n_bii
sau
: lungime_n_bii
Exemplu:
struct
{ unsigned a:1;
unsigned b:1;
unsigned c:2;
unsigned d:2;
unsigned e:3;
} indicatori;
Data indicatori se aloc ntr-un cuvnt calculator, adic pe 16 bii. Componentele ei
sunt:
-

a un bit;
b un bit;
c doi bii;
d doi bii;
e trei bii.
80

La cmpuri ne putem referi la fel ca i la componentele oricrei structuri. Deci la


indicatorii de mai sus ne putem referi prin urmtoarele construcii:
-

indicatori.a
indicatori.b
indicatori.c
indicatori.d
indicatori.e

Alocarea biilor este dependent de calculator. De obicei biii se aloc de la


dreapta spre stnga ca n figura de mai jos:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

a
b
c
d
e
Observaii:
1o. Dac un cmp nu poate fi alocat n limitele unui cuvnt, el se aloc n ntregime n
cuvntul urmtor.
2o. Nici un cmp nu poate avea o dimensiune mai mare dect 16 bii.
3o. Formatul fr nume (al doilea format) pentru cmp se folosete pentru cadraje. Acest lucru
este util atunci cnd sunt zone de bii neutilizate n cadrul unui cuvnt. De asemenea,
utilizarea formatului cu lungime egal cu zero permite ca alocarea cmpurilor urmtoare lui s
se fac n cuvntul urmtor.
4o. O structur care are i componente cmpuri poate avea i componente obinuite.
5o. Nu se pot defini tablouri de cmpuri.
6o. Unui cmp nu i se poate aplica operatorul adres.
Cmpurile se utilizeaz frecvent la scrierea unor programe de sistem, cum ar fi : drivere
pentru periferice, compilatoare, etc.
Utilizarea cmpurilor poate conduce la programe cu o portabilitate redus. Totodat,
accesul la date pe bii conduce la creterea numrului de operaii, fiind necesare deplasri i
operaii pe bii suplimentare, fapt ce poate conduce att la creterea timpului de execuie a
programelor, ct i la creterea memoriei utilizate. Ori datele pe bii se folosesc chiar n ideea
de a economisi memorie.

8.6. Tipul enumerat


Tipul enumerat permite utilizatorului s foloseasc n program nume sugestive n locul
unor valori numerice. De exemplu, n locul numrului unei luni calendaristice, se poate folosi
denumirea ei:
1 ian
2 feb
3 mar
81

n locul valorilor 0 i 1 se pot folosi cuvintele FALS i ADEVRAT.


Prin aceasta, se introduce o mai mare claritate n programe, deoarece valorile numerice
sunt nlocuite prin diferite sensuri atribuite lor.
Un tip enumerat se introduce printr-o declaraie de forma:
enum nume {nume0, nume1, . . . , numen};
Prin aceast declaraie se definete tipul enumerat nume, iar numei are valoarea i. O
form mai general a declaraiei de mai sus permite programatorului s foreze valorile
numelor din acolad. n acest scop, se pot folosi construcii de forma:
numei= eci
unde eci este o expresie constant de tip int.
Cu alte cuvinte, unui nume i se poate atribui o valoare sau valoarea lui coincide cu a
numelui precedent mrit cu 1. Dac primului nume din acolad nu i se atribuie o valoare, el
are valoarea 0. Numele nume0, nume1,. . . , numen trebuie s fie nume diferite. Ele sunt
constante i valoarea lor se stabilete prin declaraia n care au fost scrise. Domeniul lor de
valabilitate este definit de domeniul de valabilitate al declaraiei prin care se definesc:
- instruciunea compus care conine declaraia;
- fiierul surs n care este scris declaraia, dac este extern oricrei funcii.
Valorile atribuite lui nume0, nume1, . . . , numen sunt de obicei diferite, dar unele pot
s i coincid.
Dup ce s-a introdus un tip enumerat, se pot declara date de tipul respectiv
printr-o declaraie de forma:
enum nume lista_de_variabile;
Datele de tip enumerat se consider de tip int i se pot utiliza n program oriunde este
legal s apar o dat de tip int.
Observaii:
1o. Se pot utiliza, ca i n cazul structurilor, construcii de forma:
enum nume {nume0, nume1,. . . , numen} lista_de_variabile;
sau
enum { nume0, nume1,. . . , numen} lista_de_variabile;
2o. De asemenea, se poate utiliza construcia typedef pentru a atribui un nume unui tip
enumerat:
typedef enum nume {nume0, nume1,. . . , numen} NUME;
n continuare se pot declara date de tipul NUME, astfel:
NUME lista_de_variabile;
Exemple:
1) enum luna{ian=1,feb,mar,apr,mai,iun,iul,aug,sep,oct,nov,dec};
enum luna luna_calendaristica
Prin prima declaraie se introduce tipul enumerat luna. Mulimea de valori asociate
acestui tip este format din numerele ntregi 1,2, . . . , 12. Se pot utiliza denumirile:
82

ian ia valoarea 1
feb ia valoarea 2
...
dec ia valoarea 12
A doua construcie declar data luna_calendaristica de tipul luna. Ei i se pot atribui
valori prin expresii de atribuire de forma:
luna_calendaristica = mar sau
luna_calendaristica = mai + 4
2) typedef enum {luni, marti,miercuri,joi,vineri,sambata,duminica} ZI;
ZI z;
Variabila z este de tip ZI. Se poate utiliza n expresii de forma:
z=marti;
if(z<sambata)
// trateaza ziua de lucru
else
// trateaza zi de odihna

83

9. Liste
9.1. Date structurate definite recursiv
Limbajul C permite definirea de tipuri structurate recursiv (autorefereniate). Acest
lucru se face cu ajutorul pointerilor, i anume un element al structurii poate s fie un pointer
spre tipul de dat introdus prin structura respectiv:
struct nume
{ declaratii
struct nume *p;
declaratii
};
Un tip definit ca mai sus se spune c este un tip autoreferit sau recursiv. O dat
structurat declarat printr-un astfel de tip se spune c este autoreferit sau recursiv. Datele
structurate recursive au numeroase aplicaii n prelucrarea listelor nlnuite i arborescente.
Dou tipuri structurate t1 i t2 pot s conin fiecare un pointer spre celalalt. n acest
caz se va proceda ca mai jos:
struct t1;

// o declaratie inainte fara de care nu se poate


// declara tipul t2

struct t2
{ declaratii
struct t1 *pt1;
declaratii
};
struct t1
{ declaratii
struct t2 *pt2;
declaratii
};

9.2. Liste nlnuite


Datele structurate se pot organiza n tablouri sau n structuri recursive introducnd n
tipul structurat unul sau mai muli pointeri spre tipul structurat respectiv. Astfel se stabilete o
relaie de ordine (uneori chiar mai multe) ntre elementele mulimii de date structurate; de
asemenea, mulimea rescpectiv se poate organiza n mod dinamic, adugnd elemente noi
sau suprimndu-le pe cele care nu mai sunt necesare.
Definiie O mulime dinamic de structuri recursive de acelai tip i care satisfac una sau mai
multe relaii de ordine introduse prin pointeri se numete list nlnuit. Elementele listei se
mai numesc noduri.
Cele mai utilizate tipuri de list sunt:
- lista simplu nlnuit;
- lista circular simplu nlnuit;
- lista dublu nlnuit;
- lista circular dublu nlnuit;.
Cele patru tipuri de liste sunt exemplificate grafic astfel:
84

capul

list liniar simplu nlnuit;

capul

list liniar circular simplu nlnuit;


capul

list liniar dublu nlnuit;

capul

list liniar circular dublu nlnuit;

9.3. Lista liniar simplu nlnuit


O list simplu nlnuit; este o list nlnuit; ale crei noduri satisfac o singur
relaie de ordine introdus prin pointeri.
Tipul unui nod dintr-o list simplu nlnuit; se poate declara n dou moduri:
a)
struct tnod
{ declaratii
struct tnod *urmator;
declaratii
};

b)

typedef struct tnod


{ declaratii
85

struct tnod *urmator;


declaratii
} TNOD;
Observaii
1o. Varianta b) este mai folosit.
2o. Pointerul urmtor introduce o relaie de ordine ntre nodurile de tip TNOD.
3o. Ultimul nod al listei va avea pointerul urmator = NULL.
4o. Pentru nodurile interioare ale listei pointerul urmator va avea valori adrese; dac
urmator din nodul a pointeaz spre nodul b, spunem c nodul b este succesorul lui a.
Operaiile ce se pot efectua asupra unei liste simplu nlnuit;
- crearea listei;
- accesul la un nod al listei;
- inserarea unui nod nlnuit;
- tergerea unui nod dintr-o list;
- tergerea unei liste.
Memorarea listelor se poate face:
- dinamic (n memoria intern);
- static (n fiiere).
Pentru modul dinamic se va folosi funcia malloc la crearea listei ct i la inserarea de
noduri, iar la tergerea de noduri funcia free.

9.4. Crearea i afiarea unei liste


Vom crea o list ce conine n noduri informaii despre numere ntregi i ptratele lor.
Avem dou funcii: una de creare care ntoarce adresa capului listei i o funcie de afiare a
informaiei din noduri. Vom comenta instruciunile importante din program.
#include <stdio.h>
#include <alloc.h>
typedef struct nod
{
int nr;
int patrat;
struct nod* leg;
} NOD;

// definirea tipului NOD

NOD *creaza(void)
// functia de creare, intoarce adresa capului
{ NOD *cap,*p,*pc;
int i,lung;
printf("\n\n\n\ creare lista simplu inlantuita\n\n");
lung = sizeof(NOD);
pc=(NOD *)malloc(lung);
// pc este un pointer curent, in el se vor pune adresel noi
cap=pc;
printf("dati informatia elementului : ");
scanf ("%d",&i);

while (i>0)
{ p=pc;
p->nr=i;

// crearea listei se termina la numar negativ


// pointer ce pastreaza adresa noua
// incarcarea informatiei

86

p->patrat=i*i;
pc=(NOD *)malloc(lung);
// se cere o noua adresa de memorie
p->leg=pc;
// se leaga pointerul leg la noua adresa
printf("dati informatia elementului : ");
scanf ("%d",&i);
}
p->leg=NULL;
free(pc);
return cap;

// ultimul nod al listei are pointerul leg = NULL


// eliberarea ultimei adrese care de fapt nu face parte din lista
// returneaza adresa capului listei

}
void afisare(NOD *p)
// functia de afisare a listei
{
while (p != NULL)
// cat timp n-am ajuns la ultimul nod
{ printf ("\n numarul %d si patratul sau %d", p->nr,p->patrat);
p=p->leg;
// trecerea la urmatorul nod al listei
}
}
void main (void)
{ NOD *capul;
clrscr();
capul=creaza();
afisare(capul);
getch();
}

// functia principala

// lista e cunoscuta prin adresa capului

87

10. Prelucrarea fiierelor


10.1. Fiiere
n general, prin fiier nelegem o colecie ordonat de elemente numite nregistrri,
care sunt pstrate pe diferite suporturi de memorie extern. Suportul de memorie extern cel
mai folosit este suportul magnetic (de obicei discuri sub forma de flopy i hardiscuri sau
band magnetic care e din ce n ce mai rar folosit). Suportul magnetic este reutilizabil
deoarece zona utilizat pentru a pstra nregistrrile unui fiier poate fi ulterior reutilizat
pentru a pstra nregistrrile altui fiier.
Datele introduse de la un terminal se consider c formeaz un fiier de intrare.
nregistrarea n acest caz, de obicei, este format din datele tastate la terminal pe un rnd deci
caracterul de rnd nou (newline) este terminator de nregistrare. n mod analog, datele care se
afieaz pe terminal formeaz un fiier de ieire. nregistrarea poate fi format din caracterele
unui rnd.
Un fiier are o nregistrare care marcheaz sfritul de fiier. n cazul fiierelor de
intrare de la tastatur sfritul de fiier se genereaz prin:
CTRL/Z
El poate fi pus n eviden folosind constanta simbolic EOF definit n fiierul stdio.h.
Prelucrarea fiierelor implic un numr de operaii specifice acestora. Dou operaii
sunt absolut necesare la prelucrarea oricrui fiier:
- deschiderea fiierului;
- nchiderea fiierului.
Aceste operaii de deschidere i nchidere a unui fiier se pot realiza prin intermediul unor
funcii speciale din biblioteca standard a limbajului C. Alte operaii privind prelucrarea
fiierelor sunt:
- crearea unui fiier;
- consultarea unui fiier;
- actualizarea unui fiier;
- adugarea de nregistrri ntr-un fiier;
- poziionarea ntr-un fiier;
- tergerea unui fiier.
Prelucrarea fiierelor se poate face la dou nivele. Primul nivel face apel direct la sistemul
de operare i se numete nivelul inferior de prelucrare al fiierelor. Cel de-al doilea nivel de
prelucrare se realizeaz prin utilizarea unor proceduri specializate n prelucrarea fiierelor
care, printre altele, pot rezerva i gestiona automat zone tampon necesare realizrii operaiilor
de intrare/ieire, i se numete nivelul superior de prelucrare al fiierelor

10.2. Nivelul inferior de prelucrare al fiierelor


La acest nivel de prelucrare se folosesc 5 funcii:
- open (creat)- pentru deschiderea fiierelor;
- read
- pentru citirea din fiier;
- write
- pentru citirea din fiier;
- lseek
- pentru poziionarea n fiier;
- close
- pentru nchiderea fiierului.

88

10.2.1. Deschiderea unui fiier


Orice fiier nainte de a fi prelucrat trebuie deschis. Aceast operaie se realizeaz prin
intermediul funciei open al crui prototip este urmtorul:
int open (const char *cale, int acces);
unde:
- cale este un pointer spre un ir de caractere care definete calea spre fiierul care se
deschide (n cea mai simpl form este numele fiierului dac se afl n directorul curent)
- acces este o variabil de tip ntreg care poate lua una din valorile:
- O_RDONLY
- fiierul se deschide numai n citire (consultare);
- O_WRONLY
- fiierul se deschide numai n scriere (creare);
(sau O_CREAT)
- O_RDWR
- fiierul se deschide n citire/scriere;
- O_APPEND- fiierul se deschide la sfrit pentru adugare;
- O_BINARY - fiierul se prelucreaz binar;
- O_TEXT
- fiierul este de tip text.
Unele valori din cele de mai sus se pot combina cu ajutorul operatorului |. De exemplu
O_RDWR | O_BINARY pentru deschiderea fiierului n scriere/citire binar.
Observaii:
1o. Funcia open ntoarce descriptorul de fiier care este o valoare intreag ce va identifica
fiierul n toate celelalte operaii care se vor realiza asupra lui. Dac deschiderea unui fiier nu
reuete (de obicei unul din parametrii este eronat) atunci funcia open returneaz valoarea 1.
2o. Pentru a putea utiliza funcia open trebuie incluse fiierele header io.h i fcntl.h.
3o. Pentru a crea un fiier nou se va folosi funcia creat n locul funciei open cu prototipul:
int creat (const char *cale, int mod);
unde:
- cale are aceeai semnificaie ca i la funcia open;
- mod este un ntreg care poate fi definit prin constantele simbolice de mai jos:
S_IREAD
- se poate citi fiierul;
S_IWRITE - se poate scrie n fiier;
S_IEXEC
- se poate executa programul coninut n fiier.
Utilizarea funciei presupune includerea fiierelor io.h i stat.h
4 . Implicit fiierul se consider c este de tip text.
o

Exemple:
1)
char nume_fisier[ ]=fis1.dat;
int df;
df = open (nume_fisier, O_RDONLY);
Prin apelul de mai sus se deschide n citire fiierul fis1.dat din directorul curent.
2)

int df;
df = open (c:\\borlandc\\help.txt,O_APPEND);

Se deschide n adugare fiierul help.txt din directorul borlandc de pe discul C.

10.2.2. Citirea dintr-un fiier (consultare)


89

Funcia folosit pentru operaia de citire dintr-un fiier n memorie se numete read i
are prototipul urmtor:
int read (int df, void *buf, unsigned lung);
unde:
- df este descriptorul de fiier a crui valoare a fost definit la deschidere;
- buf este pointerul spre zona de memorie n care se recepioneaz nregistrarea care se
citete;
- lung este lungimea n octei a inregistrrii citite.
Observaii:
1o. La fiecare apel funcia returneaz nregistrarea curent. La primul apel se citete prima
nregistrare din fiier, la al doilea apel se citete a doua, etc. Ordinea nregistrrilor n fiier
este cea definit la crearea fiierului.
2o. La un apel al funciei read se citesc cel mult lung octei nregistrarea avnd definit
lungimea la scrierea n fiier. Funcia rentoarce numrul de octei citii, 0(zero) la sfrit de
fiier, sau 1 la eroare. De obicei se folosesc frecvent nregistrri de 512 octei sau chiar mai
mari.
3o. Funcia read poate fi folosit pentru a citi de la intrarea standard. n acest caz,
descriptorul de fiier este 0 (stdin are 0, stdout are 1, stderr are 2 stdprn are 3 stdaux are 4).
Programatorul nu trebuie s deschid fiierele standard deoarece ele sunt deschise automat la
lansarea n execuie a programului.
4o. Utilizarea funciei read, presupune includerea fiierului io.h.

10.2.3. Scrierea ntr-un fiier (creare, actualizare, adugare)


Pentru a scrie ntr-un fiier se folosete funcia write. Se presupune c fiierul este
deschis n prealabil prin funcia creat sau open. Ea este asemntoare cu funcia read, doar c
realizeaz transferul invers, adic din memorie n fiier i are prototipul:
int write (int df, void *buf, unsigned lung);
Observaii:
1o. Funcia returneaz numrul octeilor scrii n fiier. Acesta este egal cu lung i definete
lungimea nregistrrii scrise n fiier. n cazul n care numrul returnat de funcia write difer
de parametrul lung scrierea a fost eronat i se rentoarce valoarea 1.
2o. Utilizarea funciei write implic includerea fiierlui io.h.

10.2.4. Poziionarea ntr-un fiier


Pentru a avea acces aleator la nregistrrile unui fiier se folosete o funcie de
poziionare n fiier pe anumite nregistrri dorite. Pe fiierele care au suporturi magnetice
este posibil poziionarea cu ajutorul funciei lseek care are prototipul urmtor:
long lseek (int df, long deplasament, int origine)
unde:
- df
- deplasament

este descriptorul de fiier;


definete numrul de octei peste care se va deplasa capul de
scriere/citire al discului;
90

origine

are una din valorile:


0 deplasamentul se consider de la nceputul fiierului;
1 deplasamentul se consider din poziia curent a capului de
scriere/citire;
2 deplasamentul se consider de la sfritul fiierului.

Observaii:
1o. Funcia returneaz poziia capului de citire/scriere fa de nceputul fiierului n numr de
octei sau 1L la eroare.
2o. Funcia nu realizeaz nici un transfer de informaie ci doar poziioneaz capul de
citire/scriere n fiier. Deci pentru transfer e nevoie de funciile read sau write.
3o. Utilizarea funciei presupune includerea fiierului io.h.
4o. Apelul lseek (df, 0L, 0) permite o poziionare la nceput de fiier,
iar apelul lseek (df, 0L, 2) permite o poziionare la sfrit de fiier.

10.2.5. nchiderea unui fiier


La sfritul prelucrrii unui fiier acesta trebuie nchis. Acest lucru se realizeaz
automat dac programul se termin prin apelul funciei exit. Programatorul poate nchide un
fiier folosind funcia close. Se recomand nchiderea unui fiier de ndat ce s-a terminat
prelucrarea lui. Aceasta din cauz c numrul fiierelor ce pot fi deschise simultan este
limitat. Limita este dependent de sistemul de operare i ea variaz, de obicei, n intervalul
15-25. De obicei numrul de buffere (zone tampon) asociate fiierelor se precizeaz n
fiierul autoexec.bat. Menionm c fiierele standard din limbajul C nu se nchid de
programator.
Funcia close are prototipul urmtor:
int close (int df);
unde
df este descriptorul fiierului care se nchide.
Observaii:
1o. La o nchidere normal, funcia returneaz valoarea 0 i 1 n caz de incident.
2o. Utilizarea funciei close implic includerea fiierului io.h.
Exemple
1) Vom deschide fiierul fis1.dat n creare i vom scrie n el dou nregistrri.
#include<io.h>
#include<fcntl.h>
void main (void)
{ int df,i;
df = open("fis1.dat", O_CREAT);
if (df != -1)
{ write (df,"cioban vasyle\n", 14);
write (df,"cioban andrei\n", 14);
} else printf (nu s-a deschis fisierul);
close (df);
}

// se deschide fisierul fis1.dat in creare


// se testeaza daca fiserul s-a deschis corect
// se scriu doua inregistrari

// se inchide fisierul

2) Se afieaz coninutul fiierului fis1.dat


#include<io.h>
#include<fcntl.h>
#include <stdio.h>
#include <conio.h>

91

void main (void)


{int df;
char s[14];
df = open("fis1.dat", O_RDONLY);
if (df != -1)
{ read (df, s, 14);
printf ("%.14s",s);
read (df, s, 14);
printf ("%.14s",s);
} else printf (nu s-a deschis fisierul);
close (df);
getch();
}

// se deschide fisierul n citire


// se testeaza daca deschiderea e corecta

// se asteapta un caracter de la tastatura

3) programul urmtor copiaz intrarea standard la ieierea standard folosind o zon tampon
de 80 de caractere:
#define LZT 80
#include <io.h>
void main (void)
// copiaza intrarea standard la iesirea standard
{ char zona_tampon[LZT];
int i;
while ((i = read (0, zona_tampon, LZT)) > 0) write (1, zt, i);
}

10.3. Nivelul superior de prelucrare a fiierelor


Dup cum am amintit, la acest nivel fiierele se prelucreaz cu ajutorul unor proceduri
specializate.

10.3.1. Deschiderea unui fiier


Funcia fopen se utilizeaz pentru deschiderea unui fiier. Ea returneaz un pointer
spre tipul FILE (tipul fiier), tip definit n fiierul stdio.h. Tipul FILE este un tip structurat i
el depinde de sistemul de operare. n caz de eroare, funcia fopen returneaz pointerul NULL.
Prototipul funciei fopen este urmtorul:
FILE *fopen (const char *cale, const char *mod);
unde:
- cale are aceeai semnificaie ca i n cazul funciilor open i creat.
- mod este un pointer spre un ir de caractere care definete modul de prelucrare al
fiierului dup deschidere. Acest ir de caractere se definete n felul urmtor:
- r
- deschidere n citire (read);
- w
- deschidere n scriere (write);
- a
- deschidere pentru adugare;
- r+
- deschidere pentru modificare (citire sau scriere);
- rb
- citire binar;
- wb
- scriere binar;
- r+b
- citire/scriere binar.
Observaii:
1o. Dac se deschide un fiier inexistent cu modul w sau a, atunci el este deschis n
creare.
92

2o. Dac se deschide un fiier existent cu modul w, atunci coninutul vechi al fiierului se
pierde i se va crea unul nou cu acelai nume.
3o. Menionm c, stdin, stdout, stderr, stdaux i stdprn sunt pointeri spre tipul FILE i
permit ca funciile de nivel superior de prelucrare a fiierelor s poat trata intrarea standard i
ieirile standard pe terminal i imprimant la fel ca i fiierele pe celelalte suporturi. Singura
deosebire const n aceea c aceste fiiere nu se deschid i nici nu se nchid de ctre
programator. Ele sunt deschise automat la lansarea n execuie a programului i se nchid la
apelul funciei exit.
4o. Apelul funciei se realizeaz prin construcia:
FILE *pf;
pf = fopen (FIS1.DAT,w);

10.3.2. Prelucrarea pe caractere a unui fiier


-

Fiierele pot fi scrise i citite caracter cu caracter, folosind dou funcii simple:
putc pentru scriere;
getc pentru citire.
Funcia putc are prototipul:

int putc (int c, FILE *pf);


unde:
- c este codul ASCII al caracterului care se scrie n fiier;
- pf este pointerul spre tipul FILE a crui valoare a fost returnat de funcia fopen la
deschiderea fiierului n care se scrie; pf poate fi i stdout, sdterr, stdaux, stdprn.
Funcia putc returneaz valoarea lui c respectiv 1 n caz de eroare.
Funcia getc are prototipul:
int getc (FILE *pf);
unde:
- pf este pointerul spre tipul FILE a crui valoare a fost returnat de funcia fopen la
deschiderea fiierului; n particular pf poate fi stdin.
Funcia getc returneaz codul ASCII al caracterului citit sau EOF la sfrit de fiier sau
eroare.

10.3.3. nchiderea unui fiier


nchiderea unui fiier se realizeaz cu ajutorul funciei fclose care are prototipul:
int fclose (FILE *pf);
unde:
- pf este pointerul spre tipul FILE a crui valoare a fost definit la deschiderea
fiierului prin intermediul funciei fopen.

Funcia fclose returneaz:


- 0 la nchiderea normal a fiierului;
- 1 n caz de eroare.
Exemple:

93

1) Programul urmtor copiaz intrarea standard la ieirea standard stdout, folosind funciile
getc i putc.
#include <stdio.h>
void main (void)
{ int c;
while (( c = getc (stdin)) != EOF) putc (c, stdout);
}

2) Programul urmtor copiaz intrarea standard la imprimant.


#include <stdio.h>
void main (void)
{ int c;
while (( c = getc (stdin)) != EOF) putc (c, stdprn);
}

3) Programul urmtor scrie la ieirea stdout caracterele unui fiier a crui cale este
argumentul din linia de comand. Dac nu exist un argument n linia de comand, atunci
se citete de la intrarea standard.
#include <stdio.h>
void main (int argc, char *argv[ ] )
{ FILE *pf;
int c;
if (argc = = 1)
pf = stdin;
// nu exista argument in linia de comanda
else
// se deschide fisierul a carui cale se afla in argv[1]
if (( pf = fopen (*++argv,r)) = = NULL)
{ printf (nu se poate deschide fisierul %s\n,*argv);
exit (1);
}
while (( c = getc (pf)) != EOF)
putchar(c);
exit (0);
}

10.3.4. Operaiile de intrare-ieire cu format


Biblioteca standard a limbajului C conine funcii care permit realizarea operaiilor de
intrare/ieire cu format. Astfel se pot utiliza funciile fscanf i fprintf, prima pentru citire cu
format dintr-un fiier, iar a doua pentru scriere cu format ntr-un fiier.
Funcia fscanf este asemntoare cu funcia scanf. Ea are un parametru n plus fa de
scanf. Acest parametru este un pointer spre tipul FILE i el definete fiierul din care se face
citirea. Acest pointer este primul parametru al funciei fscanf. Funcia poate fi apelat printr-o
expresie de atribuire de forma:
nr = fscanf (pf, control, lista_de_parametrii );

unde :
- pf este un pointer spre tipul FILE i valoarea lui a fost definit prin apelul funciei fopen;
definete fiierul din care se face citirea;
- ceilali parametri sunt identici cu cei utilizai la apelul funciei scanf.

94

Funcia fscanf, ca i funcia scanf, returneaz numrul cmpurilor citite din fiier. La
ntlnirea sfritului de fiier se returneaz valoarea EOF definit n fiierul stdio.h. Pentru pf
= stdin, funcia fscanf este identic cu scanf.
Funcia fprintf , ca i funcia printf, returneaz numrul caracterelor scrise n fiier
sau 1 n caz de eroare. Pentru pf = stdout, funcia fprintf este identic cu printf. De
asemenea, se pot utiliza pointerii standard obisnuii: stderr, stdaux, stdprn.
Exemplu:
Vom scrie un program cu ajutorul cruia se va crea un fiier cu numele "FIS.DAT" i care
conine nregistrri cu numele, prenumele i adresa unor persoane.
#include <stdio.h>
void main(void)
{
int n=0, i=1;
// n este numarul de nregistrari ce se va scrie in fisier
char nume[25], prenume[30], adresa[50];
FILE *pf;
printf("\n Dati numarul de inregistrari n= ");
scanf("%d",&n);
pf=fopen("FIS.DAT","w");
if (pf = = NULL)
{ printf ("Eroare la deschidere");
return;
}
do
{
printf("\n Nume : ");
scanf("%s",nume);
printf("\n Prenume : ");
scanf("%s",prenume);
printf("\n Adresa : ");
scanf("%s",adresa);
fprintf(pf,"%s %s %s", nume, prenume, adresa);
i++;
} while (i<=n);
fclose(pf);
}

10.3.5. Intrri-ieiri de iruri de caractere


Biblioteca standard a limbajului C conine funciile fgets i fputs care permit citirea
respectiv scrierea ntr-un fiier ale crui nregistrri sunt iruri de caractere.
Funcia fgets are prototipul:
char *fgets (char *s, int n, FILE *pf);
unde:
- s este pointerul spre zona n care se face citirea caracterelor;
- n-1 este numrul maxim de caractere care se citesc;
- pf este pointerul spre tipul FILE care definete fiierul din care se face citirea.
De obicei s este numele unui tablou de tip char de dimensiune cel puin n. irul se
termin cu \0 (caracterul NUL). La ntlnirea caracterului \n, citirea se oprete. n acest
caz, n zona receptoare se transfer caracterul \n i apoi caracterul NUL (\0).
n mod normal, funcia returneaz valoarea pointerului s. La ntlnirea sfritului de
fiier se returneaz valoarea NULL.

95

Funcia fputs scrie ntr-un fiier un ir de caractere care se termin prin \0. Ea are
prototipul:
int fputs (const char *s, FILE *pf);
unde:
- s este pointerul spre zona care conine irul de caractere care se scrie;
- pf este pointerul spre zona care conine irul de caractere care se scrie.
Funcia fputs returneaz codul ASCII al ultimului caracter scris sau 1 n caz de
eroare.
Aceste funcii sunt realizate folosind funcia getc pentru fgets i putc pentru fputs.
Pentru a citi de la intrarea standard stdin, se poate folosi funcia gets, care nu mai are
parametrii pf i n. Parametrul pf este implicit stdin. Funcia gets citete caracterele de la
intrarea standard pn la ntlnirea caracterului \n care nu mai este pstrat n zona spre care
pointeaz s. irul de caractere citit se termin i n acest caz prin \0.
n mod analog, pentru a scrie la ieirea standard stdout se poate folosi funcia puts,
care nu mai are parametrul pf, acesta fiind implicit stdout. n rest, funcia puts este la fel ca i
funcia fputs.

10.3.6. Poziionarea ntr-un fiier


Cu ajutorul funciei fseek se poate deplasa capul de citire/scriere al discului n vederea
prelucrrii nregistrrilor fiierului ntr-o ordine oarecare, diferit de cea secvenial (acces
aleator). Aceast funcie este asemntoare cu funcia lseek. Ea are prototipul urmtor:
int fseek (FILE *pf, long deplasament, int origine);
unde:
- pf este pointerul spre tipul FILE care definete fiierul n care se face poziionarea capului
de citire/scriere;
- deplasament i origine au aceeai semnificaie ca i n cazul funciei lseek.
Funcia fseek returneaz valoarea zero la poziionare corect i o valoare diferit de zero
n caz de eroare.
Funcia ftell indic poziia capului de citire n fiier. Ea are prototipul:
long ftell (FILE *pf);
unde:
- pf este pointerul spre tipul FILE care definete fiierul n cauz.
Funcia returneaz o valoare de tip long care definete poziia curent a capului de
citire/scriere, i anume reprezint deplasamentul n octei a poziiei capului fa de nceputul
fiierului.

10.3.7. Prelucrarea fiierelor binare


Fiierele organizate ca date binare (octeii nu sunt considerai ca fiind coduri de
caractere) pot fi prelucrate la acest nivel folosind funciile fread i fwrite. n acest caz se
consider c nregistrarea este o colecie de date structurate numite articole. La o citire, se
transfer ntr-o zon special, numit zon tampon, un numr de articole care se presupune
96

c au o lungime fix. n mod analog, la scriere se transfer din zona tampon un numr de
articole de lungime fix. Cele dou funcii au prototipurile de mai jos:
unsigned fread (void *ptr, unsigned dim, unsigned nrart, FILE *pf);
unde:
- ptr
- dim
- nrart
- pf

este pointerul spre zona tampon ce conine articolele citite (nregistrarea citit);
este un ntreg ce reprezint lungimea unui articol;
este un ntreg ce reprezint numrul articolelor care se transfer;
este un pointer spre tipul FILE care definete fiierul din care se face citirea.
unsigned fwrite (const void *ptr, unsigned dim, unsigned nrart, FILE *pf);

Parametrii funciei fwrite au aceeai semnificaie ca i parametrii funciei fread.


Ambele funcii returneaz numrul articolelor transferate sau 1 n caz de eroare.
Exemplu:
Programul urmtor citete de la intrarea standard stdin datele ale cror formate sunt definite
mai jos i le scrie n fiierul miscari.dat din directorul curent.
tip
1

denumire
TELVIZOR

um
buc

cod
0001

pret
3000000

cantitate
200

#include <stdio.h>
#define MAX 60
typedef struct { char tip[2];
char den[MAX];
int val;
char unit[3];
long cod;
float pret;
float cant;
} ARTICOL;

union {

// 6 articole in zona tampon


ARTICOL a[6];
char zt[6*sizeof(ARTICOL)];

}buf;
int cit (ARTICOL *str)
// citeste datele de la intrarea standard si le scrie in
{
// structura spre care pointeaza str
int nr,c;
float x,y;
while ((nr = scanf("%1s %60s %3d %2s %ld", str -> tip, str ->den,
&str ->val, str -> unit,&str -> cod)) != 5|| scanf("%f %f", &x,&y) != 2)

{
if(nr = = EOF) return (EOF);
printf ("rind eronat ; se reia\n");
// avans pina la newline
while ((c = getchar ()) != '\n' && c != EOF);
if (c == EOF) return (EOF);
}
// sfarsit while
str -> pret = x;

97

str -> cant = y;


return (nr);
}

// sfarsit cit

void main (void)


// creaza fisierul miscari.dat cu datele citite de la intrarea standard
{
FILE *pf;
ARTICOL a;
int i,n;
if((pf = fopen("miscari.dat", "wb")) == NULL)
{
printf("nu se poate deschide in creare fisierul miscari.dat\n");
exit(1);
}
for ( ; ; )
{
// se umple zona tampon a fisierului
for (i = 0 ;i<6 ; i++)
{
if((n=cit(&a)) == EOF) break;
buf.a[i]=a;
}
// se scrie zona tampon daca nu este vida
if(i !=0 )
if(i!=fwrite(buf.zt, sizeof(ARTICOL), i, pf))
{ printf("eroare la scrierea in fisier\n");
exit(1);
}
if(n = = EOF) break;
}
fclose(pf);
}

10.4. tergerea unui fiier


Un fiier poate fi ters apelnd funcia unlink. Aceasta are prototipul:
int unlink (const char *cale);
unde:
- cale este un pointer spre un ir de caractere identic cu cel utilizat la crearea fiierului n
funcia creat sau fopen.
Funcia unlink returneaz valoarea zero la o tergere reuit, respectiv -1 n caz de
eroare.

98

11. Funcii standard


Vom descrie cteva clase de funcii, numite clase de funcii standard aflate n
bibliotecile mediului BORLAND C. Funciile i macrourile utilizate frecvent n majoritatea
aplicaiilor se pot grupa n urmtoarele clase:
a)
b)
c)
d)
e)
f)
g)
h)
i)
j)

funcii de prelucrare a fiierelor;


funcii de alocare dinamic a memoriei;
macrouri de clasificare;
macrouri de transformare a simbolurilor;
funcii care realizeaz conversii;
funcii de prelucrare a irurilor de caractere;
funcii de calcul;
funcii pentru controlul proceselor;
funcii de gestiune a datei i a orei;
funcii de gestiune a ecranului.

Funciile de la punctele a) i b) au fost descrise n lecii anterioare.

11.1. Macrouri de clasificare


n aceast clas distingem un numr de macrouri simple care au o utilizare larg n
prelucrarea simbolurilor. Definiiile acestor macrouri se afl n fiierul ctype.h
Unul dintre macrouri este denumit isascii i are prototipul:
int isascii (int c);
Macroul returneaz o valoare diferit de zero dac valoarea lui c aparine intervalului
de numere ntregi [0,127] i zero n caz contrar. Acest macrou permite s se testeze dac
valoarea parametrului su reprezint un cod ASCII sau nu.
Celelalte macrouri au prototipul urmtor:
int nume (int c);
unde nume este unul din urmtoarele:
isalpha
isalnum
isdigit
isgraph
islower
isprint
isspace
isupper
isxdigit

dac c este codul unei litere;


dac c este codul unei litere sau cifre;
dac c este codul unei cifre;
dac c este codul unui caracter imprimabil inclusiv spaiul;
dac c este codul unei litere mici;
dac c este codul unui caracter imprimabil inclusiv spaiu;
dac c reprezint spaiu, tabulator, retur de car, rnd nou,
tabulator vertical, salt la nceput de pagin de imprimant.
dac c este codul unei litere mari;
dac c este codul unei cifre hexazecimale.

99

Exemplu:
Programul urmtor citete un fiier i l rescrie schimbnd literele mari cu litere mici.
Cile spre cele dou fiiere (surs i destinaie) sunt argumente n linia de comand:
argv[1]este un pointer spre fiierul surs;
argv[2]este un pointer spre fiierul destinaie.
#include <stdio.h>
#include <ctype.h>
void main ( int argc, char *argv[ ] )
{ FILE *pf1, *pf2;
int c;
if (argc != 3)
{ printf (linia de comanda eronata\n);
exit(1);
}
if (( pf1 = fopen (argv[1],r) ) = = NULL
{ printf (nu se poate deschide fisierul%s\n, argv[1]);
exit(1);
}
if (( pf2 = fopen (argv[2],w) ) = = NULL
{ printf (nu se poate deschide fisierul%s\n, argv[2]);
exit(1);
}
while (( c = getc (pf1)) != EOF)
if (isascii(c) && isupper (c))
putc (c - A + a, pf2);
// c este codul unei litere mari
else
putc(c, pf2);
// c este codul unei litere mici
fclose (pf1);
fclose (pf2);
// afisarea fisierului destinatie
if (( pf2 = fopen (argv[2], r) = = NULL)
{ printf (nu se poate deschide in citire:%s\n, argv[2]);
exit(1);
}
while ((c = getc(pf2)) != EOF) putchar (c);
fclose (pf2);
}

11.2. Macrouri de transformare a simbolurilor


n aceast clas distingem macrouri definite tot n fiierul ctype.h. Prototipurile
acestor macrouri sunt:
int toascii (int c);
returneaz ultimii 7 bii ai lui c (care reprezint un
cod ASCII);
int tolower (int c);
transform pe c din liter mare n liter mic;
int toupper (int c);
transform pe c din liter mic n liter mare.

11.3. Conversii
100

O dat are un format extern i un format intern. Prin conversie nelegem o


transformare a unei date dintr-un format al ei n cellalt. Conversiile se pot face sub controlul
unui format sau fr format. Dintre funciile care realizeaz conversii sub controlul formatelor
amintim:
printf;
fprintf;
scanf;
fscanf;
Aceste funcii au fost descrise n leciile anterioare. Vom da n continuare cteva
funcii care realizeaz conversii fr format i care sunt utilizate mai frecvent. Aceste funcii
au prototipurile n fiierul stdlib.h.
Funcia atoi are prototipul:
int atoi (const char *ptr);
unde:
- ptr este un pointer spre o zon de tip caracter ce conine cifre zecimale care
sunt, eventual, precedate de semnul minus;
Efectul:
-

irul de cifre spre care pointeaz ptr este convertit din ntreg zecimal n ntreg
binar de tip int.
Observaie:
1o. Funcia returneaz rezultatul acestei conversii.
Funcia atol are prototipul:
long atol (const char *ptr);
unde:
-

Efectul:
-

ptr este un pointer spre o zon de tip caracter ce conine cifre zecimale care
sunt, eventual, precedate de semnul minus;

irul de cifre spre care pointeaz ptr este convertit din ntreg zecimal n ntreg
binar de tip long.

Observaie:
1o. Funcia returneaz rezultatul acestei conversii.
Funcia atof are prototipul:
double atof (const char *ptr);
unde:
-

ptr este un pointer spre o zon de tip caracter ce conine cifre zecimale care
sunt, eventual, precedate de semnul minus (poate conine marca zecimal);

Efectul:
-

irul de cifre spre care pointeaz ptr este convertit n virgul flotant dubl
precizie.
Observaie:
1o. Funcia returneaz rezultatul acestei conversii.
Funcia itoa are prototipul:
101

char *itoa (int val, char *sir, int baza)


Efectul:
-

valoarea parametrului val se convertete din ntreg binar de tip int n baza de
numeraie definit de parametrul baza i se pstreaz n zona spre care pointeaz
sir.
Observaie:
1o. Funcia returneaz pointerul sir.
Funcia ltoa are prototipul:
char *ltoa (long val, char *sir, int baza)
Efectul:
-

valoarea parametrului val se convertete din ntreg binar de tip long n baza de
numeraie definit de parametrul baza i se pstreaz n zona spre care pointeaz
sir.
Observaie:
1o. Funcia returneaz pointerul sir.

11.4. Funcii de prelucrare a irurilor de caractere


Funciile din aceast clas implic includerea fiierului string.h. Indicm mai jos
funciile din aceast clas, utilizate mai frecvent. O parte din aceste funcii au mai fost
utilizate n diferite exemple din leciile anterioare.
Funcii de copiere:
char *strcpy (char *dest, const char *sursa);
char *strncpy (char *dest, const char *sursa, unsigned n);
-

prima funcie copiaz irul de caractere spre care pointeaz sursa n zona spre care
pointeaz dest;
a doua funcie realizeaz acelai lucru, dar copiaz cel mult primii n octei din
surs;
ambele funcii returneaz valoarea pointerului dest.

Funcii de concatenare:
int strcmp (const char *dest, const char *sursa);

char *strncat (const char *dest, const char *sursa, unsigned n);
prima funcie copiaz irul spre care pointeaz sursa la sfritul irului din zona
spre care pointeaz dest;
a doua funcie realizeaz acelai lucru, dar se copiaz cel mult primii n octei din
zona spre care pointeaz sursa;
ambele funcii returneaz valoarea pointerului dest.

Funcii de comparare:
int strcmp (const char *sir1, const char *sir2);
int stricmp (const char *sir1, const char *sir2);
int strncmp (const char *sir1, const char *sir2, unsigned n);
102

int strnicmp (const char *sir1, const char *sir2, unsigned n);
-

aceste funcii compar irurile de caractere din zonele spre care pointeaz pointerii
sir1 i sir2;

ele returneaz o valoare:


- negativ, dac irul spre care pointeaz sir1 este mai mic dect cel spre care
pointeaz sir2;
- zero, dac cele dou iruri sunt egale;
- pozitiv, dac irul spre care pointeaz sir1, este mai mare dect cel spre care
pointeaz sir2;
prezena literei i (ignore) n numele funciei nseamn c nu se face distincie ntre
literele mari i mici;
prezena literei n n numele funciei nseamn c se realizeaz comparaia pe cel
mult n octei.

Observaie:
1o. Fie irurile s1 i s2 de lungime l1 i l2. Atunci cele dou iruri sunt egale dac:
l1=l2 ( au aceeai lungime);
s1[k] = s2 [k] pentru k=0,1,...,l1
2o. irul s1 este mai mic dect irul s2 dac exist un j, j 0 i j min (l1, l2), astfel nct:
s1[j] < s2[j];
s1[k] = s2[k], pentru k=0,1, . . . , j-1.
3o. irul s1 este mai mare dect irul s2 dac exist un j, j 0 i j min(l1, l2), astfel inct:
s1[j] > s2[j];
s1[k] = s2[k], pentru k=0,1, . . . , j-1.
Funcia lungime:
unsigned strlen (const char *sir);
-

returneaz lungimea irului de caractere spre care pointeaz sir;


caracterul NUL care termin irul nu este numrat.

11.5. Funcii de calcul


Majoritatea funciilor matematice au prototipurile n fiierul math.h. Multe dintre
acestea se utilizeaz la calculul valorilor funciilor elementare i au prototipul:
double nume (double x);
unde nume este unul din urmtoarele:
acos -arccos;
asin -arcsin;
atan -arctg;
cos
-cos;
sin
-sin;
exp
-ex;
log
-ln;
103

log10
sqrt
ceil
floor
fabs
sinh
cosh
tanh

-lg;
-rdcina ptrat;
-returneaz cel mai mare ntreg mai mare sau egal cu x (partea ntreag);
-returneaz cel mai mare ntreg mai mic sau egal cu x;
-valoarea absolut;
-sinus hiperbolic;
-cosinus hiperbolic;
-tangent hiperbolic;

Alte funcii:
double atan2 (double y, double x); - returneaz arctg(y/x);
double pow (double x, double y);
- returneaz xy;
double cabs (struct complex z);
- returneaz modulul numrului
complex;
double poly (double x, int n, double c[ ] ) - returneaz valoarea polinomului
de grad n n punctul x, coeficienii
sunt c[0], . . . c[n].
Funciile care urmeaz au prototipul n fiierele stdlib.h i math.h:
int abs (int n); - returneaz valoarea absolut din ntregul n;
long labs (long n);
- returneaz valoarea absolut din ntregul long n;

11.6. Funcii pentru controlul proceselor


Aceste funcii au prototipurile n fiierul stdlib.h i n process.h i realizeaz controale
asupra programelor:
void abort (void);
- termin un program n caz de eroare;
void exit (int stare)
- termin un program i returneaz o stare; stare este
0 pentru terminare normal i diferit de zero
pentru o terminare anormal; videaz buferele
fisierelor, nchide toate fiierele;
int system (const char *comanda)

- execut o comand DOS definit prin sirul


de caractere spre care pointeaz comanda;
returneaz 0 la succes si 1 la eroare.
Aceste funcii au prototipurile n stdlib.h i n process.h.

11.7. Funcii pentru gestiunea datei i orei


Dm mai jos prototipurile a patru funcii pentru citirea/setarea datei i orei. Ele
implic includerea fiierului dos.h.
void getdate(struct date *d); - ncarc structura de tip date spre care pointeaz d
cu datele corespunztoare furnizate de sistemul de
operare;
void gettime(struct time *t); - ncarc structura de tip time spre care pointeaz t
104

cu datele corespunztoare furnizate de sistemul de


operare;
void setdate (struct date *t); - seteaz data curent n conformitate cu datele de
tip date;
void settime (struct time *t); - seteaz ora curent n conformitate cu datele de tip
time spre care pointeaz t.
Tipurile date i time sunt definite n fiierul dos.h. astfel:
struct date {
int da_year;
int da_day;
int da_mon;
};
struct time {
unsigned char ti_min;
unsigned char ti_hour;
unsigned char ti_hund;
unsigned char ti_sec;
};
Exemplu:
void main (void)
// afiseaza data si ora
{
struct date d;
struct time t;
getdate (&d);
gettime (&t);
printf (\n\t%02d/%02d/%04d,d.da_day, d.da_mon, d.da_year);
printf (\tora %02d:%02:%02\n, t.ti_hour, t.ti_min, t.ti_sec);
}

11.8. Alte funcii diverse de uz general


void clrscr (void);
void delay(unsigned i);
void sleep(unsigned i);
void nosound (void);
void sound(unsigned h);

- terge fereastra activ sau tot ecranul; prototipul n conio.h


- suspend execuia programului pentru o perioad de i
milisecunde;
- suspend execuia programului pentru o perioad de i
secunde;
- oprete difuzorul calculatorului;
- pornete difuzorul calculatorului cu un ton egal cu h Hz.

105

12. Gestiunea ecranului n mod text


Biblioteca standard a limbajelor C i C++ conine funcii pentru gestiunea ecranului.
Acesta poate fi gestionat n 2 moduri:
- modul text i
- modul grafic.
Modul text presupune c ecranul este format dintr-un numr de linii i coloane. De obicei
exist dou variante:
25 de linii x 80 de coloane = 2000 de caractere sau
25 de linii x 40 de coloane = 1000 de caractere.
Poziia pe ecran a unui caracter se definete printr-un sistem de coordonate ntregi
(x,y)
unde:
x - reprezint numrul coloanei n care este situat caracterul;
y - reprezint numrul liniei n care este situat caracterul.
Colul din stnga sus are coordonatele (1,1) iar colul din dreapta jos (80,25) sau
(40,25).
n mod implicit funciile de gestiune a ecranului n mod text au acces la tot ecranul.
Accesul poate fi limitat la o parte din ecran utiliznd aa numitele ferestre. Fereastra este un
dreptunghi care este o parte a ecranului i care poate fi gestionat independent de restul
ecranului.
Un caracter de pe ecran, pe lng coordonate, mai are i urmtoarele atribute:
- culoarea caracterului afiat;
- culoarea fondului;
- clipirea caracterului.
Aceste atribute sunt dependente de adaptorul grafic utilizat. Cele mai utilizate adaptoare
sunt:
- placa MDA, care este un adaptor monocrom;
- placa HERCULES, care este un adaptor color;
- placa CGA, care este un adaptor color;
- placa EGA, care este un adaptor color;
- placa VGA, care este un adaptor color de mare performan.
Pentru adaptoarele de mai sus se pot utiliza 8 culori de fond i 16 culori pentru afiarea
caracterelor.
Atributul unui caracter se definete cu ajutorul formulei:
atribut = 16 * culoare_fond + culoare_caracter + clipire (*)
unde:
- culoare_fond (background) = cifr ntre 0 i 7; (8 culori)
- culoare_caracter (foreground) = ntreg ntre 0 i 15; (16 culori)
- clipire = 128 (clipire) sau 0 (fr clipire)
Tabel cu numele culorilor:
106

Culoare

Constant simbolic

Valoare

negru
albastru
verde
turcoaz
rou
purpuriu
maro
gri deschis
gri nchis
albastru deschis

BLACK
BLUE
GREEN
CYAN
RED
MAGENTA
BROWN
LIGHTGRAY
DARKGRAY
LIGHTBLUE

verde deschis

LIGHTGREEN

turcoaz deschis

LIGHTCYAN

rou dechis

LIGHTRED

purpuriu magenta
galben
alb

LIGHTMAGENTA
YELLOW
WHITE

clipire

BLINK

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
128

12.1. Setarea ecranului n mod text


Setarea (punerea) se realizeaz cu ajutorul funciei textmode care are prototipul:
void textmode (int modtext);
unde
modtext poate fi exprimat numeric sau simbolic n felul urmtor:
Modul text activat
alb/negru 40 coloane
color 40 coloane
alb/negru 80 coloane
color 80 coloane
monocrom
color 43 linii pentru EGA
i 50 linii pentru VGA
modul precedent

Constant simbolic

Valoare

BW40
C40
BW80
C80
MONO

0
1
2
3
7

C4350

64
LASTMODE

-1

Modul MONO se poate seta pe un adaptor monocolor. Celelalte moduri se pot seta pe
adaptoare color.

12.2. Definirea unei ferestre


Dac dorim s partajm ecranul n zone care s poat fi gestionate independent trebuie
s definim ferestre.
O fereastr este o zon dreptunghilar de pe ecran care se poate defini cu funcia
window al crei prototip este:
void window (int stanga, int sus, int dreapta, int jos);
107

unde:
-(stanga, sus) = coordonatele colului din stnga al ferestrei;
-(dreapta, jos) = coordonatele colului din dreapta jos.
La un moment dat o singur fereastr este activ i anume aceea definit de ultimul
apel al funciei window. Funciile de gestionare a ecranului n mod text acioneaz
ntotdeauna asupra ferestrei active. Dup setarea modului text cu ajutorul funciei textmode
este activ tot ecranul.
Menionm c funcia window nu are nici un efect dac parametrii de la apel sunt
eronai.

12.3. tergerea unei ferestre


Fereastra activ se poate terge cu ajutorul funciei clrscr cu prototipul:

void clrscr(void);
fereastra activ (sau tot ecranul) devine activ; fondul are culoarea definit prin culoarea
de fond curent;
clrscr poziioneaz cursorul n poziia (1,1) a fiecrei ferestre.

12.4. Gestiunea cursorului


Programatorul poate plasa cursorul pe un caracter al ferestrei folosind funcia gotoxy
al crei prototip este:
void gotoxy (int coloana, int linie);
unde:
- (coloana, linie) sunt coordonate relative la fereastra activ, dac sunt nafara ferestrei
active apelul este ignorat;
Poziia cursorului se poate determina cu ajutorul funciilor wherex i wherey care au
prototipurile:
int wherex (void);
int wherey (void);

-returneaz numrul coloanei n care se afl cursorul


-returneaz numrul liniei n care se afl cursorul.

Exist cazuri cnd se dorete ascunderea cursorului. Acest lucru se poate realiza printr-o
secven special n care se utilizeaz funcia geninterrupt.
void ascundcursor (void) // face invizibil cursorul
{ _AH = 1;
_CH = 0x20;
geninterrupt (0x10);
}

Cursorul poate fi reafiat apelnd funcia urmtoare:

void afiscursor (void)


{ _AH = 1;
_CH = 6;

// face vizibil cursorul

108

_CL = 7;
geinterrupt (0x10);
}

AH, CH, CL reprezint unii din regitrii microprocesorului.

12.5. Determinarea parametrilor ecranului


Utilizatorul are posibilitatea s obin parametrii cureni ai ecranului prin apelarea
funciei gettextinfo al crui prototip este:
void gettextinfo (struct text_info *p);
unde structura text_info este definit n fiierul conio.h astfel:
struct text_info
{ unsigned
unsigned
unsigned
unsigned
unsigned
unsigned
unsigned
unsigned
unsigned
unsigned
unsigned

char
char
char
char
char
char
char
char
char
char
char

winleft;
wintop;
winright;
winbottom;
attribute;
normattr;
currmode;
screenheight;
screenwidth;
curx;
cury;

// amplasarea colturilor ferestrei

// culoarea fondului, a caracterelor si


// clipirea
// dimensiunea ecranului
// pozitia cursorului
};

12.6. Modurile video alb/negru


Exist dou moduri (intens i normal) care se pot activa cu funciile highvideo i
lowvideo ale cror prototipuri sunt:
void highvideo (void);
void lowvideo (void);
Intensitatea iniial este de obicei cea normal. Se poate reveni la intensitatea normal
dac se apeleaz funcia normvideo cu acelai prototip ca al celorlalte dou funcii.
Exemplu:
Vom scrie un program care afieaz parametrii ecranului:
#include <stdio.h>
#include <conio.h>
void main (void)
{ struct text_info par_ecran;
gettextinfo (&par_ecran);
printf (\nstanga: %u sus: %u dreapta: %u jos: %u\n,
par_ecran.winleft,
par_ecran.wintop,
par_ecran.winright,
par_ecran.winbottom);

109

printf (atribut: %d mod curent: %d\n,


par_ecran.normattr, par_ecran.currmode);
printf (inaltimea ecranului: %d latimea ecrnului: %d\n,
par_ecran.screenheight, par_ecran.screenwidth);
printf (coloana cursorului: %d linia cursorului: %d\n,
par_ecran.curx, par_ecran.cury);
}

12.7. Setarea culorilor


Culoarea fondului se seteaz cu ajutorul funciei textbackground cu prototipul
urmtor:
void textbackground (int culoare);
unde:
- culoare este un ntreg cuprins ntre 0 i 7 cu semnificaia din tabelul anterior al culorilor.
Culoarea caracterelor se seteaz cu ajutorul funciei textcolor cu prototipul urmtor:
void textattr (int culoare);
unde:
- culoare este un ntreg ntre 0 i 15.
Se pot seta ambele culori, precum i clipirea caracterului folosind funcia textattr de
prototip:
void textattr (int atribut);
unde:
- atribut e definit cu ajutorul relaiei (*) de la nceputul leciei.

12.8. Gestiunea textelor


Pentru afiarea caracterelor colorate n conformitate cu atributele definite prin relaia:
atribut = 16 * culoare_fond + culoare_caracter + clipire
se pot folosi funciile:
putch
- pentru afiarea unui caracter;
cputs
- pentru afiarea color a unui ir de caractere (acelai prototip ca puts);
cprintf
- pentru afiarea color sub controlul formatelor.
Alte prototipuri de funcii:
void insline (void);
void clreol (void)
void delline (void)

- insereaz o linie cu spaii n fereastr, liniile de sub


poziia cursorului se deplaseaz n jos cu o poziie;
- terge sfritul liniei ncepnd cu poziia cursorului;
- terge toat linia pe care este poziionat cursorul;

int movetext ( int stanga, int sus, int dreapta, int jos,
int stanga_dest, int dreapta_dest );
- copiaz un text dintr-o poziie n alta;
- returneaz: 1 dac textul s-a copiat cu succes i 0 n caz de eroare.
110

Textele dintr-o zon dreptunghiular a ecranului pot fi salvate sau citite dintr-o zon
de memorie cu ajutorul funciilor puttext i gettext i au prototipurile:
int gettext (int stanga, int sus, int dreapta, int jos, void *destinatie);
unde
- primii patru parametrii definesc fereastra unde se afl textul de salvat;
- destinatie este pointerul spre zona de memorie n care se salveaz textul.
i
int puttext (int stanga, int sus, int dreapta, int jos, void *sursa);
unde
- primii patru parametrii definesc fereastra unde se va scrie pe ecran textul preluat din
memorie;
- sursa este pointerul spre zona de memorie din care se transfer textul.
Ele returneaz:
- 1 la copiere cu succes;
- 0 la eroare.
Observaie:
1o. Fiecare caracter de pe ecran se pstreaz pe doi octei:
- pe un octet codul caracterului;
- pe octetul urmtor atributul caracterului.
Exemple:
1) Programul urmtor seteaz o fereastr i modurile video alb/negru.
#include <conio.h>
void main (void)
{ textmode (BW80);
window (10,4,60,4);
clrscr ();
lowvideo ();
cputs(lowvideo);
normvideo ();
cputs (normvideo);
textmode (LASTMODE);
cprintf (\n\r Acionati o tasta pentru a continua:);
getch ();
}

2) Programul urmtor afieaz toate combinaiile de culori posibile pentru fond i caractere
(adaptor EGA/VGA).
#include <conio.h>
#include <stdio.h>
void main (void)
{ static char *tculoare [ ] = {

BLACK

negru,

111

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

BLUE
albastru,
GREEN
verde,
CYAN
turcoaz,
RED
rosu,
MAGENTA
purpuriu,
BROWN
maro,
LIGHTGRAY
gri deschis,
DARKGRAY
gri inchis,
LIGHTBLUE
albastru deschis,
LIGHTGREEN
verde deschis,
LIGHTCYAN
turcoaz deschis,
LIGHTRED
rosu dechis,
LIGHTMAGENTA purpuriu magenta,
YELLOW
galben,
WHITE
alb};

int i,j,k;
struct text_info atribut;
gettextinfo (&atribut);
for (i = 0; i < 8; i++ )
// i alege culoarea fondului
{ window (3,2,60,20);
k=2;
textbackground (i);
clrscr();
for (j=0; j <10; j++, k++)
// j alege culoarea caracterului
{ textcolor (j);
gotoxy (2,k);
if (i = = j) continue;
cputs (tculoare[j]);
}
gotoxy (1,18);
printf (actionati o tasta pentru contiuare\n);
getch();
}
window (atribut.winleft, atribut.wintop, atribut.winright, atribut.winbottom);
textattr (atribut. attribute);
clrscr();
}

112

13. Gestiunea ecranului n mod grafic


Modul grafic presupune c ecranul este format din puncte luminoase (pixeli).
Numrul acestora depinde de adaptorul grafic i se numete rezoluie. O rezoluie este cu att
mai bun cu ct este mai mare. Adaptorul CGA are o rezoluie de 200 rnduri x 640 de
coloane iar EGA de 350 de rnduri x 640 de coloane. Adaptorul VGA i SVGA ofer rezoluii
n funcie de mrimea ecranului. Astfel adaptorul VGA i SVGA ofer rezoluii de pn la
768 de rnduri x 1024 de coloane pentru ecranul de 14 (inches).
Pentru gestiunea ecranului n mod grafic se pot utiliza peste 60 de funcii standard
aflate n biblioteca sistemului. Aceste funcii au prototipurile n fiierul graphics.h. Totodat
ele folosesc pointeri de tip far (de 32 de bii).

13.1. Setarea modului grafic


Modul grafic se seteaz cu ajutorul funciei initgraph. Aceast funcie poate fi folosit
singur sau mpreun cu o alt funcie numit detectgraph care determin parametrii
adaptorului grafic. Prototipul ei este:
void far detectgraph (int far *graphdriver, int far *graphmode);
unde:
-

n zona spre care pointeaz graphdriver se pstreaz una din valorile:


valoare

simbol

1
2
3
4
5
6
7
8
9
10
-

CGA
MCGA
EGA
EGA64
EGAMONO
IBM8514
HERCMONO
ATT400
VGA
PC3270

n zona spre care pointeaz graphmode se memoreaz una din valorile:


pentru CGA
valoare

simbol

0
CGAC0
1
CGAC1
2
CGAC2
3
CGAC3
corespunznd toate pentru o rezoluie de 320*200 de pixeli i permit 4 culori
4
CGAHI
are o rezoluie de 640*200 puncte i lucreaz numai alb/negru.
pentru EGA
valoare

simbol
113

0
1
pentru VGA
valoare
0
1
2

EGALO
EGAHI

are 640*200 i 16 culori


are 640*350

VGALO
VGAMED
VGAHI

are 640*200
are 640*350
are 640*480

simbol

Valorile spre care pointez graphdriver definesc nite funcii standard corespunztoare
adaptorului grafic. Aceste funcii se numesc drivere. Ele se afl n subdirectorul BGI.
Funcia detectgraph detecteaz adaptorul grafic prezent la calculator i pstreaz
valoarea corespunztoare acestuia n zona spre care pointeaz graphdriver. Modul grafic se
definete n aa fel nct el s fie cel mai performant pentru adaptorul grafic curent. Cele mai
utilizate adaptoare sunt de tip EGA (calculatoare mai vechi) i VGA i SVGA (calculatoarele
mai noi).
Apelul funciei detectgraph trebuie s fie urmat de apelul funciei initgraph. Aceasta
seteaz modul grafic n conformitate cu parametrii stabilii de apelul prealabil al funciei
detectgraph.
Funcia initgraph are prototipul:
void far initgraph (int far * graphdriver, int far *graphmode, char far *cale);
unde:
-

graphdriver i graphmode sunt pointeri cu aceeai semnificaie ca n cazul


funciei detectgraph;
cale este un pointer spre irul de caractere care definesc calea subdirectorului BGI
(Borland Graphic Interface), de exemplu:
C:\\BOLANDC\\BGI

Exemplu:
Setarea n mod implicit a modului grafic:
int driver, mod_grafic;
...
detectgraph (&driver, &mod_grafic);
initgraph(&driver, &mod_grafic, C:\\BORLANDC\\BGI);

Dup apelul funciei initgraph se pot utiliza celelalte funcii standard de gestiune
grafic a ecranului.
Din modul grafic se poate iei apelnd funcia closegraph care are prototipul:
void closegraph (void);
Funcia initgraph mai poate fi apelat i folosind constanta simbolic DETECT astfel:

int driver, mod_grafic;


...
driver = DETECT;
initgraph (&driver, &mod_grafic, C:\\BORLANDC\\BGI);

114

Constanta simbolic DETECT este definit n fiierul graphics.h, alturi de celelalte


constante simbolice care definesc driverul. Aceasta are valoarea zero.
Prin apelul de mai sus, funcia initgraph apeleaz funcia detectgraph pentru a defini
parametrii implicii ai adaptorului grafic.
Utilizatorul poate defini el nsui parametri pentru iniializarea modului grafic.

13.2. Gestiunea culorilor


Adaptoarele grafice sunt prevzute cu o zon de memorie n care se pstreaz date
specifice gestiunii ecranului. Aceast zon de memorie poart denumirea de memorie video.
n mod grafic, ecranul se consider format, dup cum am precizat, din puncte
luminoase numite pixeli. Poziia pe ecran a unui pixel se definete printr-un sistem de
coordonate (x,y) cu x coloana i y linia n care este afiat pixelul.
n cazul adaptoarelor color, unui pixel i corespunde o culoare. Culoarea pixelilor se
pstreaz pe bii n memoria video. Memoria video necesar pentru a pstra starea ecranului
setat n mod grafic, se numete pagin video. Adaptoarele pot conine mai multe pagini
video. Gestiunea culorilor este dependent de tipul de adaptor grafic existent la
microprocesor.
Numrul maxim de culori pentru adaptoarele EGA este de 64 (numerotate de la 0 la
63). Cele 64 de culori nu pot fi afiate simultan pe ecran. n cazul adaptorului EGA se pot
afia simultan 16 culori. Mulimea culorilor care pot fi afiate simultan pe ecran se numete
palet. Culorile din componena unei palete pot fi modificate de utilizator prin intermediul
funciilor standard. La iniializarea modului grafic se seteaz o palet implicit.
Pentru adaptorul EGA exist un tablou de 64 de culori (cu coduri ntre 0 i 63) din
care se selecteaz cele 16 culori pentru palet. Exist i constante simbolice foarte sugestive
cu numele culorilor n englez. Funciile de gestiune a culorilor pot avea ca parametri nu
numai codurille culorilor ci i aceste constante simbolice.
Culoarea fondului este ntotdeauna cea corespunztoare indicelui 0 din palet.
Culoarea pentru desenare este cea corespunztoare indicelui 15.
Culoarea de fond poate fi modificat cu ajutorul funciei setbkcolor care are
prototipul:
void far setbkcolor (int culoare);
unde:
- culoare = index n tabloul care definete paleta.
Exemplu:

setbkcolor (BLUE); seteaz culoarea de fond pe albastru.

Pentru a cunoate culoarea de fond curent se poate apela funcia getbkcolor de


prototip:
int far getbkcolor (void);
Ea returneaz indexul n tabloul care definete paleta pentru culoarea de fond.

Culoarea pentru desenare poate fi modificat folosind funcia setcolor de prototip:


void far setcolor(int culoare);
unde:
-

culoare = index n tabloul care definete paleta.


115

Exemplu:

setcolor (YELLOW); seteaz culoarea pentru desenare n galben.

Culoarea pentru desenare se poate determina apelnd funcia getcolor de prototip:


int far getcolor (void);
Ea returneaz indexul n tabloul care definete paleta relativ la culoarea pentru
desenare.
Paleta curent poate fi modificat cu funciile setpalette i setallpalette. Prima funcie
se folosete pentru a modifica o culoare din paleta curent i are prototipul:
void far setpalette (int index, int cod);
unde:
-

Exemplu:

index este un ntreg din {0,. . . , 15} i reprezint indexul n tabloul care definete
paleta pentru culoarea care se modic;
cod este un ntreg din intervalul {0, 1,. . . , 63} i reprezint codul culorii care o
nlocuiete n palet pe cea veche.
setpalette (DARKGRAY, 45);

modific culoarea corespunztoare


indicelui DARKGRAY (adic 8) prin
culoarea de cod 45.

Funcia setallpalette permite modificarea simultan a mai multor culori din


compunerea paletei i are prototipul:
void far setallpalette (struct palettetype far *paleta);
unde:
-

palettetype este un tip definit n fiierul graphics.h astfel


struct palettetype
{ unsigned char size;
unsigned char colors [MAXCOLOR+1];
};
cu
- size dimensiunea paletei
- colors tablou ale crui elemente au ca valori codurile
componente ale paletei care se definete.

Modificarea paletei curente cu ajutorul funciilor setpalette i setallpalette conduce la


schimbarea corespunztoare a culorilor afiate pe ecran n momentul apelului funciilor
respective.
Pentru a determina codurile culorilor componente ale paletei curente se va folosi
funcia getpalette de prototip:
void far getpalette (struct palettetype far *paleta);
Paleta implicit poate fi determinat folosind funcia getdefaultpalette de prototip:
struct palettetype *far getdefaultpalette(void);
116

Numrul culorilor dintr-o palet poate fi obinut apelnd funcia getmaxcolor de


prototip:
int far getmaxcolor (void);
Funcia returneaz numrul maxim de culori diminuat cu 1. Deci n cazul adaptorului
EGA funcia returneaz valoarea 15.
O alt funcie care determin dimensiunea paletei este funcia getpalettesize cu
prototipul:
int far getpalettesize (void);
Funcia returneaz numrul culorilor componente ale paletei. n cazul adaptorului
EGA funcia returneaz valoarea 16.
Exemplu:
Programul urmtor afieaz codurile culorilor pentru paleta implicit:
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void main (void)
{ int gd = DETECT, gm, i;
struct palettetype far *pal = (void *) 0;
initgraph (&gd, &gm, C:\\BORLANDC\\BGI);
pal = getdefaultpalette ();
for (i=0; i<16; i++)
{ printf (colors[%d]=%d\n, i, pal -> colors[i]);
getch ();
}
closegraph();
}

13.3. Starea ecranului


n mod grafic ecranul se compune din n*m pixeli. Pixelul din stnga-sus are
coordonatele (0,0), iar pixelul din dreapta-jos are coordonatele (n-1,m-1).
n BGI exist mai multe funcii care permit utilizatorului s obin informaii despre:
- coordonata maxim pe orizontal;
- coordonata maxim pe vertical;
- poziia curent (pixelul curent);
- etc.

int
int
int
int

far
far
far
far

getmaxx (void);
getmaxy (void);
getx (void);
gety (void);

returneaz abscisa maxim;


returneaz ordonata maxim;
returneaz poziia pe orizontal (abscisa) pixelului curent;
returneaz poziia pe vertical (ordonata) pixelului curent.

Exemplu:
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

117

void main (void)


{ int gd = DETECT, gm, cul_fond, cul_desen, curent_x,curent_y, maxx, maxy;
initgraph (&gd, &gm, C:\\BORLANDC\\BGI);
cul_fond = getbkcolor();
cul_desen = getcolor();
maxx = getmaxx();
maxy = getmaxy();
curent_x = getx();
curent_y = gety();
closegraph();
printf (culoarea fondului = %d\n, cul_fond);
printf (culoare desenare = %d\n, cul_desen);
printf (abscisa maxima = %d\n, maxx);
printf (ordonata maxima = %d\n, maxy);
printf (abscisa curenta = %d\n, curent_x);
printf (ordonata curenta = %d\n, curent_y);
printf (acionai o tasta pentru terminare);
getch();
}

118

14. Probleme diverse


De multe ori suntem pui n faa unor probleme pe care le nelegem uor dar nu tim
cum s le rezolvm ct mai simplu i elegant. V propunem cteva metode care bine nsuite pot
duce, uneori, la o rapid rezolvare a problemelor dificile. Evident c, nu toate problemele pot fi
ncadrate n aceste tipare propuse dar fiecare programator poate s-i formeze un astfel de
"portofoliu" de metode cu care s poate aborda orice problem. Metodele prezentate n
continuare pot constitui un nceput.

14.1. Generarea combinrilor


Fie o mulime oarecare de n elemente care poate fi pus ntr-o coresponden biunivoc
cu mulimea A={1,...,n}. Ne propunem s determinm toate m-combinrile (mn) ale mulimii A
(submulimi de m elemente ale mulimii A). Vom rezolva problema, nerecursiv, pornind de la mcombinarea V=(1,2,...,m) determinnd succesiv toate m-combinrile ordonate lexicografic
cresctor.
Fie V=(v1,v2,...,vm) o m-combinare oarecare, atunci m-combinarea urmtoare n ordine
lexicografic cresctoare se obine astfel:
a) se determin cel mai mare indice i satisfcnd relaiile:
vi<n-m+i, vi+1=n-m+i+1,..., vm-1=n-1, vm=n. (1)
b) se trece la vectorul urmtor:
(v1, ..., vi-1,vi+1,vi+2, ..., vi+n-i+1);
c) dac nu exist un astfel de indice i (care s satisfac relaiile (1)) nseamn c vectorul V
conine ultima m-combinare i anume: (n-m+1,n-m+2, ...,n).
Dm n continuare o funcie C care genereaz o m-combinare cu n elemente avnd ca
parametru cod o variabil boolean care pentru valoarea 0 genereaz prima m-combinare iar
pentru valoarea 1 genereaz urmtoarea m-combinare. Funcia comb rentoarce valoarea 1 dac
s-a generat o m-combinare oarecare i valoarea 0 dac s-a generat ultima m-combinare (n acest
caz cod are valoarea 0). Se va folosi un vector global v n care se genereaz m-combinrile.
#define dim 50
#include <stdio.h>
int v[dim+1], n, m;
// functia generatoare a m-combinarilor
int comb(cod)
int cod;
{
int i,j;
if (cod == 0)
{
for (i=1; i<=m; v[i]=i++);
return (1);
}
i=m+1;

do { i-- }

// generarea primei m-combinari 1,...,m

// cautarea indicelui i pentru satisfacerea relatiilor (1)

119

while (v[i] >= n-m+i);


if (i)
{
v[i]=v[i]+1;
for (j=i+1; j<=m; v[j]=v[j-1]+1,j++);
return (1);
}
else return (0);
}
void main(void)
{
int kod,i;
printf("\ndati n: ");
scanf ("%d",&n);
printf("\ndati m: ");
scanf ("%d",&m);
comb(0);
kod=1;
while (kod)
{ printf("\n");
for (i=1;i<=m;printf ("%3d",v[i++]));
kod = comb(kod);
}
getche();
}

14.2. Metoda greedy


Se aplic problemelor n care se d o mulime A coninnd n date (de orice natur i
structur) de intrare cerndu-se s se determine o submulime B (BA) care s ndeplineasc
anumite condiii pentru a fi acceptat. Cum, n general, exist mai multe astfel de submulimi
(numite soluii posibile) se mai d i un criteriu conform cruia dintre aceste submulimi s
alegem una singur (numit soluie optim) ca rezultat final. Foarte multe probleme de cutare
se nscriu n acest tip.
Menionm c, n general, soluiile posibile au urmtoarele proprieti:
- se presupune c este soluie posibil;
- dac B este soluie posibili CB atunci i C este soluie posibil;
Vom da n continuare o variant a tehnicii greedy (denumire care n traducere nseamnn
lcomie, nghiire) n care se pornete de la mulimea vid. Apoi se alege pe rnd, ntr-un anumit
fel, un element din A neales nc. Dac adugarea lui la soluia parial anterior construit
conduce la o soluie posibil, atunci se adaug, altfel se alege un nou element. Tot acest procedeu
se repet pentru toate elementele din A. Dm n continuare n pseudocod o procedur:
procedure GREEDY (n,A,B)
B:=;
for i=1,n do
call ALEGE (A,i,x);
call POSIBIL (B,x,cod);
if cod=1 then
call ADAUG (B,x);
endif;
endfor;
return;
end procedure
Despre procedurile apelate din GREEDY precizm urmtoarele:
120

procedura ALEGE furnizeaz n x un element din A aj{ai,...,an} i interschimb ai cu aj;


dac la fiecare pas se cerceteaz ai atunci procedura se simplific;
procedura POSIBIL verific dac elementul x poate fi adugat sau nu mulimii pariale
construit pn la pasul curent furniznd o valoare boolean cod cu semnificaia:
cod = 1, dac B U {x}, este soluie posibil
cod = 0, altfel
procedura ADAUG nlocuiete pe B cu B{x}.

Obs. Procedurile de mai sus nu sunt necesare ntotdeauna, acest fapt depinznd de complexitatea
problemei. Oricum trebuie reinut cadrul de rezolvare al acestui tip de problem.
Problem rezolvat
Se d o mulime de valori reale X={x1, . . .,xn}. Se cere submulimea YX astfel ca y /yY,
s fie maxim.
Evident c problema este foarte simpl (n Y trebuie introduse elementele strict pozitive
din X; evitm s mai construim procedurile ALEGE, POSIBIL, ADAUG) i vom da rezolvarea
ei n pseudocod:
procedure SUMA (n,X,Y,k)
integer n,X(n),Y(n),k
k:=0;
for i=1,n do
if x(i) > 0 then k:=k+1;
y(k):=x(i)
endif;
endfor;
return;
end procedure
Probleme propuse
14.2.1.
Se dau n iruri S1,S2,...,Sn ordonate cresctor, de lungimi L1,L2, ...,Ln. S se obin un ir S de
lungime L1+L2+...+Ln cu toate elementele celor n iruri ordonate cresctor (problem de
interclasare).
Indicaie: Vom interclasa succesiv cte dou iruri n final obinnd irul ordonat cresctor.
Complexitatea interclasrii a dou iruri A i B de lungimi a i b depinde direct proporional de
a+b (pentru c se fac a+b deplasri de elemente). Se pune problema gsirii ordinii optime n care
trebuie efectuate aceste interclasri astfel ca numrul total de deplasri s fie minim.
Vom da un exemplu pentru a lmuri mai bine lucrurile:
fie 3 iruri de lungimi (90,40,10). Interclasm irul S1 cu S2 apoi irul rezultat cu S3; putem nota
acest lucru formal prin (S1+S2)+S3. Se obin (90+40) + (130+10)=270 deplasri. Dac vom
interclasa irurile n ordinea (S2+S3)+S1 vom obine (40+10)+ (50+90)=190 de deplasri. De aici
concluzia c ntotdeauna vom interclasa irurile de lungimi minime din irurile rmase.

14.2.2.
121

Gsii tripletele de numere pitagorice din Nn x Nn x Nn (prin Nn notat mulimea {0,1,2,...,n}).


ncercai optimizarea timpului de execuie a programului.
14.2.3.
Fie mulimea m-combinrilor luate din n elemente i fie k<m un numr natural. S se dea un
algoritm i apoi s se scrie un program C astfel nct s se determine o submulime de mcombinri cu proprietatea c oricare dou m-combinri au cel mult k elemente comune.
14.2.4.
S se determine mulimile interior stabile (MIS) ale unui graf oarecare dat prin matricea sa de
adiacen.

14.3. Metoda backtracking (cutare cu revenire)


Aceast metod se aplic problemelor ce pot fi reprezentate sub forma unui arbore finit
iar cutarea soluiei presupune parcurgerea arborelui n adncime (DF=Depth First).
Problemele de acest tip au n general soluia de forma x=(x1, . . . ,xn) S1x . . . xSn, fiecare Sk
fiind o mulime finit. Mai facem cteva precizri preliminare:
a) pentru fiecare problem sunt date anumite relaii ntre componentele x1, . . . ,xn ale lui
x numite condiii interne;
b) produsul cartezian S=S1x...xSn se mai numete spaiul soluiilor posibile, iar soluiile
posibile care satisfac condiiile interne se numesc soluii rezultat;
c) n general se cer dou lucruri n astfel de probleme:
- determinarea tuturor soluiilor rezultat;
- determinarea doar a acelor soluii care optimizeaz o funcie obiectiv dat.
Cum rezolvm astfel de probleme? Exist dou modaliti de rezolvare:
1) tehnica direct (numit i tehnica forei brute) prin care se genereaz toate elementele
spaiului de soluii posibile i apoi se verific fiecare dac este sau nu o soluie rezultat; aceast
tehnic necesit un timp prohibitiv (dac fiecare Si are doar 2 componente complexitatea este
O(2n); totodat ar fi necesar imbricarea a n cicluri cu n aprioric necunoscut).
2) backtracking care evit generarea tuturor soluiilor posibile.
S dm n continuare cteva repere ale rezolvrii:
- soluia este construit progresiv, component cu component;
- lui xk i se atribuie o valoare (evident c numai din Sk) dac i numai dac x1, . . . ,xk-1 au deja
valori;
- se verific condiiile de continuare (strns legate de condiiile interne) dac are sens trecerea
la xk+1;
1. dac nu are sens (adic condiiile de continuare nu sunt ndeplinite atunci
facem o nou alegere pentru xk sau dac am epuizat valorile din Sk atunci k:=k-1
i se face o nou alegere pentru xk-1; .a.m.d.
2. dac are sens atunci k:=k+1 i se testeaz dac k>n:
21) dac inegalitatea este adevrat atunci se afieaz soluia astfel determinat
i k:=k-1 continund procesul de la punctul 1;
22) dac inegalitatea este fals se continu procesul de la punctul 1.
Procedura rezolvrii unor astfel de probleme este:
procedure BT(n,x)
integer n;
122

array x(n);
k:=1;
while k>0 do
cod:=0;
while ([mai exist o valoare din Sk netestat] and cod=0)
x(k):=;
if k(x(1),...,x(k)) then cod:=1;
endif;
endwhile;
if cod=0 then
k:=k-1
else
if k=n then write (x(1),...,x(n))
else k:=k+1
endif
endif;
endwhile;
return;
end procedure
Vom face cteva precizri:
1o. Predicatul k(x1, . . . , xk) reprezint condiiile de continuare pentru x1, . . . , xk;
2o. Cod este o valoare ce indic ndeplinirea/nendeplinirea condiiilor de continuare;
3o. Dac predicatul k(x1, . . . , xk) este adevrat k {1,...,n} atunci se vor afla toi vectorii
din S;
4o. Backtracking poate fi uor reprezentat pe un arbore construit astfel:
- nivelul 1 conine rdcina;
- din orice nod de pe nivelul k pleac sk muchii spre nivelul k+1, etichetate cu cele
sk elemente ale lui Sk;
- nivelul n+1 va conine s1*s2*. . .* sn noduri frunz;
- pentru fiecare nod de pe nivelul n+1 etichetele muchiilor coninute n drumul ce
leag rdcina de acest nod reprezint o soluie posibil;
Dac mulimile Sk reprezint progresii aritmetice atunci algoritmul general se modific astfel:
procedure BTA(n,a,b,h)
integer n;
array primul(n),ultimul(n),ratia(n),x(n);
k:=1;
x(1):=primul(1)-ratia(1);
while k>0 do
cod:=0;
while ( x(k)+ratia(k) ultimul(k) and cod=0 )
x(k):=x(k)+ratia(k);
if k(x(1),...,x(k)) then cod:=1 endif;
endwhile;
if cod=0 then
k:=k-1
else
if k=n then write (x(1),...,x(n))
else k:=k+1
123

x(k):=a(k)-h(k)
endif
endif;
endwhile;
return;
end procedure
unde:
- primul(n) reprezint vectorul primilor termeni ai progresiilor aritmetice;
- ultimul(n) reprezint vectorul ultimilor termeni ai progresiilor aritmetice;
- ratia(n) reprezint vectorul raiilor progresiilor aritmetice;
De reinut cele dou avantaje ale acestui procedeu:
evitarea imbricrii unui numr oarecare de cicluri aprioric variabil (n algoritmul propus se
imbric doar dou cicluri pretestate while);
evitarea construirii spaiului tuturor soluiilor posibile S1xS2x . . . xSn.

Problem rezolvat
n cte moduri se pot aranja 8 dame pe tabla de ah astfel nct s nu se "bat" reciproc. S se
foloseasc al doilea algoritm dintre cei menionai anterior.
Prima variant
Acest program respect algoritmul anterior cu unele mici modificri. Facem precizarea c
vectorul x conine n componenta x[i] numrul coloanei de pe tabla de ah pe care se va afla
dama de pe linia i. Tiprirea va reprezenta o permutare (din cele 8! soluii posibile). Se vor afla
92 de soluii. Lsm pe seama cititorului s deduc analogiile i diferenele ntre algoritm i
program.
#include <stdio.h>
#include <math.h>
void main (void)
{ int x[9],cod,k,i,nr;
k=1; x[1]=0;nr=0;
while (k>0)
{ cod=0;
while (((x[k]+1)<=8)&&(cod= =0))
{ x[k]++;
if ((k= =1) && (x[k]= =1)) cod=1;
else { i=1; cod=1;
while ((cod==1)&&(i<k))
{ if ((x[i]==x[k])||(abs(x[i]-x[k])==k-i)) cod=0;
i++; } // sfarsit while
} // sfarsit if
} // sfarsit while
if (cod= =0) k--;
else {if (k= =8)
{ printf("\n%3d. ",++nr);
for (i=1;i<9;printf("%d ",x[i++]));
}}
else x[++k]=0;
}
}

124

A doua variant:
#include <stdio.h>
#include <math.h>
#define n 100
int x[100],cod,k,nc,nsol;
int Verifica(void)
{ int i,cod1;
// cod1=1 conditiile de continuare
cod1=1;
// sunt verificate
if (k>1)
// cod1=0 in caz contrar;
for(i=1; i<= (k-1); i++)
{ if (x[k]= =x[i]) cod1=0;
if (abs(x[k]-x[i]) = = k-i) cod1=0; // (*)
}
return cod1;
}
void ScrieSolutie(void)
{ int i;
printf("\n%3d. ",++nsol);
for (i=1; i<=nc; printf("%3d",x[i++]));
}
void CitesteDate(void)
{ int i;
nsol=0;
clrscr();
nc=n+1;
while ((nc>n) || (nc<0))
{ printf ("Dati n = ");
scanf ("%d",&nc);
};
}
void main (void)
{ CitesteDate();
k=1; x[1]=0;
while (k>0)
{ cod=0;
while (((x[k]+1) <= nc) && (cod= =0))
{x[k]++;
cod=Verifica();
}
if (cod= =0) k--;
else {if (k==nc) ScrieSolutie();
else x[++k]=0;
}
}
}

A doua variant este modular, mai uor de neles i generalizeaz problema damelor
pn la tabla de ah de 100x100. Lsm pe seama cititorului modificarea funciei ScrieSolutie
pentru a afia n mod grafic tabla de ah.
Dac n funcia Verifica se terge instruciunea notat cu (*) atunci se obin toate
permutrile de n obiecte.
Probleme propuse
125

14.3.1
S se rezolve problema turelor de ah dup al doilea algoritm. n cte moduri se pot aranja n
turnuri pe tabla de ah astfel nct s nu se "bat" reciproc.
14.3.2.
S se afieze poziiile succesive ale unui cal pe tabla de ah, pornind dintr-o poziie dat, astfel
nct s fie atinse toate csuele tablei de ah.
14.3.3.
Avnd un fiier cu o mulime de cuvinte din limba romn de aceeai lungime k s se scrie un
program C care afieaz toate careurile rebusiste fr puncte negre. ( Problema e fascinant
implicnd i cunotine gramaticale dar i cunoscnd faptul c nu s-au construit careuri de 10x10
fr puncte negre manual i nici cu ajutorul calculatorului; se poate ncerca apoi i cu k:=11,12, .
. .).
14.3.4.
Un intreprinztor dispune de un capital C i are n variante de investiii. Pentru fiecare investiie i
cunoate fondurile de investiie fi precum i beneficiile bi. Se cere un program care s deduc
toate variantele posibile de investiii al intreprinztorului. Se mai dau i condiiile Cci i {1, .
. . ,n}.
14.3.5.
Avnd un graf neorientat caracterizat prin matricea costurilor s se determine prin bactraking
circuitul de cost minim pornind dintr-un vrf dat.
14.3.6.
Avnd un graf neorientat caracterizat prin matricea de inciden a vrfurilor s se determine prin
bactraking mulimile interior stabile maximale.
14.3.7.
S se determine toate cuvintele binare de lungime 10 care conin exact 5 cifre de 1.
14.3.8.
S se determine toate cuvintele binare de lungime 10 care conin cel mult 5 cifre de 1.

14.3.9.
S se determine toate cuvintele din {a,b,c}* (mulimea tuturor cuvintelor peste alfabetul se
noteaz cu * ) de lungime 10 care conin exact 2 simboluri 'a'; 3 simboluri 'b' i 5 simboluri 'c'.

14.3.10.
S se determine toate cuvintele din {a,b,c}* de lungime n care conin exact na simboluri 'a'; nb
simboluri 'b' i nc simboluri 'c' (cu condiia n=na+nb+nc).
14.3.11.
S se determine toate tripletele (x1,x2,x3) de numere astfel ca:
x1+x2+x3suma
x1*x2*x3produs
cu valorile suma i produs date iar x1S1, x2S2 i x3S3 ; S1, S2 i S3 fiind trei progresii
aritmetice date deasemenea.
126

14.3.12.
S se determine toate variantele de pronosport cu 13 rezultate din {1,x,2} care conin exact n1
simboluri '1'; nx simboluri 'x' i n2 simboluri '2' (cu condiia n1+nx+n2=13).
14.3.13.
S se determine toate variantele de pronosport cu 13 rezultate din {1,x,2} care conin cel mult n1
simboluri '1'; cel mult nx simboluri 'x' i simboluri '2' n rest (cu condiia n1+nx13).

14.4. Metoda divide et impera (divide i stpnete)


Aceast modalitate de elaborare a programelor const n mprirea repetat a unei
probleme de dimensiune mai mare n dou sau mai multe subprobleme de acelai tip urmat de
combinarea soluiilor subproblemelor rezolvate pentru a obine soluia problemei iniiale.
Se d un vector A=(a1,...,an) i trebuie efectuat o prelucrare oarecare asupra elementelor
sale.
Presupunem c:
p,q{1,...,n} cu 1 p < q m{p,p+1,...,q-1} a.. prelucrarea secvenei {ap,...,aq} se poate
face prelucrnd subsecvenele:
{ap,...,am} i {am+1,...,aq} i apoi combinnd rezultatele pentru a obine prelucrarea ntregii
secvene {ap,...,aq}.
Dac se reuete o astfel de formalizare a problemei atunci ea poate fi rezolvat cu ajutorul
acestei metode.
Vom da n continuare o procedur recursiv n pseudocod:
procedure DI (p,q,)
if q-p eps then
call
else
call
call
call
call
endif;
return;
end procedure

PREL (p,q,)
IMPARTE (p,q,m) ;
DI (p,m,);
DI (m+1,q,);
COMB (,,);

Cteva precizri se impun:


- procedura trebuie apelat prin call DI (1,n,) n obinndu-se rezultatul final;
- eps este lungimea maxim a unei secvene {ap,...,aq} notat prin (p,q) pentru care se face
prelucrarea direct fr a mai fi necesar mprirea n subprobleme;
- procedura PREL realizeaz prelucrarea direct a secvenelor (p,q);
- procedura COMB realizeaz combinarea rezultatelor i ale prelucrrii a dou secvene
vecine (p,m) i (m+1,q) obinnd rezultatul al prelucrrii ntregii secvene (p,q);
- prin procedura IMPARTE se obine valoarea lui m.
Vom da ca exemplu problema sortrii cresctoare a unui ir de ntregi prin interclasare.
- deoarece secvenele (i,i+1) sunt uor de ordonat acestea vor constitui secvenele ce se
vor
prelucra, deci eps = 1;
- m se va calcula ca (p+q)/2, deci nu mai e nevoie de procedura special IMPARTE;
127

procedura COMB va interclasa ntotdeauna dou secvene (p,m) i (m+1,q) ordonate


cresctor;
vom folosi un vector x drept structur global i vom face toate prelucrrile pe
elementele
sale nemaiavnd nevoie de zonele ,;
pentru zona vom folosi un vector local y n procedura COMB acesta coninnd
elementele corespondente din x dar ordonate cresctor; tot n procedura COMB se
vor
copia apoi elementele lui y din poriunea (p,q) n x;
evident c procedurile din schema general a algoritmului sunt funcii C cititorul fcnd
analogiile necesare.

#include <stdio.h>
#include <conio.h>
#define nrelem 100
int x[nrelem];
int n;
void PREL (int p, int q)
{int aux;
if ((p<q) && (x[p] > x[q])) {
aux=x[p];
x[p]=x[q];
x[q]=aux;
}
}
void COMB (int inf, int mijloc, int sup)
{int i,j,k,l;
int y[nrelem];
i=k=inf;
j=mijloc+1;
while (i<=mijloc && j<=sup)
{ if (x[i] <= x[j])
y[k++]=x[i++];
else
y[k++]=x[j++];
}
for(l=i; l<=mijloc; y[k++]=x[l++]);
for(l=j; l<=sup; y[k++]=x[l++]);
for(k=inf; k<=sup; x[k++]=y[k]);
}
void DI (int p, int q)
{ int m;
if ((q-p) <= 1)
PREL (p,q);
else
{m=(p+q)/2;
DI (p,m);
DI (m+1,q);
COMB (p,m,q);
}
return;
}
void main(void)
{int i;
clrscr();
printf ("dati nr de elemente\n");
scanf ("%d",&n);
for (i=1; i<=n; i++)
{printf("x[%d]=",i);

128

scanf("%d",&x[i]);
}
DI (1,n);
printf("\nsirul sortat crescator este:\n");
for (i=1; i<=n; i++) printf("x[%d]=%d\n",i,x[i]);
getch();
}

14.5. Metoda programrii dinamice


Aceast metod este aplicabil problemelor de optim n care soluia poate fi privit ca
rezultatul unui ir de decizii d1, . . . ,dn. Fiecare decizie depinde de deciziile deja luate i nu este
unic determinat (spre deosebire de tehnica greedy unde fiecare decizie care se ia trebuie s fie
unic). Totodat este necesar s fie satisfcut una din variantele principiului optimalitii pentru
a putea fi aplicat aceast metod.
Vom formaliza acest principiu al optimalitii:
- fie d1,...,dn un ir optim de decizii (SOD) care transform starea so n starea sn, trecnd prin
strile intermediare s1, . . . ,sn-1; vom nota acest fapt prin (d1,dn) este SOD pentru perechea de
stri (so,sn);
- grafic procesul este descris ca n figura urmtoare:
d1
d2
dn
*----->*---->*------>* . . . *------>*
so
s1 s2
sn-1
sn
Vom da acum mai multe variante ale principiului optimalitii:
1) dac (d1,dn) este SOD pentru (so,sn) atunci (d2,dn) este SOD pentru (s1,sn);
2) dac (d1,dn) este SOD pentru (so,sn) atunci i din {1, . . . ,n-1} avem
a) (d1,di) este SOD pentru (so,si) SAU
b) (di+1,dn) este SOD pentru (si,sn).
3) dac (d1,dn) este SOD pentru (so,sn) atunci i din {1, . . . ,n-1} avem
a) (d1,di) este SOD pentru (so,si)
I
b) (di+1,dn) este SOD pentru (si,sn).
Ultima variant este cea mai general i mai complet. Odat verificat o form a
principiului optimalitii, metoda programrii dinamice const n a scrie relaiile de recuren i
apoi de a le rezolva. n general relaiile de recuren sunt de 2 tipuri :
1) fiecare decizie di depinde de di+1,...,dn - relaii de recuren nainte, deciziile vor fi luate n
ordinea dn, dn-1, . . . ,d1;
2) fiecare decizie di depinde de d1, . . . ,di-1 - relaii de recuren napoi, deciziile vor fi luate n
ordinea d1, d2, . . . , dn.
Problem rezolvat
Fie G=(X,) un 1-graf orientat cruia i atam matricea costurilor, (fiecare arc (i,j)
este etichetat cu o valoare real strict pozitiv). Se pune problema gsirii drumului de valoare
minim (DVM) ntre oricare 2 vrfuri i i j.
129

Rezolvare
Verificm prima variant a principiului optimalitii:
- fie i, i1, i2, . . . , im, j DVM ntre i i j atunci evident c i1, . . . , im, j este DVM ntre i1 i
j;
- notm prin L(i,k) lungimea DVM dintre i i k, kX;
- notm deasemenea dkj valoarea arcului (k,j);
- ecuaiile de recuren sunt:
L(i,j) = min {L(i,k) + dkj)}
(k,j)
#include<stdio.h>
#include<values.h>
#define nn 10
int d[nn+1][nn+1],i,j,n;
int L(int i,int j)
{
int m=MAXINT;
int k,x=0;
if (i= =j) m=0;
else
for (k=1;k<=n;k++)
if (d[k][j] < MAXINT)
{ x=L(i,k)+d[k][j];
if (x<m) m=x;
}
return m;
}
void citestematrice(void)
{int i,j;
printf("\ndati dimensiunea matricii distantelor : ");
scanf("%d",&n);
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{ printf("d[%d,%d]= ",i,j);
scanf ("%d",&d[i][j]);
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) if (d[i][j]= = -1) d[i][j]=MAXINT;
}

void citeste(void)
{ printf("\ndati varful initial : ");
scanf("%d",&i);
printf("\ndati varful final : ");
scanf("%d",&j);
}
void afiseaza(int val)
{ printf("\nvdlm intre varful %d si %d este %d",i,j,val);
}
void main(void)
{ int vdlm;

130

clrscr();
citestematrice();
citeste();
vdlm=L(i,j);
afiseaza(vdlm);
getch();
}

Probleme propuse
14.5.1.
Se d o matrice A=(aij), i=1,...,n; j=1,...,m cu elementele din mulimea {0,1,2}. Dou elemente
din A aij i akl se numesc 4-vecine dac i-k+j-l = 1.
Notm cu So, S1 i S2 submulimile formate din elementele matricii egale cu 0, 1 respectiv 2.
Submulimea S1 se mparte n grupuri astfel: aij i akl fac parte din acelai grup dac sunt 4-vecine
sau dac apq S1 : aij i apq sunt 4-vecine iar apk i akl sunt din acelasi grup. Un element akl So
l vom numi spion al grupului G S1 dac aij G a.. akl i aij s fie vecine. Un spion este
perfect dac are toi vecinii din G.
Se cere:
a) cel mai numeros grup (S1) care are un singur spion (dac exist).
b) toate grupurile care au cel puin doi spioni perfeci.
14.5.2.
Se d o matrice cu elemente care au valori din {d,o}, care reprezint un teren cu drumuri {d} i
obstacole {o}. n acest teren se afl un tanc i o int. Acest tanc poate primi comenzi pentru
rotirea evii (cu 90o n ambele sensuri), pentru deplasare (n direcia evii cu o linie sau cu o
coloan) sau pentru tragere (n direcia evii pentru a nimeri inta) n cazul n care ntre tanc i
int nu exist nici un obstacol. Considernd c deplasarea nu se poate efectua prin obstacole, se
cere cel mai scurt drum necesar tancului pentru a putea distruge inta i irul comenzilor efectuate
n cazul n care exist un astfel de drum.
14.5.3.
Se d o matrice cu elemente din {0,1,2,3}, reprezentnd o pdure cu capcane (0) i crri (1). n
aceast pdure se afl o vulpe (2) i mai muli lupi (3). Fiecare lup ncearc s se apropie de
vulpe fr a ti unde se afl capcanele, iar n cazul n care cade ntr-o capcan, moare. Vulpea
ncearc s se ndeprteze de cel mai apropiat lup, avnd ns posibilitatea s descopere i
capcanele i s le ocoleasc. Att vulpea ct i lupii i pot modifica poziia doar cu o linie sau cu
o coloan n fiecare moment. S se spun dac vulpea reuete s scape de lupi.

14.5.4.
Se consider un teren dreptunghiular sub forma unei matrici A de m linii i n coloane.
Elementele aij ale matricii conin cotele (nlimile) diferitelor poriuni astfel obinute. Se
presupune c o bil se gsete pe o poriune (io,jo) avnd cota a(io,jo).
Se cere un program care s precizeze toate traseele (ncepnd cu (io,jo) ) posibile pe care le poate
urma bila spre a iei din teren, tiind c bila se poate deplasa n orice poriune de teren 4nvecinat cu o cot strict inferioar cotei terenului pe care se gsete bila.
14.5.5.
Se cere un program care rezolv problema labirintului (nerecursiv).
131

O matrice de m linii i n coloane reprezint un labirint dac:


a(i,j) = o - semnificnd culoar;
a(i,j) = 1 - semnificnd zid.
Un traseu de ieire pornind de la o poriune (io,jo) trebuie s fie o succesiune de perechi (io, jo),
(i1, j1) . . . (ik, jk) astfel nct 2 perechi nvecinate s fie 4-vecine i a(ip,jp)=0
p=0, . . . ,k.

TEM:
S se scrie programe C care rezolv 5 probleme propuse din capitolul 14.

132

PARTEA II
Limbajul C++

133

15. Faciliti noi n limbajul C++


15.1. Extinderea limbajului C
Programele scrise n limbajul C pot fi executate i folosind compilatorul C++
(eventual cu modificri nesemnificative). Deci limbajul C++ conine toate instruciunile
limbajului C, dar n plus ofer i faciliti noi. Cele mai importante avantaje ale limbajului
C++ fa de C sunt urmtoarele. S-a introdus noiunea de clas prin care se definete un tip
abstract de date, astfel programatorul are posibilitatea folosirii metodei de programare prin
abstractizarea datelor. Clasele pot fi organizate ntr-o ierarhie de clase, deci este posibil i
programarea prin metode orientate obiect.
n continuare prezentm nite elemente noi ale limbajul C++, iar n seciunile care
urmeaz vor fi tratate faciliti noi, mai importante, referitoare la operatori, structuri, reuniuni,
tipuri enumerare i funcii.
Exist un nou tip de comentarii n limbajul C++ de forma: // comentariu
n acest caz, tot ce se afl dup caracterele // se va ignora de ctre compilator, pn la
sfritul rndului.
n limbajul C o instruciune compus este de forma:
{
declaraii
instruciuni
}

Deci toate declaraiile trebuie s fie nainte de prima instruciune. n limbajul C++
declaraiile i instruciunile pot fi n orice ordine n interiorul unei instruciuni compuse.
Nu exist instruciuni de intrare/ieire n limbajul C, i nici n C++. n schimb n
limbajul C exist biblioteci standard de funcii pentru operaii de intrare/ieire. Ele pot fi
folosite i n limbajul C++, dar n afar de aceste biblioteci, mai exist i ierarhii de clase
pentru realizarea operaiilor de intrare/ieire. Ele vor fi tratate pe larg n capitolul 4, dar pentru
a putea utiliza aceste clase, prezentm aici nite exemple simple, ilustrative.
Dispozitivului standard de intrare respectiv ieire s-au ataat aa numitele streamuri
standard: cin pentru stdin i cout pentru stdout. Operaiile de intrare se vor efectua aplicnd
operatorul >> streamului standar cin, iar cele de ieire aplicnd operatorul << streamului
standard cout.
Prezentm n continuare exemple de operaii de intrare/ieire folosind streamurile
standard cin i cout. Pentru fiecare exemplu se va specifica n form de comentariu
echivalentul operaiei, folosind funcii din biblioteca standard a limbajului C.
Ierarhia de clase pentru operaii de intrare/ieire poate fi utilizat numai dac se
include fiierul iostream.h. n continuare prezentm exemple pentru operaii de afiare pe
dispozitivul standard de ieire:
#include <iostream.h>
int main() {
...
int x;
...
cout << x; // printf("%d", x);
...
double y;
...
cout << y; // printf("%lg", y);
...
char z;

134

...
cout << z; // printf("%c", z);
...
char t[] = "exemplu";
...
cout << t; // printf("%s", t);
...
}

Exemple pentru operaii de citire de la dispozitivul standard de intrare:


#include <iostream.h>
int main() {
...
int x;
...
cin >> x; // scanf("%d", &x);
...
double y;
...
cin >> y; // scanf("%lf", &y);
...
char z[20];
...
cin >> z; // scanf("%s", z);
...
}

Fr a intra acum n detalii menionm c operatorii << respectiv >> pot fi aplicai
nlnuit. Deci de exemplu
int w;
...
cout << "w = " << w + 25 << '\n';

este echivalent cu
int w;
...
printf("w = %d\n", w);

Trecerea la linie nou se poate efectua i cu


cout << endl;

Deci n loc de apelarea funciei printf de mai sus, s-ar fi putut scrie i:
cout << "w = " << w + 25 << endl;

Exist o diferen minor dintre limbajele C i C++, referitoare la constantele de tip


caracter. Ele sunt memorate pe doi octei n limbajul C i au tipul int. n limbajul C++ ns
exist dou feluri de constante de tip caracter. Memorate pe un octet ele au tipul char, iar pe
doi octei au tipul int.
S considerm urmtorul exemplu. Fiierul caracter.cpp:
#include <iostream.h>
#include <conio.h>
int main() {
clrscr();

135

cout << 'A' << " cod: " << int('A') << " octeti: "
<< sizeof 'A' << endl;
cout << 'B' << " cod: " << int('B') << " octeti: "
<< sizeof 'B' << endl;
cout << 'AB' << " octeti: " << sizeof 'AB' << endl;
cout << "\'B\' * 256 + \'A\' = " << 'B' * 256 + 'A';
return 0;
}

Dup executarea programului se obine:


A cod: 65 octeti: 1
B cod: 66 octeti: 1
16961 octeti: 2
'B' * 256 + 'A' = 16961

Observm c ntr-adevr caracterul 'AB' este de tip int i n octetul mai semnificativ
este memorat codul ASCII al caracterului 'B', iar n cel mai puin semnificativ codul ASCII al
caracterului 'A'.
n limbajul C utilizarea operatorului de conversie explicit se face sub forma:
(tip) expresie

n limbajul C++ operatorul de conversie explicit se poate aplica i n modul urmtor:


tip(expresie)

S considerm urmtorul exemplu. Fiierul conv1.cpp:


#include <iostream.h>
#include <conio.h>
typedef double* p_double;
int main() {
double x = 4.0 / 3;
void* p;
clrscr();
cout << x << endl;
cout << int( x ) << endl; // int( x ) este echivalenta cu
// (int) x
p = &x;
cout << *p_double(p) << endl; // *p_double(p) este
// echivalenta cu
// *(double *) p
return 0;
}

Dup executarea programului obinem:


1.333333
1
1.333333

Rezult c ntr-adevr s-a realizat conversia variabilei x de la tipul double la tipul int,
i conversia variabilei p de la tipul void* la tipul double*. Menionm c n acest caz,
introducerea tipului p_double a fost obligatorie. Dac s-ar fi folosit conversia explicit
(double*) p, atunci nu ar fi fost necesar definirea tipului p_double.
136

n limbajul C++ putem defini i conversii utilizator ataate unor tipuri definite de
programator. Conversiile utilizator se vor trata n seciunea 16.8.

15.2. Operatori
15.2.1. Operatorul de rezoluie
Dac o dat global se redefinete ntr-o funcie, atunci referirea la data global se
poate face cu ajutorul operatorului de rezoluie. Numele datei globale trebuie precedat de
caracterele ::, deci de operatorul de rezoluie.
Prezentm n continuare un exemplu. Fiierul rezol.cpp:
#include <iostream.h>
#include <conio.h>
int x = 65;
int main() {
clrscr();
char x = 'A';
cout << "Variabila globala: " << ::x << endl;
cout << "Variabila locala: " << x << endl;
return 0;
}

Dup execuie se obine:


Variabila globala: 65
Variabila locala: A

Deci se observ c referirea la variabila global x de tip int s-a fcut cu ajutorul
operatorului de rezoluie (::x ). Menionm c operatorul de rezoluie se va folosi i n cazul
n care se face referire la datele respectiv funciile membru ale unei clase.

15.2.2. Folosirea operatorului adres pentru definirea tipului referin


n limbajul C operatorul & se folosete pentru a determina adresa unei variabile. n
limbajul C++ acest operator mai este folosit i pentru definirea tipului referin. Am vzut c
n cazul pointerilor construcia
tip *

reprezint un tip pointer. La fel i n cazul de fa prin


tip &

se va nelege un tip referin. Cu ajutorul tipului referin putem defini nume alternative
pentru date deja cunoscute. Aceste nume alternative pot fi utilizate n continuare n locul
datelor respective. Deasemenea acest tip de date se va folosi i pentru realizarea apelului prin
referin n limbajul C++.
Dac se folosete ca i parametru formal ntr-o funcie, atunci tipul referin va fi de
forma:
tip & parametru_formal

137

iar declararea unei variabile de tip referin se va face sub forma


tip & nume = dat;

Exemplu. Fiierul refer1.cpp.


#include <iostream.h>
#include <conio.h>
void afiseaza(int x[], int y, int* z)
{
cout << "x[0] = " << x[0] << " "
<< "y = " << y << " "
<< "*z = " << *z << endl;
}
int main() {
clrscr();
int x[4] = {10, 20, 30, 40};
int& y = x[0]; // y este referinta la primul element
// al vectorului x
int* z = x;
// z este pointer catre vectorul x
afiseaza(x, y, z);
y = 50;
afiseaza(x, y, z);
*z = 60;
afiseaza(x, y, z);
return 0;
}

Prin execuie se obine:


x[0] = 10
x[0] = 50
x[0] = 60

y = 10
y = 50
y = 60

*z = 10
*z = 50
*z = 60

Observm c aceeai zon de memorie este accesat n trei feluri. Primul element al
vectorului x este x[0], y este referin la acest element, deci un nume sinonim pentru x[0], iar
z este un pointer ctre vectorul x, astfel *z reprezentnd acceai zon de memorie ca i x[0].
Rezult c valoarea oricrei dintre aceste trei elemente s-ar schimba, valorile celorlalte se
schimb n mod automat.
Exist posibilitatea definirii unei referine i la o funcie. Acest lucru este prezentat n
exemplul urmtor. Fiierul refer2.cpp:
#include <iostream.h>
#include <math.h>
#include <conio.h>
typedef double TipFunctie(double);
int main() {
clrscr();
const double Pi = 3.141592653;
TipFunctie& sinus = sin;
cout << sinus( Pi / 2 ); // se afiseaza valoarea 1
return 0;
}

138

n exemplul de mai sus s-a declarat o referin la funcia standard de bibliotec sin,
deci numele sinus poate fi folosit n locul funciei sin pentru a calcula sinusul unui numr real.
Tipul referin este cel mai des folosit pentru realizarea apelului prin referin, ceea ce
se trateaz n paragraful urmtor.

15.2.3. Apelul prin referin


n limbajul C apelul este prin valoare. Acest lucru nseamn c parametrii actuali i cei
formali sunt memorate n diferite locaii de memorie. La apelarea funciei parametrii actuali
i transmit valoarea lor parametrilor formali corespunztoare. De aceea modificarea
parametrilor formali nu are nici un efect asupra parametrilor actuali. Urmtorul exemplu scris
n limbajul C ilustreaz acest lucru. Fiierul valoare1.cpp:
#include <stdio.h>
#include <conio.h>
void nu_modifica( int x )
{
x += 20; // valoarea parametrului formal x
}
// se mareste cu 20
int main () {
int y;
y = 10;
clrscr();
nu_modifica( y ); // parametrul actual y nu se modifica
printf("y = %d", y);
return 0;
}

Constatm c ntr-adevr parametrul actual y nu se modific, i programul afieaz


valoarea 10.
Totui i n limbajul C se poate realiza modificarea parametrilor actuali, cu ajutorul
pointerilor. Acest lucru este prezentat n urmtorul exemplu.
Fiierul valoare2.cpp:
#include <stdio.h>
#include <conio.h>
void modifica( int *x ) // parametrul formal x este un
{
// pointer catre un intreg
*x += 20; // intregul spre care pointeaza x
}
// se mareste cu 20
int main () {
int y;
y = 10;
clrscr();
modifica( &y );

// La apelare adresa lui y se transmite


// prin valoare parametrului formal x,
// deci intregul spre care pointeaza x
// adica *x, va fi chiar y.
// In consecinta modificarea valorii lui
// *x ne conduce la modificarea
// valorii lui y.
printf("y = %d", y);
return 0;

139

n exemplul de mai sus s-a realizat modificarea valorii parametrului actual y, dar i n
acest caz s-a folosit de fapt apel prin valoare. S-a transmis valoarea adresei lui y pointerului
x. Deci prin intermediul pointerilor apelul prin valoare s-a transformat n apel prin referin.
Menionm c numele unui vector de elemente este un pointer ctre primul element al
vectorului. n consecin, dac un parametru actual este un vector, atunci valorile elementelor
acestui vector pot fi modificate n interiorul funciei.
Aceast modalitate de realizare a apelului prin referin, prin intermediul pointerilor,
de multe ori ngreuneaz munca programatorului, de aceea n limbajul C++ s-a introdus i
apelul prin referin propriu zis.
Apelul prin referin se realizeaz cu ajutorul tipului referin. n cazul folosirii tipului
referin parametrul formal este un nume alternativ pentru parametrul actual, deci ele
reprezint aceeai zon de memorie i ca atare orice modificare a parametrului formal
conduce la modificarea parametrului actual corespunztor. Iat cum se modific exemplul de
mai sus folosind apelul prin referin. Fiierul refer3.cpp:
#include <iostream.h>
#include <conio.h>
void modifica( int& x ) // parametrul formal x este o
{
// referinta la un intreg
x += 20; // valoarea parametrului formal x
}
// se mareste cu 20
int main () {
int y;
y = 10;
clrscr();
modifica( y );

// Parametrul formal x este un nume


// alternativ pentru y, deci modificarea
// valorii lui x conduce la modificarea
// valorii lui y.
cout << "y = " << y;
return 0;

Putem constata c apelul prin referin este mult mai simplu de utilizat, dect
realizarea ei prin intermediul pointerilor.

15.2.4. Operatori pentru alocare i dezalocare dinamic a memoriei


Pentru alocare i dezalocare de memorie n limbajul C pot fi folosite funciile malloc
i free. n limbajul C++ exist i operatori pentru realizarea acestui lucru.
Alocarea memoriei se poate face folosind operatorul new, n unul din urmtoarele trei
moduri:
a)
b)
c)

new tip
new tip(expresie_1)
new tip[expresie_1]

n toate cele trei cazuri construciile de mai sus formeaz o expresie pe care o vom
nota cu expresie_2. Valoarea expresiei expresie_2 este egal cu adresa de nceput a zonei de
memorie alocate, i este zero n cazul unei erori.
n cazul a) se aloc zon de memorie pentru o variabil de tipul exprimat prin tip. n
cazul b) zona de memorie alocat se iniializeaz cu valoarea expresiei expresie_1, iar n cazul
c) se aloc memorie un numr de expresie_1 de zone de memorie de tipul menionat. Deci n
140

cazul a) i b) se vor aloca un numr de sizeof(tip) octei, iar n cazul c) expresie_1 *


sizeof(tip) octei.
n tabelul 1 prezentm trei exemple de secvene de programe echivalente, n care se
folosete operatorul new n diferite moduri.
Eliberarea zonei de memorie alocate cu operatorul new se va face cu ajutorul
operatorului delete. S considerm pointerul q pentru care s-a alocat memorie folosind
operatorul delete.
Dac alocarea zonei de memorie s-a fcut cu una din variantele a) sau b) atunci
dezalocarea se va face n forma:
delete q;

iar dac alocarea s-a fcut prin varianta c) atunci eliberarea zonei de memorie se va face cu
delete [expresie_1] q;

unde expresie_1 este expresia care s-a folosit la alocare. De obicei ea poate s lipseasc.
Nr. crt.
1.
2.
3.

C++

C sau C++

double* d;
d = new double;
int* i;
i = new int(20);
double* p;
p = new double[10];

double *d;
d = (double *) malloc(sizeof(double));
int* i;
i = new int;
*i = 20;
d = (double *) malloc(10 * sizeof(double));

Tabelul 1: folosirea operatorului new

15.3. Structuri, reuniuni i tipuri enumerare


n limbajul C++ numele dup cuvntul cheie struct, union respectiv enum poate fi
folosit pentru identificarea structurii, reuniunii respectiv a tipului enumerare. De exemplu n
limbajul C struct nume formeaz un tip de date, n limbajul C++ nu mai este nevoie de
cuvntul cheie struct pentru a se referi la structura deja declarat.
De exemplu s considerm structura:
struct
char
char
long
};

Persoana {
nume[15];
prenume[15];
telefon;

Atunci urmtoarea declaraie este corect n C++ i greit n C.


Persoana Adalbert, Marta;

O alt proprietate important este c n limbajul C++ o funcie poate avea structuri ca
i parametrii. Deasemenea i valoarea returnat de o funcie poate fi o structur. n consecin
valoarea returnat de funcie i parametrii unei funcii pot fi structuri, pointeri ctre structuri i
referine la structuri.
Se pot efectua instruciuni de atribuire cu structurile de acelai tip. De exemplu:
Persoana A, Adalbert;
A = Adalbert;

141

n interiorul unei structuri se poate declara o reuniune anonim n limbajul C++ sub
forma:
struct nume {
declaraii
union {
declaraii
};
declaraii
} lis_de_nume;

n acest caz, referirea la elementele declarate n interiorul reuniunii anonime, se face


ca i referirea la elementele structurii.
n limbajul C++ primul element al unei reuniuni poate fi iniializat.
Considerm urmtorul exemplu. Fiierul union1.cpp:
#include <iostream.h>
#include <conio.h>
union date {
int x[8];
double y[2];
};
int main() {
clrscr();
date D = {1, 2, 3, 4, 5, 6, 7, 8}; // initializarea lui x
for(int i=1; i <= 8; i++)
cout << "x[" << i-1 << "]=" << D.x[i-1]
<< ( i%4 ? " " : "\n");
D.y[0] = 1.5;
D.y[1] = 2.4;
cout << "\ny[0] = " << D.y[0]
<< " y[1] = " << D.y[1] << endl;
return 0;
}

Prin execuie se obine:


x[0]=1
x[4]=5

x[1]=2
x[5]=6

y[0] = 1.5

x[2]=3
x[6]=7

x[3]=4
x[7]=8

y[1] = 2.4

Rezult c primul element (vectorul x) poate fi iniializat la fel cum s-ar fi procedat n
cazul iniializrii elementului n sine (adic a unui vector).
n limbajul C++ variabilelor de tip enumerare se pot atribui numai elementele
componente ale tipului. Numerele asociate cu aceste elemente nu se pot atribui variabilelor de
tip enumerare. Exemplu
enum culori {rosu, galben, verde};
culori Semafor;
...
Semafor = 1;
// corect in C, gresit in C++
Semafor = galben; // corect

n continuare ne vom ocupa de faciliti noi oferite de limbajul C++ referitoare la


funcii.
142

15.4. Funcii
15.4.1. Iniializarea parametrilor formali ai funciilor
Parametrii formali ai unei funcii pot fi iniializai n limbajul C++. Vom spune c
parametrii iniializai au o valoare implicit. n lista parametrilor formali elementele
iniializate vor fi declarate n felul urmtor:
tip nume = expresie

Totui exist anumite restricii n acest sens. Dac exist att parametri formali
iniializai ct i neiniializai, atunci cei neiniializai trebuie s fie nainte de cei iniializai n
lista parametrilor formali.
Prezentm un exemplu n continuare. Fiierul init1.cpp:
#include <iostream.h>
#include <conio.h>
void intilniri(char* nume, char* loc,
int ora = 12, int minut = 0)
{
cout << "Nume: " << nume << endl
<< "Loc:
" << loc
<< endl
<< "Ora:
" << ora
<< endl
<< "Minut: " << minut << "\n\n";
}
int main() {
clrscr();
cout << "Intilniri:\n\n";
intilniri("Daniel", "Facultatea de Litere");
intilniri("Eva", "Scoala Generala", 14);
intilniri("bunica", "acasa", 17, 20);
return 0;
}

Dac se execut programul se obine:


Intilniri:
Nume:
Loc:
Ora:
Minut:

Daniel
Facultatea de Litere
12
0

Nume:
Loc:
Ora:
Minut:

Eva
Scoala Generala
14
0

Nume:
Loc:
Ora:
Minut:

bunica
acasa
17
20

Observm c paramerii formali ora i minut sunt iniializai cu valorile implicite 12,
respectiv 0. Dac parametrii formali iniializai nu au parametrii actuali corespunztori, atunci
se vor folosi valorile lor implicite, n caz contrar se va ignora valoarea implicit i se vor
folosi parametrii actuali corespunztori.
143

Este interesant de remarcat c la iniializare se poate folosi orice expresie, deci de


exemplu i apeluri de funcii. n urmtorul exemplu apar parametrii formali iniializai n acest
fel. Fiierul init2.cpp:
#include <iostream.h>
#include <conio.h>
#include <dos.h>
char ziua()
{
date* d;
getdate( d );
return d->da_day;
}
char luna()
{
date* d;
getdate( d );
return d->da_mon;
}
int anul()
{
date* d;
getdate( d );
return d->da_year;
}
void scrie_data( char z = ziua(),
char l = luna(),
int a = anul() )
{
cout << "ziua: " << int(z)
<< " luna: " << int(l)
<< " anul: " << a << endl;
}
int main() {
clrscr();
scrie_data();
scrie_data(11);
scrie_data(18,4);
scrie_data(27,7,1991);
return 0;
}

n exemplul de mai sus s-a folosit c n fiierul dos.h este declarat o structur
pentru memorarea datelor sub forma:
struct date {
int da_year; // Anul exprimat printr-un ntreg.
char da_day; // Caracterul se convertete n int
// i astfel se obine ziua.
char da_mon; // Caracterul se convertete n int
// i astfel se obine luna.
};

Funcia getdate are un parametru, care este un pointer ctre structura de mai sus, n
care se va returna data curent.
144

Fiierul se poate compila cu un compilator C++ sub sistemul de operare Dos sau
Windows, i dup execuie se obine un rezultat care poate varia n funcie de data curent. De
exemplu (data curent: 24 august 1998):
ziua:
ziua:
ziua:
ziua:

24
11
18
27

luna:
luna:
luna:
luna:

8
8
4
7

anul:
anul:
anul:
anul:

1998
1998
1998
1991

Din rezultate reiese c n cazul n care nu s-a specificat parametru actual, s-a folosit
valoarea implicit, deci data curent.

15.4.2. Funcii care returneaz tipuri referin


n limbajul C++ tipul returnat de o funcie poate fi i tip referin. n acest caz antetul
funciei va fi de forma:
tip& nume(lista_parametrilor_formali)

Funciile care returneaz tipuri referin pot apare att pe partea dreapt ct i pe
partea stng a operatorilor de atribuire. Se spune c ele pot fi att operanzi lvalue (left value)
ct i rvalue (right value). Un operand lvalue poate s apar pe partea stng, iar unul rvalue
pe partea dreapt a operatorilor de atribuire.
S considerm urmtorul exemplu. ntr-o urn se afl bile albe i negre. Se extrage un
numr de bile din urn la ntmplare. S se scrie un program pentru aflarea numrului de bile
albe, respectiv negre extrase. Fiierul refer4.cpp:
#include
#include
#include
#include

<iostream.h>
<conio.h>
<stdlib.h>
<time.h>

long& alb_negru( long& ref_alb, long& ref_negru)


{
if ( random(2) )
return ref_alb;
return ref_negru;
}
void main() {
long nr_alb = 0;
long nr_negru = 0;
long nrbile;
clrscr();
cout << "Numarul de bile extrase: ";
cin >> nrbile;
randomize();
for( long i = 0; i < nrbile; i++ )
alb_negru( nr_alb, nr_negru )++;
cout << "Bile albe: " << nr_alb
<< "\nBile negre: " << nr_negru;
}

Dup compilarea sub sistemul de operare Dos sau Windows cu Borland C++, i
executarea programului, se obine un rezultat care depinde de numrul total de bile, numr
citit de la intrarea standard. Numrul de bile albe respectiv negre extrase se determin n mod
aleator. Deci rezultatul poate fi:
145

Numarul de bile extrase: 50


Bile albe: 27
Bile negre: 23

Menionm c s-a evideniat numrul citit. Observm c funcia alb_negru returneaz


o referin ctre unul din parametrii nr_alb sau nr_negru n funcie de faptul c bila extras
este alb sau neagr. Este important de menionat c parametrii formali ref_alb i ref_negru ai
funciei alb_negru trebuie s fie declarate ca i referine la tipul long. n caz contrar, n cadrul
funciei s-ar ncerca returnarea unei referine ctre o variabil local (unul din parametrii
formali), ceea ce este nepermis.
n cadrul funciei main s-a apelat funcia alb_negru i totodat s-a folosit i operatorul
de incrementare. Acest lucru este posibil datorit faptului c funcia returneaz o referin la o
variabil declarat n funcia main, i ca atare apelul funciei se comport ca i un nume de
variabil.
Bineneles aceast problem simpl s-ar fi putut rezolva i fr folosirea unei funcii,
care returneaz o referin. n varianta fr funcia care returneaz o referin, operatorul de
incrementare s-ar fi apelat de dou ori, pentru dou variabile distincte (nr_alb i nr_negru). n
cazul unei probleme mai complexe, de exemplu dac operatorul de incrementare s-ar fi
nlocuit cu operaii mai complicate, utilizarea unei funcii, care returneaz o referin, poate
simplifica considerabil programul.
n capitolele care urmeaz, n cadrul definirii claselor, ne vom ntlni cu situaii, n
care apare necesitatea declarrii unor funcii, care returneaz o referin ctre clasa curent.

15.4.3. Suprancrcarea funciilor


n limbajul C numele funciilor distincte trebuie s fie diferit. n limbajul C++ ns
putem defini funcii diferite avnd numele identic. Aceast proprietate se numete
suprancrcarea funciilor. De obicei funciile se vor suprancrca n cazul n care nite
operaii asemntoare sunt descrise de mai multe variante ale unor funcii. n general aceste
funcii se refer la proprieti asemntore, aplicate pentru parametrii formali diferii.
Pentru a putea face distincie ntre funciile suprancrcate, antetul acestor funcii
trebuie s fie diferit. Astfel tipul parametrilor actuali va determina funcia care se va apela.
Prezentm un exemplu legat de variantele funciei trigonometrice atan, pentru
calcularea numrului . Fiierul sup_fun1.cpp:
#include
#include
#include
#include

<iostream.h>
<conio.h>
<math.h>
<complex.h>

/*
double atan(double x);
double atan2(double y, double x);
long double atanl(long double (x));
long double atan2l(long double (y), long double (x));
complex atan(complex x);
*/

int main() {
clrscr();
cout.precision(18);

// numerele reale vor fi afisate


// cu 18 zecimale
cout << 4 * atan( 1 ) << endl;
cout << 4 * atanl( 1 ) << endl;

146

cout << atan2(0, -1) << endl;


cout << atan2l(0, -1) << endl;
cout << real( 4 * atan( complex(1, 0) ) ) << endl;
return 0;
}

n fiierul de mai sus, n form de comentariu s-au specificat cele cinci variante ale
funciei atan. Primele patru sunt declarate n fiierul math.h, ele au numele distincte i se
refer la parametrii formali de tip real. n Borland C++ exist funcia atan, cu acelai nume ca
i prima funcie referitoare la parametrii de tip real. Aceast funcie este declarat n fiierul
complex.h, i se apeleaz n cazul unui parametru actual de tip complex.
Dup executarea programului obinem:
3.141592653589793116
3.141592653589793238
3.141592653589793116
3.141592653589793238
3.141592653589793116

Observm c n cazul n care nu s-au folosit variabile de tip long double (funciile atan
cu parametrii de tip double respectiv complex, i funcia atan2) primele 15 zecimale sunt
corecte, iar pentru funciile atanl i atan2l toate cele 18 zecimale obinute sunt corecte.
Exemplul de mai sus se poate modifica astfel nct toate cele cinci variante ale funciei
atan s aib acelai nume. Pentru a realiza acest lucru vom scrie trei funcii cu numele atan,
care vor apela funciile atan2, atanl respectiv atan2l. Deoarece antetul funciilor va fi diferit,
se poate efectua suprancrcarea lor. Prezentm n continuare aceast variant modificat a
programului. Fiierul sup_fun2.cpp:
#include
#include
#include
#include

<iostream.h>
<conio.h>
<math.h>
<complex.h>

//double atan(double x);


double atan(double y, double x)
{
return atan2(y, x);
}
long double atan(long double (x))
{
return atanl(x);
}
long double atan(long double (y), long double (x))
{
return atan2l(y, x);
}
//complex atan(complex x);
int main() {
clrscr();
cout.precision(18);
cout << 4 * atan( 1.0 ) << endl;
cout << 4 * atan( 1.0L ) << endl;
cout << atan(0.0, -1.0) << endl;
cout << atan(0.0L, -1.0L) << endl;

147

cout << real( 4 * atan( complex(1, 0) ) ) << endl;


return 0;
}

Dac se execut programul, se obine acelai rezultat ca i pentru programul


precedent. n acest caz numele tuturor funciilor este acelai, deci funcia care se va apela, se
determin dup numrul i tipul parametrilor actuali. De aceea trebuie specificat cu exactitate
tipul parametrilor formali (de exemplu 1.0 este constant de tip double, i 1.0L este constant
de tip long double).
n exemplul de mai sus se poate determina ntotdeauna cu exactitate funcia care se va
apela, deoarece exist o funcie pentru care att numrul ct i tipul parametrilor formali este
acelai ca i pentru funcia respectiv. Nu este ns ntotdeauna n aa fel. De exemplu, dac n
funcia main din exemplul de mai sus am fi scris
cout << 4 * atan( 1 ) << endl;

atunci deoarece parametrul actual al funciei atan este de tip ntreg, nici unul din cele cinci
funcii nu are un antet corespunztor. n astfel de cazuri se vor lua n considerare urmtoarele
reguli. Etapele determinrii funciei care se va apela:
1. Dac exist o funcie pentru care tipul parametrilor formali coincide cu tipul
parametrilor actuali corespunztoare (deci i numrul parametrilor formali
coincide cu numrul parametrilor actuali), atunci aceast funcie se va apela.
2. Altfel, funcia se va determina folosind conversia implicit pentru tipurile standard
(de exemplu tipul int se va converti n double). n acest caz nu vor fi pierderi de
informaii.
3. Dac nu s-a reuit determinarea unei funcii, atunci se ncearc o conversie pentru
tipuri standard cu eventuale pierderi de informaii (de exemplu conversie din tipul
long n tipul int).
4. Dac nici n acest fel nu s-a reuit determinarea funciei, atunci se ncearc
aplicarea unor conversii i tipurilor definite de programator.
Rezult c n cazul expresiei cout << 4 * atan( 1 ) << endl; se va ncerca aplicarea etapei
a doua a determinrii funciei, dar deoarece s-ar putea aplica att o conversie de la tipul int la
double, ct i la long double, apare o ambiguitate care nu poate fi rezolvat de compilator, i
se va afia un mesaj de eroare la compilare.
Dac ns revenim la exemplul din fiierul sup_fun1.cpp putem constata c la
compilarea expresiei amintite nu au aprut mesaje de eroare. Acest lucru se datoreaz faptului
c funcia atan a fost suprancrcat n aa fel nct au existat numai dou variante ale ei (una
cu parametru formal de tip double, iar alta de tip complex). n acest caz exist o singur
conversie implicit posibil pentru tipuri standard, deci se va aplica conversia de la tipul int la
double, i se va apela funcia atan cu un singur parametru de tip double.

15.4.4. Funcii inline


Apelarea unei funcii se face printr-un salt la adresa unde este memorat corpul
funciei, iar dup executarea instruciunilor funciei, se face o revenire la locul apelrii. n
cazul n care funcia este foarte simpl operaiile necesare apelrii pot fi mai complicate dect
instruciunile funciei. n limbajul C aceast problem se poate rezolva prin folosirea unor
macrouri. Ele se definesc n modul urmtor:
#define nume(list_de_parametrii) ir_de_caractere

148

n faza de preprocesare apelurile macrourilor vor fi nlocuite cu irul de caractere


specificat, n care parametrii formali vor fi nlocuii cu parametrii actuali. Observm c
apelarea macrourilor seamn cu apelarea funciilor. Totui exist multe diferene. De
exemplu apelarea unei funcii se face printr-un salt la adresa corpului funciei dup care se
revine la locul apelrii, iar apelarea macrourilor se face prin nlocuirea cu expresia
corespunztoare n faza de preprocesare. Diferena cea mai important este c n cazul apelrii
unei funcii, se verific corespondena dintre tipurile parametrilor formali i cei actuali, iar
dac este necesar se face i o conversie la tipul corespunztor. Astfel de verificri nu se fac n
cazul macrourilor, ceea ce este un dezavantaj fiindc poate s conduc la apariia unor erori.
Dac sunt folosite n mod corect, avantajul macrourilor este c ele pot fi apelate pentru
expresii de tipuri diferite.
n urmtorul exemplu vom defini trei macrouri pentru calculul valorii absolute a unui
numr, i vom atrage atenia asupra posibilitii apariiei unor erori. Fiierul macro1.cpp:
#include <iostream.h>
#include <conio.h>
#define Val_abs(x) ((x) > 0 ? (x) : -(x))
#define Val_abs_eronata_1(x) ( x > 0 ? x : -x )
#define Val_abs_eronata_2(x) (x) > 0 ? (x) : -(x)
int main() {
int y = 10;
long z = -50000;
clrscr();
cout << Val_abs( y ) << endl;
cout << Val_abs( z ) << endl;
cout << Val_abs(4000L * y + z ) << endl;
cout << Val_abs_eronata_1(4000L * y + z ) << endl;
cout << 15 + Val_abs( -y ) << endl;
cout << 15 + Val_abs_eronata_2( -y ); cout << endl;
cout << Val_abs( y++ ) << endl;
cout << "y = " << y << endl;
return 0;
}

Dup execuie se obine:


10
50000
10000
-90000
25
5
11
y = 12

Dac parantezele ar lipsi din definirea macroului Val_abs(x), n unele expresii ar putea
s apar erori. ntr-adevr prin expresia
cout << Val_abs_eronata_1(4000L * y + z ) << endl;

se obine rezultat necorespunztor (-90000 n loc de 10000), deoarece n faza de preprocesare


macroul Val_abs_eronata_1 s-a nlocuit cu:
( 4000L * y + z > 0 ? 4000L * y + z : -4000L * y + z )

i rezultatul va fi: 4000L * y + z = -90000. Nici n cazul expresiei


149

cout << 15 + Val_abs_eronata_2( -y );

nu se obine rezultat corect (5 n loc de 25), deoarece macroul Val_abs_eronata_2 se


nlocuiete cu
(-y) > 0 ? (-y) : -(-y)

i expresia va deveni:
cout << 15 +(-y) > 0 ? (-y) : -(-y);

Operatorul de adunare fiind mai prioritar dect operatorul ?:, se evalueaz mai nti
expresia 15 + (-y), i se va afia valoarea 5. n sfrit, menionm c i n cazul n care
macroul este definit corect pot s apar situaii n care rezultatul nu va fi cel ateptat. Dac
apelarea macroului s-ar face ca i n cazul funciilor, atunci prin evaluarea expresiei
cout << Val_abs( y++ ) << endl;

s-ar afia valoarea 10, iar valoarea parametrului y dup evaluarea expresiei ar fi 11. n cazul
nostru ns s-a afiat valoarea 11, iar dup evaluarea expresiei s-a obinut valoarea y = 12.
Explicaia const i de aceast dat n modul de apelare a macrourilor. n faza de preprocesare
macroul Val_abs se nlocuiete cu urmtorul ir de caractere:
((y++) > 0 ? (y++) : -(y++))

Dup evaluarea expresiei (y++) > 0, se incrementeaz valoarea lui y, deci rezultatul
expresiei condiionale va fi egal cu 11, dup care se incrementeaz nc o dat valoarea
variabilei y. n consecin dup evaluarea expresiei se obine y = 12.
Aceste neajunsuri ale macrourilor pot fi nlturate cu ajutorul funciilor inline.
Apelarea funciilor inline se face n mod asemntor cu apelarea macrourilor. Apelul funciei
se va nlocui cu corpul funciei, deci operaia de salt la adresa corpului funciei i revenirea la
locul apelrii nu sunt necesare. Comparativ cu macrouri, funcia inline are avantajul, c se va
verifica corespondena dintre tipurile parametrilor formali i cei actuali, i se va face o
conversie de tipuri, dac este necesar. Astfel posibilitatea apariiei unor erori este diminuat.
Declararea sau definirea unei funcii inline se face n mod obinuit, dar antetul funciei se va
ncepe cu cuvntul cheie inline.
Exemplul de mai sus se poate transcrie folosind o funcie inline, n modul urmtor.
Fiierul inline1.cpp:
#include <iostream.h>
#include <conio.h>
inline long Val_abs( long x )
{
return x > 0 ? x : -x;
}
int main() {
int y = 10;
long z = -50000;
clrscr();
cout << Val_abs( y ) << endl;
cout << Val_abs( z ) << endl;
cout << Val_abs(4000L * y + z ) << endl;
cout << 15 + Val_abs( -y ) << endl;
cout << Val_abs( y++ ) << endl;
cout << "y = " << y << endl;
return 0;
}

150

Dup executarea programului se obine:


10
50000
10000
25
10
y = 11

Putem constata c, n acest caz, toate rezultatele obinute sunt corecte. Menionm c o
funcie inline nu se poate folosi numai ntr-un singur modul, funcia nu poate s conin
operaii mai sofisticate, de exemplu nici instruciuni de ciclare. n cazul definirii claselor ne
vom ntlni foarte des cu funcii inline.

151

16. Noiunea de clas


16.1. Realizarea proteciei datelor prin metoda programrii modulare
Dezvoltarea programelor prin programare procedural nseamn folosirea unor funcii
i proceduri pentru scrierea programelor. n limbajul C lor le corespund funciile care
returneaz o valoare sau nu. ns n cazul aplicaiilor mai mari ar fi de dorit s putem realiza
i o protecie corespunztoare a datelor. Acest lucru ar nsemna c numai o parte a funciilor
s aib acces la datele problemei, acelea care se refer la datele respective. Programarea
modular ofer o posibilitate de realizare a proteciei datelor prin folosirea clasei de memorie
static. Dac ntr-un fiier se declar o dat aparinnd clasei de memorie static n afara
funciilor, atunci ea poate fi folosit ncepnd cu locul declarrii pn la sfritul modulului
respectiv, dar nu i n afara lui.
S considerm urmtorul exemplu simplu referitor la prelucrarea vectorilor de numere
ntregi. S se scrie un modul referitor la prelucrarea unui vector cu elemente ntregi, cu funcii
corespunztoare pentru iniializarea vectorului, eliberarea zonei de memorie ocupate i
ridicarea la ptrat, respectiv afiarea elementelor vectorului. O posibilitate de implementare a
modulului este prezentat n fiierul vector1.cpp:
#include <iostream.h>
static int* e;
static int d;

//elementele vectorului
//dimensiunea vectorului

void init(int* e1, int d1) //initializare


{
d = d1;
e = new int[d];
for(int i = 0; i < d; i++)
e[i] = e1[i];
}
void distr()
{
delete [] e;
}

//eliberarea zonei de memorie ocupata

void lapatrat()
{
for(int i = 0; i < d; i++)
e[i] *= e[i];
}

//ridicare la patrat

void afiseaza()
{
for(int i = 0; i < d; i++)
cout << e[i] << ' ';
cout << endl;
}

//afisare

Modulul se compileaz separat obinnd un program obiect. De exemplu sub sistemul


de operare Unix folosind comanda
CC c vector1.cpp

obinem programul obiect vector1.o. Un exemplu de program principal este prezentat n


fiierul vector2.cpp:
extern void init( int*, int);//extern poate fi omis

152

extern void distr();


extern void lapatrat();
extern void afiseaza();
//extern int* e;
int main() {
int x[5] = {1, 2, 3, 4, 5};
init(x, 5);
lapatrat();
afiseaza();
distr();
int y[] = {1, 2, 3, 4, 5, 6};
init(y, 6);
//e[1]=10;
eroare, datele sunt protejate
lapatrat();
afiseaza();
distr();
return 0;
}

Sub sistemul de operare Unix prin comanda:


CC vector2.cpp vector1.o o vector2

obinem fiierul executabil vector2. Observm c dei n programul principal se lucreaz cu


doi vectori nu putem s le folosim mpreun, deci de exemplu modulul vector1.cpp nu poate
fi extins astfel nct s realizeze i adunarea a doi vectori. n vederea nlturrii acestui
neajuns s-au introdus tipurile abstracte de date.

16.2. Tipuri abstracte de date


Tipurile abstracte de date realizeaz o legtur mai strns ntre datele problemei i
operaiile (funciile) care se refer la aceste date. Declararea unui tip abstract de date este
asemntoarea cu declararea unei structuri, care n afar de date mai cuprinde i declararea
sau definira funciilor referitoare la acestea.
De exemplu n cazul vectorilor cu elemente numere ntregi putem declara tipul
abstract:
struct vect {
int* e;
int d;
void init(int* e1, int d1);
void distr() { delete [] e; }
void lapatrat();
void afiseaza();
};

Funciile declarate sau definite n interiorul structurii vor fi numite funcii membru iar
datele date membru. Dac o funcie membru este definit n interiorul structurii (ca i funcia
distr din exemplul de mai sus), atunci ea se consider funcie inline. Dac o funcie membru
se definete n afara structurii, atunci numele funciei se va nlocui cu numele tipului abstract
urmat de operatorul de rezoluie (::) i numele funciei membru. Astfel funciile init, lapatrat
i afiseaza vor fi definite n modul urmtor:
void vect::init(int *e1, int d1)
{
d = d1;
e = new int[d];
for(int i = 0; i < d; i++)

153

e[i] = e1[i];
}

void vect::lapatrat()
{
for(int i = 0; i < d; i++)
e[i] *= e[i];
}
void vect::afiseaza()
{
for(int i = 0; i < d; i++)
cout << e[i] << ' ';
cout << endl;
}

Dei prin metoda de mai sus s-a realizat o legtur ntre datele problemei i funciile
referitoare la aceste date, structurile ca tipuri abstracte de date nu ne permit protejarea datelor,
deci ele pot fi accesate de orice funcie utilizator, nu numai de funciile membru. Acest
neajuns se poate nltura cu ajutorul claselor.

16.3. Declararea claselor


Un tip abstract de date clas se declar ca i o structur, dar cuvntul cheie struct se
nlocuiete cu class. Ca i n cazul structurilor referirea la tipul de dat clas se face cu
numele dup cuvntul cheie class (numele clasei). Protecia datelor se realizeaz cu
modificatorii de protecie: private, protected i public. Dup modificatorul de protecie se
pune caracterul :. Modificatorul private i protected reprezint date protejate, iar public date
neprotejate. Domeniul de valabilitate a modificatorilor de protecie este pn la urmtorul
modificator din interiorul clasei, modificatorul implicit fiind private.
De exemplu clasa vector se poate declara n modul urmtor:
class vector {
int* e; //elementele vectorului
int d;
//dimensiunea vectorului
public:
vector(int* e1, int d1);
~vector() { delete [] e; }
void lapatrat();
void afiseaza();
};

Se observ c datele membru e i d au fost declarate ca date de tip private (protejate),


iar funciile membru au fost declarate publice (neprotejate). Deci programatorul nu are acces
la datele membru numai prin funciile membru. Bineneles o parte din datele membru pot fi
declarate publice, i unele funcii membru pot fi declarate protejate, dac natura problemei
cere acest lucru. n general datele membru protejate nu pot fi accesate numai de funciile
membru ale clasei respective i eventual de alte funcii numite funcii prietene (sau funcii
friend).
O funcie prieten pentru o clas, se declar n mod obinuit, dar declaraia trebuie s
nceap cu cuvntul cheie friend. Declaraia trebuie s fie n interiorul clasei pentru care
funcia respectiv va fi funcie prieten. Funcia prieten se definete n mod obinuit, fr
cuvntul cheie friend.
154

Funciile membru ale altor clase pot fi funcii prietene pentru o clas. Dac dorim ca
toate funciile membru ale unei clase Clasa_1 s fie funcii prietene pentru o clas Clasa_2,
atunci clasa Clasa_1 se va declara ca i clas prieten pentru clasa Clasa_2. Acest lucru se
poate realiza n modul urmtor:
class Clasa_1; // Clasa Clasa_1 se definete n mod incomplet
// n prealabil, pentru a se putea referi la ea.
// Ea va fi definit n mod complet ulterior.
class Clasa_2 {
...
friend Clasa_1; // Clasa_1 este clas prieten pentru Clasa_2
...
}

Este ns de dorit s se evite utilizarea funciilor i claselor prietene pentru a obine o


protecie mai bun a datelor.
n seciunile 18.2.5. i 18.3.3. vom da exemple de funcii prietene, i totodat vom
prezenta o modalitate de a evita aceste funcii prietene.
O alt observaie important referitoare la exemplul de mai sus este c iniializarea
datelor membru i eliberarea zonei de memorie ocupat s-a fcut prin funcii membru
specifice.
Datele declarate cu ajutorul tipului de dat clas se numesc obiectele clasei, sau
simplu obiecte. Ele se declar n mod obinuit n forma:
nume_clas list_de_obiecte;

De exemplu un obiect de tip vector se declar n modul urmtor:


vector v;

Iniializarea obiectelor se face cu o funcie membru specific numit constructor. n


cazul distrugerii unui obiect se apelez automat o alt funcie membru specific numit
destructor. n cazul exemplului de mai sus
vector(int* e1, int d1);

este un constructor, iar


~vector() { delete [] e; }

este un destructor.
Tipurile abstracte de date de tip struct pot fi i ele considerate clase cu toate
elementele neprotejate. Observm c constructorul de mai sus este declarat n interiorul clasei,
dar nu este definit, iar destructorul este definit n interiorul clasei. Rezult c destructorul este
o funcie inline. Definirea funciilor membru care sunt declarate, dar nu sunt definite n
interiorul clasei se face ca i la tipuri abstracte de date de tip struct, folosind operatorul de
rezoluie.

16.4. Referirea la elementele claselor. Pointerul this


Referirea la datele respectiv funciile membru ale claselor se face cu ajutorul
operatorilor . sau -> ca i n cazul referirii la elementele unei structuri. De exemplu dac se
declar:
vector v;
vector* p;

atunci afiarea vectorului v respectiv a vectorului referit de pointerul p se face prin:


v.afiseaza();

155

p->afiseaza();

n interiorul funciilor membru ns referirea la datele respectiv funciile membru ale


clasei se face simplu prin numele acestora fr a fi nevoie de operatorul punct ( . ) sau sgeat
( -> ). De fapt compilatorul genereaz automat un pointer special, pointerul this, la fiecare
apel de funcie membru, i folosete acest pointer pentru identificarea datelor i funciilor
membru.
Pointerul this va fi declarat automat ca pointer ctre obiectul curent. n cazul
exemplului de mai sus pointerul this este adresa vectorului v respectiv adresa referit de
pointerul p.
Dac n interiorul corpului funciei membru afiseaza se utilizeaz de exemplu data
membru d, atunci ea este interpretat de ctre compilator ca i this->d.
Pointerul this poate fi folosit i n mod explicit de ctre programator, dac natura
problemei necesit acest lucru.
n continuare prezentm un exemplu complet de clas vector, cu funcii membru
pentru ridicarea la ptrat respectiv afiarea elementelor vectorului, precum i o funcie
membru pentru adunarea a doi vectori. Fiierul vector3.cpp (conine clasa vector):
#include <iostream.h>
#include <stdlib.h>
class vector {
private:
int* e; //elementele vectorului
int d;
//dimensiunea vectorului
public:
vector(int* e1, int d1);
~vector() { delete [] e; }
void lapatrat();
vector suma(vector& v1);
void afiseaza();
};
vector::vector(int* e1, int d1)
{
d = d1;
e = new int[d];
for(int i = 0; i < d; i++)
e[i] = e1[i];
}
void vector::lapatrat()
{
for(int i = 0; i < d; i++)
e[i] *= e[i];
}
vector vector::suma(vector& v1)
{
if (d != v1.d)
{
cerr << "Eroare: dimensiune diferita";
exit(1);
}
int* x = new int[d];
for(int i = 0; i < d; i++)
x[i] = e[i] + v1.e[i];
vector y(x, d);
delete [] x;
return y;

156

}
void vector::afiseaza()
{
for(int i = 0; i < d; i++)
cout << e[i] << ' ';
cout << endl;
}

Fiierul vector4.cpp (conine funcia main):


#include "vector3.cpp"
int main() {
int x[] = {1, 2, 3, 4, 5};
vector v(x, 5);
int y[] = {2, 4, 6, 8, 10};
vector t(y, 5);
v.suma(t).afiseaza();
return 0;
}

16.5. Constructorul
16.5.1. Iniializarea obiectelor prin constructor
Iniializarea obiectelor se face cu o funcie membru specific numit constructor.
Numele constructorului trebuie s coincid cu numele clasei. O clas poate s aib mai muli
constructori. n acest caz aceste funcii membru au numele comun, ceea ce se poate face
datorit posibilitii de suprancrcare a funciilor. Bineneles n acest caz numele i/sau tipul
parametrilor formali trebuie s fie diferit, altfel compilatorul nu poate s aleag constructorul
corespunztor.
Constructorul nu returneaz o valoare. n acest caz nu este permis nici folosirea
cuvntului cheie void.
Prezentm n continuare un exemplu de tip clasa cu mai muli constructori, avnd ca
date membru numele i prenumele unei persoane, i cu o funcie membru pentru afiarea
numelui complet. Fiierul pereche1.cpp (conine definiia clasei persoana):
#include <string.h>
#include <iostream.h>
class persoana {
char* nume;
char* prenume;
public:
persoana();
//constructor implicit
persoana(char* n, char* p); //constructor
persoana(const persoana& p1);
//constructor
//de copiere
~persoana();
//destructor
void afiseaza();
};
persoana::persoana()
{
nume = prenume = 0;
cout << "Apelarea constructorului implicit" << endl;
}

157

persoana::persoana(char* n, char* p)
{
nume = new char[strlen(n)+1];
prenume = new char[strlen(p)+1];
strcpy(nume, n);
strcpy(prenume, p);
}

persoana::persoana(const persoana& p1)


{
nume = new char[strlen(p1.nume)+1];
strcpy(nume, p1.nume);
prenume = new char[strlen(p1.prenume)+1];
strcpy(prenume, p1.prenume);
cout << "Apelarea constructorului de copiere" << endl;
}
persoana::~persoana()
{
delete[] nume;
delete[] prenume;
}
void persoana::afiseaza()
{
cout << prenume << ' ' << nume << endl;
}

Fiierul pereche2.cpp (conine funcia main):


#include "pereche1.cpp"
int main() {
persoana A; //se apeleaza constructorul implicit
A.afiseaza();
persoana B("Stroustrup", "Bjarne");
B.afiseaza();
persoana *C = new persoana("Kernighan","Brian");
C->afiseaza();
delete C;
persoana D(B); //echivalent cu persoana D = B;
//se apeleaza constructorul de copire
D.afiseaza();
return 0;
}

Observm prezena a doi constructori specifici: constructorul implicit i constructorul


de copiere. Dac o clas are constructor fr parametri atunci el se va numi constructor
implicit. Constructorul de copiere se folosete la iniializarea obiectelor folosind un obiect de
acelai tip (n exemplul de mai sus o persoan cu numele i prenumele identic). Constructorul
de copiere se declar n general n forma:
nume_clas(const nume_clas& obiect);

Cuvntul cheie const exprim faptul c argumentul constructorului de copiere nu se


modific.
158

16.5.2 Apelarea constructorilor n cazul n care datele membru sunt obiecte


O clas poate s conin ca date membru obiecte ale unei alte clase. Declarnd clasa
sub forma:
class nume_clasa {
nume_clasa_1 ob_1;
nume_clasa_2 ob_2;
...
nume_clasa_n ob_n;
...
};

antetul constructorului clasei nume_clasa va fi de forma:


nume_clasa(lista_de_argumente):
ob_1(l_arg_1), ob_2(l_arg_2), ..., ob_n(l_arg_n)

unde lista_de_argumente respectiv l_arg_i reprezint lista parametrilor formali ai


constructorului clasei nume_clasa respectiv ai obiectului ob_i.
Din lista ob_1(l_arg_1), ob_2(l_arg_2), ..., ob_n(l_arg_n) pot s lipseasc
obiectele care nu au constructori definii de programator, sau obiectul care se iniializeaz cu
un constructor implicit, sau cu toi parametrii implicii.
Dac n clas exist date membru de tip obiect atunci se vor apela mai nti
constructorii datelor membru, iar dup aceea corpul de instruciuni al constructorului clasei
respective. Fiierul pereche3.cpp:
#include "pereche1.cpp"
class pereche {
persoana sot;
persoana sotie;
public:
pereche() //definitia constructorului implicit
{
//se vor apela constructorii impliciti
}
//pentru obiectele sot si sotie
pereche(persoana& sotul, persoana& sotia);
pereche(char* nume_sot, char* prenume_sot,
char* nume_sotie, char* prenume_sotie):
sot(nume_sot, prenume_sot),
sotie(nume_sotie, prenume_sotie)
{
}
void afiseaza();
};

inline pereche::pereche(persoana& sotul, persoana& sotia):


sot(sotul), sotie(sotia)
{
}
void pereche::afiseaza()
{
cout << "Sot: ";
sot.afiseaza();
cout << "Sotie: ";
sotie.afiseaza();
}

159

int main() {
persoana A("Pop", "Ion");
persoana B("Popa", "Ioana");
pereche AB(A, B);
AB.afiseaza();
pereche CD("C","C","D","D");
CD.afiseaza();
pereche EF;
EF.afiseaza();
return 0;
}

Observm c n cazul celui de al doilea constructor, parametrii formali sot i sotie au


fost declarai ca i referine la tipul persoana. Dac ar fi fost declarai ca parametri formali de
tip persoana, atunci n cazul declaraiei:
pereche AB(A, B);

constructorul de copiere s-ar fi apelat de patru ori. n astfel de situaii se creaz mai nti
obiecte temporale folosind constructorul de copiere (dou apeluri n cazul de fa), dup care
se execut constructorii datelor membru de tip obiect (nc dou apeluri).

16.6. Destructorul
Destructorul este funcia membru care se apeleaz n cazul distrugerii obiectului.
Destructorul obiectelor globale se apeleaz automat la sfritul funciei main ca parte a
funciei exit. Deci nu este indicat folosirea funciei exit ntr-un destructor, pentru c acest
lucru duce la un ciclu infinit. Destructorul obiectelor locale se execut automat la terminarea
blocului n care s-au definit. n cazul obiectelor alocate dinamic, de obicei destructorul se
apeleaz indirect prin operatorul delete (obiectul trebuie s fi fost creat cu operatorul new).
Exist i un mod explicit de apelare a destructorului, n acest caz numele destructorului
trebuie precedat de numele clasei i operatorul de rezoluie.
Numele destructorului ncepe cu caracterul ~ dup care urmeaz numele clasei. Ca i
n cazul constructorului, destructorul nu returneaz o valoare i nu este permis nici folosirea
cuvntului cheie void. Apelarea destructorului n diferite situaii este ilustrat de urmtorul
exemplu. Fiierul destruct.cpp:
#include <iostream.h>
#include <string.h>
class scrie { //scrie pe stdout ce face.
char* nume;
public:
scrie(char* n);
~scrie();
};
scrie::scrie(char* n)
{
nume = new char[strlen(n)+1];
strcpy(nume, n);
cout << "Am creat obiectul: " << nume << '\n';
}
scrie::~scrie()
{
cout << "Am distrus obiectul: " << nume << '\n';
delete nume;

160

}
void functie()
{
cout << "Apelare functie" << '\n';
scrie local("Local");
}
scrie global("Global");
int main() {
scrie* dinamic = new scrie("Dinamic");
functie();
cout << "Se continua programul principal"
<< '\n';
delete dinamic;
return 0;
}

16.7. Suprancrcarea operatorilor


16.7.1. Metoda general de suprancrcare
Tipurile abstracte de date au avantajul de a ngloba datele i operaiile referitoare la
aceste date. Operaiile sunt exprimate cu ajutorul funciilor membru sau cu funcii prietene.
Totui ar fi mai avantajos dac s-ar putea folosi i operatori pentru exprimarea acestor
operaii. n exemplul referitor la clasa vector am vzut c putem scrie o funcie membru care
calculeaz suma a doi vectori, respectiv una care afieaz vectorul, dar apelarea funciilor se
face destul de complicat, n urmtoarea form:
v.suma(t).afiseaza();

Ar fi mult mai simplu i mai expresiv, dac am putea scrie:


cout << v + t;

Pentru acesta trebuie suprancrcat operatorul + pentru adunarea a doi vectori, i


operatorul << pentru afiarea unui vector la dispozitivul standard de ieire stdout.
n limbajul C++ nu se pot defini operatori noi, dar exist posibilitatea suprancrcrii
operatorilor existeni. Exist i cteva excepii (de exemplu nu pot fi suprancrcai urmtorii
operatori: . :: i ?:). Prin suprancrcare nu se poate modifica faptul c operatorul este unar sau
binar, nici prioritatea operatorilor i nici direcia de evaluare (asociativitatea) lor.
Suprancrcarea operatorilor se face cu funcii membru sau prietene specifice. Ele se
comport ca orice alt funcie membru sau prieten, dar numele funciei trebuie s fie format
din cuvntul cheie operator, dup care pot fi eventual i caractere albe, i care va fi urmat de
operatorul respectiv. Reamintim c n general spaiile, taburile ('\t') i caracterele de trecere
la linie nou ('\n') se numesc caractere albe.
Numrul parametrilor funciilor cu care se suprancarc operatorul se poate determina
cu exactitate din faptul c operatorul este unar sau binar i dac funcia este funcie membru
sau prieten. De exemplu dac operatorul este binar i suprancrcarea se face printr-o funcie
membru atunci ea va avea un singur parametru, dac se folosete o funcie prieten atunci va
avea doi parametri.
n cazul clasei vector operatorul + se poate suprancrca n modul urmtor:
class vector {
private:

161

int* e; //elementele vectorului


int d;
//dimensiunea vectorului
public:
vector(int* e1, int d1);
~vector() { delete [] e; }
vector operator +(vector& v1);

//supraincarcarea

//operatorului +
void afiseaza();
};
vector vector::operator +(vector& v1)
{
if (d != v1.d)
{
cerr << "Eroare: dimensiune diferita";
exit(1);
}
int* x = new int[d];
for(int i = 0; i < d; i++)
x[i] = e[i] + v1.e[i];
vector y(x, d);
delete [] x;
return y;
}

Observm c de fapt nu s-a fcut altceva dect s-a nlocuit numele funciei cu
operator +. Operatorul se apeleaz n modul urmtor:
int main() {
int x[] = {1, 2, 3, 4, 5};
vector v(x, 5);
int y[] = {2, 4, 6, 8, 10};
vector t(y, 5);
(v+t).afiseaza();
return 0;
}

16.7.2. Suprancrcarea operatorilor de atribuire


n genereal se poate folosi operatorul de atribuire (=) i pentru obiecte. Prezentm n
continuare un exemplu pentru calcule cu fracii. Fiierul fractie1.cpp:
#include <iostream.h>
class fractie {
int numarator;
int numitor;
public:
fractie(int numarator1, int numitor1);
fractie operator *( fractie& r); //calculeaza
//produsul
//a doua fractii
//nu simplifica
void afiseaza();
};
inline fractie::fractie(int numarator1 = 1,
int numitor1 = 0)
{

162

numarator = numarator1;
numitor = numitor1;
}
inline fractie fractie::operator *(fractie& r)
{
return fractie(numarator*r.numarator,
numitor*r.numitor);
}
inline void fractie::afiseaza()
{
if ( numitor )
cout << numarator << " / " << numitor << endl;
else
cerr << "Fractie incorecta";
}
int main() {
fractie x(3,4);
fractie y(5, 7);
fractie z;
z = x * y;
z.afiseaza();
return 0;
}

Observm c operatorul de atribuire se poate utiliza i rezultatul este cel dorit, deci se
afieaz produsul celor dou fracii. Din pcate ns nu este ntotdeauna aa. Prezentm un alt
exemplu legat de clasa vector pentru ilustrarea acestui lucru. Fiierul vector5.cpp:
#include <iostream.h>
#include <stdlib.h>
class vector {
private:
int* e; //elementele vectorului
int d;
//dimensiunea vectorului
public:
vector(int* e1, int d1);
~vector();
void operator++();
//incrementarea elementelor
//vectorului
void afiseaza(char* text); //afiseaza adresa
//primului element,
//dupa care afiseaza
//elementele
};
vector::vector(int* e1, int d1)
{
d = d1;
e = new int[d];
for(int i = 0; i < d; i++)
e[i] = e1[i];
}
vector::~vector()
{
cout
<< "S-a eliberat zona de memorie de dimensiune "
<< d*sizeof(int)

163

<< " incepind de la adresa: "


delete[] e;

<< e << endl;

}
void vector::operator++()
{
for(int i = 0; i < d; i++)
e[i]++;
}
void vector::afiseaza(char* text)
{
cout
<< "Vectorul " << text
<< " (adresa primului element si elementele):\n";
cout << e << endl;
for(int i = 0; i < d; i++)
cout << e[i] << ' ';
cout << endl;
}
int main() {
int x[] = {1, 2, 3, 4, 5};
vector v(x, 5);
int y[] = {20, 40, 60, 80, 100};
vector t(y, 5);
v.afiseaza("v");
t.afiseaza("t");
t = v;
cout << "Dupa atribuirea t = v:\n";
t.afiseaza("t");
++v;
cout
<< "Dupa incrementarea elementelor "
<< "vectorului v:" << endl;
v.afiseaza("v");
t.afiseaza("t");
return 0;
}

Dup o execuie a programului rezultatele vor fi de exemplu:


Vectorul v (adresa primului element si elementele):
0x222a8
1 2 3 4 5
Vectorul t (adresa primului element si elementele):
0x222c8
20 40 60 80 100
Dupa atribuirea t = v:
Vectorul t (adresa primului element si elementele):
0x222a8
1 2 3 4 5
Dupa incrementarea elementelor vectorului v:
Vectorul v (adresa primului element si elementele):
0x222a8
2 3 4 5 6
Vectorul t (adresa primului element si elementele):
0x222a8
2 3 4 5 6
S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa:
0x222a8

164

S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa:


0x222a8

Se observ c nu s-a obinut ceea ce s-a dorit, deoarece prin instruciunea t = v s-a
obinut atribuirea adresei primului element al vectorului v datei membru e, corespunztoare
vectorului t i nu s-au atribuit elementele n sine. De aceea orice modificare a elementelor
vectorului v duce n continuare la modificarea elementelor vectorului t (n cazul nostru prin
incrementarea elementelor vectorului v se incrementeaz i elementele vectorului t).
Un alt neajuns este c nu s-a eliberat zona de memorie alocat iniial elementelor
vectorului t dar s-a eliberat de dou ori cea rezervat pentru elementele vectorului v.
Explicaia rezultatelor de mai sus const n faptul c operatorul de atribuire (=) este
suprancrcat n mod implicit astfel nct s realizeze o copiere bit cu bit a datelor membru. n
exemplul referitor la fracii prin copierea bit cu bit se obin rezultatele dorite dar n al doilea
exemplu ns, nu. n general prin copierea bit cu bit nu se obin rezultate corespunztoare
atunci cnd cel putin una dintre datele membru este un pointer. n acest caz suprancrcarea
operatorului de atribuire se poate face n modul urmtor:
class vector {
private:
int* e; //elementele vectorului
int d;
//dimensiunea vectorului
public:
vector(int* e1, int d1);
vector(const vector& v1);
~vector();
void operator++();
//incrementarea elementelor
//vectorului
vector& operator =(const vector& v1);
//supraincarcarea
//operatorului
//de atribuire
void afiseaza(char* text); //afiseaza adresa
//primului element,
//dupa care afiseaza
//elementele
};
vector& vector::operator =(const vector& v1)
{
if (this != &v1)
{
delete[] e;
d = v1.d;
e = new int[d];
for(int i = 0; i < d; i++)
e[i] = v1.e[i];
}
return *this;
}

Operatorii de atribuire +=, -=, *=, /= nu sunt suprancrcai n mod implicit, deci
trebuie suprancrcai de ctre programator. De exemplu operatorul += pentru clasa vector
poate fi suprancrcat n modul urmtor:
vector& vector::operator +=(vector& v1)
{
return *this = *this + v1;
}

165

Atragem atenia asupra deosebirii dintre apelul operatorului de atribuire i a


constructorului implicit. De exemplu dac se declar un obiect ob1 aparinnd clasei
nume_clasa, n felul urmtor
nume_clasa ob1; //se apeleaza constructorul
//implicit, sau constructorul cu
//toti parametrii impliciti

atunci prin declaraia


nume_clasa ob2 = ob1; //apelarea constructorului
//de copiere

se va apela constructorul de copiere i nu operatorul de atribuire. Construcia de mai sus este


echivalent cu:
nume_clasa ob2(ob1);

//apelarea constructorului
//de copiere

Exis totui asemnarea dintre operatorul de atribuire i constructorul de copiere c, n


cazul n care nu exist constructor de copiere definit de programator, se va apela un
constructor de copiere implicit care va realiza o iniializare a obiectului printr-o copiere bit cu
bit. Constructorul de copiere se va apela n general i n urmtoarele dou situaii:
- dac un parametru al unei funcii este un obiect
- dac o funcie returneaz un obiect
De aceea n cazul n care copierea bit cu bit nu d rezultate corespunztoare, este
recomandat ca programatorul s defineasc un constructor de copiere, chiar i n cazul n care
nu se dorete iniializarea unui obiect printr-un alt obiect. Prezentm un exemplu pentru
ilustrare. Fiierul vector6.cpp:
#include <iostream.h>
#include <stdlib.h>

class vector {
private:
int* e; //elementele vectorului
int d;
//dimensiunea vectorului
public:
vector(int* e1, int d1);
vector(vector& v1);
~vector();
vector operator +(vector v1); //supraincarcarea
//adunarii
vector& operator =(const vector& v1);
//supraincarcarea
//operatorului
//de atribuire
void afiseaza(char* text); //afiseaza adresa
//primului element,
//dupa care afiseaza
//elementele
};
vector::vector(int* e1, int d1)
{
d = d1;
e = new int[d];
for(int i = 0; i < d; i++)
e[i] = e1[i];
cout
<< "Apel constructor.\n"
<< "Adresa primului element: " << e << endl;

166

}
vector::vector(vector& v1)
{
d = v1.d;
e = new int[d];
for(int i = 0; i < d; i++)
e[i] = v1.e[i];
cout << "Apel constructor de copiere.\n"
<< "Adresa primului element: " << e << endl;
}
vector::~vector()
{
cout
<< "Apel destructor.\n"
<< "S-a eliberat zona de memorie de dimensiune "
<< d*sizeof(int)
<< " incepind de la adresa: " << e << endl;
delete[] e;
}
vector vector::operator +(vector v1)
{
if (d != v1.d)
{
cerr << "Eroare: dimensiune diferita";
exit(1);
}
int* x = new int[d];
for(int i = 0; i < d; i++)
x[i] = e[i] + v1.e[i];
vector temp(x, d);
temp.afiseaza("temp");
return temp;
//return vector(x, d);
}
vector& vector::operator =(const vector& v1)
{
if (this != &v1)
{
cout
<< "Apel operator de atribuire ( = ).\n"
<< "S-a eliberat zona de memorie "
<< "de dimensiune " << d*sizeof(int)
<< " incepind de la adresa: "
<< e << endl;
delete[] e;
d = v1.d;
e = new int[d];
for(int i = 0; i < d; i++)
e[i] = v1.e[i];
}
return *this;
}
void vector::afiseaza(char* text)
{
cout
<< "Vectorul " << text
<< " (adresa primului element si elementele):\n";
cout << e << endl;

167

for(int i = 0; i < d; i++)


cout << e[i] << ' ';
cout << endl;
}

Fiierul vector7.cpp:
#include "vector6.cpp"
int main() {
int x[] = {1, 2, 3, 4, 5};
vector vx(x, 5);
int y[] = {20, 40, 60, 80, 100};
vector vy(y, 5);
int z[] = {300, 600, 900, 1200, 1500};
vector vz(z, 5);
vx.afiseaza("vx");
vy.afiseaza("vy");
vz.afiseaza("vz");
vz = vx + vy;
cout << "Dupa instructiunea vz = vx + vy:\n";
vz.afiseaza("vz");
return 0;
}

Prin execuie se obine de exemplu:


Apel constructor.
Adresa primului element: 0x22cd8
Apel constructor.
Adresa primului element: 0x22cf8
Apel constructor.
Adresa primului element: 0x22d18
Vectorul vx (adresa primului element si elementele):
0x22cd8
1 2 3 4 5
Vectorul vy (adresa primului element si elementele):
0x22cf8
20 40 60 80 100
Vectorul vz (adresa primului element si elementele):
0x22d18
300 600 900 1200 1500
Apel constructor de copiere.
Adresa primului element: 0x22d38
Apel constructor.
Adresa primului element: 0x22d78
Vectorul temp (adresa primului element si elementele):
0x22d78
21 42 63 84 105
Apel constructor de copiere.
Adresa primului element: 0x22d98
Apel destructor.
S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa:
0x22d78
Apel destructor.
S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa:
0x22d38
Apel operator de atribuire ( = ).
S-a eliberat zona de memorie de dimensiune 20 incepind de la adresa:
0x22d18
Dupa instructiunea vz = vx + vy:

168

Vectorul vz (adresa primului


0x22d18
21 42 63 84 105
Apel destructor.
S-a eliberat zona de memorie
0x22d98
Apel destructor.
S-a eliberat zona de memorie
0x22d18
Apel destructor.
S-a eliberat zona de memorie
0x22cf8
Apel destructor.
S-a eliberat zona de memorie
0x22cd8

element si elementele):

de dimensiune 20 incepind de la adresa:

de dimensiune 20 incepind de la adresa:

de dimensiune 20 incepind de la adresa:

de dimensiune 20 incepind de la adresa:

Se observ c s-a apelat constructorul de copiere de dou ori. Prima dat pentru
crearea parametrului operatorului de adunare prin copierea obiectului vy. Destructorul acestui
obiect s-a apelat automat dup ce s-a prsit funcia membru corespunztoare operatorului de
adunare. Constructorul de copiere s-a apelat a doua oar atunci cnd s-a creat obiectul anonim
vx+vy. Destructorul acestui obiect anonim s-a apelat ns numai la prsirea funciei main. De
aceea dac elementele iniiale a vectorului vz nu se folosesc, ar fi mai convenabil dac
vectorul vz s-ar iniializa printr-un constructor. De exemplu cu funcie main din fiierul
vector8.cpp:
#include "vector6.cpp"
int main() {
int x[] = {1, 2, 3, 4, 5};
vector vx(x, 5);
int y[] = {20, 40, 60, 80, 100};
vector vy(y, 5);
vx.afiseaza("vx");
vy.afiseaza("vy");
vector vz = vx + vy;
cout << "Dupa instructiunea vz = vx + vy:\n";
vz.afiseaza("vz");
return 0;
}

n acest caz constructorul de copiere se apeleaz tot de dou ori, dar nu se mai creaz
obiectul anonim vx+vy.

16.7.3. Suprancrcarea operatorilor de incrementare i decrementare


n exemplul din fiierul vector5.cpp s-a suprancrcat operatorul de incrementare.
n funcia main s-a executat instruciunea
++v;

pentru un obiect v al clasei vector. Dac n locul acestei instruciuni am fi scris v++; atunci
n faza de compilare ar fi aprut un mesaj de avertisment. De exemplu n Borland C++ apare
mesajul: "Overloaded prefix 'operator ++' used as a postfix operator", deci operatorul ++
prefixat s-a folosit ca i operator postfixat. Acelai mesaj de avertisment apare i n urmtorul
exemplu. Fiierul increm1.cpp:
#include <iostream.h>
#include <conio.h>
class Clasa {

169

int x;
public:
Clasa() { x = 0; }
Clasa& operator++();
void scrie_x();
};
Clasa& Clasa::operator++()
{
++x;
cout << "S-a apelat operatorul ++ prefixat\n";
return *this;
}
void Clasa::scrie_x()
{
cout << "x = " << x << endl;
}
int main() {
Clasa cl;
clrscr();
cl.scrie_x();
cl++;
cl.scrie_x();
(++cl)++;
cl.scrie_x();
return 0;
}

n faza de compilare, mesajul de avertisment de mai sus apare de dou ori, pentru cele
dou apeluri ale operatorului de incrementare postfixat. Prin executarea programului se
obine:
x =
S-a
x =
S-a
S-a
x =

0
apelat operatorul ++ prefixat
1
apelat operatorul ++ prefixat
apelat operatorul ++ prefixat
3

Din rezultatul de mai sus nu reiese c s-a apelat operatorul ++ postfixat de dou ori.
Operatorul de incrementare postfixat poate fi suprancrcat cu o funcie membru, care are un
parametru de tip int. La apelarea unui operator postfixat, acest parametru va lua n mod
automat valoarea zero. Deoarece valoarea parametrului nu se va folosi n corpul funciei
membru, numele lui poate fi omis. Exemplul de mai sus se poate modifica n felul urmtor.
Fiierul increm2.cpp:
#include <iostream.h>
#include <conio.h>
class Clasa {
int x;
public:
Clasa() { x = 0; }
Clasa& operator++();
Clasa& operator++( int );
void scrie_x();
};
Clasa& Clasa::operator++()

170

{
++x;
cout << "S-a apelat operatorul ++ prefixat\n";
return *this;
}
Clasa& Clasa::operator++( int )
{
x++;
cout << "S-a apelat operatorul ++ postfixat\n";
return *this;
}
void Clasa::scrie_x()
{
cout << "x = " << x << endl;
}
int main() {
Clasa cl;
clrscr();
cl.scrie_x();
cl++;
cl.scrie_x();
(++cl)++;
cl.scrie_x();
return 0;
}

n acest caz nu apar mesaje de avertisment la compilare. Rezultatul obinut prin


execuie este:
x =
S-a
x =
S-a
S-a
x =

0
apelat operatorul ++ postfixat
1
apelat operatorul ++ prefixat
apelat operatorul ++ postfixat
3

ntr-adevr s-a afiat de fiecare dat mesajul corespunztor apelului operatorului


prefixat respectiv postfixat. Menionm c operatorului de decrementare prefixat, respectiv
postfixat se poate suprancrca n mod analog.

16.8. Conversii definite de programator


16.8.1. Efectuarea conversiilor
n mod automat n limbajele C i C++ se aplic o conversie n urmtoarele trei situaii:
-

dac un operator se refer la operanzi de tipuri diferite;


dac tipul parametrului actual al unei funcii difer de tipul parametrului formal
corespunztor;
dac tipul valorii returnate de o funcie, tip specificat n antetul funciei, difer de
tipul expresiei din instruciunea return.
171

n primul caz, dac operatorul nu este de atribuire, se va folosi regula conversiilor


implicite. Deci mai nti tipurile char i enum se convertesc n int, dup care tipurile
"inferioare" se convertesc spre cele "superioare" (de exemplu int n double). Dac operatorul
este de atribuire, atunci tipul expresiei din partea dreapt se convertete spre tipul expresiei
din partea stng. n cazul al doilea tipul parametrului actual se convertete spre tipul
parametrului formal. n cazul al treilea tipul expresiei din instruciunea return se convertete
spre tipul specificat n antetul funciei.

16.8.2. Conversii implicite definite de programator


Considerm urmtorul exemplu referitor la clasa numerelor raionale. Clasa va avea
dou date membru de tip long pentru memorarea numrtorului i numitorului. Obiectele
clasei se vor iniializa printr-un constructor. Se va suprancrca operatorul ~, care va efectua
simplificarea fraciei, i operatorul *, pentru nmulirea a dou fracii. Deasmenea, se va defini
o funcie membru afiseaza, pentru afiarea unei fracii. Fiierul conv2.cpp:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
long cmmdc( long a, long b);
class fractie {
long numarator;
long numitor;
public:
fractie(long a, long b);
fractie& operator~ ();
fractie operator *(fractie r);
void afiseaza();
};

// simplificare fractie

inline fractie::fractie(long a, long


{
numarator = a;
numitor = b;
}

b)

fractie& fractie::operator~()
{
long d = cmmdc(numarator, numitor);
numarator /= d;
numitor /= d;
return *this;
}
inline fractie fractie::operator *(fractie r)
{
return ~fractie(numarator*r.numarator,
numitor*r.numitor);
}
inline void fractie::afiseaza()
{
if ( numitor )
cout << numarator << " / " << numitor << endl;
else
cerr << "Fractie incorecta";
}

172

long cmmdc( long a, long b)


{
long r;
while ( b )
{
r = a % b;
a = b;
b = r;
}
return a;
}
int main() {
clrscr();
fractie x(2,5);
fractie y(15, 8);
fractie z(0, 1);
fractie w(4, 1);
z = x * y;
z.afiseaza();
z = x * w;
// z = x * 4; ar fi gresit
z.afiseaza();
return 0;
}

Prin executarea programului se obine:


3 / 4
8 / 5

Observm c operatorul de nmulire se poate folosi numai pentru nmulirea a dou


numere raionale. Deci n cazul unei expresii de forma urmtoare
z = x * 4;

compilatorul ar semnala o eroare. Totui ar fi de dorit ca expresiile de forma de mai sus, s fie
corecte. Pentru a realiza acest lucru, trebuie definit o conversie implicit din tipul long n
tipul fractie.
n general, conversiile dintr-un tip standard ntr-un tip abstract se realizeaz cu
ajutorul unui constructor al tipului abstract. Constructorul trebuie s aib un parametru, care
aparine tipului standard, iar dac sunt i ali parametri, ei trebuie s fie iniializai.
n cazul de mai sus constructorul, care realizeaz conversia din tipul long n tipul
fractie, poate fi declarat sub forma:
fractie(long a, long b = 1);

sau
fractie(long a = 0, long b = 1);

sau
fractie(long a);

De exemplu, dac fiierul conv2.cpp se modific astfel nct constructorul s fie


declarat n forma:
fractie(long a = 0, long b = 1);

atunci expresia
z = x * 4;

este corect i se obine numrul raional 8 / 5. Al doilea operand al operatorului de nmulire


trebuie s aparin tipului fracie. Cu ajutorul constructorului se va crea un obiect anonim
173

fractie(4, 1), care se va folosi n locul operandului al doilea. Menionm, c de fapt s-au
fcut dou conversii. Constanta 4 este de tipul int, deci la apelarea constructorului s-a fcut

o conversie de la tipul int la tipul long, dup care s-a fcut o conversie din tipul long n tipul
fractie, prin crearea obiectului anonim.
Totui compilatorul semnalizeaz o eroare la ntlnirea unei expresii de forma
urmtoare:
z = 4 * x;

Explicaia este, c n cazul expresiei x * 4, se apeleaz funcia membru operator*


pentru obiectul x. ns n cazul expresiei 4 * x, se ncearc apelarea unei funcii membru
(operatorul de nmulire) pentru constanta 4, ceea ce conduce la eroare.
O soluie ar putea fi ca operatorul de nmulire s se defineas printr-o funcie prieten,
de exemplu n modul urmtor:
class fractie {
long numarator;
long numitor;
public:
...
friend fractie operator*(fractie p, fractie q);
...
};
fractie operator*(fractie p, fractie q)
{
return ~fractie(p.numarator*q.numarator,
p.numitor*q.numitor);
}

Dac operatorul de nmulire se definete n modul de mai sus, atunci ambele expresii
x * 4 i 4 * x vor fi corecte. Dezavantajul acestei metode este, c prin utilizarea unei
funcii prieten, gradul de protecie a datelor scade. n continuare prezentm o alt metod de
nlturare a erorii de mai sus, astfel nct s nu fie nevoie de introducerea unei funcii prieten.
Vom defini o funcie membru inmultire, care se va apela de ctre funcia, care suprancarc
operatorul de nmulire.
class fractie {
long numarator;
long numitor;
public:
...
fractie inmultire(fractie r);
...
};
inline fractie fractie::inmultire(fractie r)
{
return ~fractie(numarator*r.numarator,
numitor*r.numitor);
}
fractie operator*(fractie p, fractie q)
{
return p.inmultire( q );
}

Observm c i n acest caz, ambele expresii sunt corecte i nu s-a folosit funcie
prieten.
174

Definirea adecvat a constructorului poate s conduc i la conversii dintr-un tip


abstract ntr-un alt tip abstract. Prezentm un exemplu pentru msurarea unei lungimi, n care
sunt definite dou clase, folosind dou uniti de msur diferite. Cu ajutorul clasei
Lungime_m se va memora lungimea n metri (mm, cm, dm i m), iar folosind clasa
Lungime_inch memorarea lungimii se va realiza n inch (line, inch, foot i yard). Relaiile
dintre cele dou uniti de msur sunt urmtoarele:
1 line
1 inch
1 foot
1 yard

=
=
=

10 lines
12 inches
3 feet

=
=
=
=

2.54 mm
2.54 cm
30.48 cm
91.44 cm

Deasemenea, programul va realiza conversia din inch n metri. Fiierul lung1.cpp:


#include <iostream.h>
#include <conio.h>
class
int
int
int
int

Lungime_inch {
line;
inch;
foot;
yard;

public:
Lungime_inch(int l = 0, int i = 0, int f = 0, int y = 0);
long line_lungime();
void afisare();
};
class Lungime_m {
int mm;
int cm;
int dm;
int m;
double rest;
// restul, care ramane la conversie
public:
Lungime_m( int mm_1 = 0, int cm_1 = 0,
int dm_1 = 0, int m_1 = 0);
Lungime_m(Lungime_inch L);
// conversie
void afisare();
};
Lungime_inch::Lungime_inch( int l, int i, int f, int y)
{
line = l;
inch = i;
foot = f;
yard = y;
}
long Lungime_inch::line_lungime()
{
return line + 10 * inch + 120 * foot + 360 * yard;
}
void Lungime_inch::afisare()
{
if ( line )
cout << line << " line ";
if ( inch )

175

cout << inch << " inch ";


if ( foot )
cout << foot << " foot ";
if ( yard )
cout << yard << " yard";
cout << endl;
}
Lungime_m::Lungime_m( int mm_1, int cm_1, int dm_1, int m_1)
{
mm = mm_1;
cm = cm_1;
dm = dm_1;
m = m_1;
rest = 0;
}
Lungime_m::Lungime_m(Lungime_inch L)
{
rest = 2.54 * L.line_lungime();
long mm_lung = rest;
rest -= mm_lung;
m = mm_lung / 1000;
mm_lung %= 1000;
dm = mm_lung / 100;
mm_lung %= 100;
cm = mm_lung / 10;
mm_lung %= 10;
mm = mm_lung;
}
void Lungime_m::afisare()
{
if ( m )
cout << m << " m ";
if ( dm )
cout << dm << " dm ";
if ( cm )
cout << cm << " cm ";
if ( mm + rest )
cout << mm + rest << " mm ";
cout << endl;
}
int main() {
clrscr();
Lungime_inch y(5, 2, 1, 3);
y.afisare();
cout << y.line_lungime() << " line" << endl;
cout << 2.54 * y.line_lungime() << " mm" << endl;
Lungime_m x;
x = y;
x.afisare();
return 0;
}

Prin executarea programului se obine:


5 line 2 inch 1 foot 3 yard
1225 line
3111.5 mm
3 m 1 dm 1 cm 1.5 mm

176

Observm, c i n acest caz s-a folosit un constructor pentru realizarea conversiei din
tipul Lungime_inch n tipul Lungime_m. Clasa Lungime_inch s-a declarat nainte de clasa
Lungime_m, deoarece constructorul, care realizeaz conversia, folosete tipul Lungime_inch.

16.8.3. Suprancrcarea operatorului de conversie explicit


Conversia dintr-un tip abstract ntr-un tip standard se poate realiza prin
suprancrcarea operatorului de conversie explicit. Pentru a realiza conversia din tipul
abstract Clasa n tipul standard tip_standard este necesar o funcie membru de forma
urmtoare:
class Clasa {
...
public:
...
operator tip_standard(); // declaraie
...
};
Clasa::operator tip_standard()
{
...
return expresie; // se returneaz o expresie avnd tipul
// tip_standard
}

Obiectul de tip Clasa se va converti n valoarea expresiei returnate de ctre funcia


membru, care suprancarc operatorul de conversie explicit. Menionm c n declaraia
funciei membru, care suprancarc operatorul de conversie explicit, nu trebuie specificat
tipul, care se returneaz, deoarece acest tip va fi ntotdeauna tipul standard la care se face
conversia. Dac se ncearc specificarea tipului returnat, atunci compilatorul va semnala
eroare. n urmtorul exemplu se va efectua conversia unui numr raional ntr-un numr de tip
double. Fiierul conv3.cpp:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class fractie {
long numarator;
long numitor;
public:
fractie(long a, long b);
operator double();
fractie operator* (fractie r);

//calculeaza produsul
//a doua fractii,
//nu simplifica

void afiseaza();
};
inline fractie::fractie(long a, long
{
numarator = a;
numitor = b;
}
fractie::operator double()
{
return double(numarator) / numitor;

177

b)

}
inline fractie fractie::operator* (fractie r)
{
return fractie(numarator*r.numarator,
numitor*r.numitor);
}
inline void fractie::afiseaza()
{
if ( numitor )
cout << numarator << " / " << numitor << endl;
else
cerr << "Fractie incorecta";
}
int main() {
clrscr();
fractie x(2,5);
double y;
y = x * 4.5;
cout << y << endl;
y = 4.5 * x;
cout << y << endl;
return 0;
}

Prin executarea programului obinem:


1.8
1.8

Menionm c dac parametrul formal b al constructorului clasei fractie ar fi fost


iniializat, atunci ar fi aprut o eroare la compilare. ntr-adevr, n acest caz expresia x * 4.5
se poate evalua n urmtoarele dou moduri:
- conversia variabilei x n tipul double i efectuarea nmulirii a dou date de tip
double;
- conversia constantei de tip double 4.5 n tipul long (cu trunchiere), urmat de
conversia n tipul abstract fractie prin apelarea constructorului, i efectuarea
nmulirii a dou numere raionale.
Deoarece evaluarea expresiei x * 4.5 se poate face n dou moduri diferite,
compilatorul nu poate evalua expresia, i se va semnala o eroare.
Suprancrcarea operatorului de conversie explicit se poate folosi i pentru conversii
dintr-un tip abstract ntr-un alt tip abstract. n continuare se va relua exemplul referitor la
msurarea unei lungimi n metri, respectiv n inch. Se va suprancrca operatorul de conversie
explicit din inch n metri. Fiierul lung2.cpp:
#include <iostream.h>
#include <conio.h>
class Lungime_m {
int mm;
int cm;
int dm;
int m;
double rest;
// restul, care ramane la conversie
public:
Lungime_m( int mm_1 = 0, int cm_1 = 0,
int dm_1 = 0, int m_1 = 0, double rest_1 = 0);

178

void afisare();
};
class Lungime_inch {
int line;
int inch;
int foot;
int yard;
public:
Lungime_inch(int l = 0, int i = 0, int f = 0, int y = 0);
operator Lungime_m();
long line_lungime();
void afisare();
};
Lungime_m::Lungime_m( int mm_1, int cm_1,
int dm_1, int m_1, double rest_1)
{
mm = mm_1;
cm = cm_1;
dm = dm_1;
m = m_1;
rest = rest_1;
}
void Lungime_m::afisare()
{
if ( m )
cout << m << " m ";
if ( dm )
cout << dm << " dm ";
if ( cm )
cout << cm << " cm ";
if ( mm + rest )
cout << mm + rest << " mm ";
cout << endl;
}
Lungime_inch::Lungime_inch( int l, int i, int f, int y)
{
line = l;
inch = i;
foot = f;
yard = y;
}
Lungime_inch::operator Lungime_m()
{
double rest_1 = 2.54 * line_lungime();
long mm_1 = rest_1;
rest_1 -= mm_1;
long m_1 = mm_1 / 1000;
mm_1 %= 1000;
long dm_1 = mm_1 / 100;
mm_1 %= 100;
long cm_1 = mm_1 / 10;
mm_1 %= 10;
return Lungime_m(mm_1, cm_1, dm_1, m_1, rest_1);
}
long Lungime_inch::line_lungime()
{
return line + 10 * inch + 120 * foot + 360 * yard;

179

}
void Lungime_inch::afisare()
{
if ( line )
cout << line << " line ";
if ( inch )
cout << inch << " inch ";
if ( foot )
cout << foot << " foot ";
if ( yard )
cout << yard << " yard";
cout << endl;
}
int main() {
clrscr();
Lungime_inch y(5, 2, 1, 3);
y.afisare();
cout << y.line_lungime() << " line" << endl;
cout << 2.54 * y.line_lungime() << " mm" << endl;
Lungime_m x;
x = y;
x.afisare();
return 0;
}

Observm, c rezultatul obinut dup execuie este identic cu cel al fiierului


clasa

lung1.cpp. Menionm c n acest caz clasa Lungime_m trebuie declarat nainte de


Lungime_inch, deoarece tipul Lungime_m se folosete n cadrul clasei Lungime_inch.

n principiu conversia dintr-un tip abstract ntr-un alt tip abstract se poate realiza n
dou moduri: cu ajutorul unui constructor i prin suprancrcarea operatorul de conversie
explicit. ns n cazul unei aplicaii concrete, numai unul din aceste dou moduri poate fi
folosit. n caz contrar compilatorul va semnala o eroare.

180

17. Metoda programrii orientate obiect


17.1. Bazele teoretice ale metodei programrii orientate obiect
Prin folosirea tipurilor abstracte de date, se creaz un tot unitar pentru gestionarea
datelor i a operaiilor referitoare la aceste date. Cu ajutorul tipului abstract clas se realizeaz
i protecia datelor, deci elementele protejate nu pot fi accesate numai de funciile membru ale
clasei respective. Aceast proprietate a obiectelor se numete ncapsulare (encapsulation).
n viaa de zi cu zi ns ne ntlnim nu numai cu obiecte separate, dar i cu diferite
legturi ntre aceste obiecte, respectiv ntre clasele din care obiectele fac parte. Astfel se
formeaz o ierarhie de clase. Rezult a doua proprietate a obiectelor: motenirea
(inheritance). Acest lucru nseamn c se motenesc toate datele i funciile membru ale
clasei de baz de ctre clasa derivat, dar se pot aduga elemente noi (date membru i funcii
membru) n clasa derivat. n cazul n care o clas derivat are mai multe clase de baz se
vorbete despre motenire multipl.
O alt proprietate important a obiectelor care aparin clasei derivate este c funciile
membru motenite pot fi suprancrcate. Acest lucru nseamn c o operaie referitoare la
obiectele care aparin ierarhiei are un singur identificator, dar funciile care descriu aceast
operaie pot fi diferite. Deci numele funciei i lista parametrilor formali este aceeai n clasa
de baz i n clasa derivat, dar descrierea funciilor difer ntre ele. Astfel n clasa derivat
funciile membru pot fi specifice referitoare la clasa respectiv, dei operaia se identific prin
acelai nume. Aceast proprietate se numete polimorfism.
Noiunea de polimorfism ne conduce n mod firesc la problematica determinrii
funciei membru care se va apela n cazul unui obiect concret. S considerm urmtorul
exemplu. Declarm clasa de baz baza, i o clas derivat din acest clas de baz, clasa
derivata. Clasa de baz are dou funcii membru: functia_1 i functia_2. n interiorul funciei
membru functia_1 se apeleaz functia_2. n clasa derivat se suprancarc funcia membru
functia_1, dar funcia membru functia_2 nu se suprancarc. n programul principal se declar
un obiect al clasei derivate i se apeleaz funcia membru functia_2 motenit de la clasa de
baz. n limbajul C++ acest exemplu se scrie n urmtoarea form. Fiierul virtual1.cpp:
#include <iostream.h>
#include <conio.h>
class baza {
public:
void functia_1();
void functia_2();
};
class derivata : public baza {
public:
void functia_1();
};
void baza::functia_1()
{
cout << "S-a apelat functia
<< " a clasei de baza"
}
void baza::functia_2()
{
cout << "S-a apelat functia
<< " a clasei de baza"
functia_1();
}

membru functia_1"
<< endl;

membru functia_2"
<< endl;

181

void derivata::functia_1()
{
cout << "S-a apelat functia membru functia_1"
<< " a clasei derivate" << endl;
}
int main() {
clrscr();
derivata D;
D.functia_2();
return 0;
}

Prin execuie se obine urmtorul rezultat:


S-a apelat functia membru functia_2 a clasei de baza
S-a apelat functia membru functia_1 a clasei de baza

ns acest lucru nu este rezultatul dorit, deoarece n cadrul funciei main s-a apelat
funcia membru functia_2 motenit de la clasa de baz, dar funcia membru functia_1 apelat
de functia_2 s-a determinat nc n faza de compilare. n consecin, dei funcia membru
functia_1 s-a suprancrcat n clasa derivat nu s-a apelat funcia suprancrcat ci funcia
membru a clasei de baz. De fapt i din rezultatul execuiei programului se obine acelai
lucru.
Acest neajuns se poate nltura cu ajutorul introducerii noiunii de funcie membru
virtual. Dac funcia membru este virtual, atunci la orice apelare a ei, determinarea funciei
membru corespunztoare a ierarhiei de clase nu se va face la compilare ci la execuie, n
funcie de natura obiectului pentru care s-a fcut apelarea. Aceast proprietate se numete
legare dinamic, iar dac determinarea funciei membru se face la compilare, atunci se
vorbete de legare static.

17.2. Declararea claselor derivate


O clas derivat se declar n felul urmtor:
class nume_clas_derivat : lista_claselor_de_baz {
//date membru noi i funcii membru noi
};

unde lista_claselor_de_baz este de forma:


elem_1, elem_2, ..., elem_n

i elem_i pentru orice 1 i n poate fi


public clas_de_baz_i

sau
protected clas_de_baz_i

sau
private clas_de_baz_i

Cuvintele cheie public, protected i private se numesc i de aceast dat modificatori


de protecie. Ele pot s lipseasc, n acest caz modificatorul implicit fiind private. Accesul la
elementele din clasa derivat este prezentat n tabelul 2.
Observm c elementele de tip private ale clasei de baz sunt inaccesibile n clasa
derivat. Elementele de tip protected i public devin de tip protected, respectiv private dac
modificatorul de protecie referitor la clasa de baz este protected respectiv private, i rmn
182

neschimbate dac modificatorul de protecie referitor la clasa de baz este public. Din acest
motiv n general datele membru se declar de tip protected i modificatorul de protecie
referitor la clasa de baz este public. Astfel datele membru pot fi accesate, dar rmn protejate
i n clasa derivat.
Accesul la elementele
din clasa de baz
public
protected
private
public
protected
private
public
protected
private

Modificatorii de
protecie referitoare la
clasa de baz
public
public
public
protected
protected
protected
private
private
private

Accesul la elementele
din clasa derivat
public
protected
inaccesibil
protected
protected
inaccesibil
private
private
inaccesibil

Tabelul 2: accesul la elementele din clasa derivat

17.3. Funcii membru virtuale


Revenim la exemplul din seciunea 17.1. Am vzut c dac se execut programul
virtual1.cpp se apeleaz funciile membru functia_1 i functia_2 ale clasei de baz. ns
funcia membru functia_1 fiind suprancrcat n clasa derivat, ar fi de dorit ca funcia
suprancrcat s fie apelat n loc de cea a clasei de baz.
Acest lucru se poate realiza declarnd functia_1 ca funcie membru virtual. Astfel
pentru orice apelare a funciei membru functia_1, determinarea acelui exemplar al funciei
membru din ierarhia de clase care se va executa, se va face la execuie i nu la compilare.
Aceast proprietate se numete legare dinamic.
n limbajul C++ o funcie membru se declar virtual n cadrul declarrii clasei
respective n modul urmtor. Antetul funciei membru se va ncepe cu cuvntul cheie virtual.
O funcie membru se declar virtual n clasa de baz. Suprancrcrile ei se vor
considera virtuale n toate clasele derivate ale ierarhiei.
n cazul exemplului de mai sus declararea clasei de baz se modific n felul urmtor.
class baza {
public:
virtual void functia_1();
void functia_2();
};

Rezultatul obinut prin execuie se modific astfel:


S-a apelat functia membru functia_2 a clasei de baza
S-a apelat functia membru functia_1 a clasei derivate

Deci ntr-adevr se apeleaz funcia membru functia_1 a clasei derivate. Prezentm n


continuare un alt exemplu n care apare necesitatea introducerii funciilor membru virtuale. n
acest caz funcia membru virtual va fi de fapt suprancrcarea unui operator.
S se defineasc clasa fractie referitoare la numerele raionale, avnd ca date membru
numrtorul i numitorul fraciei. Clasa trebuie s aib un constructor, valoarea implicit
183

pentru numrtor fiind zero iar pentru numitor unu, precum i doi operatori: * pentru
nmulirea a dou fracii i *= pentru nmulirea obiectului curent cu fracia dat ca i
parametru. Deasemenea clasa fractie trebuie s aib i o funcie membru pentru afiarea unui
numr raional. Folosind clasa fractie ca i clas de baz se va defini clasa derivat
fractie_scrie, pentru care se va suprancrca operatorul de nmulire * astfel nct concomitent
cu efectuarea nmulirii s se afieze pe stdout operaia respectiv. Operaia *= nu se va
suprancrca, dar operaia efectuat trebuie s se afieze pe dispozitivul standard de ieire i n
acest caz. n limbajul C++ clasele se definesc n modul urmtor. Fiierul fractie2.cpp:
#include <conio.h>
#include <iostream.h>
class fractie {
protected:
int numarator;
int numitor;
public:
fractie(int numarator1, int numitor1);
fractie operator *( fractie& r); //calculeaza
//produsul
//a doua fractii
//nu simplifica
fractie& operator *=( fractie& r);
void afiseaza();
};

fractie::fractie(int numarator1 = 1,
int numitor1 = 0)
{
numarator = numarator1;
numitor = numitor1;
}
fractie fractie::operator *(fractie& r)
{
return fractie(numarator*r.numarator,
numitor*r.numitor);
}
fractie& fractie::operator *=( fractie& q )
{
*this = *this * q;
return *this;
}
void fractie::afiseaza()
{
if ( numitor )
cout << numarator << " / " << numitor;
else
cerr << "Fractie incorecta";
}
class fractie_scrie: public fractie{
public:
fractie_scrie( int numarator1 = 0, int numitor1 = 1 ):
fractie(numarator1, numitor1) {}
fractie operator *( fractie& r);
};

184

fractie fractie_scrie::operator * (fractie& q)


{
fractie r = fractie(*this) * q;
cout << "(";
this->afiseaza();
cout << ") * (";
q.afiseaza();
cout << ") = ";
r.afiseaza();
cout << endl;
return r;
}
int main()
{
clrscr();
fractie p(3,4), q(5,2), r;
r = p *= q;
p.afiseaza();
cout << endl;
r.afiseaza();
cout << endl;
fractie_scrie p1(3,4), q1(5,2);
fractie r1, r2;
r1 = p1 * q1;
r2 = p1 *= q1;
p1.afiseaza();
cout << endl;
r1.afiseaza();
cout << endl;
r2.afiseaza();
cout << endl;
return 0;
}

Prin execuie se obine:


15
15
(3
15
15
15

/
/
/
/
/
/

8
8
4) * (5 / 2) = 15 / 8
8
8
8

Observm c rezultatul nu este cel dorit, deoarece operaia de nmulire s-a afiat
numai o singur dat, i anume pentru expresia r1 = p1 * q1. n cazul expresiei r2 = p1 *= q1
ns nu s-a afiat operaia de nmulire. Acest lucru se datoreaz faptului c funcia membru
operator *= nu s-a suprancrcat n clasa derivat. Deci s-a apelat operatorul *= motenit de
la clasa fractie. n interiorul operatorului *= s-a apelat funcia membru operator *, dar
deoarece aceast funcie membru s-a determinat nc n faza de compilare, rezult c s-a
apelat operatorul de nmulire referitor la clasa fractie i nu cel referitor la clasa derivat
fractie_scrie. Deci afiarea operaiei s-a efectuat numai o singur dat.
Soluia este, ca i n exemplul anterior, declararea unei funcii membru virtuale, i
anume operatorul * se va declara virtual. Deci declararea clasei de baz se modific n felul
urmtor:
class fractie {
protected:
int numarator;
int numitor;

185

public:
fractie(int numarator1, int numitor1);
virtual fractie operator *( fractie& r);
fractie& operator *=( fractie& r);
void afiseaza();
};

Dup efectuarea acestei modificri prin executarea programului obinem:


15
15
(3
(3
15
15
15

/
/
/
/
/
/
/

8
8
4) * (5 / 2) = 15 / 8
4) * (5 / 2) = 15 / 8
8
8
8

Deci se observ c afiarea operaiei s-a fcut de dou ori, pentru ambele expresii.
Funciile virtuale, ca i alte funcii membru de fapt, nu trebuie neaprat suprancrcate n
clasele derivate. Dac nu sunt suprancrcate atunci se motenete funcia membru de la un
nivel superior.
Determinarea funciilor membru virtuale corespunztoare se face pe baza unor tabele
construite i gestionate n mod automat. Obiectele claselor care au funcii membru virtuale
conin i un pointer ctre tabela construit. De aceea gestionarea funciilor membru virtuale
necesit mai mult memorie i un timp de execuie mai ndelungat.

17.4. Clase virtuale


Din cauza motenirii multiple se poate ntmpla ca o clas de baz s fie prezent n
mai multe exemplare ntr-o clas derivat. S considerm urmtorul exemplu:

Figura 1. Date membru motenite n dou exemplare


n acest caz datele membru ale clasei animal vor fi motenite n dou exemplare de
ctre clasa cine. Primul exemplar se motenete prin clasa domestic iar cel de al doilea prin
clasa mamifer. Aceste date membru pot fi accesate folosind operatorul de rezoluie precedat
de numele clasei prin care se face motenirea. Fiierul animal1.cpp:
#include <iostream.h>
#include <string.h>
#include <conio.h>
class animal {
protected:
char nume[20];
public:
animal(char* n);
};

186

class mamifer : public animal {


protected:
int greutate;
public:
mamifer(char* n, int g);
};

class domestic : public animal {


protected:
int comportament;
public:
domestic(char* n, int c);
};
class caine : public mamifer, public domestic {
protected:
int latra;
public:
caine(char* n, int g, int c, int l);
void scrie_date();
};
animal::animal(char* n)
{
strcpy(nume, n);
}
mamifer::mamifer(char* n, int g): animal(n)
{
greutate = g;
}
domestic::domestic(char* n, int c): animal(n)
{
comportament = c;
}
caine::caine(char* n, int g, int c, int l): mamifer(n, g),
domestic(n, c)
{
latra = l;
}
void caine::scrie_date()
{
cout << "Numele (mostenit prin clasa mamifer): "
<< mamifer::nume << endl;
cout << "Numele (mostenit prin clasa domestic): "
<< domestic::nume << endl;
cout << "Greutatea: " << greutate << endl;
cout << "Comportmentul: " << comportament << endl;
if ( latra )
cout << "Latra: da" << endl;
else
cout << "Latra: nu" << endl;
}
int main() {
clrscr();
caine B("boxer", 12, 9, 1);
B.scrie_date();
return 0;
}

187

Observm c data membru nume s-a motenit n dou exemplare de ctre clasa cine,
i referirea la aceste date s-a fcut prin mamifer::nume respectiv domestic::nume. Prin
execuie se va afia data membru nume de dou ori, motenit prin cele dou clase.
Deasemenea se va afia greutatea i comportamentul cinelui, i faptul c latr sau nu.
Ar fi de dorit ns ca numele s fie memorat numai ntr-un singur exemplar n
obiectele clasei cine. Acest lucru se poate realiza cu ajutorul claselor virtuale.
Dac nu se dorete ca datele membru a unei clase de baz s fie prezente n mai multe
exemplare ntr-o clas derivat, atunci se folosesc clase virtuale. Clasele de baz devin
virtuale prin motenire, dac se specific acest lucru prin plasarea cuvntului cheie virtual n
faa numelui clasei, n lista claselor de baz. Astfel clasa de baz respectiv va deveni virtual
referitor la clasa derivat.
Exemplul de mai sus se modific n modul urmtor. Fiierul animal2.cpp:
#include <iostream.h>
#include <string.h>
#include <conio.h>
class animal {
protected:
char nume[20];
public:
animal(char* n);
};
class mamifer : virtual public animal {
protected:
int greutate;
public:
mamifer(char* n, int g);
};

//clasa animal va fi
//virtuala referitor
//la clasa mamifer

class domestic : virtual public animal { //clasa animal va fi


protected:
//virtuala referitor
int comportament;
//la clasa domestic
public:
domestic(char* n, int c);
};
class caine : public mamifer, public domestic {
protected:
int latra;
public:
caine(char* n, int g, int c, int l);
void scrie_date();
};
animal::animal(char* n)
{
strcpy(nume, n);
}
mamifer::mamifer(char* n, int g): animal(n)
{
greutate = g;
}
domestic::domestic(char* n, int c): animal(n)
{

188

comportament = c;
}
caine::caine(char* n, int g, int c, int l):
animal(n),
//trebuie apelat constructorul clasei animal
mamifer(n, g),//in mod explicit, deoarece ea fiind virtuala
domestic(n, c)//constructorul nu se va apela prin unul din
{ latra = l; } //clasele mamifer sau domestic
void caine::scrie_date()
{
cout << "Numele: " << nume << endl;
cout << "Greutatea: " << greutate << endl;
cout << "Comportmentul: " << comportament << endl;
if ( latra )
cout << "Latra: da" << endl;
else
cout << "Latra: nu" << endl;
}
int main() {
clrscr();
caine B("boxer", 12, 9, 1);
B.scrie_date();
return 0;
}

ntr-o ierarhie complicat de clase unele clase de baz pot fi motenite n mai multe
exemplare ntr-o clas derivat. ntre aceste exemplare pot fi virtuale i nevirtuale. S
considerm urmtorul exemplu referitor la acest lucru :

Figura 2. Ierarhie de clase


n aceast ierarhie de obiecte clasa de baz A se motenete n mod virtual de ctre
clasele B i C i n mod nevirtual de ctre clasa D. Clasele de baz ale clasei G sunt B, C, D,
E i F, toate motenirile fiind nevirtuale cu excepia clasei F. Se pot pune atunci urmtoarele
probleme. n ce ordine vor fi executate constructorii n cazul n care se creaz un obiect al
clasei G? Dac o clas de baz este motenit n mai multe exemplare ntr-o clas derivat
printre care pot fi att virtuale ct i nevirtuale, atunci de cte ori se va apela constructorul
clasei de baz? Pentru a da rspuns la aceste ntrebri s considerm urmtorul program.
Fiierul virtual2.cpp:
#include <iostream.h>
#include <string.h>
#include <conio.h>

class A {
public:
A(char* a) {
cout << "Se apeleaza constructorul clasei A"
<< " cu parametrul " << a << endl;

189

}
};
class B: virtual public A {
public:
B(char* b): A(b) {
cout << "Se apeleaza constructorul clasei B"
<< " cu parametrul " << b << endl;
}
};
class C: virtual public A {
public:
C(char* c): A(c) {
cout << "Se apeleaza constructorul clasei C"
<< " cu parametrul " << c << endl;
}
};
class D: public A {
public:
D(char* d): A(d) {
cout << "Se apeleaza constructorul clasei D"
<< " cu parametrul " << d << endl;
}
};
class E {
public:
E(char* e) {
cout << "Se apeleaza constructorul clasei E"
<< " cu parametrul " << e << endl;
}
};
class F {
public:
F(char* f) {
cout << "Se apeleaza constructorul clasei F"
<< " cu parametrul " << f << endl;
}
};
class G: public B, public C, public D, public E, virtual public F {
public:
G(char* g): A(g), B(g), C(g), D(g), E(g), F(g) {
cout << "Se apeleaza constructorul clasei G"
<< " cu parametrul " << g << endl;
}
};
int main() {
clrscr();
G ob("obiect");
return 0;
}

Prin execuie se obine urmtorul rezultat:


Se apeleaza constructorul clasei A cu parametrul obiect
Se apeleaza constructorul clasei F cu parametrul obiect
Se apeleaza constructorul clasei B cu parametrul obiect

190

Se
Se
Se
Se
Se

apeleaza
apeleaza
apeleaza
apeleaza
apeleaza

constructorul
constructorul
constructorul
constructorul
constructorul

clasei
clasei
clasei
clasei
clasei

C
A
D
E
G

cu
cu
cu
cu
cu

parametrul
parametrul
parametrul
parametrul
parametrul

obiect
obiect
obiect
obiect
obiect

Acest rezultat se datoreaz urmtoarelor reguli.


n cazul n care se creaz un obiect al clasei derivate, mai nti vor fi executate
constructorii claselor de baz virtuale n ordinea din lista_claselor_de_baza
(vezi declaraia claselor derivate), apoi constructorii claselor de baz nevirtuale n
ordinea din lista_claselor_de_baza.
Dac ntr-o ierarhie de clase, o clas de baz se motenete n mai multe exemplare
ntr-o clas derivat, atunci la crearea unui obiect al clasei derivate se va executa
constructorul clasei de baz o dat pentru toate exemplarele virtuale, i nc de
attea ori cte exemplare nevirtuale exist.
Deci n cazul exemplului de mai sus, mai nti se execut constructorii claselor A i F
(ele fiind virtuale), apoi constructorii claselor B, C, D i E (clase nevirtuale). Constructorul
clasei D apeleaz mai nti constructorul clasei A (ea fiind nevirtual de aceast dat). De
aceea ordinea de apelare a constructorilor va fi: A, F, B, C, A, D, E i G.

17.5. Clase abstracte. Funcia membru virtual pur


n cazul unei ierarhii de clase mai complicate, clasa de baz poate avea nite
proprieti generale despre care tim, dar nu le putem defini numai n clasele derivate. De
exemplu s considerm ierarhia de clase din figura 3.
Observm c putem determina nite proprieti referitoare la clasele derivate. De
exemplu greutatea medie, durata medie de via i viteza medie de deplasare. Aceste
proprieti se vor descrie cu ajutorul unor funcii membru. n principiu i pentru clasa animal
exist o greutate medie, durat medie de via i vitez medie de deplasare. Dar aceste
proprieti ar fi mult mai greu de determinat i ele nici nu sunt importante pentru noi ntr-o
generalitate de acest fel. Totui pentru o tratare general ar fi bine, dac cele trei funcii
membru ar fi declarate n clasa de baz i redefinite n clasele derivate. n acest scop s-a
introdus noiunea de funcie membru virtual pur.

Figura 3. Ierarhie de clase referitoare la animale


Funcia virtual pur este o funcie membru care este declarat, dar nu este definit n
clasa respectiv. Ea trebuie definit ntr-o clas derivat. Funcia membru virtual pur se
declar n modul urmtor. Antetul obinuit al funciei este precedat de cuvntul cheie virtual,
i antetul se termin cu = 0. Dup cum arat numele i declaraia ei, funcia membru virtual
pur este o funcie virtual, deci selectarea exemplarului funciei din ierarhia de clase se va
face n timpul execuiei programului.
Clasele care conin cel puin o funcie membru virtual pur se vor numi clase
abstracte.
Deoarece clasele abstracte conin funcii membru care nu sunt definite, nu se pot crea
obiecte aparinnd claselor abstracte. Dac funcia virtual pur nu s-a definit n clasa derivat
191

atunci i clasa derivat va fi clas abstract i ca atare nu se pot defini obiecte aparinnd
acelei clase.
S considerm exemplul de mai sus i s scriem un program, care referitor la un
porumbel, urs sau cal determin dac el este gras sau slab, rapid sau ncet, respectiv tiner sau
btrn. Afiarea acestui rezultat se va face de ctre o funcie membru a clasei animal care nu
se suprancarc n clasele derivate. Fiierul abstract.cpp:
#include <conio.h>
#include <iostream.h>
class animal {
protected:
double greutate; // kg
double virsta;
// ani
double viteza;
// km / h
public:
animal( double g, double v1, double v2);
virtual double greutate_medie() = 0;
virtual double durata_de_viata_medie() = 0;
virtual double viteza_medie() = 0;
int gras() { return greutate > greutate_medie(); }
int rapid() { return viteza > viteza_medie(); }
int tiner()
{ return 2 * virsta < durata_de_viata_medie(); }
void afiseaza();
};
animal::animal( double g, double v1, double v2)
{
greutate = g;
virsta = v1;
viteza = v2;
}
void animal::afiseaza()
{
cout << ( gras() ? "gras, " : "slab, " );
cout << ( tiner() ? "tiner, " : "batran, " );
cout << ( rapid() ? "rapid" : "incet" ) << endl;
}
class porumbel : public animal {
public:
porumbel( double g, double v1, double v2):
animal(g, v1, v2) {}
double greutate_medie() { return 0.5; }
double durata_de_viata_medie() { return 6; }
double viteza_medie() { return 90; }
};
class urs: public animal {
public:
urs( double g, double v1, double v2):
animal(g, v1, v2) {}
double greutate_medie() { return 450; }
double durata_de_viata_medie() { return 43; }
double viteza_medie() { return 40; }
};
class cal: public animal {

192

public:
cal( double g, double v1, double v2):
animal(g, v1, v2) {}
double greutate_medie() { return 1000; }
double durata_de_viata_medie() { return 36; }
double viteza_medie() { return 60; }
};
int main() {
clrscr();
porumbel p(0.6, 1, 80);
urs u(500, 40, 46);
cal c(900, 8, 70);
p.afiseaza();
u.afiseaza();
c.afiseaza();
return 0;
}

Observm c dei clasa animal este clas abstract, este util introducerea ei, pentru c
multe funcii membru pot fi definite n clasa de baz i motenite fr modificri n cele trei
clase derivate.

193

18. Ierarhii de clase pentru operaii de intrare/ieire


18.1. Streamuri
n seciunea 15.1 am prezentat nite cunotine de baz referitoare la ierarhiile de clase
existente n C++ pentru realizarea operaiilor de intrare/ieire. Am vzut c nu exist
instruciuni de intrare/ieire n limbajul C, i nici n C++. n schimb n limbajul C s-au definit
funcii standard de bibliotec, iar n C++ ierarhii de clase pentru operaii de intrare/ieire. n
continuare ne vom ocupa de cele dou ierarhii de clase definite n C++ n vederea efecturii
operaiilor de intrare/ieire.
Operaiile de intrare/ieire sunt realizate de ctre cele dou ierarhii de clase cu ajutorul
noiunii de stream. Printr-un stream vom nelege un flux de date de la mulimea datelor surs
(tastatur, fiier sau zon de memorie) la mulimea datelor destinaie (monitor, fiier sau zon
de memorie). Cele dou ierarhii de clase sunt declarate n fiierul iostream.h, deci acest fiier
va trebui inclus de fiecare dat, cnd se lucreaz cu ierarhiile de clase pentru intrare/ieire.
Prima ierarhie de clase este:

Figura 4. Ierarhia de clase cu rdcina n streambuf


Clasa streambuf se poate folosi pentru gestionarea zonelor tampon i pentru operaii
de intrare/ieire simple. A doua ierarhie de clase este mai complicat. Prezentm n continuare
o parte a ei.

Figura 5. Ierarhia de clase cu rdcina n ios


Legtura dintre cele dou ierarhii de clase s-a realizat printr-o dat membru a clasei
ios, care este un pointer ctre clasa streambuf. Clasa ios este clas de baz virtual att pentru
clasa istream, ct i pentru ostream. Astfel elementele definite n clasa ios vor fi prezente
numai ntr-un singur exemplar n clasa iostream.
Clasa istream realizeaz o conversie din caracterele unui obiect de tip streambuf,
conform unui format specificat. Folosind clasa ostream se poate efectua o conversie conform
unui format specificat, n caractere memorate ntr-un obiect de tip streambuf, iar clasa
iostream permite conversii n ambele direcii.
194

Clasele istream_withassign, ostream_withassign i iostream_withassign sunt clase


derivate, avnd clasele de baz istream, ostream respectiv iostream. n plus operatorul de
atribuire (=) este suprancrcat n dou moduri, de aceste clase.
Clasele derivate din clasa istream sau ostream se vor numi clase stream, iar obiectele
claselor derivate din clasa ios se vor numi streamuri. Exist urmtoarele patru streamuri
standard definite n fiierul iostream.h:
Stream
cin
cout
cerr
clog

Obiect al clasei
istream_withassign
ostream_withassign
ostream_withassign
ostream_withassign

Corespunde fiierului standard


stdin
stdout
stderr (fr zone tampon)
stderr (cu zone tampon)

Tabelul 3. Streamuri standard


n continuare ne vom ocupa de folosirea streamurilor standard, pentru realizarea
operaiilor de intrare/ieire conform unui format specificat.

18.2. Ieiri formatate


18.2.1. Operatorul de inserare
Operaiile de scriere pe dispozitivul standard de ieire, ntr-un fiier, sau ntr-o zon de
memorie se pot efectua cu ajutorul operatorului <<, care n acest caz se va numi operator de
inserare.
Operandul din partea stng al operatorului << trebuie s fie un obiect al clasei
ostream (bineneles i obiectele claselor derivate din clasa ostream se consider obiecte ale
clasei ostream). Pentru operaiile de scriere pe dispozitivul standard de ieire se va folosi
obiectul cout.
Operandul din partea dreapt al operatorului << poate fi o expresie. Pentru tipul
corespunztor expresiei, trebuie s fie suprancrcat operatorul <<. n cazul tipurilor standard
operatorul de inserare este suprancrcat cu o funcie membru de forma:
ostream& operator << (nume_tip_standard);

Menionm c pentru tipurile abstracte programatorul poate suprancrca operatorul de


inserare. S considerm acum din nou exemplul prezentat n seciunea 15.1, ntr-o form mai
detaliat, pentru a ilustra asemnarea cu utilizarea funciei printf. Fiierul stream1.cpp:
#include <stdio.h>
#include <iostream.h>
#include <conio.h>
int main() {
clrscr();
int x = 230;
cout << x << '\n';
printf("%d\n", x);
double y = 543.67;
cout << y << '\n';
printf("%lg\n", y);
char z = 'a';
cout << z << '\n';
printf("%c\n", z);

195

char t[] = "exemplu";


cout << t << '\n';
printf("%s\n", t);
int *v = &x;
cout << v << '\n';
printf("%p", v);
return 0;
}

Dup executarea programului se obine:


230
230
543.67
543.67
a
a
exemplu
exemplu
0xfff4
FFF4

Observm c rezultatul afirii irurilor de caractere i datelor de tip int, double i char
este acelai. n cazul afirii unui pointer, valoarea afiat este identic, dar formatul difer
(dac se scrie cu printf se folosesc litere mari i nu se afieaz baza, iar n cazul scrierii cu
cout se folosesc litere mici i se afieaz baza).
Deoarece operatorul << returneaz o referin ctre clasa curent, operatorul se poate
aplica n mod nlnuit. Acest lucru este prezentat n exemplul urmtor. Fiierul
stream2.cpp:
#include <stdio.h>
#include <iostream.h>
#include <conio.h>
int main() {
clrscr();
int x = 10;
cout << "x (dupa incrementare) = " << x
<< "\nx (inainte de incrementare) = " << x++;
x = 10;
cout << "\n";
printf("x (dupa incrementare) = %d\
\nx (inainte de incrementare) = %d", x, x++);
return 0;
}

Prin execuie obinem:


x
x
x
x

(dupa incrementare) = 11
(inainte de incrementare) = 10
(dupa incrementare) = 11
(inainte de incrementare) = 10

n afar de faptul c operatorul de inserare se poate aplica n mod nlnuit, se observ


c evaluarea expresiilor afiate s-a fcut n ordine invers comparativ cu afiarea. Numai n
acest fel se poate explica faptul c valoarea afiat mai repede este deja incrementat, iar cea
afiat mai trziu are valoarea iniial. De fapt i pentru funcia printf este valabil acelai
lucru.
196

Ordinea de evaluare a expresiilor, care se afieaz folosind operatorul de inserare n


mod nlnuit, este ilustrat mai explicit de urmtorul exemplu. Fiierul stream3.cpp:
#include <iostream.h>
#include <conio.h>
char* f1()
{
cout << "Evaluare functie f1\n";
return "afisare 1\n";
}
char* f2()
{
cout << "Evaluare functie f2\n";
return "afisare 2\n";
}
int main() {
clrscr();
cout << f1() << f2();
return 0;
}

Dup executarea programului obinem:


Evaluare functie f2
Evaluare functie f1
afisare 1
afisare 2

Deci evaluarea funciei f2 s-a fcut nainte de evaluarea funciei f1.

18.2.2. Funcia membru setf


n limbajul C funcia printf ne permite afiarea datelor conform unui format specificat
de programator. Acest lucru se poate realiza i cu ajutorul ierarhiilor de clase definite n
limbajul C++. n clasa ios s-a declarat o dat membru x_flags, care se refer la formatul cu
care se vor efectua operaiile de intrare/ieire. Tot n clasa ios s-a definit un tip enumerare cu
care se pot face referiri la biii datei membru x_flags. Tipul enumerare este urmtorul:
class ios {
public:
...
enum {
skipws
left
right
internal
dec
oct
hex
showbase
showpoint

=
=
=
=
=
=
=
=
=

// se face salt peste caractere albe la citire


// la scriere se cadreaz la stnga
// la scriere se cadreaz la dreapta
// caracterele de umplere vor fi dup
// semn, sau dup baz
0x0010, // se face conversie n zecimal
0x0020, // se face conversie n octal
0x0040, // se face conversie n hexazecimal
0x0080, // se afieaz i baza
0x0100, // apare i punctul zecimal n cazul
// numerelor reale
0x0001,
0x0002,
0x0004,
0x0008,

197

uppercase = 0x0200,
showpos

// n hexazecimal se afieaz litere mari

// numerele ntregi pozitive sunt afiate


// cu semnul n fa
0x0800, // afiare numere reale cu exponent
0x1000, // afiare numere reale fr exponent
0x2000, // se videaz zonele tampon dup scriere
0x4000 // dup scriere se videaz stdout i stderr

= 0x0400,

scientific=
fixed
=
unitbuf
=
stdio
=
};
...
};

Data membru x_flags are o valoare implicit pentru fiecare tip standard. Astfel
rezultatul afirii datelor care aparin tipurilor standard, va fi n mod implicit identic cu
rezultatul afirii cu funcia printf, folosind specificatori de format care corespund tipurilor
respective. Prezentm aceast legtur ntre tipuri i specificatori de format, n tabelul 4.
Dac se modific biii corespunztori datei membru x_flags, atunci i rezultatul afirii
se va schimba, conform formatului specificat. Acest lucru se poate realiza cu ajutorul unor
funcii membru.
Tip
int
long
unsigned
long unsigned
float
double
long double
char
ir de caractere

Specificator de format
corespunztor
%d
%ld
%u
%lu
%g
%lg
%Lg
%c
%s

Tabelul 4. Legtura dintre tipuri i specificatori de format


Pentru a putea lucra mai uor cu biii corespunztori formatului, s-au determinat trei
grupe ale biilor datei membru x_flags. Fiecare grup are un nume, care este de fapt numele
unei constante statice de tip long declarate n clasa ios. Aceste grupe sunt:
- adjustfield (right, left i internal), pentru modul de cadrare;
- basefield (dec, oct i hex), pentru determinarea bazei;
- floatfield (scientific i fixed), pentru scrierea numerelor reale.
Fiecare grup are proprietatea c numai un singur bit poate fi setat n cadrul grupei. Biii datei
membru x_flags pot fi setai cu ajutorul funciei membru setf al clasei ios. Funcia membru
setf are dou forme:
long setf(long format);

i
long setf(long setbit, long grupa);

Prima variant a funciei membru setf seteaz biii corespunztor parametrului de tip
long: format. Dac un bit din format este egal cu unu, atunci bitul corespunztor din x_flags
va fi unu, iar dac bitul din format este egal cu zero, atunci bitul corepunztor din x_flags
rmne neschimbat.
198

A doua variant a funciei membru setf seteaz un bit din una dintre cele trei grupe
adjustfield, basefield sau floatfield. Cu ajutorul parametrului setbit se determin bitul care se
va seta. n locul unde se afl bitul trebuie s fie unu, iar n rest zero. n parametrul al doilea
trebuie specificat numele grupei. n acest caz se anuleaz biii corespunztori grupei dup care
se seteaz biii din setbit. Ambele variante ale funciei setf returneaz valoarea datei membru
x_flags nainte de modificare.
Referirea la biii datei membru x_flags se face cu numele clasei ios, urmat de
operatorul de rezoluie i numele bitului din tipul enumerare. Referirea la numele unei grupe
se face n mod analog, nlocuid numele din tipul enumerare cu numele grupei. Utilizarea
funciei membru setf este ilustrat de urmtorul exemplu. Fiierul stream4.cpp:
#include <stdio.h>
#include <iostream.h>
#include <conio.h>
void binar_c( long x )
{
printf("x_flags: ");
for( int i = 8*sizeof(long)-1; i >= 0; i--)
{
printf("%d", (x >> i)& 1);
if ( !(i % 8) )
printf(" ");
}
printf("\n");
}
void nume_bit_1( char* s[], long x )
{
for( int i = 0; i < 15; i++)
if ( (x >> i)& 1 )
printf("%-16s", s[i]);
printf("\n");
}
void afisare( char* s[], long x )
{
binar_c( x );
nume_bit_1( s, x);
}
int main() {
char* nume_enum[] = {
"skipws",
"left",
"right",
"internal",
"dec",
"oct",
"hex",
"showbase",
"showpoint",
"uppercase",
"showpos",
"scientific",
"fixed",
"unitbuf",
"stdio"
};

199

clrscr();
afisare( nume_enum, cout.flags() );
cout.setf(ios::oct, ios::basefield);
afisare( nume_enum, cout.flags() );
cout << 36 << '\n';
afisare( nume_enum, cout.flags() );
cout.setf(ios::showbase);
cout.setf(ios::hex, ios::basefield);
afisare( nume_enum, cout.flags() );
cout << 36 << '\n';
afisare( nume_enum, cout.flags() );
return 0;
}

Prin executarea programului obinem:


x_flags:
skipws
x_flags:
skipws
44
x_flags:
skipws
x_flags:
skipws
0x24
x_flags:
skipws

00000000 00000000 00100000 00000001


unitbuf
00000000 00000000 00100000 00100001
oct
unitbuf
00000000 00000000 00100000 00100001
oct
unitbuf
00000000 00000000 00100000 11000001
hex
showbase

unitbuf

00000000 00000000 00100000 11000001


hex
showbase

unitbuf

Funcia afisare, din exemplul de mai sus, afieaz mai nti biii datei membru x_flags,
dup care se scriu numele biilor cu valoarea egal cu unu. Mai nti s-a setat bitul oct i s-a
afiat valoarea constantei de tip ntreg 36, folosind o conversie n octal. Astfel s-a obinut
valoarea 44. Dup aceea s-au setat biii hex i showbase, i s-a afiat valoarea constantei nc
o dat, astfel obinnd valoarea 0x24. Observm c folosind a doua variant a funciei
membru setf pentru setarea bitului hex, s-a obinut i anularea bitului oct, aa cum am dorit.
Menionm c numele i valoarea tuturor biilor tipului enumerare s-ar fi putut afia de
exemplu cu urmtoarea funcie:
void nume_bit( char* s[], long x )
{
for( int i = 0; i < 15; i++)
printf("%-11s:%2d\n", s[i], (x >> i)& 1);
}

O alt observaie este urmtoarea. Pentru afiarea biilor datei membru x_flags s-a
folosit funcia printf i nu ierarhia de clase declarat n fiierul iostream.h. Dei n general o
funcie de forma
void binar_stream( long x )
{
for( int i = 8*sizeof(long)-1; i >= 0; i-- )
cout << ((x >> i)& 1) << (i%8 ? "": " ");
cout << '\n';
}

ar afia corect biii datei membru x_flags, vor apare erori n cazul n care este setat
bitul showbase i unul dintre biii oct sau hex. n aceste cazuri se va afia i baza setat, deci
200

numerele ntregi vor fi precedate de zero n cazul conversiei n octal, i de 0x sau 0X n cazul
conversiei n hexazecimal.

18.2.3. Funciile membru width, fill i precision


Ca i n cazul funciei printf, se poate determina lungimea minim a cmpului n care
se va afia data respectiv. Aceast valoare este memorat n data membru x_width a clasei
ios. Valoarea implicit a datei membru x_width este zero, ceea ce nseamn c afiarea se va
face pe attea caractere cte sunt necesare.
Valoarea datei membru x_width poate fi determinat sau modificat cu funcia
membru width a clasei ios. Ea are urmtoarele dou forme:
int width();

i
int width( int lungime );

Prima form a funciei membru width returneaz valoarea datei membru x_width. A
doua variant modific valoarea datei membru x_width la valoarea determinat de lungime, i
returneaz vechea valoare a lui x_width.
Este foarte important de menionat c dup orice operaie de intare/ieire valoarea
datei membru x_width se va reseta la valoarea zero. Deci dac nu se determin o lungime a
cmpului nainte de o operaie de inserare, atunci se va folosi valoarea implicit.
Dac lungimea cmpului, n care se face afiarea, este mai mare dect numrul de
caractere, care vor fi afiate, atunci cadrarea se va face n mod implicit la dreapta, i spaiul
rmas se va completa cu caractere de umplere. n mod implicit caracterele de umplere sunt
spaii, dar ele pot fi modificate cu ajutorul funciei membru fill a clasei ios. Clasa ios are o
dat membru x_fill n care se memoreaz caracterul de umplere. Funcia membru fill are
urmtoarele dou forme:
char fill();

i
char fill( char car );

Prima variant returneaz caracterul de umplere curent. A doua form a funciei


membru fill modific data membru x_fill la caracterul car, i returneaz vechea valoare a
caracterului de umplere.
n cazul n care se afieaz valoarea unor numere reale, se poate determina precizia,
adic numrul de zecimale, care se va folosi la scrierea datelor. Clasa ios are o dat membru
x_precision, care are valoarea implicit egal cu zero. n mod implicit datele de tip real se vor
afia cu ase zecimale. Funcia membru precision are urmtoarele dou forme:
int precision();

i
int precision( int p );

Prima variant a funciei membru precision returneaz valoarea curent a datei


membru x_precision. A doua variant atribuie valoarea parametrului p datei membru
x_precision i returneaz valoarea anterioar. Funcia membru precision s-a folosit n fiierul
sup_fun1.cpp pentru a determina numrul de cifre care vor fi afiate n cazul calculrii
201

numrului . Prezentm n continuare un alt exemplu, n care se vor folosi funciile membru
din acest paragraf. Fiierul stream5.cpp:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
const double pi = 3.141592653;

void scrie_width_precision_c()
{
printf("x_width:
%d\n", cout.width() );
printf("x_precision: %d\n", cout.precision() );
}
void afisare_pi_c()
{
printf("*");
cout << pi;
printf("*\n");
}
int main() {
clrscr();
scrie_width_precision_c();
afisare_pi_c();
cout.width(7);
cout.precision(2);
scrie_width_precision_c();
afisare_pi_c();
scrie_width_precision_c();
afisare_pi_c();
cout.width(7);
cout.fill('@');
scrie_width_precision_c();
afisare_pi_c();
return 0;
}

Dup executarea programului obinem:


x_width:
x_precision:
*3.141593*
x_width:
x_precision:
*
3.14*
x_width:
x_precision:
*3.14*
x_width:
x_precision:
*@@@3.14*

0
0
7
2
0
2
7
2

Caracterele '*' s-au afiat pentru a evidenia cmpul n care se face scrierea datelor.
Observm c ntr-adevr valoarea implicit a datelor membru x_width i x_precision este
zero, deci afiarea se va face cu ase zecimale.
Dup modificarea acestor date membru la valorile x_width=7 respectiv x_precision=2,
afiarea se face n cmpul de apte caractere, cu dou zecimale, numrul fiind cadrat la
202

dreapta. Dup operaia de scriere valoarea datei membru x_width devine zero, dar valoarea
datei membru x_precision nu se modific.
Folosind funcia membru width, se atribuie datei membru x_width din nou valoarea
apte. n continuare se folosete funcia membru fill pentru a determina un caracter de
umplere diferit de caracterul blanc.
Menionm c dac n loc de apelarea funciei printf, s-ar fi folosit ierarhia de clase
declarat n fiierul iostream.h, atunci nu s-ar fi obinut ceea ce s-a dorit. De exemplu, dac n
loc de funciile scrie_width_precision_c i afisare_pi_c s-ar fi folosit funciile:

void scrie_width_precision_stream()
{
cout << "x_width:
" << cout.width() << '\n';
cout << "x_precision: " << cout.precision() << '\n';
}
void afisare_pi_stream()
{
cout << "*" << pi << "*\n";
}

atunci s-ar fi obinut:


x_width:
x_precision:
*3.141593*
x_width:
x_precision:
*3.14*
x_width:
x_precision:
*3.14*
x_width:
x_precision:
*3.14*

0
0
7
2
0
2
7
2

n acest caz, de fiecare dat, valoarea lui se afieaz pe un cmp de lungime egal cu
numrul de caractere afiate. Explicaia este c datei membru x_width dup prima operaie de
inserare s-a atribuit valoarea zero, prima operaie fiind afiarea irului de caractere
"x_width: ", i nu scrierea valorii lui .
Exist o legtur strns ntre funciile membru prezentate n acest paragraf i
formatrile din cadrul funciei printf. Acest lucru este ilustrat de urmtorul exemplu. Fiierul
stream6.cpp:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
int main() {
clrscr();
const double x = 987.65432;
cout << "*";
cout.width(11);
cout.precision(4);
cout.fill('0');
cout << x << "*\n";
printf("*%011.4lf*\n", x);
return 0;

203

Prin execuie obinem:


*000987.6543*
*000987.6543*

n exemplul de mai sus s-a afiat valoarea constantei de tip real x n dou moduri: cu
ajutorul streamurilor, i folosind funcia printf. Putem s constatm c s-a obinut acelai
rezultat. Dei n acest caz scrierea cu funcia printf este mai compact, afiarea cu streamuri
este mai general, deoarece caracterul de umplere poate fi orice alt caracter, nu numai '0'.

18.2.4. Manipulatori
Biii datei membru x_flags, care corespund conversiei, pot fi setai i ntr-un alt mod,
folosind funcii membru speciale, numite manipulatori. Avantajul manipulatorilor este c ei
returneaz o referin la un stream, deci apelurile acestor funcii membru pot fi nlnuite.
O parte a manipulatorilor este declarat n fiierul iostream.h, iar celelalte n fiierul
iomanip.h. Manipulatorii declarai n fiierul iostream.h sunt:
endl

trecere la linie nou i vidarea zonei tampon


corespunztoare streamului
inserarea caracterului '\0'
vidarea zonei tampon a unui obiect al clasei ostream
conversie n zecimal
conversie n hexazecimal
conversie n octal
setarea bitului skipws

ends
flush
dec
hex
oct
ws

Tabelul 5. Manipulatorii declarai n fiierul iostream.h


Manipulatorii declarai n fiierul iomanip.h sunt:
setbase(int b)

resetiosflags(long x)
setiosflags(long x)
setfill(int f)
setprecision(int p)
setw(int w)

setarea bazei sistemului de numeraie


corespunztoare conversiei, la valoarea
b{0,8,10,16}
tergerea biilor specificai n parametrul x,
din data membru x_flags
setarea biilor din data membru x_flags,
specificai n parametrul x
datei membru x_fill i se atribuie valoarea
parametrului f
datei membru x_precision i se atribuie
valoarea parametrului p
datei membru x_width i se atribuie valoarea
parametrului w

Tabelul 6. Manipulatorii declarai n fiierul iomanip.h


Folosind manipulatori exemplul din fiierul stream6.cpp se poate transcrie n
urmtorul mod. Fiierul stream7.cpp:
204

#include
#include
#include
#include

<iostream.h>
<iomanip.h>
<stdio.h>
<conio.h>

int main() {
clrscr();
const double x = 987.65432;
cout << "*" << setw(11) << setprecision(4)
<< setfill('0') << x << "*" << endl;
printf("*%011.4lf*\n", x);
return 0;
}

Rezultatul obinut este identic cu cel al programului anterior. Manipulatorii s-au apelat
n mod nlnuit, deci programul devine mai simplu. n urmtorul exemplu se va afia
valoarea datei membru x_flags n binar, hexazecimal, octal i zecimal. Fiierul stream8.cpp:
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
void binar_c( long x )
{
printf("x_flags: ");
for( int i = 8*sizeof(long)-1; i >= 0; i--)
{
printf("%d", (x >> i)& 1);
if ( !(i % 8) )
printf(" ");
}
printf("\n");
}
int main() {
clrscr();
binar_c( cout.flags()
cout << "Hexazecimal:
<< "Octal:
<< "Zecimal:
return 0;
}

);
" << hex << cout.flags() << endl
" << oct << cout.flags() << endl
" << dec << cout.flags() << endl;

Rezultatul obinut este urmtorul:


x_flags: 00000000 00000000 00100000 00000001
Hexazecimal: 2001
Octal:
20001
Zecimal:
8193

n acest caz nu a fost necesar includerea fiierului iomanip.h, deoarece manipulatorii


hex, oct i dec sunt declarate n fiierul iostream.h.
Dac se schimb modul de conversie cu un manipulator, ea rmne valabil n
continuare, pn la o nou modificare a conversiei.

18.2.5. Suprancrcarea operatorului de inserare

205

n paragrafele de mai sus ne-am ocupat de modul de folosire a operatorului de inserare


pentru afiarea datelor care aparin tipurilor standard. Ar fi de dorit ca operatorul << s fie
utilizabil i n cazul tipurilor abstracte de date. De exemplu n cazul clasei numerelor raionale
class Fractie{
int numarator;
int numitor;
public:
Fractie( int a, int b) { numarator = a; numitor = b;}
...
};

afiarea unei fracii ar trebui s se efectueze n forma


cout << f;

unde f este un obiect al clasei fracie.


Acest lucru se poate realiza, dac se suprancarc operatorul de inserare pentru
afiarea obiectelor de tip Fractie.
Deoarece operandul din stnga al operatorului << este un stream, suprancrcarea
operatorului se poate efectua cu o funcie prieten a clasei Fractie. Pentru o clas oareacare cu
numele Clasa, operatorul de inserare se poate suprancrca cu urmtoarea funcie prieten:
class Clasa {
...
friend ostream& operator<<(ostream&, Clasa);
...
};

Pentru clasa Fractie suprancrcarea operatorului de inserare printr-o funcie prieten se


poate face n felul urmtor. Fiierul fractie2.cpp:
#include <iostream.h>
#include <conio.h>
class Fractie{
int numarator;
int numitor;
public:
Fractie( int a, int b) { numarator = a; numitor = b;}
friend ostream& operator<<( ostream& s, Fractie f);
};
ostream& operator<<( ostream& s, Fractie f)
{
s << f.numarator << " / " << f.numitor;
return s;
}
int main() {
clrscr();
Fractie f1(3,5);
cout << f1 << endl;
return 0;
}

Dac se execut programul se obine:


3 / 5

206

Deci se afieaz numrul raional, aa cum am dorit. Apelarea operatorului de inserare


se poate aplica n mod nlnuit, deoarece funcia prieten a clasei Fractie returneaz o
referin la streamul curent.
Dei metoda de mai sus este simpl i d rezultat corect, ea are dezavantajul c
utilizarea unei funcii prieten micoreaz gradul de protecie a datelor. Deci datele membru
protejate pot fi modificate n interiorul funciei prieten, care suprancarc operatorul de
inserare. n continuare prezentm o modalitate de suprancrcare a operatorului <<, fr a
introduce o funcie prieten. Pentru o clas oarecare cu numele Clasa, acest lucru poate fi
realizat cu ajutorul unei funcii membru afisare, care se va apela de ctre funcia care
suprancarc operatorul de inserare. Deci:
class Clasa {
...
public:
ostream& afisare(ostream& s);
...
};
ostream& Clasa::afisare(ostream& s)
{
...
return s;
}
ostream& operator<<(ostream& s, Clasa c1)
{
return c1.afisare(s);
}

n cazul clasei Fractie suprancrcarea operatorului de inserare fr utilizarea unei


funcii prieten se poate efectua n modul urmtor. Fiierul fractie3.cpp:
#include <iostream.h>
#include <conio.h>
class Fractie{
int numarator;
int numitor;
public:
Fractie( int a, int b) { numarator = a; numitor = b;}
ostream& afisare( ostream& s );
};
ostream& Fractie::afisare( ostream& s )
{
s << numarator << " / " << numitor;
return s;
}
ostream& operator<<( ostream& s, Fractie f)
{
return f.afisare( s );
}
int main() {
clrscr();
Fractie f1(3,5);
cout << f1 << endl;
return 0;

207

Rezultatul obinut prin executarea acestui program este identic cu cel al fiierului
dar n acest caz nu s-a folosit funcia prieten.

fractie2.cpp,

18.3. Intrri formatate


18.3.1. Operatorul de extragere
Operaiile de intrare se realizeaz cu ajutorul operatorului >>, care n acest caz se va
numi operator de extragere.
Operandul din stnga al operatorului de extragere trebuie s fie un obiect al clasei
istream, sau a unei clase derivate din clasa istream. Operandul din dreapta va fi o expresie,
care poate aparine att unui tip standard, ct i unui tip abstract. n cazul tipurilor standard se
va apela o funcie membru a clasei istream de forma:
istream& operator>>( tip_standard& );

n cazul tipurilor abstracte programatorul poate suprancrca operatorul de extragere.


Pentru a prezenta legtura cu funcia scanf, revenim la exemplul din seciunea 15.1, n
urmtoarea form mai detaliat. Fiierul stream9.cpp:
#include <iostream.h>
#include <conio.h>
int main() {
clrscr();
int x;
cout << "x (int) = ";
cin >> x; // scanf("%d", &x);
double y;
cout << "y (double) = ";
cin >> y; // scanf("%lf", &y);
char z[20];
cout << "z (sir de caractere) = ";
cin >> z; // scanf("%s", z);
cout << "Datele citite sunt:\n"
<< "x = " << x << endl
<< "y = " << y << endl
<< "z = " << z << endl;
return 0;
}

Prin executarea programului obinem urmtorul rezultat (s-au evideniat caracterele


citite de la intrare).
x (int) = 123
y (double) = 45.67
z (sir de caractere) = exemplu
Datele citite sunt:
x = 123
y = 45.67
z = exemplu

Acelai rezultat se obine i prin executarea programului urmtor. Fiierul stream10.cpp:


#include <stdio.h>

208

#include <conio.h>
int main() {
clrscr();
int x;
printf("x (int) = ");
scanf("%d", &x);
double y;
printf("y (double) = ");
scanf("%lf", &y);
char z[20];
printf("z (sir de caractere) = ");
scanf("%s", z);
printf("Datele citite sunt:\
\nx = %d\ny = %lg\nz = %s\n", x, y, z);
return 0;
}

Rezult, c n cazurile de mai sus, nu exist nici o diferen ntre citirea datelor de la
intrarea standard folosind operatorul de extragere, respectiv cu funcia scanf. Totui n
anumite cazuri pot s apar diferene. De exemplu secvena
char c;
...
cin >> c;

nu este identic cu
char c;
...
scanf("%c", &c);

Diferena apare n cazul n care la intrare caracterul curent este un caracter alb, deci
spaiu, tab sau caracter newline (trecere la rnd nou). Prezentm n continuare un exemplu, din
care rezult, c ntr-adevr cele dou secvene de program nu sunt identice.
Vom defini o clas c_alb pentru gestionarea caracterelor, i vom suprancrca
operatorul de inserare pentru aceast clas. Suprancrcarea se va face astfel nct caracterele
albe s fie afiate n mod vizibil (' ' pentru spaii, '\t' pentru taburi i '\n' pentru caractere
newline). Caracterele diferite de cele albe vor fi afiate nemodificate. Fiierul stream11.cpp:
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class c_alb {
char c;
public:
c_alb( char c_1 ) { c = c_1; }
ostream& afisare( ostream& s);
};
ostream& c_alb::afisare( ostream& s)
{
switch ( c ) {
case '\n': s << "\'\\n\'"; break;
case '\t': s << "\'\\t\'"; break;
case ' ' : s << "\' \'"; break;
default:
s << c;

209

}
return s;
}
ostream& operator<<(ostream& s, c_alb car)
{
return car.afisare( s );
}
int main() {
clrscr();
char v;
cout << "Citire caracter (cu operatorul >>). Intrare = ";
cin >> v;
cout << "Caracterul citit (cu operatorul >>) este: "
<< c_alb(v) << endl;
printf("Citire caracter (cu functia scanf). Intrare = ");
scanf("%c", &v);
cout << "Caracterul citit (cu functia scanf) este: "
<< c_alb(v) << endl;
return 0;
}

Dup executarea programului, obinem un rezultat de forma:


Citire caracter (cu operatorul >>).
Caracterul citit (cu operatorul >>)
Citire caracter (cu functia scanf).
Caracterul citit (cu functia scanf)

Intrare =
este: q
Intrare =
este: '\t'

q
q

La intrare s-a tastat mai nti un caracter tab, dup aceea caracterul 'q', urmat de
trecerea la un rnd nou. Acelai lucru s-a repetat i n cazul citirii cu scanf. La scriere,
caracterul v s-a convertit mai nti ntr-un obiect anonim de tip c_alb, pentru a obine o afiare
corespunztoare a caracterelor albe.
Din cele de mai sus rezult c prin citirea cu operatorul de extragere s-a obinut primul
caracter diferit de caracterele albe. Dac s-a apelat funcia scanf, s-a citit caracterului curent,
indiferent dac el a fost caracter alb sau nu.
Menionm c diferena dintre cele dou modaliti de citire se poate nltura, dac se
anuleaz bitul skipws al datei membru x_flags a clasei ios (valoarea acestui bit n mod implicit
este unu).
n cazul funciei scanf, cmpul din care se face citirea ncepe cu primul caracter diferit
de caracterele albe i se termin, dac urmtorul caracter este alb, sau caracterul respectiv nu
mai corespunde formatului.
n cazul citirii unui ir de caractere, lungimea maxim a cmpului, din care se face
citirea, se determin cu funcia membru width a clasei ios. Este valabil tot ce s-a spus referitor
la funcia membru width n seciunea 18.2.3, dar n acest caz valoarea datei membru x_width
se interpreteaz ca i lungimea maxim a cmpului, din care se face citirea, n loc de lungimea
minim a cmpului n care se face afiarea. Un avantaj important al funciei membru width,
comparativ cu utilizarea funciei scanf, este c parametrul actual al funciei width poate fi
orice expresie, n timp ce aceast valoare n cazul funciei scanf poate fi numai o constant.
Acest lucru se poate folosi pentru nlturarea erorilor, care ar putea s apar din cauza citirii
unui numr mai mare de caractere, dect zona de memorie alocat. Considerm urmtorul
exemplu pentru ilustrare. Fiierul stream12.cpp:
#include <iostream.h>
#include <conio.h>

210

int main() {
clrscr();
char* t;
int max;
cout << "max = ";
cin >> max;
t = new char[max]; // cel mult max-1 caractere si
// caracterul nul
cout << "Citire cel mult " << max-1 << " caractere): ";
cin.width(max);
cin >> t;
cout << "Caracterele citite sunt: " << t;
delete [] t;
return 0;
}

Dup executarea programului obinem urmtorul rezultat (s-au evideniat datele de intrare).
max = 5
Citire cel mult 4 caractere: abcdefghij
Caracterele citite sunt: abcd

Se observ, c dei la intrare s-au tastat mai multe caractere, nu s-a citit dect numrul
de caractere, care se poate memora n spaiul alocat irului.
n cazul operaiilor de extragere se pot folosi i manipulatorii definii n paragraful
18.2.4, cu excepia manipulatorilor endl, ends, flush i setbase. Manipulatorul ws poate fi
folosit numai n operaii de extragere. Se pot folosi i celelalte funcii membru ale clasei ios,
de exemplu funcia membru setf.

18.3.2. Starea de eroare


Dac citirea nu s-a terminat cu succes, atunci streamul ajunge ntr-o stare de eroare,
despre care putem obine informaii cu ajutorul datei membru state a clasei ios.
Starea de eroare este caracterizat de biii datei membru state. Data membru state este
de tipul int, iar referirea la biii ei se poate face cu ajutorul tipului enumerare io_state, definit
n clasa ios n modul urmtor.
class ios {
public:
...
enum io_state
goodbit =
eofbit
=
failbit =

{
0x00, // operaie de intrare/ieire corect
0x01, // s-a ajuns la sfrit de fiier
0x02, // ultima operaie de intrare/ieire s-a
// terminat cu insucces
badbit
= 0x04, // operaie invalid
hardfail = 0x80 // eroare irecuperabil

};
...
};

Biii eofbit, failbit, badbit i hardfail se vor numi bii de eroare. Valorile biilor datei
membru state pot fi determinate folosind urmtoarele funcii membru ale clasei ios.
int good();

//
//

goodbit este setat, deci nici unul din biii de eroare


nu este setat
211

int eof();
int fail();
int bad();

//
//
//

bitul eofbit este setat


cel puin unul din biii failbit, badbit sau hardfail este setat
cel puin unul din biii badbit sau hardfail este setat

Aceste funcii membru returneaz valori diferite de zero, dac condiiile scrise sub
form de comentarii sunt ndeplinite. Funcia membru
int rdstate();

returneaz valoarea datei membru state. Modificarea valorilor biilor datei membru state se
poate efectua cu ajutorul funciei membru clear a clasei ios, funcie declarat n urmtorul
mod:
void clear(int = 0);

Dac se apeleaz funcia membru clear fr parametru, atunci toi biii de eroare se
vor anula, n afar de bitul hardfail, care nu se poate anula. Dac parametrul activ este
prezent, atunci data membru state va lua valoarea parametrului. Pentru a seta un anumit bit se
va folosi numele bitului precedat de numele clasei ios i operatorul de rezoluie. Dac se
folosete o construcie de forma
cin.clear( ios::badbit | cin.rdstate() );

ceilali bii vor rmne nemodificai. n acest caz s-a setat numai bitul badbit, iar ceilali au
rmas nemodificai. n urmtorul exemplu este prezentat modul de utilizare al funciei clear.
Fiierul stare1.cpp:
#include <iostream.h>
#include <conio.h>
void binar( int x )
{
for( int i = 8*sizeof(int)-1; i >= 0; i-- )
cout << ((x >> i)& 1) << (i%8 ? "": " ");
cout << endl;
}
void nume_bit_1( char* s[], int x )
{
int good_setat = 1;
for( int i = 0; i < 5; i++)
if ( (x >> i)& 1 ) {
good_setat = 0;
cout.width(16);
cout.setf(ios::left, ios::adjustfield);
cout << s[i+1];
}
if ( good_setat ) {
cout.width(16);
cout.setf(ios::left, ios::adjustfield);
cout << s[0];
}
cout << endl;
}
void afisare( char* s[], int x )
{
cout << "Data membru state: ";
binar( x );
nume_bit_1( s, x);
}

212

int main() {
char* nume_state[] = {
"goodbit",
"eofbit",
"failbit",
"badbit",
"hardfail"
};
char t[255];
clrscr();
afisare( nume_state, cin.rdstate() );
int x;
cout << "x = ";
cin >> x;
cout << "fail() = " << cin.fail() << endl;
afisare( nume_state, cin.rdstate() );
cout << "
Setarea bitului badbit\n";
cin.clear(ios::badbit | cin.rdstate() );
afisare( nume_state, cin.rdstate() );
cout << "
Anularea bitilor de eroare\n"
<< "
si vidarea zonei tampon la intrare\n";
cin.clear();
// anularea bitilor de eroare
cin.getline(t, 255);
// vidarea zonei tampon la intrare
afisare( nume_state, cin.rdstate() );
int y;
cout << "y = ";
cin >> y;
cout << "fail() = " << cin.fail() << endl;
afisare( nume_state, cin.rdstate() );
return 0;
}

Dac se execut programul, se obine urmtorul rezultat (sunt evideniate caracterele citite).
Data membru state: 00000000 00000000
goodbit
x = a
fail() = 2
Data membru state: 00000000 00000010
failbit
Setarea bitului badbit
Data membru state: 00000000 00000110
failbit
badbit
Anularea bitilor de eroare
si vidarea zonei tampon la intrare
Data membru state: 00000000 00000000
goodbit
y = 3140
fail() = 0
Data membru state: 00000000 00000000
goodbit

Funcia binar afieaz valoarea parametrului actual de tip int, n modul n care este
memorat, iar funcia nume_bit_1 afieaz numele biilor cu valoarea unu. Funcia afisare
apeleaz mai nti funcia binar, iar dup aceea funcia nume_bit_1. Deci un apel de forma
afisare( nume_state, cin.rdstate() );

afieaz mai nti data membru state n forma n care este memorat n calculator, iar dup
aceea numele tuturor biilor setai ai datei membru state.
213

Observm c n cazul citirii variabilei de tip ntreg x, la intrare nu s-a aflat un numr
ntreg (s-a tastat caracterul a). De aceea streamul cin a intrat n stare de eroare, i s-a setat
bitul failbit. Funcia fail a returnat o valoare diferit de zero (valoarea 2 corespunztoare
bitului failbit). Dup aceea s-a setat bitul badbit de ctre programator folosind funcia
membru clear. n continuare, anularea biilor de eroare s-a fcut tot cu funcia membru clear.
nainte de o nou citire trebuie vidat zona tampon corespunztoare intrrii standard.
Acest lucru se poate efectua prin citirea unui ir de caractere. Dac citirea s-ar fi fcut cu
ajutorul operatorului de extragere, atunci irul de caractere citit s-ar fi terminat la primul
caracter alb. De aceea s-a folosit funcia membru getline a clasei istream, funcie care este
declarat n urmtoarele forme:
istream& getline(signed char* z, int n, char c = '\n');

i
istream& getline(unsigned char* z, int n, char c='\n');

Funcia membru getline citete din streamul clasei istream un numr de cel mult n-1
caractere. Citirea se termin la ntlnirea caracterului c, sau dac s-au citit toate cele n-1
caractere. Menionm c nu se iau n considerare formatrile referitoare la streamul din care
se citete.
Deoarece funcia membru getline citete i caracterele albe, ea poate fi folosit pentru
vidarea zonei tampon i n cazul n care la intrare s-au tastat mai multe caractere albe.
Observm c ntr-adevr dup anularea biilor de eroare i vidarea zonei tampon, se poate citi
i valoarea altor date, n acest caz valoarea variabilei y.
Faptul c un stream se afl n stare de eroare sau nu, poate fi verificat i n urmtoarele
dou moduri:
- folosind suprancrcarea operatorului '!';
- folosind conversia streamului ntr-un pointer de tip void*.
Operatorul '!' este suprancrcat cu funcia membru
int operator !();

a clasei ios. Valoarea returnat va fi diferit de zero, dac cel puin unul din biii failbit,
badbit sau hardfail este setat, deci dac funcia membru fail() returneaz o valoare diferit de
zero.
Operatorul '!' se va folosi n urmtorul exemplu. S se defineasc o clas pentru
prelucrarea datelor de tip ntreg. Pentru clasa respectiv se va defini o funcie membru citeste,
care va realiza citirea unei date de tip ntreg. n cazul unei eventuale erori, citirea se va repeta
pn cnd se va citi o dat corect, sau se ajunge la sfrit de fiier. Pentru scrierea datei de tip
ntreg se va suprancrca operatorul de inserare. Fiierul stare2.cpp:
#include <iostream.h>
#include <string.h>
#include <conio.h>
class Intreg {
int x;

// data de tip intreg


// care se prelucreaza
char* afisare_text; // textul afisat inainte de citire
char* mesaj_eroare;
// mesajul care va apare in cazul
// unei erori
public:
Intreg( int x_1 = 0, char* scrie = "Intreg = ",
char* mesaj_eroare = "Eroare la citire.\n");
istream& citeste(istream& s);
ostream& afiseaza(ostream& s);
};

214

Intreg::Intreg( int x_1, char* scrie, char* mesaj)


{
x = x_1;
afisare_text = new char[strlen(scrie)+1];
strcpy(afisare_text, scrie);
mesaj_eroare = new char[strlen(mesaj)+1];
strcpy(mesaj_eroare, mesaj);
}
istream& Intreg::citeste(istream& s)
{
char t[255];
do {
cout << afisare_text;
s >> x;
if ( s.eof() )
return s;
if ( !s ) {
s.clear();
// anularea bitilor de eroare
s.get(t, 255);
// vidarea zonei tampon la intrare
if ( s.get() == EOF ) //citeste '\n' sau EOF
return s;
cout << mesaj_eroare;
}
else return s;
} while ( 1 );
}
ostream& Intreg::afiseaza(ostream& s)
{
s << x;
return s;
}
ostream& operator <<(ostream& s, Intreg n)
{
return n.afiseaza(s);
}

int main() {
clrscr();
Intreg i(0, "i = ");
i.citeste( cin );
if ( cin.good() )
{
cout << "Valoarea citita este: " << i;
}
return 0;
}

Prin executarea programului obinem urmtorul rezultat (s-au evideniat caracterele citite).
i = abcd
Eroare la citire.
i = efg123
Eroare la citire.
i = 45
Valoarea citita este: 45

215

Clasa Intreg are urmtoarele trei date membru: ntregul propriu zis; textul, care se va
afia nainte de citire i mesajul, care va apare n cazul unei erori nainte de a relua citirea.
Observm c n corpul funciei membru citeste s-a folosit operatorul '!' pentru a testa
dac a aprut o eroare la citire sau nu. n cazul n care nu s-a tastat un ntreg, streamul intr n
stare de eroare, deci biii de eroare trebuie anulai i trebuie vidat zona tampon. Anularea
biilor de eroare s-a fcut cu funcia membru clear.
Vidarea zonei tampon s-a realizat cu ajutorul funciei membru get a clasei istream. Ea
are mai multe forme, din care amintim urmtoarele:
int get();
istream& get(signed char*, int, char = '\n');
istream& get(unsigned char*, int, char = '\n');

Prima form a funciei membru get extrage urmtorul caracter din streamul curent. n
caz de sfrit de fiier returneaz EOF, deci valoarea -1. A doua, respectiv a treia form a
funciei membru get este asemntoare cu cele dou forme a funciei membru getline.
Diferena este c n cazul funciei get caracterul terminal, determinat prin parametrul al
treilea, nu se extrage din streamul curent, iar n cazul funciei getline se extrage i caracterul
terminal.
Vidarea zonei tampon s-ar fi putut efectua ca i n cazul exemplului din fiierul
stare1.cpp, folosind funcia membru getline n modul urmtor:
s.getline(t, 255);

Dac nlocuim secvena


s.get(t, 255);
// vidarea zonei tampon la intrare
if ( s.get() == EOF ) //citeste '\n' sau EOF
return s;

din fiierul stare1.cpp, cu apelarea funciei getline, obinem un rezultat asemntor, dar pot
s apar i diferene, de exemplu n cazul urmtor.
Rezultatul obinut prin executarea programului stare2.cpp, varianta cu funcia
membru getline este
i = abc^Z
Eroare la citire.
i =

i varianta cu funcia membru get este


i =

abc^Z

Dei ambele rezultate pot fi considerate corecte, credem c n acest caz nu mai este
necesar afiarea mesajului de eroare i a textului pentru reluarea citirii. De aceea varianta cu
funcia membru get este cea preferat.
O alt modalitate de a verifica faptul c streamul este n stare de eroare sau nu, este
conversia spre tipul void*. Rezultatul conversiei este pointerul NUL, deci valoarea zero, dac
cel puin unul din biii failbit, badbit sau hardfail este setat, adic funcia membru fail()
returneaz o valoare diferit de zero. n caz contrar se obine un pointer diferit de zero. De
obicei aceast conversie se va efectua n cazul construciilor de forma urmtoare:
if ( stream >> data )
...

216

Rezultatul expresiei stream >> data este o referin la obiectul stream, care aparine
clasei istream. n cazul de mai sus rezultatul acestei expresii se va converti n mod automat
ntr-un pointer de tip void*. Astfel se poate testa, dac un bit de eroare, diferit de bitul eofbit,
este setat sau nu.
De exemplu funcia membru citeste a clasei Intreg se poate scrie n urmtoarea form:
istream& Intreg::citeste(istream& s)
{
char t[255];
do {
cout << afisare_text;
if ( (s >> x) || (s.eof()) )
return s;
s.clear();
// anularea bitilor de eroare
s.get(t, 255);
// vidarea zonei tampon la intrare
if ( s.get() == EOF ) //citeste '\n' sau EOF
return s;
cout << mesaj_eroare;
} while ( 1 );
}

i rezultatul obinut este identic cu cel al programului din fiierul stare2.cpp.

18.3.3. Suprancrarea operatorului de extragere


Ca i n cazul operatorului de inserare, operatorul de extragere se poate suprancrca
pentru tipuri abstracte definite de programator. Pentru o clas oareacare cu numele Clasa,
operatorul de extragere se poate suprancrca cu urmtoarea funcie prieten:
class Clasa {
...
friend istream& operator>>(istream&, Clasa&);
...
};

n vederea mririi gradului de protecie a datelor se va evita utilizarea funciilor


prieten. De exemplu, se poate folosi o funcie membru citire, care se va apela de ctre funcia,
care suprancarc operatorul de extragere, n modul urmtor:

class Clasa {
...
public:
istream& citire(istream& s);
...
};
istream& Clasa::citire(istream& s)
{
...
return s;
}
istream& operator>>(istream& s, Clasa& c1)
{
return c1.citire(s);

217

De exemplu, n cazul fiierului stare2.cpp


suprancrca n modul urmtor:

operatorul de extragere se va

istream& operator >>(istream& s, Intreg& n)


{
return n.citeste(s);
}

Dac se modific fiierului stare2.cpp, astfel nct s fie suprancrcat operatorul de


extragere, atunci expresia
i.citeste( cin );

din funcia main poate fi scris n forma:


cin >> i;

Observm c funcia, care suprancarc operatorul de extragere, nu are acces la datele


protejate ale clasei Intreg. Deci s-a realizat o protecie mai bun a datelor, dect prin folosirea
unei funcii prieten.

218

19. Anex
19.1. Un memento al sintaxei limbajului C
Sintaxa limbajului C
a)
b)
c)
d)
e)

n descrierea sintaxei limbajului vom folosi urmtoarele notaii:


::= cu semnificaia "prin definiie este";
pentru a separa alternativele unei definiii sintactice;
< > pentru definirea unor metavariabile;
[ ] pentru delimitarea elementelor opionale;
.. pentru a indica elementele care se pot repeta.

Exemplu: Pentru a defini un "ntreg_zecimal" ca o succesiune de cifre semnificative vom scrie:


<ntreg_zecimal> ::= <Cifr_nenul> [<Cifr>] ..

A. Atomi lexicali
<Cuvinte_cheie> ::= autobreakcasecharconstcontinuedefaultdodouble| elseenum
externfloatforgotoifint|longregister | returnshortsignedsizeof
static | struct switchtypedefunionunsignedvoidvolatilewhile
<Nume> ::= <Liter>[<Liter><Cifr>] ..
<Liter> ::= abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRS TUVWXYZ_
<Cifr> ::= 0123456789
<Constant> ::= <Constant_ntreag><Constant_real>
<Constant_enumerare><Constant_caracter>
<Constant_ntreag> ::= <Numr_ntreg>[<Sufix_ntreg>
<Numr_ntreg> ::= <ntreg_zecimal><ntreg_hexazecimal><ntreg_octal>
<ntreg_zecimal> ::= <Cifr_nenul>[<Cifr>] ..
<Cifr_nenul> ::= 123456789
<ntreg_hexazecimal> ::= 0x<Cifr_hexa> ..
<Cifr_hexa> ::= <Cifr>abcdefABCDEF
<ntreg_octal> ::= 0[<Cifr_octal>] ..
<Cifr_octal> ::= 01234567
<Sufix_ntreg> ::= <Sufix_unsigned>[<Sufix_long>]<Sufix_long>[<Sufix_unsigned>]
219

<Sufix_unsigned> ::= Uu
<Sufix_long> ::= Ll
<Constant_real> ::= <Constant_fract>[<Exponent>][<Sufix_real>]
<Cifr> .. <Exponent>[<Sufix_real>]
<Constant_fract> ::= [<Cifr>] .. . <Cifr>..<Cifr>.. .
<Exponent> ::= Ee[+-]<Cifr> ..
<Sufix_real> ::= FfLl
<Constant_enumerare> ::= <Nume>
<Constant_caracter> ::= '<Caracter_C> ..'L'<Caracter_C>'
<Caracter_C> ::= <Orice_caracter_tipribil_cu excepia: ' i \>
<Secven_escape>
<Secven_escape> ::= \"\'\?\\\a\b\f\n\r\t\v
\<Cifr_octal>[<Cifr_octal>[<Cifr_octal>]]
\x<Cifr_hexa> ..
<ir_de_caractere> ::= "[<Caracter_S> ..]"
<Caracter_S> ::= <Orice_caracter_tipribil_cu excepia: ' i \><Secvena_escape>
<Operatori_i_semne_de_punctuaie >::=

+-*/%^&~!=->+=-=*=
/=%=^=&=!=<<>><<=>>== =<=
>=<>&&!!++--,()[]{};?:...

B. Declaraii
<Unitate_de_compilare> ::= <O_declaraie> ..
<O_declaraie> ::= <Def_funcie><Declaraie>;
<Declaraie> ::= [<Specificatori>][<List_declaratori>]
<Specificatori> ::= <Specificator> ..
<Specificator> ::= <Clas_memorare><Tip>typedef
<Clas_memorare> ::= autoregisterstaticextern
<Tip> ::= <Tip_simplu><Nume_typedef><Calificator>
<Descriere_tip_enumerare><Descriere_tip_neomogen>
220

<Tip_simplu> ::= charshortintlongsignedunsignedfloatdouble void


<Nume_typedef> ::= <Nume>
<Descriere_tip_enumerare> ::= enum<Nume>enum[<Nume>]{<Lista_enum>}
<Lista_enum> ::= <Enumerator>[,<Enumerator>] ..
<Enumerator> ::= <Nume>[=<Expr_constant_>{]
<Descriere_tip_neomogen> ::= <Tip_neomogen><Nume>
<Tip_neomogen>[<Nume>]{<List__cmpuri>}
<Tip_neomogen> ::= structunion
<List_cmpuri> ::= <Decl_cmpuri>;[<Decl_cmpuri>;] ..
<Decl_cmpuri> ::= [<Specificatori>][<Cmp>[,<Cmp>] ..]
<Cmp> ::= <Declarator>[<Nume>]:<Expr_constant>
<List_declaratori> ::= <Decl_init>[,<Decl_init>] ..
<Decl_init> ::= <Declarator>[<Iniializare>]
<Declarator> ::= [<Indirect>..]<Denumire>[<Decl_F_T>]
<Indirect> ::= *[<Calificator> ..]
<Denumire> ::= <Nume>(<Declarator>)
<Decl_F_T> ::= ([<L_declar_par>])<Decl_tablou> ..
<Decl_tablou> ::= [][<Expr_constant>]
<L_declar_par> ::= <Decl_par>[,<Decl_par>] .. [, ..]
<Decl_par> ::= <Tip>..<Declarator><Decl_tip>
<Decl_tip> ::= <Tip>..[<Indirect>..][(<Decl_tip>)][<Decl_F_T>]
<Iniializare> ::= =<Expr> ={<List_init>[,]}
<List_init>::=<Expr>[,<List_init> ..{<List_init>}[,<List_init>] ..
<Def_funcie> ::= [<Specif_funcie> ..]<Declarator>[<Declaraie>;] ..<Bloc>
<Specif_funcie> ::=

externstatic<Tip>
221

C. Expresii
<Expr> ::= <Expresie>[,<Expresie>] ..
<Expresie> ::= <Expr_condiional>|<Expr_unar><Oper_atribuire><Expresie>
<Oper_atribuire> ::= +*=/=%=+=-=<<=>>=&=^=!=
<Expr_condiional> ::= <Expr_SAU_logic>
<Expr_SAU_logic> ? <Expr> : <Expr_condiional>
<Expr_SAU_logic> ::= <Expr_I_logic><Expr_SAU_logic> !! <Expr_I_logic>
<Expr_I_logic> ::= <Expr_SAU><Expr_I_logic> && <Expr_SAU>
<Expr_SAU> ::= <Expr_SAU_exclusiv><Expr_SAU><Expr_SAU_exclusiv>
<Expr_SAU_exclusiv> ::= <Expr_I><Expr_SAU_exclusiv> ^ <Expr_I>
<Expr_I> ::= <Expr_egalitate><Expr_I> & <Expr_egalitate>
<Expr_egalitate> ::= <Expr_relaional><Expr_egalitate>==Expr_relaional>
<Expr_egalitate> != <Expr_relaional>
<Expr_relaional> ::= <Expr_deplasare> <Expr_relaional> < <Expr_deplasare>
<Expr_relaional> > <Expr_deplasare>
<Expr_relaional> <= <Expr_deplasare>
<Expr_relaional> >= <Expr_deplasare>
<Expr_deplasare> ::= <Expr_aditiv> <Expr_deplasare> << <Expr_aditiv>
<Expr_deplasare> >> <Expr_aditiv>
<Expr_aditiv> ::= <Expr_multiplicativ><Expr_aditiv> + <Expr_multiplicativ>
<Expr_aditiv> - <Expr_multiplicativ>

<Expr_multiplicativ>::=

<Expr_multiplicativ> * <Expr_prefixat>
<Expr_multiplicativ> / <Expr_prefixat>
<Expr_multiplicativ> % <Expr_prefixat>

<Expr_prefixat> ::= <Expr_unar>(<Decl_tip>)<Expr_prefixat>

<Expr_unar> ::= <Expr_postfixat><Op_unar><Expr_prefixat>


++<Expr_unar> --<Expr_unar>sizeof<Expr_unar>
sizeof(<Decl_tip>)
222

<Op_unar> ::= &*+-~!


<Expr_postfixat> ::= <Termen><Expr_postfixat>[<Expr>]
<Expr_postfixat>(<List_Expresii>) <Expr_postfixat> . <Nume>
<Expr_postfixat> -> <Nume><Expr_postfixat> ++ <Expr_postfixat>-<Termen> ::= <Nume><Constant><ir_de_caractere>(<Expr>)
<List_Expresii> ::= [<Expr>] ..
<Expr_constant> ::= <Expr_condiional>

D. Instruciuni
<Instr>::= <Instr_etichetat><Instr_compus> <Instr_expresie>Instr_de_selecie>
<Instr_de_ciclare><Instr_de_salt>
<Instr_etichetat> ::= <Nume>:<Instr>case <Expr_constant { :<Instr>
default : <Instr>
<Instr_compus> ::= <Bloc>
<Bloc> ::= {[<Declaraie>;] .. [<Instr>] ..}
<Instr_expresie> ::= [<Expr>];

<Instr_de_selecie> ::= if (<Expr>) <Instr>if (<Expr>) <Instr> else <Instr>


switch (<Expr>) <Instr>
<Instr_de_ciclare> ::= while (<Expr>)<Instr>;
do <Instr> while (<Expr>);
for ( [<Expr>];[<Expr>];[<Expr>] ) [<Instr>];
<Instr_de_salt> ::= goto <Nume>;continue;break;return [<Expr>];

223

19.2. Lista programelor C++


Prezentm lista programelor mpreun cu numrul seciunii n care se afl programul
respectiv.
Nr.
crt.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.

Nume fiier

Seciune

caracter.cpp
conv1.cpp
rezol.cpp
refer1.cpp
refer2.cpp
valoare1.cpp
valoare2.cpp
refer3.cpp
union1.cpp
init1.cpp
init2.cpp
refer4.cpp
sup_fun1.cpp
sup_fun2.cpp
macro1.cpp
inline1.cpp
vector1.cpp
vector2.cpp
vector3.cpp
vector4.cpp
pereche1.cpp
pereche2.cpp
pereche3.cpp
destruct.cpp
fractie1.cpp
vector5.cpp
vector6.cpp
vector7.cpp
vector8.cpp

15.1.
15.1.
15.2.1.
15.2.2.
15.2.2.
15.2.3.
15.2.3.
15.2.3.
15.3.
15.4.1.
15.4.1.
15.4.2.
15.4.3.
15.4.3.
15.4.4.
15.4.4.
16.1.
16.1.
16.4.
16.4.
16.5.1.
16.5.1.
16.5.2
16.6.
16.7.2.
16.7.2.
16.7.2.
16.7.2.
16.7.2.

Nr.
crt.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.

224

Nume fiier

Seciune

increm1.cpp
increm2.cpp
conv2.cpp
lung1.cpp
conv3.cpp
lung2.cpp
virtual1.cpp
fractie2.cpp
animal1.cpp
animal2.cpp
virtual2.cpp
abstract.cpp
stream1.cpp
stream2.cpp
stream3.cpp
stream4.cpp
stream5.cpp
stream6.cpp
stream7.cpp
stream8.cpp
fractie2.cpp
fractie3.cpp
stream9.cpp
stream10.cpp
stream11.cpp
stream12.cpp
stare1.cpp
stare2.cpp

16.7.3.
16.7.3.
16.8.2.
16.8.2.
16.8.3.
16.8.3.
17.1.
17.3.
17.4.
17.4.
17.4.
17.5.
18.2.1.
18.2.1.
18.2.1.
18.2.2.
18.2.3.
18.2.3.
18.2.4.
18.2.4.
18.2.5.
18.2.5.
18.3.1.
18.3.1.
18.3.1.
18.3.1.
18.3.2.
18.3.2.

20. Bibliografie:
[1]

***: Borland C++. Getting Started. Borland International, 1991.

[2]

***: Borland C++. User's Guide. Borland International, 1991.

[3]

***: Borland C++. Programmer's Guide. Borland International, 1991.

[4]

***: Borland C++. Library Reference. Borland International, 1991.

[5]

Benk T., Mor G.: Object Windows, ComputerBooks, Budapest, 1993.

[6]

Benk T., Poppe A., Benk L.: Bevezets a Borland C++ programozsba,
ComputerBooks, Budapest, 1991.

[7]

Decker R., Hirschfield S.: The Object Concept. An Introduction to Computer


Programming Using the C++, PWS Publishing Company, 1995.

[8]

Horowitz E., Sahni S., Mehta D.: Fundamentals od Data Structures in C++,
Computer Science Press, New York, 1995.

[9]

Horstmann C.S.: Practical Object-Oriented Development in C++ and Java,


John Wiley and Sons, 1997.

[10]

Brian W.Kerninghan, Dennis M. Ritchie, The C Programming Language, INC.


Englewood Cliffs, New Jersey, 1978.

[11]

G. Moldovan, M. Lupea, V.Cioban, Probleme pentru programare n limbajul C,


Litografia Universitii Babe-Bolyai, Cluj-Napoca, 1995.

[12]

Negrescu L.: Limbajele C i C++ pentru nceptori. Limbajul C++, Editura


Microinformatica, 1994.

[13]

Stroustrup B.: The C++ Programming Language, Second Edition, AT&T Bell
Telephone Laboratories, 1991.

225