CURS PROGRAMARE MODULARĂ
Transcript of CURS PROGRAMARE MODULARĂ
1
UNIVERSITATEA DIN PETROȘANI
CURS
PROGRAMARE MODULARĂ
CONF DR ING STOICUȚA OLIMPIU - COSTINEL
2019
2
Capitolul 1
11 Noțiunea de subprogram
Subprogramul este o secvenţă de instrucţiuni care rezolvă o anumită sarcină şi care poate fi
descrisă separat de blocul rădăcină şi lansată icircn execuţie din cadrul unui bloc ori de cacircte ori este
nevoie Icircn limbajul C++ subprogramele se mai numesc şi funcţii
Un subprogram este un ansamblu ce poate conţine tipuri de date variabile şi instrucţiuni
destinate unei anumite prelucrări (calcule citiri scrieri)
Subprogramul poate fi executat doar dacă este apelat de către un program sau un alt subprogram
Avantajele utilizării subprogramelor icircn cadrul unor aplicații sunt
permite economisirea de memorie şi de timp alocat Un grup de instrucţiuni care trebuie să se
execute de mai multe ori icircntr-o aplicaţie (chiar cu date de intrare şi de ieşire diferite) se va scrie o
singură dată icircntr-un subprogram şi se va executa prin apelarea subprogramului ori de cacircte ori este
nevoie
permite lucrul icircn echipă la rezolvarea unor sarcini complexe pentru aplicaţiile mari Fiecare
programator va putea să scrie mai multe subprograme independent de ceilalţi programatori din
echipă Pentru a realiza subprogramul este suficient să i se precizeze programatorului
specificaţiile subprogramului datele de intrare datele de ieşire şi problema pe care trebuie să o
rezolve
depanarea şi actualizarea aplicaţiei se fac mai uşor După implementare şi intrarea icircn
exploatare curentă o aplicaţie poate necesita modificări ca urmare a schimbării unor cerinţe
Este mult mai simplu să se gacircndească modificarea la nivelul unui subprogram decacirct la nivelul
icircntregii aplicții
creşte portabilitatea programelor Subprogramele sunt concepute independent de restul
aplicaţiei şi unele dintre ele pot fi preluate fără un efort prea mare şi icircn alte aplicaţii icircn care
trebuie să fie rezolvate sarcini similare
O parte din subprogram se contruieşte ca subprogram dacă un algoritm cuprinde icircn mai multe
locuri aceeaşi secvenţă de operaţii executabilă pentru aceleaşi date sau pentru date diferite Icircn loc ca
subprogramul să cuprindă icircn acelaşi loc acelaşi grup de instrucţiuni concepacircnd grupul de intrucţiuni ca
subprogram el va apărea icircn program o singură dată şi se va activa de mai multe ori Partea respectivă de
program rezolvă o subproblemă din cele icircn care se descompune problema complexă
De exemplu considerăm următoarea secvenţă de program care memorează icircn variabila max
valoarea maximă dintre valorile variabilelor icircntregi a b și c
Din cadrul programului de mai sus se observă că cele două instrucţiuni condiţionale
(instrucțiunile if) realizează acelaşi lucru determină valoarea maximă dintre două numere icircntregi
Procesul de calcul este acelaşi diferă doar valorile numerelor icircntregi pentru care se execută cele două
instrucțiuni
Pe de altă parte determinarea maximului dintre două numere icircntregi se poate modela matematic
ca o funcţie
3
Icircn aceste condiții secțiunea de program ce calculează maximul dintre 3 numere icircntregi se poate
scrie astfel
12 Tipuri de subprograme
Clasificarea subprogramelor
Subprograme standard (subprograme de sistem) ndash utilizarea lor presupune includerea fişierului
ce conţine prototipul dorit şi apelarea subprogramului Aceste subprograme sunt predefinite icircn
biblioteci ale limbajului de programare (Exemplu include ltcmathgt include un set de funcții
utilizate icircn realizarea operațiilor și transformărilor matematice obijnuite funcții trigonometrice
funcții hiperbolice funcții exponențiale și logaritmice funcțiile putere pow sqrt funcții de
eroare funcții de rotunjire și rest funcții de manipulare cu punct flotant funcții de diferență ndash
minim ndashmaxim)
Subprograme definite de utilizator ndash sunt descrise de progamator pentru rezolvarea unor cerinţe
specifice Utilizarea lor presupune precizarea prototipului definirea şi apelul (a se vedea
exemplu din paragraful anterior)
Subprograme apelate ca instrucţiuni procedurale ndash returnează una mai multe sau nici o valoare
prin intermediul parametrilor Exemple de apeluri de funcţii procedurale implementate icircn
limbajul C++
o clrscr ( ) Apelul unei funcţii procedurale fără parametri (CLeaR SCReen) care şterge
informaţiile afişate pe ecranul calculatorului
o randomize ( ) Apelul unei funcţii procedurale fără parametri care iniţializează
generatorul de numere aleatoare
o swab(s1s2n) Apelul unei funcţii procedurale cu trei parametri copiază n caractere (n
fiind un număr par) din şirul de caractere s1 la icircnceputul şirului de caractere s2
inversacircnd caracterele adiacente Parametrul s2 este un parametru de intrare-ieşire iar
parametrii s1 şi n sunt parametri de intrare
o gotoxy(xy) Apelul unei funcţii procedurale cu doi parametri icircn modul de lucru text
mută cursorul icircn fereastra de text curentă icircn poziţia precizată prin coordonatele x şi y
Parametrii x şi y sunt parametri de intrare
Subprograme apelate ca operanzi ndash returnează un rezultat chiar prin numele său şi eventual alte
rezultate prin parametri Subprogramul se activează icircn interiorul unei expresii unde este folosit
ca operand Exemple de apeluri de funcţii operand implementate icircn limbajul C++
o x=35 e=5+floor(x) La calculul expresiei care se atribuie variabilei e se activează
funcţia floor() prin care se determină cel mai mare icircntreg mai mic decacirct valoarea
parametrului Funcţia are un singur parametru ndash x care este parametru de intrare şi are
valoarea 35 Rezultatul (data de ieşire) este furnizat prin numele funcţiei şi are valoarea
3 Aşadar variabilei de memorie e i se va atribui valoarea 8 (5+3)
o for (i=0ilt=sqrt(n)i++) La calculul expresiei ce se atribuie valorii finale a contorului
structurii repetitive for se activează funcţia sqrt(n) care furnizează radicalul de ordinul 2
4
din valoarea parametrului Funcţia are un singur parametru ndash n care este parametru de
intrare
o x = sqrt (pow(32)+ pow(42)) La calculul expresiei care se atribuie variabilei x se
activează de două ori funcţia pow() o dată pentru a calcula 3 la puterea 2 returnacircnd
valoarea 9 şi o dată pentru a calcula 4 la puterea 2 returnacircnd valoarea 16 Funcţia pow()
are doi parametri de intrare primul este baza iar al doilea este exponentul Rezultatul
este furnizat prin numele funcţiei Rezultatul obţinut prin evaluarea expresiei 9+16 = 25
va fi parametru de intrare pentru funcţia sqrt()care extrage radicalul de ordinul 2 din
valoarea lui Rezultatul funcţiei sqrt() ndash data de ieşire ndash este furnizat prin numele funcţiei
şi are valoarea 5 El este atribuit variabilei de memorie x Aşadar variabila de memorie x
va avea valoarea 5
13 Structura subprogramelor
Un subprogram (funcţie) are o definiţie şi atacirctea apeluri cacircte sunt necesare
Definiţia unui subprogram reprezintă de fapt descrierea unui proces de calcul cu ajutorul
variabilelor virtuale (parametri formali) iar apelul unui subprogram icircnseamnă execuţia procesului de
calcul pentru cazuri concrete (cu ajutorul parametrilor reali efectivi actuali)
Parametri formali apar icircn antetul subprogramului şi sunt utilizaţi de subprogram pentru
descrierea abstractă a unui proces de calcul
Parametri actuali apar icircn instrucţiunea de apelare a uni subprogram şi sunt folosiţi la execuţia
unui proces de calcul pentru valori concrete
Parametrii formali nu sunt variabile O variabilă este caracterizată de nume tip şi adresă
Legarea unui parametru formal la o adresă se realizează icircn timpul execuţiei instrucţiunii de apelare a
subprogramului
Icircn limbajul C++ există trei elemente implicate icircn utilizarea unui subprogram
definiţia subprogramului ndash conţine numele subprogramului tipul argumentelor şi al valorilor
returnate şi specifică ceea ce trebuie să realizeze subprogramul
prototipul subprogramului ndash comunică compilatorului informaţii despre subprogram (modul icircn
care se poate apela subprogramul)
apelul subprogramului ndash execută subprogramul
Subprogramul se poate identifica printr-un nume care este folosit atacirct pentru definiţia
subprogramului cacirct şi prin prototip şi activarea lui (apelarea lui) Apelarea subprogramului icircn cadrul
unui bloc icircnseamnă activarea subprogramului adică lansarea lui icircn execuţie Subprogramul poate fi
apelat ori de cacircte ori este nevoie (nu există restricţii pentru numărul de apeluri) Modulul apelant se
execută secvenţial (instrucţiune cu instrucţiune) La apelarea subprogramului este părăsit blocul
modulului apelant şi se trece la executarea instrucţiunilor din subprogram După ce se termină
executarea acestor instrucţiuni se revine la blocul apelant şi se continuă execuţia cu instrucţiunea care
urmează apelului
5
Definiţia unui subprogram este formată din antetul şi corpul subprogramului
a Antetul subprogramului Este o linie de recunoaştere a subprogramului icircn care i se atribuie
un nume El specifică icircnceputul subprogramului
Corpul subprogramului La fel ca orice bloc C++ este icircncapsulat icircntr-o instrucţiune compusă
delimitată de caracterele şi este format din două părţi
Partea declarativă Conţine definiţii de elemente folosite numai icircn interiorul subprogramului
tipuri de date constante şi variabile de memorie Nu se pot defini şi alte subprograme (nu
este valabilă tehnica de imbricare a subprogramelor existentă icircn alte limbaje de programare)
Partea executabilă Conţine instrucţiunile prin care sunt descrise acţiunile realizate de
subprogram
Subprogramul trebuie să aibă un antet prin care se precizează interfaţa dintre programul apelant
şi subprogram El conţine trei categorii de informaţii
Tipul rezultatului Pentru funcţiile operand se precizează tipul rezultatului furnizat de
subprogram prin chiar numele său Pentru funcţiile procedurale tipul rezultatului este void
(nu icircntoarce nici un rezultat prin numele său rezultatele vor fi icircntoarse prin parametrii
subprogramului) Dacă nu se precizează tipul rezultatului compilatorul va considera că
acesta este implicit de tip int
Numele subprogramului Este un identificator unic care se atribuie subprogramului
Numele trebuie să respecte aceleaşi reguli ca orice identificator C++ Parametrii folosiţi
pentru comunicare Pentru fiecare parametru se precizează numele şi tipul
Parametrii folosiţi pentru comunicare Pentru fiecare parametru se precizează numele şi
tipul
Antetul unui subprogram are următoarea formă
unde lista de parametrii are următoarea formă
Exemple aferente limbajului C++
float alfa (int a int b float c)
Acesta este antetul unei funcţii operand care furnizează un rezultat de tip float Numele funcţiei
este alfa iar parametrii folosiţi pentru comunicare sunt a şi b de tip int şi c de tip float
void beta (int a float b float c char d)
Acesta este antetul unei funcţii procedurale Numele funcţiei este beta iar parametrii folosiţi
pentru comunicare sunt a de tip int b şi c de tip float şi d de tip char
void gama ( )
6
Acesta este antetul unei funcţii procedurale Numele funcţiei este gama şi nu foloseşte parametri
pentru comunicare
Observația 1 Separarea parametrilor icircn listă se face prin caracterul virgulă ()
Observația 2 Tipul parametrilor poate fi
orice tip standard al sistemului folosit pentru date elementare
o icircntreg (int unsigned long) real (double float long double) sau caracter (char sau
unsigned char)
o tipul pointer sau orice tip de structură de date (vector matrice şir de caractere sau
icircnregistrare)
orice tip definit de utilizator icircnainte de a defini subprogramul
Observația 3 Numele subprogramului poate fi folosit icircn trei locuri distincte
icircn prototipul subprogramului unde are un rol declarativ
icircn antetul subprogramului unde are un rol de definiţie dar şi declarativ
icircn apelul subprogramului unde are rol de activare
b Corpul subprogramului este un bloc care conţine atacirct instrucţiuni declarative cacirct şi
instrucţiuni imperative Variabilele de memorie declarate icircn corpul subprogramului se
numesc variabile locale Icircn cazul unei funcţii operand ultima instrucţiune din corpul
subprogramului trebuie să fie instrucţiunea return care are sintaxa
Valoarea obţinută prin evaluarea expresiei ltexpresiegt va fi atribuită funcţiei operand (va fi
valoarea returnată prin numele funcţiei) Rezultatul expresiei trebuie să fie de acelaşi tip cu tipul funcţiei
sau de un tip care poate fi convertit implicit icircn tipul funcţiei
Cacircnd compilatorul C++ icircntacirclneşte icircntr-un subprogram instrucţiunea return termină execuţia
subprogramului şi redă controlul modulului apelant Prin urmare dacă veţi scrie icircn subprogram după
instrucţiunea return alte instrucţiuni ele nu vor fi executate
Prototipul subprogramului este o linie de program aflată icircnaintea modulului care apelează
subprogramul prin care se comunică compilatorului informaţii despre subprogram (se declară
subprogramul)
Prin declararea programului compilatorul primeşte informaţii despre modul icircn care se poate
apela subprogramul şi poate face verificări la apelurile de subprogram icircn ceea ce priveşte tipul
parametrilor folosiţi pentru comunicare şi a modului icircn care poate face conversia acestor parametri
Un subprogram pentru a putea fi folosit trebuie declarat Pentru declararea lui se foloseşte
prototipul El conţine trei categorii de informaţii la fel ca şi antetul subprogramului tipul rezultatului
numele subprogramului şi tipul parametrilor folosiţi pentru comunicare Pentru fiecare parametru din
antetul subprogramului se poate preciza numai tipul nu şi numele lui
Prototipul unui subprogram este de forma
unde lista tipului parametrilor are următoarea forma
7
Observația 4 Separarea tipurilor de parametri icircn listă se face prin caracterul virgulă () Lista trebuie să
conţină atacirctea tipuri de parametri cacircţi parametri au fost definiţi icircn antetul subprogramului Icircn listă se
precizează tipul de dată la care se referă icircn aceeaşi ordine icircn care au fost scrişi parametrii la definirea lor
icircn antet
Observația 5 Spre deosebire de antetul subprogramului prototipul se termină cu caracterul
Pentru funcţiile al căror antet a fost precizat anterior (a se vedea exemplele anterior prezentate)
prototipurile vor fi
float alfa (int int float)
void beta (int float float char)
void gama ()
Subprogramul trebuie să fie cunoscut atunci cacircnd se cere prin apel activarea lui
dacă subprogramul este standard trebuie inclus fişierul care conţine prototipul subprogramului
icircn fişierul sursă
dacă subprogramul este utilizator trebuie declarat fie prin folosirea prototipului fie prin
definirea lui icircnaintea apelului
Icircn funcţie de modul icircn care a fost definit subprogramul se activează fie printr-o instrucţiune
procedurală fie ca operand icircntr-o expresie
Pentru funcţiile al căror antet a fost precizat anterior activarea se poate face astfel
exemplul 1
int xy float zw
w = alfa (xyz)
exemplul 2
int x float yz char w
beta (x y z w)
exemplul 3
gama ()
Orice subprogram trebuie declarat şi definit Declararea unui subprogram este necesară pentru
ca el să fie cunoscut de subprogramele care icircl apelează Declararea lui poate fi făcută fie prin prototip
fie prin definiţia lui (antetul icircmpreună cu corpul subprogramului) Pentru a declara subprogramul fie
scrieţi prototipul icircnaintea subprogramelor care icircl apelează putacircnd scrie apoi definiţia lui oriunde icircn
program fie definiţi subprogramul icircnaintea subprogramului care icircl apelează Icircn cele ce urmează se
prezintă un exemplu
Aşadar
Prototipul subprogramului declară subprogramul
Apelul subprogramului execută subprogramul
8
Antetul subprogramului specifică numele subprogramului şi tipul argumentelor şi al valorilor
returnate iar corpul subprogramului icircl defineşte adică specifică ceea ce trebuie să realizeze
subprogramul
Icircn concluzie forma generală a unui subprogram este prezentată mai jos
unde
tip_returnat Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi int char
char long float void etc Icircn cazul icircn care tipul rezultatului este diferit de void corpul funcţiei
trebuie să conţină cel puţin o instrucţiune return Icircnstrucţiunea return va specifica valoarea
calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat
nume_funcție Reprezintă numele dat funcţiei de către cel ce o defineşte pentru a o putea apela
lista parametrilor formali Reprezintă o listă de declaraţii de variabile separate prin virgulă
Această listă poate să fie şi vidă
instrucțiune Este o instrucţiune vidă sau o instrucţiune simplă sau o instrucţiune compusă
Icircn altă odine de idei construirea subprogramelor se face prin
Antet ndash conţine date despre tipul rezultatului nume şi parametrii efectivi Dacă funcţia nu
returnează nimic tipul este void()
Corp ndash este un bloc de instrucţiuni declarative şi imperative (de execuţie) ce definesc modul de
acţiune al subprogramului
Prototip ndash pentru a putea fi apelată funcţia trebuie declarată Conţine date despre tipul
rezultatului nume şi parametrii efectivi
Apel ndash este modul prin care subprogramul e pus icircn execuţie Apelul poate fi făcut ori de cacircte ori
este nevoie
Parametrii ndash datele care circulă icircntre modulul appelant şi apelat se introduc icircntre paranteze
după numele subprogramului
Modul apelant ndash blocul ce conţine apelul subprogramului
Modul apelat ndash blocul ce conţine funcţia (subprogramul apelat)
Exemplu
9
Icircn memoria internă fiecărui subprogram i se alocă o zonă de memorie icircn care este icircncărcat codul
executabil
La apelarea unui subprogram compilatorul icirci predă controlul adică icircncep să se execute
instrucţiunile subprogramului pacircnă la icircntacirclnirea unei instrucţiuni return sau pacircnă la sfacircrşitul blocului
care formează corpul subprogramului după care compilatorul redă controlul modulului apelant adică va
continua execuţia cu instrucţiunea care urmează icircn modulul apelant imediat după instrucţiunea care a
apelat subprogramul Acest mecanism de transfer al controlului se poate realiza deoarece icircntr-o zonă de
memorie internă numită stiva sistemului (stack) se păstrează temporar informaţii despre subprogramul
apelant Aceste informaţii sunt introduse icircn stivă atunci cacircnd este apelat subprogramul Ele formează
instanţa subprogramului
Etapele executate la apelarea subprogramului sunt
1 Se icircntrerupe execuţia modulului apelant
2 Se pregăteşte stiva sistemului astfel
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
2
Capitolul 1
11 Noțiunea de subprogram
Subprogramul este o secvenţă de instrucţiuni care rezolvă o anumită sarcină şi care poate fi
descrisă separat de blocul rădăcină şi lansată icircn execuţie din cadrul unui bloc ori de cacircte ori este
nevoie Icircn limbajul C++ subprogramele se mai numesc şi funcţii
Un subprogram este un ansamblu ce poate conţine tipuri de date variabile şi instrucţiuni
destinate unei anumite prelucrări (calcule citiri scrieri)
Subprogramul poate fi executat doar dacă este apelat de către un program sau un alt subprogram
Avantajele utilizării subprogramelor icircn cadrul unor aplicații sunt
permite economisirea de memorie şi de timp alocat Un grup de instrucţiuni care trebuie să se
execute de mai multe ori icircntr-o aplicaţie (chiar cu date de intrare şi de ieşire diferite) se va scrie o
singură dată icircntr-un subprogram şi se va executa prin apelarea subprogramului ori de cacircte ori este
nevoie
permite lucrul icircn echipă la rezolvarea unor sarcini complexe pentru aplicaţiile mari Fiecare
programator va putea să scrie mai multe subprograme independent de ceilalţi programatori din
echipă Pentru a realiza subprogramul este suficient să i se precizeze programatorului
specificaţiile subprogramului datele de intrare datele de ieşire şi problema pe care trebuie să o
rezolve
depanarea şi actualizarea aplicaţiei se fac mai uşor După implementare şi intrarea icircn
exploatare curentă o aplicaţie poate necesita modificări ca urmare a schimbării unor cerinţe
Este mult mai simplu să se gacircndească modificarea la nivelul unui subprogram decacirct la nivelul
icircntregii aplicții
creşte portabilitatea programelor Subprogramele sunt concepute independent de restul
aplicaţiei şi unele dintre ele pot fi preluate fără un efort prea mare şi icircn alte aplicaţii icircn care
trebuie să fie rezolvate sarcini similare
O parte din subprogram se contruieşte ca subprogram dacă un algoritm cuprinde icircn mai multe
locuri aceeaşi secvenţă de operaţii executabilă pentru aceleaşi date sau pentru date diferite Icircn loc ca
subprogramul să cuprindă icircn acelaşi loc acelaşi grup de instrucţiuni concepacircnd grupul de intrucţiuni ca
subprogram el va apărea icircn program o singură dată şi se va activa de mai multe ori Partea respectivă de
program rezolvă o subproblemă din cele icircn care se descompune problema complexă
De exemplu considerăm următoarea secvenţă de program care memorează icircn variabila max
valoarea maximă dintre valorile variabilelor icircntregi a b și c
Din cadrul programului de mai sus se observă că cele două instrucţiuni condiţionale
(instrucțiunile if) realizează acelaşi lucru determină valoarea maximă dintre două numere icircntregi
Procesul de calcul este acelaşi diferă doar valorile numerelor icircntregi pentru care se execută cele două
instrucțiuni
Pe de altă parte determinarea maximului dintre două numere icircntregi se poate modela matematic
ca o funcţie
3
Icircn aceste condiții secțiunea de program ce calculează maximul dintre 3 numere icircntregi se poate
scrie astfel
12 Tipuri de subprograme
Clasificarea subprogramelor
Subprograme standard (subprograme de sistem) ndash utilizarea lor presupune includerea fişierului
ce conţine prototipul dorit şi apelarea subprogramului Aceste subprograme sunt predefinite icircn
biblioteci ale limbajului de programare (Exemplu include ltcmathgt include un set de funcții
utilizate icircn realizarea operațiilor și transformărilor matematice obijnuite funcții trigonometrice
funcții hiperbolice funcții exponențiale și logaritmice funcțiile putere pow sqrt funcții de
eroare funcții de rotunjire și rest funcții de manipulare cu punct flotant funcții de diferență ndash
minim ndashmaxim)
Subprograme definite de utilizator ndash sunt descrise de progamator pentru rezolvarea unor cerinţe
specifice Utilizarea lor presupune precizarea prototipului definirea şi apelul (a se vedea
exemplu din paragraful anterior)
Subprograme apelate ca instrucţiuni procedurale ndash returnează una mai multe sau nici o valoare
prin intermediul parametrilor Exemple de apeluri de funcţii procedurale implementate icircn
limbajul C++
o clrscr ( ) Apelul unei funcţii procedurale fără parametri (CLeaR SCReen) care şterge
informaţiile afişate pe ecranul calculatorului
o randomize ( ) Apelul unei funcţii procedurale fără parametri care iniţializează
generatorul de numere aleatoare
o swab(s1s2n) Apelul unei funcţii procedurale cu trei parametri copiază n caractere (n
fiind un număr par) din şirul de caractere s1 la icircnceputul şirului de caractere s2
inversacircnd caracterele adiacente Parametrul s2 este un parametru de intrare-ieşire iar
parametrii s1 şi n sunt parametri de intrare
o gotoxy(xy) Apelul unei funcţii procedurale cu doi parametri icircn modul de lucru text
mută cursorul icircn fereastra de text curentă icircn poziţia precizată prin coordonatele x şi y
Parametrii x şi y sunt parametri de intrare
Subprograme apelate ca operanzi ndash returnează un rezultat chiar prin numele său şi eventual alte
rezultate prin parametri Subprogramul se activează icircn interiorul unei expresii unde este folosit
ca operand Exemple de apeluri de funcţii operand implementate icircn limbajul C++
o x=35 e=5+floor(x) La calculul expresiei care se atribuie variabilei e se activează
funcţia floor() prin care se determină cel mai mare icircntreg mai mic decacirct valoarea
parametrului Funcţia are un singur parametru ndash x care este parametru de intrare şi are
valoarea 35 Rezultatul (data de ieşire) este furnizat prin numele funcţiei şi are valoarea
3 Aşadar variabilei de memorie e i se va atribui valoarea 8 (5+3)
o for (i=0ilt=sqrt(n)i++) La calculul expresiei ce se atribuie valorii finale a contorului
structurii repetitive for se activează funcţia sqrt(n) care furnizează radicalul de ordinul 2
4
din valoarea parametrului Funcţia are un singur parametru ndash n care este parametru de
intrare
o x = sqrt (pow(32)+ pow(42)) La calculul expresiei care se atribuie variabilei x se
activează de două ori funcţia pow() o dată pentru a calcula 3 la puterea 2 returnacircnd
valoarea 9 şi o dată pentru a calcula 4 la puterea 2 returnacircnd valoarea 16 Funcţia pow()
are doi parametri de intrare primul este baza iar al doilea este exponentul Rezultatul
este furnizat prin numele funcţiei Rezultatul obţinut prin evaluarea expresiei 9+16 = 25
va fi parametru de intrare pentru funcţia sqrt()care extrage radicalul de ordinul 2 din
valoarea lui Rezultatul funcţiei sqrt() ndash data de ieşire ndash este furnizat prin numele funcţiei
şi are valoarea 5 El este atribuit variabilei de memorie x Aşadar variabila de memorie x
va avea valoarea 5
13 Structura subprogramelor
Un subprogram (funcţie) are o definiţie şi atacirctea apeluri cacircte sunt necesare
Definiţia unui subprogram reprezintă de fapt descrierea unui proces de calcul cu ajutorul
variabilelor virtuale (parametri formali) iar apelul unui subprogram icircnseamnă execuţia procesului de
calcul pentru cazuri concrete (cu ajutorul parametrilor reali efectivi actuali)
Parametri formali apar icircn antetul subprogramului şi sunt utilizaţi de subprogram pentru
descrierea abstractă a unui proces de calcul
Parametri actuali apar icircn instrucţiunea de apelare a uni subprogram şi sunt folosiţi la execuţia
unui proces de calcul pentru valori concrete
Parametrii formali nu sunt variabile O variabilă este caracterizată de nume tip şi adresă
Legarea unui parametru formal la o adresă se realizează icircn timpul execuţiei instrucţiunii de apelare a
subprogramului
Icircn limbajul C++ există trei elemente implicate icircn utilizarea unui subprogram
definiţia subprogramului ndash conţine numele subprogramului tipul argumentelor şi al valorilor
returnate şi specifică ceea ce trebuie să realizeze subprogramul
prototipul subprogramului ndash comunică compilatorului informaţii despre subprogram (modul icircn
care se poate apela subprogramul)
apelul subprogramului ndash execută subprogramul
Subprogramul se poate identifica printr-un nume care este folosit atacirct pentru definiţia
subprogramului cacirct şi prin prototip şi activarea lui (apelarea lui) Apelarea subprogramului icircn cadrul
unui bloc icircnseamnă activarea subprogramului adică lansarea lui icircn execuţie Subprogramul poate fi
apelat ori de cacircte ori este nevoie (nu există restricţii pentru numărul de apeluri) Modulul apelant se
execută secvenţial (instrucţiune cu instrucţiune) La apelarea subprogramului este părăsit blocul
modulului apelant şi se trece la executarea instrucţiunilor din subprogram După ce se termină
executarea acestor instrucţiuni se revine la blocul apelant şi se continuă execuţia cu instrucţiunea care
urmează apelului
5
Definiţia unui subprogram este formată din antetul şi corpul subprogramului
a Antetul subprogramului Este o linie de recunoaştere a subprogramului icircn care i se atribuie
un nume El specifică icircnceputul subprogramului
Corpul subprogramului La fel ca orice bloc C++ este icircncapsulat icircntr-o instrucţiune compusă
delimitată de caracterele şi este format din două părţi
Partea declarativă Conţine definiţii de elemente folosite numai icircn interiorul subprogramului
tipuri de date constante şi variabile de memorie Nu se pot defini şi alte subprograme (nu
este valabilă tehnica de imbricare a subprogramelor existentă icircn alte limbaje de programare)
Partea executabilă Conţine instrucţiunile prin care sunt descrise acţiunile realizate de
subprogram
Subprogramul trebuie să aibă un antet prin care se precizează interfaţa dintre programul apelant
şi subprogram El conţine trei categorii de informaţii
Tipul rezultatului Pentru funcţiile operand se precizează tipul rezultatului furnizat de
subprogram prin chiar numele său Pentru funcţiile procedurale tipul rezultatului este void
(nu icircntoarce nici un rezultat prin numele său rezultatele vor fi icircntoarse prin parametrii
subprogramului) Dacă nu se precizează tipul rezultatului compilatorul va considera că
acesta este implicit de tip int
Numele subprogramului Este un identificator unic care se atribuie subprogramului
Numele trebuie să respecte aceleaşi reguli ca orice identificator C++ Parametrii folosiţi
pentru comunicare Pentru fiecare parametru se precizează numele şi tipul
Parametrii folosiţi pentru comunicare Pentru fiecare parametru se precizează numele şi
tipul
Antetul unui subprogram are următoarea formă
unde lista de parametrii are următoarea formă
Exemple aferente limbajului C++
float alfa (int a int b float c)
Acesta este antetul unei funcţii operand care furnizează un rezultat de tip float Numele funcţiei
este alfa iar parametrii folosiţi pentru comunicare sunt a şi b de tip int şi c de tip float
void beta (int a float b float c char d)
Acesta este antetul unei funcţii procedurale Numele funcţiei este beta iar parametrii folosiţi
pentru comunicare sunt a de tip int b şi c de tip float şi d de tip char
void gama ( )
6
Acesta este antetul unei funcţii procedurale Numele funcţiei este gama şi nu foloseşte parametri
pentru comunicare
Observația 1 Separarea parametrilor icircn listă se face prin caracterul virgulă ()
Observația 2 Tipul parametrilor poate fi
orice tip standard al sistemului folosit pentru date elementare
o icircntreg (int unsigned long) real (double float long double) sau caracter (char sau
unsigned char)
o tipul pointer sau orice tip de structură de date (vector matrice şir de caractere sau
icircnregistrare)
orice tip definit de utilizator icircnainte de a defini subprogramul
Observația 3 Numele subprogramului poate fi folosit icircn trei locuri distincte
icircn prototipul subprogramului unde are un rol declarativ
icircn antetul subprogramului unde are un rol de definiţie dar şi declarativ
icircn apelul subprogramului unde are rol de activare
b Corpul subprogramului este un bloc care conţine atacirct instrucţiuni declarative cacirct şi
instrucţiuni imperative Variabilele de memorie declarate icircn corpul subprogramului se
numesc variabile locale Icircn cazul unei funcţii operand ultima instrucţiune din corpul
subprogramului trebuie să fie instrucţiunea return care are sintaxa
Valoarea obţinută prin evaluarea expresiei ltexpresiegt va fi atribuită funcţiei operand (va fi
valoarea returnată prin numele funcţiei) Rezultatul expresiei trebuie să fie de acelaşi tip cu tipul funcţiei
sau de un tip care poate fi convertit implicit icircn tipul funcţiei
Cacircnd compilatorul C++ icircntacirclneşte icircntr-un subprogram instrucţiunea return termină execuţia
subprogramului şi redă controlul modulului apelant Prin urmare dacă veţi scrie icircn subprogram după
instrucţiunea return alte instrucţiuni ele nu vor fi executate
Prototipul subprogramului este o linie de program aflată icircnaintea modulului care apelează
subprogramul prin care se comunică compilatorului informaţii despre subprogram (se declară
subprogramul)
Prin declararea programului compilatorul primeşte informaţii despre modul icircn care se poate
apela subprogramul şi poate face verificări la apelurile de subprogram icircn ceea ce priveşte tipul
parametrilor folosiţi pentru comunicare şi a modului icircn care poate face conversia acestor parametri
Un subprogram pentru a putea fi folosit trebuie declarat Pentru declararea lui se foloseşte
prototipul El conţine trei categorii de informaţii la fel ca şi antetul subprogramului tipul rezultatului
numele subprogramului şi tipul parametrilor folosiţi pentru comunicare Pentru fiecare parametru din
antetul subprogramului se poate preciza numai tipul nu şi numele lui
Prototipul unui subprogram este de forma
unde lista tipului parametrilor are următoarea forma
7
Observația 4 Separarea tipurilor de parametri icircn listă se face prin caracterul virgulă () Lista trebuie să
conţină atacirctea tipuri de parametri cacircţi parametri au fost definiţi icircn antetul subprogramului Icircn listă se
precizează tipul de dată la care se referă icircn aceeaşi ordine icircn care au fost scrişi parametrii la definirea lor
icircn antet
Observația 5 Spre deosebire de antetul subprogramului prototipul se termină cu caracterul
Pentru funcţiile al căror antet a fost precizat anterior (a se vedea exemplele anterior prezentate)
prototipurile vor fi
float alfa (int int float)
void beta (int float float char)
void gama ()
Subprogramul trebuie să fie cunoscut atunci cacircnd se cere prin apel activarea lui
dacă subprogramul este standard trebuie inclus fişierul care conţine prototipul subprogramului
icircn fişierul sursă
dacă subprogramul este utilizator trebuie declarat fie prin folosirea prototipului fie prin
definirea lui icircnaintea apelului
Icircn funcţie de modul icircn care a fost definit subprogramul se activează fie printr-o instrucţiune
procedurală fie ca operand icircntr-o expresie
Pentru funcţiile al căror antet a fost precizat anterior activarea se poate face astfel
exemplul 1
int xy float zw
w = alfa (xyz)
exemplul 2
int x float yz char w
beta (x y z w)
exemplul 3
gama ()
Orice subprogram trebuie declarat şi definit Declararea unui subprogram este necesară pentru
ca el să fie cunoscut de subprogramele care icircl apelează Declararea lui poate fi făcută fie prin prototip
fie prin definiţia lui (antetul icircmpreună cu corpul subprogramului) Pentru a declara subprogramul fie
scrieţi prototipul icircnaintea subprogramelor care icircl apelează putacircnd scrie apoi definiţia lui oriunde icircn
program fie definiţi subprogramul icircnaintea subprogramului care icircl apelează Icircn cele ce urmează se
prezintă un exemplu
Aşadar
Prototipul subprogramului declară subprogramul
Apelul subprogramului execută subprogramul
8
Antetul subprogramului specifică numele subprogramului şi tipul argumentelor şi al valorilor
returnate iar corpul subprogramului icircl defineşte adică specifică ceea ce trebuie să realizeze
subprogramul
Icircn concluzie forma generală a unui subprogram este prezentată mai jos
unde
tip_returnat Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi int char
char long float void etc Icircn cazul icircn care tipul rezultatului este diferit de void corpul funcţiei
trebuie să conţină cel puţin o instrucţiune return Icircnstrucţiunea return va specifica valoarea
calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat
nume_funcție Reprezintă numele dat funcţiei de către cel ce o defineşte pentru a o putea apela
lista parametrilor formali Reprezintă o listă de declaraţii de variabile separate prin virgulă
Această listă poate să fie şi vidă
instrucțiune Este o instrucţiune vidă sau o instrucţiune simplă sau o instrucţiune compusă
Icircn altă odine de idei construirea subprogramelor se face prin
Antet ndash conţine date despre tipul rezultatului nume şi parametrii efectivi Dacă funcţia nu
returnează nimic tipul este void()
Corp ndash este un bloc de instrucţiuni declarative şi imperative (de execuţie) ce definesc modul de
acţiune al subprogramului
Prototip ndash pentru a putea fi apelată funcţia trebuie declarată Conţine date despre tipul
rezultatului nume şi parametrii efectivi
Apel ndash este modul prin care subprogramul e pus icircn execuţie Apelul poate fi făcut ori de cacircte ori
este nevoie
Parametrii ndash datele care circulă icircntre modulul appelant şi apelat se introduc icircntre paranteze
după numele subprogramului
Modul apelant ndash blocul ce conţine apelul subprogramului
Modul apelat ndash blocul ce conţine funcţia (subprogramul apelat)
Exemplu
9
Icircn memoria internă fiecărui subprogram i se alocă o zonă de memorie icircn care este icircncărcat codul
executabil
La apelarea unui subprogram compilatorul icirci predă controlul adică icircncep să se execute
instrucţiunile subprogramului pacircnă la icircntacirclnirea unei instrucţiuni return sau pacircnă la sfacircrşitul blocului
care formează corpul subprogramului după care compilatorul redă controlul modulului apelant adică va
continua execuţia cu instrucţiunea care urmează icircn modulul apelant imediat după instrucţiunea care a
apelat subprogramul Acest mecanism de transfer al controlului se poate realiza deoarece icircntr-o zonă de
memorie internă numită stiva sistemului (stack) se păstrează temporar informaţii despre subprogramul
apelant Aceste informaţii sunt introduse icircn stivă atunci cacircnd este apelat subprogramul Ele formează
instanţa subprogramului
Etapele executate la apelarea subprogramului sunt
1 Se icircntrerupe execuţia modulului apelant
2 Se pregăteşte stiva sistemului astfel
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
3
Icircn aceste condiții secțiunea de program ce calculează maximul dintre 3 numere icircntregi se poate
scrie astfel
12 Tipuri de subprograme
Clasificarea subprogramelor
Subprograme standard (subprograme de sistem) ndash utilizarea lor presupune includerea fişierului
ce conţine prototipul dorit şi apelarea subprogramului Aceste subprograme sunt predefinite icircn
biblioteci ale limbajului de programare (Exemplu include ltcmathgt include un set de funcții
utilizate icircn realizarea operațiilor și transformărilor matematice obijnuite funcții trigonometrice
funcții hiperbolice funcții exponențiale și logaritmice funcțiile putere pow sqrt funcții de
eroare funcții de rotunjire și rest funcții de manipulare cu punct flotant funcții de diferență ndash
minim ndashmaxim)
Subprograme definite de utilizator ndash sunt descrise de progamator pentru rezolvarea unor cerinţe
specifice Utilizarea lor presupune precizarea prototipului definirea şi apelul (a se vedea
exemplu din paragraful anterior)
Subprograme apelate ca instrucţiuni procedurale ndash returnează una mai multe sau nici o valoare
prin intermediul parametrilor Exemple de apeluri de funcţii procedurale implementate icircn
limbajul C++
o clrscr ( ) Apelul unei funcţii procedurale fără parametri (CLeaR SCReen) care şterge
informaţiile afişate pe ecranul calculatorului
o randomize ( ) Apelul unei funcţii procedurale fără parametri care iniţializează
generatorul de numere aleatoare
o swab(s1s2n) Apelul unei funcţii procedurale cu trei parametri copiază n caractere (n
fiind un număr par) din şirul de caractere s1 la icircnceputul şirului de caractere s2
inversacircnd caracterele adiacente Parametrul s2 este un parametru de intrare-ieşire iar
parametrii s1 şi n sunt parametri de intrare
o gotoxy(xy) Apelul unei funcţii procedurale cu doi parametri icircn modul de lucru text
mută cursorul icircn fereastra de text curentă icircn poziţia precizată prin coordonatele x şi y
Parametrii x şi y sunt parametri de intrare
Subprograme apelate ca operanzi ndash returnează un rezultat chiar prin numele său şi eventual alte
rezultate prin parametri Subprogramul se activează icircn interiorul unei expresii unde este folosit
ca operand Exemple de apeluri de funcţii operand implementate icircn limbajul C++
o x=35 e=5+floor(x) La calculul expresiei care se atribuie variabilei e se activează
funcţia floor() prin care se determină cel mai mare icircntreg mai mic decacirct valoarea
parametrului Funcţia are un singur parametru ndash x care este parametru de intrare şi are
valoarea 35 Rezultatul (data de ieşire) este furnizat prin numele funcţiei şi are valoarea
3 Aşadar variabilei de memorie e i se va atribui valoarea 8 (5+3)
o for (i=0ilt=sqrt(n)i++) La calculul expresiei ce se atribuie valorii finale a contorului
structurii repetitive for se activează funcţia sqrt(n) care furnizează radicalul de ordinul 2
4
din valoarea parametrului Funcţia are un singur parametru ndash n care este parametru de
intrare
o x = sqrt (pow(32)+ pow(42)) La calculul expresiei care se atribuie variabilei x se
activează de două ori funcţia pow() o dată pentru a calcula 3 la puterea 2 returnacircnd
valoarea 9 şi o dată pentru a calcula 4 la puterea 2 returnacircnd valoarea 16 Funcţia pow()
are doi parametri de intrare primul este baza iar al doilea este exponentul Rezultatul
este furnizat prin numele funcţiei Rezultatul obţinut prin evaluarea expresiei 9+16 = 25
va fi parametru de intrare pentru funcţia sqrt()care extrage radicalul de ordinul 2 din
valoarea lui Rezultatul funcţiei sqrt() ndash data de ieşire ndash este furnizat prin numele funcţiei
şi are valoarea 5 El este atribuit variabilei de memorie x Aşadar variabila de memorie x
va avea valoarea 5
13 Structura subprogramelor
Un subprogram (funcţie) are o definiţie şi atacirctea apeluri cacircte sunt necesare
Definiţia unui subprogram reprezintă de fapt descrierea unui proces de calcul cu ajutorul
variabilelor virtuale (parametri formali) iar apelul unui subprogram icircnseamnă execuţia procesului de
calcul pentru cazuri concrete (cu ajutorul parametrilor reali efectivi actuali)
Parametri formali apar icircn antetul subprogramului şi sunt utilizaţi de subprogram pentru
descrierea abstractă a unui proces de calcul
Parametri actuali apar icircn instrucţiunea de apelare a uni subprogram şi sunt folosiţi la execuţia
unui proces de calcul pentru valori concrete
Parametrii formali nu sunt variabile O variabilă este caracterizată de nume tip şi adresă
Legarea unui parametru formal la o adresă se realizează icircn timpul execuţiei instrucţiunii de apelare a
subprogramului
Icircn limbajul C++ există trei elemente implicate icircn utilizarea unui subprogram
definiţia subprogramului ndash conţine numele subprogramului tipul argumentelor şi al valorilor
returnate şi specifică ceea ce trebuie să realizeze subprogramul
prototipul subprogramului ndash comunică compilatorului informaţii despre subprogram (modul icircn
care se poate apela subprogramul)
apelul subprogramului ndash execută subprogramul
Subprogramul se poate identifica printr-un nume care este folosit atacirct pentru definiţia
subprogramului cacirct şi prin prototip şi activarea lui (apelarea lui) Apelarea subprogramului icircn cadrul
unui bloc icircnseamnă activarea subprogramului adică lansarea lui icircn execuţie Subprogramul poate fi
apelat ori de cacircte ori este nevoie (nu există restricţii pentru numărul de apeluri) Modulul apelant se
execută secvenţial (instrucţiune cu instrucţiune) La apelarea subprogramului este părăsit blocul
modulului apelant şi se trece la executarea instrucţiunilor din subprogram După ce se termină
executarea acestor instrucţiuni se revine la blocul apelant şi se continuă execuţia cu instrucţiunea care
urmează apelului
5
Definiţia unui subprogram este formată din antetul şi corpul subprogramului
a Antetul subprogramului Este o linie de recunoaştere a subprogramului icircn care i se atribuie
un nume El specifică icircnceputul subprogramului
Corpul subprogramului La fel ca orice bloc C++ este icircncapsulat icircntr-o instrucţiune compusă
delimitată de caracterele şi este format din două părţi
Partea declarativă Conţine definiţii de elemente folosite numai icircn interiorul subprogramului
tipuri de date constante şi variabile de memorie Nu se pot defini şi alte subprograme (nu
este valabilă tehnica de imbricare a subprogramelor existentă icircn alte limbaje de programare)
Partea executabilă Conţine instrucţiunile prin care sunt descrise acţiunile realizate de
subprogram
Subprogramul trebuie să aibă un antet prin care se precizează interfaţa dintre programul apelant
şi subprogram El conţine trei categorii de informaţii
Tipul rezultatului Pentru funcţiile operand se precizează tipul rezultatului furnizat de
subprogram prin chiar numele său Pentru funcţiile procedurale tipul rezultatului este void
(nu icircntoarce nici un rezultat prin numele său rezultatele vor fi icircntoarse prin parametrii
subprogramului) Dacă nu se precizează tipul rezultatului compilatorul va considera că
acesta este implicit de tip int
Numele subprogramului Este un identificator unic care se atribuie subprogramului
Numele trebuie să respecte aceleaşi reguli ca orice identificator C++ Parametrii folosiţi
pentru comunicare Pentru fiecare parametru se precizează numele şi tipul
Parametrii folosiţi pentru comunicare Pentru fiecare parametru se precizează numele şi
tipul
Antetul unui subprogram are următoarea formă
unde lista de parametrii are următoarea formă
Exemple aferente limbajului C++
float alfa (int a int b float c)
Acesta este antetul unei funcţii operand care furnizează un rezultat de tip float Numele funcţiei
este alfa iar parametrii folosiţi pentru comunicare sunt a şi b de tip int şi c de tip float
void beta (int a float b float c char d)
Acesta este antetul unei funcţii procedurale Numele funcţiei este beta iar parametrii folosiţi
pentru comunicare sunt a de tip int b şi c de tip float şi d de tip char
void gama ( )
6
Acesta este antetul unei funcţii procedurale Numele funcţiei este gama şi nu foloseşte parametri
pentru comunicare
Observația 1 Separarea parametrilor icircn listă se face prin caracterul virgulă ()
Observația 2 Tipul parametrilor poate fi
orice tip standard al sistemului folosit pentru date elementare
o icircntreg (int unsigned long) real (double float long double) sau caracter (char sau
unsigned char)
o tipul pointer sau orice tip de structură de date (vector matrice şir de caractere sau
icircnregistrare)
orice tip definit de utilizator icircnainte de a defini subprogramul
Observația 3 Numele subprogramului poate fi folosit icircn trei locuri distincte
icircn prototipul subprogramului unde are un rol declarativ
icircn antetul subprogramului unde are un rol de definiţie dar şi declarativ
icircn apelul subprogramului unde are rol de activare
b Corpul subprogramului este un bloc care conţine atacirct instrucţiuni declarative cacirct şi
instrucţiuni imperative Variabilele de memorie declarate icircn corpul subprogramului se
numesc variabile locale Icircn cazul unei funcţii operand ultima instrucţiune din corpul
subprogramului trebuie să fie instrucţiunea return care are sintaxa
Valoarea obţinută prin evaluarea expresiei ltexpresiegt va fi atribuită funcţiei operand (va fi
valoarea returnată prin numele funcţiei) Rezultatul expresiei trebuie să fie de acelaşi tip cu tipul funcţiei
sau de un tip care poate fi convertit implicit icircn tipul funcţiei
Cacircnd compilatorul C++ icircntacirclneşte icircntr-un subprogram instrucţiunea return termină execuţia
subprogramului şi redă controlul modulului apelant Prin urmare dacă veţi scrie icircn subprogram după
instrucţiunea return alte instrucţiuni ele nu vor fi executate
Prototipul subprogramului este o linie de program aflată icircnaintea modulului care apelează
subprogramul prin care se comunică compilatorului informaţii despre subprogram (se declară
subprogramul)
Prin declararea programului compilatorul primeşte informaţii despre modul icircn care se poate
apela subprogramul şi poate face verificări la apelurile de subprogram icircn ceea ce priveşte tipul
parametrilor folosiţi pentru comunicare şi a modului icircn care poate face conversia acestor parametri
Un subprogram pentru a putea fi folosit trebuie declarat Pentru declararea lui se foloseşte
prototipul El conţine trei categorii de informaţii la fel ca şi antetul subprogramului tipul rezultatului
numele subprogramului şi tipul parametrilor folosiţi pentru comunicare Pentru fiecare parametru din
antetul subprogramului se poate preciza numai tipul nu şi numele lui
Prototipul unui subprogram este de forma
unde lista tipului parametrilor are următoarea forma
7
Observația 4 Separarea tipurilor de parametri icircn listă se face prin caracterul virgulă () Lista trebuie să
conţină atacirctea tipuri de parametri cacircţi parametri au fost definiţi icircn antetul subprogramului Icircn listă se
precizează tipul de dată la care se referă icircn aceeaşi ordine icircn care au fost scrişi parametrii la definirea lor
icircn antet
Observația 5 Spre deosebire de antetul subprogramului prototipul se termină cu caracterul
Pentru funcţiile al căror antet a fost precizat anterior (a se vedea exemplele anterior prezentate)
prototipurile vor fi
float alfa (int int float)
void beta (int float float char)
void gama ()
Subprogramul trebuie să fie cunoscut atunci cacircnd se cere prin apel activarea lui
dacă subprogramul este standard trebuie inclus fişierul care conţine prototipul subprogramului
icircn fişierul sursă
dacă subprogramul este utilizator trebuie declarat fie prin folosirea prototipului fie prin
definirea lui icircnaintea apelului
Icircn funcţie de modul icircn care a fost definit subprogramul se activează fie printr-o instrucţiune
procedurală fie ca operand icircntr-o expresie
Pentru funcţiile al căror antet a fost precizat anterior activarea se poate face astfel
exemplul 1
int xy float zw
w = alfa (xyz)
exemplul 2
int x float yz char w
beta (x y z w)
exemplul 3
gama ()
Orice subprogram trebuie declarat şi definit Declararea unui subprogram este necesară pentru
ca el să fie cunoscut de subprogramele care icircl apelează Declararea lui poate fi făcută fie prin prototip
fie prin definiţia lui (antetul icircmpreună cu corpul subprogramului) Pentru a declara subprogramul fie
scrieţi prototipul icircnaintea subprogramelor care icircl apelează putacircnd scrie apoi definiţia lui oriunde icircn
program fie definiţi subprogramul icircnaintea subprogramului care icircl apelează Icircn cele ce urmează se
prezintă un exemplu
Aşadar
Prototipul subprogramului declară subprogramul
Apelul subprogramului execută subprogramul
8
Antetul subprogramului specifică numele subprogramului şi tipul argumentelor şi al valorilor
returnate iar corpul subprogramului icircl defineşte adică specifică ceea ce trebuie să realizeze
subprogramul
Icircn concluzie forma generală a unui subprogram este prezentată mai jos
unde
tip_returnat Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi int char
char long float void etc Icircn cazul icircn care tipul rezultatului este diferit de void corpul funcţiei
trebuie să conţină cel puţin o instrucţiune return Icircnstrucţiunea return va specifica valoarea
calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat
nume_funcție Reprezintă numele dat funcţiei de către cel ce o defineşte pentru a o putea apela
lista parametrilor formali Reprezintă o listă de declaraţii de variabile separate prin virgulă
Această listă poate să fie şi vidă
instrucțiune Este o instrucţiune vidă sau o instrucţiune simplă sau o instrucţiune compusă
Icircn altă odine de idei construirea subprogramelor se face prin
Antet ndash conţine date despre tipul rezultatului nume şi parametrii efectivi Dacă funcţia nu
returnează nimic tipul este void()
Corp ndash este un bloc de instrucţiuni declarative şi imperative (de execuţie) ce definesc modul de
acţiune al subprogramului
Prototip ndash pentru a putea fi apelată funcţia trebuie declarată Conţine date despre tipul
rezultatului nume şi parametrii efectivi
Apel ndash este modul prin care subprogramul e pus icircn execuţie Apelul poate fi făcut ori de cacircte ori
este nevoie
Parametrii ndash datele care circulă icircntre modulul appelant şi apelat se introduc icircntre paranteze
după numele subprogramului
Modul apelant ndash blocul ce conţine apelul subprogramului
Modul apelat ndash blocul ce conţine funcţia (subprogramul apelat)
Exemplu
9
Icircn memoria internă fiecărui subprogram i se alocă o zonă de memorie icircn care este icircncărcat codul
executabil
La apelarea unui subprogram compilatorul icirci predă controlul adică icircncep să se execute
instrucţiunile subprogramului pacircnă la icircntacirclnirea unei instrucţiuni return sau pacircnă la sfacircrşitul blocului
care formează corpul subprogramului după care compilatorul redă controlul modulului apelant adică va
continua execuţia cu instrucţiunea care urmează icircn modulul apelant imediat după instrucţiunea care a
apelat subprogramul Acest mecanism de transfer al controlului se poate realiza deoarece icircntr-o zonă de
memorie internă numită stiva sistemului (stack) se păstrează temporar informaţii despre subprogramul
apelant Aceste informaţii sunt introduse icircn stivă atunci cacircnd este apelat subprogramul Ele formează
instanţa subprogramului
Etapele executate la apelarea subprogramului sunt
1 Se icircntrerupe execuţia modulului apelant
2 Se pregăteşte stiva sistemului astfel
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
4
din valoarea parametrului Funcţia are un singur parametru ndash n care este parametru de
intrare
o x = sqrt (pow(32)+ pow(42)) La calculul expresiei care se atribuie variabilei x se
activează de două ori funcţia pow() o dată pentru a calcula 3 la puterea 2 returnacircnd
valoarea 9 şi o dată pentru a calcula 4 la puterea 2 returnacircnd valoarea 16 Funcţia pow()
are doi parametri de intrare primul este baza iar al doilea este exponentul Rezultatul
este furnizat prin numele funcţiei Rezultatul obţinut prin evaluarea expresiei 9+16 = 25
va fi parametru de intrare pentru funcţia sqrt()care extrage radicalul de ordinul 2 din
valoarea lui Rezultatul funcţiei sqrt() ndash data de ieşire ndash este furnizat prin numele funcţiei
şi are valoarea 5 El este atribuit variabilei de memorie x Aşadar variabila de memorie x
va avea valoarea 5
13 Structura subprogramelor
Un subprogram (funcţie) are o definiţie şi atacirctea apeluri cacircte sunt necesare
Definiţia unui subprogram reprezintă de fapt descrierea unui proces de calcul cu ajutorul
variabilelor virtuale (parametri formali) iar apelul unui subprogram icircnseamnă execuţia procesului de
calcul pentru cazuri concrete (cu ajutorul parametrilor reali efectivi actuali)
Parametri formali apar icircn antetul subprogramului şi sunt utilizaţi de subprogram pentru
descrierea abstractă a unui proces de calcul
Parametri actuali apar icircn instrucţiunea de apelare a uni subprogram şi sunt folosiţi la execuţia
unui proces de calcul pentru valori concrete
Parametrii formali nu sunt variabile O variabilă este caracterizată de nume tip şi adresă
Legarea unui parametru formal la o adresă se realizează icircn timpul execuţiei instrucţiunii de apelare a
subprogramului
Icircn limbajul C++ există trei elemente implicate icircn utilizarea unui subprogram
definiţia subprogramului ndash conţine numele subprogramului tipul argumentelor şi al valorilor
returnate şi specifică ceea ce trebuie să realizeze subprogramul
prototipul subprogramului ndash comunică compilatorului informaţii despre subprogram (modul icircn
care se poate apela subprogramul)
apelul subprogramului ndash execută subprogramul
Subprogramul se poate identifica printr-un nume care este folosit atacirct pentru definiţia
subprogramului cacirct şi prin prototip şi activarea lui (apelarea lui) Apelarea subprogramului icircn cadrul
unui bloc icircnseamnă activarea subprogramului adică lansarea lui icircn execuţie Subprogramul poate fi
apelat ori de cacircte ori este nevoie (nu există restricţii pentru numărul de apeluri) Modulul apelant se
execută secvenţial (instrucţiune cu instrucţiune) La apelarea subprogramului este părăsit blocul
modulului apelant şi se trece la executarea instrucţiunilor din subprogram După ce se termină
executarea acestor instrucţiuni se revine la blocul apelant şi se continuă execuţia cu instrucţiunea care
urmează apelului
5
Definiţia unui subprogram este formată din antetul şi corpul subprogramului
a Antetul subprogramului Este o linie de recunoaştere a subprogramului icircn care i se atribuie
un nume El specifică icircnceputul subprogramului
Corpul subprogramului La fel ca orice bloc C++ este icircncapsulat icircntr-o instrucţiune compusă
delimitată de caracterele şi este format din două părţi
Partea declarativă Conţine definiţii de elemente folosite numai icircn interiorul subprogramului
tipuri de date constante şi variabile de memorie Nu se pot defini şi alte subprograme (nu
este valabilă tehnica de imbricare a subprogramelor existentă icircn alte limbaje de programare)
Partea executabilă Conţine instrucţiunile prin care sunt descrise acţiunile realizate de
subprogram
Subprogramul trebuie să aibă un antet prin care se precizează interfaţa dintre programul apelant
şi subprogram El conţine trei categorii de informaţii
Tipul rezultatului Pentru funcţiile operand se precizează tipul rezultatului furnizat de
subprogram prin chiar numele său Pentru funcţiile procedurale tipul rezultatului este void
(nu icircntoarce nici un rezultat prin numele său rezultatele vor fi icircntoarse prin parametrii
subprogramului) Dacă nu se precizează tipul rezultatului compilatorul va considera că
acesta este implicit de tip int
Numele subprogramului Este un identificator unic care se atribuie subprogramului
Numele trebuie să respecte aceleaşi reguli ca orice identificator C++ Parametrii folosiţi
pentru comunicare Pentru fiecare parametru se precizează numele şi tipul
Parametrii folosiţi pentru comunicare Pentru fiecare parametru se precizează numele şi
tipul
Antetul unui subprogram are următoarea formă
unde lista de parametrii are următoarea formă
Exemple aferente limbajului C++
float alfa (int a int b float c)
Acesta este antetul unei funcţii operand care furnizează un rezultat de tip float Numele funcţiei
este alfa iar parametrii folosiţi pentru comunicare sunt a şi b de tip int şi c de tip float
void beta (int a float b float c char d)
Acesta este antetul unei funcţii procedurale Numele funcţiei este beta iar parametrii folosiţi
pentru comunicare sunt a de tip int b şi c de tip float şi d de tip char
void gama ( )
6
Acesta este antetul unei funcţii procedurale Numele funcţiei este gama şi nu foloseşte parametri
pentru comunicare
Observația 1 Separarea parametrilor icircn listă se face prin caracterul virgulă ()
Observația 2 Tipul parametrilor poate fi
orice tip standard al sistemului folosit pentru date elementare
o icircntreg (int unsigned long) real (double float long double) sau caracter (char sau
unsigned char)
o tipul pointer sau orice tip de structură de date (vector matrice şir de caractere sau
icircnregistrare)
orice tip definit de utilizator icircnainte de a defini subprogramul
Observația 3 Numele subprogramului poate fi folosit icircn trei locuri distincte
icircn prototipul subprogramului unde are un rol declarativ
icircn antetul subprogramului unde are un rol de definiţie dar şi declarativ
icircn apelul subprogramului unde are rol de activare
b Corpul subprogramului este un bloc care conţine atacirct instrucţiuni declarative cacirct şi
instrucţiuni imperative Variabilele de memorie declarate icircn corpul subprogramului se
numesc variabile locale Icircn cazul unei funcţii operand ultima instrucţiune din corpul
subprogramului trebuie să fie instrucţiunea return care are sintaxa
Valoarea obţinută prin evaluarea expresiei ltexpresiegt va fi atribuită funcţiei operand (va fi
valoarea returnată prin numele funcţiei) Rezultatul expresiei trebuie să fie de acelaşi tip cu tipul funcţiei
sau de un tip care poate fi convertit implicit icircn tipul funcţiei
Cacircnd compilatorul C++ icircntacirclneşte icircntr-un subprogram instrucţiunea return termină execuţia
subprogramului şi redă controlul modulului apelant Prin urmare dacă veţi scrie icircn subprogram după
instrucţiunea return alte instrucţiuni ele nu vor fi executate
Prototipul subprogramului este o linie de program aflată icircnaintea modulului care apelează
subprogramul prin care se comunică compilatorului informaţii despre subprogram (se declară
subprogramul)
Prin declararea programului compilatorul primeşte informaţii despre modul icircn care se poate
apela subprogramul şi poate face verificări la apelurile de subprogram icircn ceea ce priveşte tipul
parametrilor folosiţi pentru comunicare şi a modului icircn care poate face conversia acestor parametri
Un subprogram pentru a putea fi folosit trebuie declarat Pentru declararea lui se foloseşte
prototipul El conţine trei categorii de informaţii la fel ca şi antetul subprogramului tipul rezultatului
numele subprogramului şi tipul parametrilor folosiţi pentru comunicare Pentru fiecare parametru din
antetul subprogramului se poate preciza numai tipul nu şi numele lui
Prototipul unui subprogram este de forma
unde lista tipului parametrilor are următoarea forma
7
Observația 4 Separarea tipurilor de parametri icircn listă se face prin caracterul virgulă () Lista trebuie să
conţină atacirctea tipuri de parametri cacircţi parametri au fost definiţi icircn antetul subprogramului Icircn listă se
precizează tipul de dată la care se referă icircn aceeaşi ordine icircn care au fost scrişi parametrii la definirea lor
icircn antet
Observația 5 Spre deosebire de antetul subprogramului prototipul se termină cu caracterul
Pentru funcţiile al căror antet a fost precizat anterior (a se vedea exemplele anterior prezentate)
prototipurile vor fi
float alfa (int int float)
void beta (int float float char)
void gama ()
Subprogramul trebuie să fie cunoscut atunci cacircnd se cere prin apel activarea lui
dacă subprogramul este standard trebuie inclus fişierul care conţine prototipul subprogramului
icircn fişierul sursă
dacă subprogramul este utilizator trebuie declarat fie prin folosirea prototipului fie prin
definirea lui icircnaintea apelului
Icircn funcţie de modul icircn care a fost definit subprogramul se activează fie printr-o instrucţiune
procedurală fie ca operand icircntr-o expresie
Pentru funcţiile al căror antet a fost precizat anterior activarea se poate face astfel
exemplul 1
int xy float zw
w = alfa (xyz)
exemplul 2
int x float yz char w
beta (x y z w)
exemplul 3
gama ()
Orice subprogram trebuie declarat şi definit Declararea unui subprogram este necesară pentru
ca el să fie cunoscut de subprogramele care icircl apelează Declararea lui poate fi făcută fie prin prototip
fie prin definiţia lui (antetul icircmpreună cu corpul subprogramului) Pentru a declara subprogramul fie
scrieţi prototipul icircnaintea subprogramelor care icircl apelează putacircnd scrie apoi definiţia lui oriunde icircn
program fie definiţi subprogramul icircnaintea subprogramului care icircl apelează Icircn cele ce urmează se
prezintă un exemplu
Aşadar
Prototipul subprogramului declară subprogramul
Apelul subprogramului execută subprogramul
8
Antetul subprogramului specifică numele subprogramului şi tipul argumentelor şi al valorilor
returnate iar corpul subprogramului icircl defineşte adică specifică ceea ce trebuie să realizeze
subprogramul
Icircn concluzie forma generală a unui subprogram este prezentată mai jos
unde
tip_returnat Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi int char
char long float void etc Icircn cazul icircn care tipul rezultatului este diferit de void corpul funcţiei
trebuie să conţină cel puţin o instrucţiune return Icircnstrucţiunea return va specifica valoarea
calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat
nume_funcție Reprezintă numele dat funcţiei de către cel ce o defineşte pentru a o putea apela
lista parametrilor formali Reprezintă o listă de declaraţii de variabile separate prin virgulă
Această listă poate să fie şi vidă
instrucțiune Este o instrucţiune vidă sau o instrucţiune simplă sau o instrucţiune compusă
Icircn altă odine de idei construirea subprogramelor se face prin
Antet ndash conţine date despre tipul rezultatului nume şi parametrii efectivi Dacă funcţia nu
returnează nimic tipul este void()
Corp ndash este un bloc de instrucţiuni declarative şi imperative (de execuţie) ce definesc modul de
acţiune al subprogramului
Prototip ndash pentru a putea fi apelată funcţia trebuie declarată Conţine date despre tipul
rezultatului nume şi parametrii efectivi
Apel ndash este modul prin care subprogramul e pus icircn execuţie Apelul poate fi făcut ori de cacircte ori
este nevoie
Parametrii ndash datele care circulă icircntre modulul appelant şi apelat se introduc icircntre paranteze
după numele subprogramului
Modul apelant ndash blocul ce conţine apelul subprogramului
Modul apelat ndash blocul ce conţine funcţia (subprogramul apelat)
Exemplu
9
Icircn memoria internă fiecărui subprogram i se alocă o zonă de memorie icircn care este icircncărcat codul
executabil
La apelarea unui subprogram compilatorul icirci predă controlul adică icircncep să se execute
instrucţiunile subprogramului pacircnă la icircntacirclnirea unei instrucţiuni return sau pacircnă la sfacircrşitul blocului
care formează corpul subprogramului după care compilatorul redă controlul modulului apelant adică va
continua execuţia cu instrucţiunea care urmează icircn modulul apelant imediat după instrucţiunea care a
apelat subprogramul Acest mecanism de transfer al controlului se poate realiza deoarece icircntr-o zonă de
memorie internă numită stiva sistemului (stack) se păstrează temporar informaţii despre subprogramul
apelant Aceste informaţii sunt introduse icircn stivă atunci cacircnd este apelat subprogramul Ele formează
instanţa subprogramului
Etapele executate la apelarea subprogramului sunt
1 Se icircntrerupe execuţia modulului apelant
2 Se pregăteşte stiva sistemului astfel
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
5
Definiţia unui subprogram este formată din antetul şi corpul subprogramului
a Antetul subprogramului Este o linie de recunoaştere a subprogramului icircn care i se atribuie
un nume El specifică icircnceputul subprogramului
Corpul subprogramului La fel ca orice bloc C++ este icircncapsulat icircntr-o instrucţiune compusă
delimitată de caracterele şi este format din două părţi
Partea declarativă Conţine definiţii de elemente folosite numai icircn interiorul subprogramului
tipuri de date constante şi variabile de memorie Nu se pot defini şi alte subprograme (nu
este valabilă tehnica de imbricare a subprogramelor existentă icircn alte limbaje de programare)
Partea executabilă Conţine instrucţiunile prin care sunt descrise acţiunile realizate de
subprogram
Subprogramul trebuie să aibă un antet prin care se precizează interfaţa dintre programul apelant
şi subprogram El conţine trei categorii de informaţii
Tipul rezultatului Pentru funcţiile operand se precizează tipul rezultatului furnizat de
subprogram prin chiar numele său Pentru funcţiile procedurale tipul rezultatului este void
(nu icircntoarce nici un rezultat prin numele său rezultatele vor fi icircntoarse prin parametrii
subprogramului) Dacă nu se precizează tipul rezultatului compilatorul va considera că
acesta este implicit de tip int
Numele subprogramului Este un identificator unic care se atribuie subprogramului
Numele trebuie să respecte aceleaşi reguli ca orice identificator C++ Parametrii folosiţi
pentru comunicare Pentru fiecare parametru se precizează numele şi tipul
Parametrii folosiţi pentru comunicare Pentru fiecare parametru se precizează numele şi
tipul
Antetul unui subprogram are următoarea formă
unde lista de parametrii are următoarea formă
Exemple aferente limbajului C++
float alfa (int a int b float c)
Acesta este antetul unei funcţii operand care furnizează un rezultat de tip float Numele funcţiei
este alfa iar parametrii folosiţi pentru comunicare sunt a şi b de tip int şi c de tip float
void beta (int a float b float c char d)
Acesta este antetul unei funcţii procedurale Numele funcţiei este beta iar parametrii folosiţi
pentru comunicare sunt a de tip int b şi c de tip float şi d de tip char
void gama ( )
6
Acesta este antetul unei funcţii procedurale Numele funcţiei este gama şi nu foloseşte parametri
pentru comunicare
Observația 1 Separarea parametrilor icircn listă se face prin caracterul virgulă ()
Observația 2 Tipul parametrilor poate fi
orice tip standard al sistemului folosit pentru date elementare
o icircntreg (int unsigned long) real (double float long double) sau caracter (char sau
unsigned char)
o tipul pointer sau orice tip de structură de date (vector matrice şir de caractere sau
icircnregistrare)
orice tip definit de utilizator icircnainte de a defini subprogramul
Observația 3 Numele subprogramului poate fi folosit icircn trei locuri distincte
icircn prototipul subprogramului unde are un rol declarativ
icircn antetul subprogramului unde are un rol de definiţie dar şi declarativ
icircn apelul subprogramului unde are rol de activare
b Corpul subprogramului este un bloc care conţine atacirct instrucţiuni declarative cacirct şi
instrucţiuni imperative Variabilele de memorie declarate icircn corpul subprogramului se
numesc variabile locale Icircn cazul unei funcţii operand ultima instrucţiune din corpul
subprogramului trebuie să fie instrucţiunea return care are sintaxa
Valoarea obţinută prin evaluarea expresiei ltexpresiegt va fi atribuită funcţiei operand (va fi
valoarea returnată prin numele funcţiei) Rezultatul expresiei trebuie să fie de acelaşi tip cu tipul funcţiei
sau de un tip care poate fi convertit implicit icircn tipul funcţiei
Cacircnd compilatorul C++ icircntacirclneşte icircntr-un subprogram instrucţiunea return termină execuţia
subprogramului şi redă controlul modulului apelant Prin urmare dacă veţi scrie icircn subprogram după
instrucţiunea return alte instrucţiuni ele nu vor fi executate
Prototipul subprogramului este o linie de program aflată icircnaintea modulului care apelează
subprogramul prin care se comunică compilatorului informaţii despre subprogram (se declară
subprogramul)
Prin declararea programului compilatorul primeşte informaţii despre modul icircn care se poate
apela subprogramul şi poate face verificări la apelurile de subprogram icircn ceea ce priveşte tipul
parametrilor folosiţi pentru comunicare şi a modului icircn care poate face conversia acestor parametri
Un subprogram pentru a putea fi folosit trebuie declarat Pentru declararea lui se foloseşte
prototipul El conţine trei categorii de informaţii la fel ca şi antetul subprogramului tipul rezultatului
numele subprogramului şi tipul parametrilor folosiţi pentru comunicare Pentru fiecare parametru din
antetul subprogramului se poate preciza numai tipul nu şi numele lui
Prototipul unui subprogram este de forma
unde lista tipului parametrilor are următoarea forma
7
Observația 4 Separarea tipurilor de parametri icircn listă se face prin caracterul virgulă () Lista trebuie să
conţină atacirctea tipuri de parametri cacircţi parametri au fost definiţi icircn antetul subprogramului Icircn listă se
precizează tipul de dată la care se referă icircn aceeaşi ordine icircn care au fost scrişi parametrii la definirea lor
icircn antet
Observația 5 Spre deosebire de antetul subprogramului prototipul se termină cu caracterul
Pentru funcţiile al căror antet a fost precizat anterior (a se vedea exemplele anterior prezentate)
prototipurile vor fi
float alfa (int int float)
void beta (int float float char)
void gama ()
Subprogramul trebuie să fie cunoscut atunci cacircnd se cere prin apel activarea lui
dacă subprogramul este standard trebuie inclus fişierul care conţine prototipul subprogramului
icircn fişierul sursă
dacă subprogramul este utilizator trebuie declarat fie prin folosirea prototipului fie prin
definirea lui icircnaintea apelului
Icircn funcţie de modul icircn care a fost definit subprogramul se activează fie printr-o instrucţiune
procedurală fie ca operand icircntr-o expresie
Pentru funcţiile al căror antet a fost precizat anterior activarea se poate face astfel
exemplul 1
int xy float zw
w = alfa (xyz)
exemplul 2
int x float yz char w
beta (x y z w)
exemplul 3
gama ()
Orice subprogram trebuie declarat şi definit Declararea unui subprogram este necesară pentru
ca el să fie cunoscut de subprogramele care icircl apelează Declararea lui poate fi făcută fie prin prototip
fie prin definiţia lui (antetul icircmpreună cu corpul subprogramului) Pentru a declara subprogramul fie
scrieţi prototipul icircnaintea subprogramelor care icircl apelează putacircnd scrie apoi definiţia lui oriunde icircn
program fie definiţi subprogramul icircnaintea subprogramului care icircl apelează Icircn cele ce urmează se
prezintă un exemplu
Aşadar
Prototipul subprogramului declară subprogramul
Apelul subprogramului execută subprogramul
8
Antetul subprogramului specifică numele subprogramului şi tipul argumentelor şi al valorilor
returnate iar corpul subprogramului icircl defineşte adică specifică ceea ce trebuie să realizeze
subprogramul
Icircn concluzie forma generală a unui subprogram este prezentată mai jos
unde
tip_returnat Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi int char
char long float void etc Icircn cazul icircn care tipul rezultatului este diferit de void corpul funcţiei
trebuie să conţină cel puţin o instrucţiune return Icircnstrucţiunea return va specifica valoarea
calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat
nume_funcție Reprezintă numele dat funcţiei de către cel ce o defineşte pentru a o putea apela
lista parametrilor formali Reprezintă o listă de declaraţii de variabile separate prin virgulă
Această listă poate să fie şi vidă
instrucțiune Este o instrucţiune vidă sau o instrucţiune simplă sau o instrucţiune compusă
Icircn altă odine de idei construirea subprogramelor se face prin
Antet ndash conţine date despre tipul rezultatului nume şi parametrii efectivi Dacă funcţia nu
returnează nimic tipul este void()
Corp ndash este un bloc de instrucţiuni declarative şi imperative (de execuţie) ce definesc modul de
acţiune al subprogramului
Prototip ndash pentru a putea fi apelată funcţia trebuie declarată Conţine date despre tipul
rezultatului nume şi parametrii efectivi
Apel ndash este modul prin care subprogramul e pus icircn execuţie Apelul poate fi făcut ori de cacircte ori
este nevoie
Parametrii ndash datele care circulă icircntre modulul appelant şi apelat se introduc icircntre paranteze
după numele subprogramului
Modul apelant ndash blocul ce conţine apelul subprogramului
Modul apelat ndash blocul ce conţine funcţia (subprogramul apelat)
Exemplu
9
Icircn memoria internă fiecărui subprogram i se alocă o zonă de memorie icircn care este icircncărcat codul
executabil
La apelarea unui subprogram compilatorul icirci predă controlul adică icircncep să se execute
instrucţiunile subprogramului pacircnă la icircntacirclnirea unei instrucţiuni return sau pacircnă la sfacircrşitul blocului
care formează corpul subprogramului după care compilatorul redă controlul modulului apelant adică va
continua execuţia cu instrucţiunea care urmează icircn modulul apelant imediat după instrucţiunea care a
apelat subprogramul Acest mecanism de transfer al controlului se poate realiza deoarece icircntr-o zonă de
memorie internă numită stiva sistemului (stack) se păstrează temporar informaţii despre subprogramul
apelant Aceste informaţii sunt introduse icircn stivă atunci cacircnd este apelat subprogramul Ele formează
instanţa subprogramului
Etapele executate la apelarea subprogramului sunt
1 Se icircntrerupe execuţia modulului apelant
2 Se pregăteşte stiva sistemului astfel
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
6
Acesta este antetul unei funcţii procedurale Numele funcţiei este gama şi nu foloseşte parametri
pentru comunicare
Observația 1 Separarea parametrilor icircn listă se face prin caracterul virgulă ()
Observația 2 Tipul parametrilor poate fi
orice tip standard al sistemului folosit pentru date elementare
o icircntreg (int unsigned long) real (double float long double) sau caracter (char sau
unsigned char)
o tipul pointer sau orice tip de structură de date (vector matrice şir de caractere sau
icircnregistrare)
orice tip definit de utilizator icircnainte de a defini subprogramul
Observația 3 Numele subprogramului poate fi folosit icircn trei locuri distincte
icircn prototipul subprogramului unde are un rol declarativ
icircn antetul subprogramului unde are un rol de definiţie dar şi declarativ
icircn apelul subprogramului unde are rol de activare
b Corpul subprogramului este un bloc care conţine atacirct instrucţiuni declarative cacirct şi
instrucţiuni imperative Variabilele de memorie declarate icircn corpul subprogramului se
numesc variabile locale Icircn cazul unei funcţii operand ultima instrucţiune din corpul
subprogramului trebuie să fie instrucţiunea return care are sintaxa
Valoarea obţinută prin evaluarea expresiei ltexpresiegt va fi atribuită funcţiei operand (va fi
valoarea returnată prin numele funcţiei) Rezultatul expresiei trebuie să fie de acelaşi tip cu tipul funcţiei
sau de un tip care poate fi convertit implicit icircn tipul funcţiei
Cacircnd compilatorul C++ icircntacirclneşte icircntr-un subprogram instrucţiunea return termină execuţia
subprogramului şi redă controlul modulului apelant Prin urmare dacă veţi scrie icircn subprogram după
instrucţiunea return alte instrucţiuni ele nu vor fi executate
Prototipul subprogramului este o linie de program aflată icircnaintea modulului care apelează
subprogramul prin care se comunică compilatorului informaţii despre subprogram (se declară
subprogramul)
Prin declararea programului compilatorul primeşte informaţii despre modul icircn care se poate
apela subprogramul şi poate face verificări la apelurile de subprogram icircn ceea ce priveşte tipul
parametrilor folosiţi pentru comunicare şi a modului icircn care poate face conversia acestor parametri
Un subprogram pentru a putea fi folosit trebuie declarat Pentru declararea lui se foloseşte
prototipul El conţine trei categorii de informaţii la fel ca şi antetul subprogramului tipul rezultatului
numele subprogramului şi tipul parametrilor folosiţi pentru comunicare Pentru fiecare parametru din
antetul subprogramului se poate preciza numai tipul nu şi numele lui
Prototipul unui subprogram este de forma
unde lista tipului parametrilor are următoarea forma
7
Observația 4 Separarea tipurilor de parametri icircn listă se face prin caracterul virgulă () Lista trebuie să
conţină atacirctea tipuri de parametri cacircţi parametri au fost definiţi icircn antetul subprogramului Icircn listă se
precizează tipul de dată la care se referă icircn aceeaşi ordine icircn care au fost scrişi parametrii la definirea lor
icircn antet
Observația 5 Spre deosebire de antetul subprogramului prototipul se termină cu caracterul
Pentru funcţiile al căror antet a fost precizat anterior (a se vedea exemplele anterior prezentate)
prototipurile vor fi
float alfa (int int float)
void beta (int float float char)
void gama ()
Subprogramul trebuie să fie cunoscut atunci cacircnd se cere prin apel activarea lui
dacă subprogramul este standard trebuie inclus fişierul care conţine prototipul subprogramului
icircn fişierul sursă
dacă subprogramul este utilizator trebuie declarat fie prin folosirea prototipului fie prin
definirea lui icircnaintea apelului
Icircn funcţie de modul icircn care a fost definit subprogramul se activează fie printr-o instrucţiune
procedurală fie ca operand icircntr-o expresie
Pentru funcţiile al căror antet a fost precizat anterior activarea se poate face astfel
exemplul 1
int xy float zw
w = alfa (xyz)
exemplul 2
int x float yz char w
beta (x y z w)
exemplul 3
gama ()
Orice subprogram trebuie declarat şi definit Declararea unui subprogram este necesară pentru
ca el să fie cunoscut de subprogramele care icircl apelează Declararea lui poate fi făcută fie prin prototip
fie prin definiţia lui (antetul icircmpreună cu corpul subprogramului) Pentru a declara subprogramul fie
scrieţi prototipul icircnaintea subprogramelor care icircl apelează putacircnd scrie apoi definiţia lui oriunde icircn
program fie definiţi subprogramul icircnaintea subprogramului care icircl apelează Icircn cele ce urmează se
prezintă un exemplu
Aşadar
Prototipul subprogramului declară subprogramul
Apelul subprogramului execută subprogramul
8
Antetul subprogramului specifică numele subprogramului şi tipul argumentelor şi al valorilor
returnate iar corpul subprogramului icircl defineşte adică specifică ceea ce trebuie să realizeze
subprogramul
Icircn concluzie forma generală a unui subprogram este prezentată mai jos
unde
tip_returnat Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi int char
char long float void etc Icircn cazul icircn care tipul rezultatului este diferit de void corpul funcţiei
trebuie să conţină cel puţin o instrucţiune return Icircnstrucţiunea return va specifica valoarea
calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat
nume_funcție Reprezintă numele dat funcţiei de către cel ce o defineşte pentru a o putea apela
lista parametrilor formali Reprezintă o listă de declaraţii de variabile separate prin virgulă
Această listă poate să fie şi vidă
instrucțiune Este o instrucţiune vidă sau o instrucţiune simplă sau o instrucţiune compusă
Icircn altă odine de idei construirea subprogramelor se face prin
Antet ndash conţine date despre tipul rezultatului nume şi parametrii efectivi Dacă funcţia nu
returnează nimic tipul este void()
Corp ndash este un bloc de instrucţiuni declarative şi imperative (de execuţie) ce definesc modul de
acţiune al subprogramului
Prototip ndash pentru a putea fi apelată funcţia trebuie declarată Conţine date despre tipul
rezultatului nume şi parametrii efectivi
Apel ndash este modul prin care subprogramul e pus icircn execuţie Apelul poate fi făcut ori de cacircte ori
este nevoie
Parametrii ndash datele care circulă icircntre modulul appelant şi apelat se introduc icircntre paranteze
după numele subprogramului
Modul apelant ndash blocul ce conţine apelul subprogramului
Modul apelat ndash blocul ce conţine funcţia (subprogramul apelat)
Exemplu
9
Icircn memoria internă fiecărui subprogram i se alocă o zonă de memorie icircn care este icircncărcat codul
executabil
La apelarea unui subprogram compilatorul icirci predă controlul adică icircncep să se execute
instrucţiunile subprogramului pacircnă la icircntacirclnirea unei instrucţiuni return sau pacircnă la sfacircrşitul blocului
care formează corpul subprogramului după care compilatorul redă controlul modulului apelant adică va
continua execuţia cu instrucţiunea care urmează icircn modulul apelant imediat după instrucţiunea care a
apelat subprogramul Acest mecanism de transfer al controlului se poate realiza deoarece icircntr-o zonă de
memorie internă numită stiva sistemului (stack) se păstrează temporar informaţii despre subprogramul
apelant Aceste informaţii sunt introduse icircn stivă atunci cacircnd este apelat subprogramul Ele formează
instanţa subprogramului
Etapele executate la apelarea subprogramului sunt
1 Se icircntrerupe execuţia modulului apelant
2 Se pregăteşte stiva sistemului astfel
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
7
Observația 4 Separarea tipurilor de parametri icircn listă se face prin caracterul virgulă () Lista trebuie să
conţină atacirctea tipuri de parametri cacircţi parametri au fost definiţi icircn antetul subprogramului Icircn listă se
precizează tipul de dată la care se referă icircn aceeaşi ordine icircn care au fost scrişi parametrii la definirea lor
icircn antet
Observația 5 Spre deosebire de antetul subprogramului prototipul se termină cu caracterul
Pentru funcţiile al căror antet a fost precizat anterior (a se vedea exemplele anterior prezentate)
prototipurile vor fi
float alfa (int int float)
void beta (int float float char)
void gama ()
Subprogramul trebuie să fie cunoscut atunci cacircnd se cere prin apel activarea lui
dacă subprogramul este standard trebuie inclus fişierul care conţine prototipul subprogramului
icircn fişierul sursă
dacă subprogramul este utilizator trebuie declarat fie prin folosirea prototipului fie prin
definirea lui icircnaintea apelului
Icircn funcţie de modul icircn care a fost definit subprogramul se activează fie printr-o instrucţiune
procedurală fie ca operand icircntr-o expresie
Pentru funcţiile al căror antet a fost precizat anterior activarea se poate face astfel
exemplul 1
int xy float zw
w = alfa (xyz)
exemplul 2
int x float yz char w
beta (x y z w)
exemplul 3
gama ()
Orice subprogram trebuie declarat şi definit Declararea unui subprogram este necesară pentru
ca el să fie cunoscut de subprogramele care icircl apelează Declararea lui poate fi făcută fie prin prototip
fie prin definiţia lui (antetul icircmpreună cu corpul subprogramului) Pentru a declara subprogramul fie
scrieţi prototipul icircnaintea subprogramelor care icircl apelează putacircnd scrie apoi definiţia lui oriunde icircn
program fie definiţi subprogramul icircnaintea subprogramului care icircl apelează Icircn cele ce urmează se
prezintă un exemplu
Aşadar
Prototipul subprogramului declară subprogramul
Apelul subprogramului execută subprogramul
8
Antetul subprogramului specifică numele subprogramului şi tipul argumentelor şi al valorilor
returnate iar corpul subprogramului icircl defineşte adică specifică ceea ce trebuie să realizeze
subprogramul
Icircn concluzie forma generală a unui subprogram este prezentată mai jos
unde
tip_returnat Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi int char
char long float void etc Icircn cazul icircn care tipul rezultatului este diferit de void corpul funcţiei
trebuie să conţină cel puţin o instrucţiune return Icircnstrucţiunea return va specifica valoarea
calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat
nume_funcție Reprezintă numele dat funcţiei de către cel ce o defineşte pentru a o putea apela
lista parametrilor formali Reprezintă o listă de declaraţii de variabile separate prin virgulă
Această listă poate să fie şi vidă
instrucțiune Este o instrucţiune vidă sau o instrucţiune simplă sau o instrucţiune compusă
Icircn altă odine de idei construirea subprogramelor se face prin
Antet ndash conţine date despre tipul rezultatului nume şi parametrii efectivi Dacă funcţia nu
returnează nimic tipul este void()
Corp ndash este un bloc de instrucţiuni declarative şi imperative (de execuţie) ce definesc modul de
acţiune al subprogramului
Prototip ndash pentru a putea fi apelată funcţia trebuie declarată Conţine date despre tipul
rezultatului nume şi parametrii efectivi
Apel ndash este modul prin care subprogramul e pus icircn execuţie Apelul poate fi făcut ori de cacircte ori
este nevoie
Parametrii ndash datele care circulă icircntre modulul appelant şi apelat se introduc icircntre paranteze
după numele subprogramului
Modul apelant ndash blocul ce conţine apelul subprogramului
Modul apelat ndash blocul ce conţine funcţia (subprogramul apelat)
Exemplu
9
Icircn memoria internă fiecărui subprogram i se alocă o zonă de memorie icircn care este icircncărcat codul
executabil
La apelarea unui subprogram compilatorul icirci predă controlul adică icircncep să se execute
instrucţiunile subprogramului pacircnă la icircntacirclnirea unei instrucţiuni return sau pacircnă la sfacircrşitul blocului
care formează corpul subprogramului după care compilatorul redă controlul modulului apelant adică va
continua execuţia cu instrucţiunea care urmează icircn modulul apelant imediat după instrucţiunea care a
apelat subprogramul Acest mecanism de transfer al controlului se poate realiza deoarece icircntr-o zonă de
memorie internă numită stiva sistemului (stack) se păstrează temporar informaţii despre subprogramul
apelant Aceste informaţii sunt introduse icircn stivă atunci cacircnd este apelat subprogramul Ele formează
instanţa subprogramului
Etapele executate la apelarea subprogramului sunt
1 Se icircntrerupe execuţia modulului apelant
2 Se pregăteşte stiva sistemului astfel
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
8
Antetul subprogramului specifică numele subprogramului şi tipul argumentelor şi al valorilor
returnate iar corpul subprogramului icircl defineşte adică specifică ceea ce trebuie să realizeze
subprogramul
Icircn concluzie forma generală a unui subprogram este prezentată mai jos
unde
tip_returnat Reprezintă tipul rezultatului calculat şi returnat de funcţie şi poate fi int char
char long float void etc Icircn cazul icircn care tipul rezultatului este diferit de void corpul funcţiei
trebuie să conţină cel puţin o instrucţiune return Icircnstrucţiunea return va specifica valoarea
calculată şi returnată de funcţie care trebuie să fie de acelaşi tip ca şi tip_returnat
nume_funcție Reprezintă numele dat funcţiei de către cel ce o defineşte pentru a o putea apela
lista parametrilor formali Reprezintă o listă de declaraţii de variabile separate prin virgulă
Această listă poate să fie şi vidă
instrucțiune Este o instrucţiune vidă sau o instrucţiune simplă sau o instrucţiune compusă
Icircn altă odine de idei construirea subprogramelor se face prin
Antet ndash conţine date despre tipul rezultatului nume şi parametrii efectivi Dacă funcţia nu
returnează nimic tipul este void()
Corp ndash este un bloc de instrucţiuni declarative şi imperative (de execuţie) ce definesc modul de
acţiune al subprogramului
Prototip ndash pentru a putea fi apelată funcţia trebuie declarată Conţine date despre tipul
rezultatului nume şi parametrii efectivi
Apel ndash este modul prin care subprogramul e pus icircn execuţie Apelul poate fi făcut ori de cacircte ori
este nevoie
Parametrii ndash datele care circulă icircntre modulul appelant şi apelat se introduc icircntre paranteze
după numele subprogramului
Modul apelant ndash blocul ce conţine apelul subprogramului
Modul apelat ndash blocul ce conţine funcţia (subprogramul apelat)
Exemplu
9
Icircn memoria internă fiecărui subprogram i se alocă o zonă de memorie icircn care este icircncărcat codul
executabil
La apelarea unui subprogram compilatorul icirci predă controlul adică icircncep să se execute
instrucţiunile subprogramului pacircnă la icircntacirclnirea unei instrucţiuni return sau pacircnă la sfacircrşitul blocului
care formează corpul subprogramului după care compilatorul redă controlul modulului apelant adică va
continua execuţia cu instrucţiunea care urmează icircn modulul apelant imediat după instrucţiunea care a
apelat subprogramul Acest mecanism de transfer al controlului se poate realiza deoarece icircntr-o zonă de
memorie internă numită stiva sistemului (stack) se păstrează temporar informaţii despre subprogramul
apelant Aceste informaţii sunt introduse icircn stivă atunci cacircnd este apelat subprogramul Ele formează
instanţa subprogramului
Etapele executate la apelarea subprogramului sunt
1 Se icircntrerupe execuţia modulului apelant
2 Se pregăteşte stiva sistemului astfel
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
9
Icircn memoria internă fiecărui subprogram i se alocă o zonă de memorie icircn care este icircncărcat codul
executabil
La apelarea unui subprogram compilatorul icirci predă controlul adică icircncep să se execute
instrucţiunile subprogramului pacircnă la icircntacirclnirea unei instrucţiuni return sau pacircnă la sfacircrşitul blocului
care formează corpul subprogramului după care compilatorul redă controlul modulului apelant adică va
continua execuţia cu instrucţiunea care urmează icircn modulul apelant imediat după instrucţiunea care a
apelat subprogramul Acest mecanism de transfer al controlului se poate realiza deoarece icircntr-o zonă de
memorie internă numită stiva sistemului (stack) se păstrează temporar informaţii despre subprogramul
apelant Aceste informaţii sunt introduse icircn stivă atunci cacircnd este apelat subprogramul Ele formează
instanţa subprogramului
Etapele executate la apelarea subprogramului sunt
1 Se icircntrerupe execuţia modulului apelant
2 Se pregăteşte stiva sistemului astfel
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
10
se introduce adresa de revenire icircn modulul apelant
se introduc valorile parametrilor cu care a fost apelat subprogramul
se rezervă spaţiu pentru variabilele locale declarate icircn subprogram
3 Se lansează icircn execuţie codul executabil al subprogramului apelat
Etapele executate la terminarea subprogramului sunt
1 Se eliberează din stivă spaţiul ocupat de variabilele locale şi de parametri
2 Se extrage din stivă adresa de revenire icircn modulul apelant
3 Se continuă execuţia cu instrucţiunea de la adresa extrasă din stivă
Astfel pentru exemplele anterioare de declaraţii de subprograme la apelarea lor icircn stivă se vor
introduce următoarele informaţii
Aşadar icircn timpul execuţiei subprogramului icircn stivă sunt păstrate datele cu care el lucrează
variabilele locale şi parametrii cu care a fost apelat Instrucţiunile subprogramului pot modifica aceste
date Modificările se execută asupra valorilor memorate icircn stivă Cacircnd se termină execuţia
subprogramului trebuie să se reia execuţia modulului apelant cu instrucţiunea de adresă de revenire
Pentru a se ajunge icircn stivă la adresa de revenire spaţiul ocupat de parametri şi de variabilele
locale este eliberat şi se pierd valorile lor
Exemplu Să se verifice dacă un număr natural n citit de la tastatură este număr prim Pentru
testarea numărului se va folosi un subprogram Obiectivul acestui exemplu este exemplificarea modului
icircn care este folosită stiva sistemului la apelarea unui subprogram
Funcţia prim(a) furnizează prin numele său o valoare icircntreagă ce poate fi interpretată ca o valoarea
logică 0 ndash false sau 1 ndash true Icircn variabila locală x se calculează valoarea funcţiei prim (1 sau 0 icircn funcţie
de numărul a ndash dacă este sau nu este număr prim)
Conţinutul stivei sistemului va fi
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
11
14 Transmiterea parametrilor
Transferul de parametri este o tehnică folosită pentru schimbul de date icircntre module
Transmiterea datelor icircntre apelant şi apelat se poate face fie prin parametri fie prin variabile
globale Prin utilizarea variabilelor globale nu se face un transfer propriu-zis ci se folosesc icircn comun
anumite zone de memorie
Transferul se poate face prin valoare sau prin referinţăadresă
Datele care se transferă icircntre apelant şi apelat se introduc icircntre paranteze după identificatorul
subprogramului
Icircn antetul subprogramului parametrii se icircnscriu prin tip şi nume separaţi prin virgulă fără a fi
grupaţi Ei se numesc parametrii formali
Icircn apelul subprogramului se icircnscriu separaţi prin virgulă icircn aceeaşi mordine ca icircn antet prin
valori concreteEi se numesc parametrii efectivi (actuali)
Regula de corspondenţă notifică o anumită concordanţă icircntre numărul ordinea şi tipul
parametrilor formali şi a parametrilor efectivi
Numărul parametrilor formali poate să difere de numărul parametrilor efectivi icircn cazul funcţiilor
cu număr de parametri variabil respectiv icircn cazul supraicircncărcării funcţiilor
Tipul parametrilor formali poate să difere de tipul parametrilor efectiviicircn cazul conversiei
implicite a parametrilor efectivi icircn tipul parametrilor formali ca o operaţie de atribuire respectiv
icircn cazul supraicircncărcării funcţiei
Numele parametrilor formali poate să difere de numele parametrilor efectivi
Parametrii sunt memoraţi icircn segmentul de stivă icircn ordinea icircnscrierii lor
Exemplu Să se construiască un subprogram care să calculeze valoarea absolută a unui număr
real Numele subprogramului este mod_r Acest subprogram va fi construit icircn două variante ca funcţie
procedurală şi ca funcţie operand Icircn ambele cazuri modulul apelant va fi funcţia rădăcină iar modulul
apelat va fi subprogramul mod_r Principalul scop a acestui exemplu este exemplificarea modului icircn care
poate fi construit un subprogram C++
Varianta 1
Icircn cazul funcţiei procedurale subprogramul va afişa valoarea modulului numărului şi nu va
furniza niciun rezultat funcţiei rădăcină care icircl apelează El va primi valoarea numărului de la funcţia
rădăcină prin intermediul parametrului
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm
12
Varianta 2
Icircn cazul funcţiei operand subprogramul va returna funcţiei rădăcină prin numele său valoarea
absolută a numărului El va primi valoarea numărului de la funcţia rădăcină prin intermediul
parametrului
Transmiterea prin referinţăadresă - se transmite adresa parametrului actual Este utilizată la
prelucrarea unei variabile icircn interiorul unei funcţii astfel icircncacirct la revenirea din funcţie variabila să
reţină valoarea modificată nu valoarea de intrare
Icircn momentul apelării subprogramului icircn stivă este icircncărcată adresa de memorie la care se găseşte
valoarea parametrului Subprogramul va lucra direct icircn zona de memorie icircn care se găseşte data Atacirct
modulul apelant cacirct şi subprogramul lucrează asupra aceleiaşi date şi orice modificare a valorii acestui
parametru făcută icircn subprogram se va reflecta şi icircn modulul apelant La terminarea execuţiei
subprogramului este eliberată din stivă zona icircn care este memorată adresa parametrului
Parametrul prin intermediul căruia se face transferul prin referinţă se numeşte parametru
variabilă
Acest transfer se recomandă pentru parametrii de intrare-ieşire sau parametrii de ieşire Modulul
apelant transmite prin aceşti parametri date de intrare-ieşire către subprogram subprogramul preia data
13
o prelucrează şi o returnează modulului apelant Acest parametru mai poate fi şi un rezultat (dată de
ieşire) obţinut icircn urma prelucrărilor din subprogram care este returnat apoi modulului apelant
Distincţia dintre un parametru valoare şi un parametru variabilă (definirea tipului de transfer) se
face icircn lista de parametri formali din antetul subprogramului icircn care parametrii variabilă sunt precedaţi
de operatorul adresă de memorie amp (Parametrii transmişi prin referinţă vor fi precedaţi de caracterul
ampersand ldquoamprdquo atacirct la declararea cacirct şi la definirea funcţiei)
Un exemplu de antet de subprogram (subprogramul furnizează prin parametrii ma şi mg media
aritmetică şi respectiv media geometrică a două numere transmise subprogramului prin parametrii a şi
b) este
Apelarea acestui subprogram se va face prin medie(xym1m2)
Din modulul apelant se transmit parametrilor a şi b care sunt parametri valoare ndash valorile
variabilelor x şi respectiv y iar parametrilor ma şi mb care sunt de tip parametri variabilă ndash adresele
variabilelor m1 şi respectiv m2
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin referinţă trebuie să
fie variabile de memorie
Transmiterea prin referinţă icircnseamnă că parametrii sunt transmişi prin referinţă tunci cacircnd ne
interesează ca la revenirea din subprogram variabila transmisă să reţină valoarea stabilită icircn timpul
execuţiei subprogramului Pentru aceasta parametrii efectivi trebuie să fie referinţe la variabile
Subprogramul reţine icircn stivă adresa variabilei
Transmiterea prin valoare ndash se transmite o copie a parametrului actual Este utilizată la
relucrarea unei variabile icircn interiorul unei funcţii La revenirea din funcţie variabila nu reţine valoarea
modificată ci valoarea de intrare
Modulul apelant transmite prin parametru către subprogram date de intrare Icircn momentul
apelării subprogramului o copie a valorii parametrului este icircncărcată icircn stivăEl este văzut icircn
subprogram ca o variabilă locală care este iniţializată cu valoarea transmisă de modulul apelant prin
parametrul actual din apel Valoarea acestei variabile se poate modifica icircn subprogram dar această
modificare nu se va reflecta şi icircn modulul apelant deoarece modificarea se face icircn stivă şi la terminarea
execuţiei subprogramului zona din stivă icircn care este memorat parametrul este eliberată
Parametrul prin intermediul căruia se face transferul prin valoare se numeşte parametru
valoare
Acest transfer se foloseşte icircn general numai pentru parametrii de intrare Icircn cazul icircn care parametrii
transmişi prin valoare sunt parametri de ieşire sau de intrare-ieşire pentru a putea transmite rezultatul
obţinut icircn subprogram către modulul apelant se pot folosi variabile de tip pointeri
Un exemplu de antet de subprogram pentru un astfel de transfer (subprogramul furnizează prin
parametrii ma şi mg media aritmetică şi respectiv media geometrică a două numere transmise
subprogramului prin parametrii a şi b) este prezentat mai jos
14
Apelarea acestui subprogram se va face prin medie(xyampm1ampm2)
Parametrilor a şi b li se transmit din modulul apelant valorile variabilelor x şi respectiv y iar
parametrilor de tip pointer ma şi mb valoarea adreselor variabilelor m1 şi respectiv m2
Parametrii transmişi prin valoare se folosesc doar ca parametrii de intrare Pentru parametrii de
ieşire se va folosi instrucţiunea return()
Icircn apel parametrii efectivi corespunzători parametrilor formali transmişi prin valoare pot fi
valori variabile expresii sau alte funcţii
Transmiterea prin valoare se utilizează atunci cacircnd suntem interesaţi ca subprogramul să lucreze
cu acea valoare dar icircn prelucrare nu ne interesează ca parametrul efectiv cel din blocul apelant să
reţină valoarea modificată icircn subprogram
Se pot transmite prin valoare
Valorile reţinute de variabile parametrii efectivi trebuie să fie numele variabilelor care se trimit
prin valoare
Expresii care pot conţine şi funcţii parametrii efectivi sunt expresii care mai icircntacirci se
evaluează
Observația 1 Pentru transmiterea unor rezultate din subprogram către modulul apelant (parametru de
ieşire sau de intrare-ieşire) se foloseşte fie transferul prin referinţă fie transferul prin valoare folosind
variabile de tip pointeri
Observația 2 Parametrii actuali corespunzători parametrilor valoare pot fi exprimaţi prin
valoare (constantă)
expresie
variabilă de memorie
adresă a unei variabile de memorie (este obligatorie icircn cazul icircn care parametrii formali sunt de
tip pointer)
Observația 3 Parametrii formali corespunzători parametrilor valoare pot fi iniţializaţi icircn antetul
subprogramului La apelul subprogramului parametrilor formali li se atribuie valoarea parametrilor
actuali Dacă lipseşte un parametru actual parametrul formal va fi iniţializat cu valoarea din listă
Observația 4 Parametrii actuali corespunzători parametrilor variabilă pot fi exprimaţi numai prin
variabile de memorie
Exemplu Să se construiască un subprogram care să realizeze interschimbarea valorilor a două
variabile de memorie icircntregi Obiectivul acestui exemplu este exemplificarea modului icircn care pot fi
transmişi parametrii icircntre subprograme
15
Numele subprogramului este schimb Modulul apelant va fi funcţia rădăcină iar modulul apelat
va fi subprogramul schimb Din funcţia rădăcină se vor transfera subprogramului parametrii x şi y care
reprezintă variabilele a căror valoare se interschimbă Acest subprogram va fi construit icircn trei variante
icircn funcţie de modul icircn care sunt transferaţi parametrii
Varianta 1 Transferul parametrilor se face prin valoare
Varianta 2 Transferul parametrilor se face prin valoare folosind variabile de tip pointer
Varianta 3 Transferul parametrilor se face prin referință
15 Apelul subprogramelor
Apelul subprogramului este modul prin care subprogramul este pus icircn execuţie Apelul
subprogramului se poate realiza icircn două moduri
Printr-o instrucţiune de apel
Ca operand icircntr-o expresie
16
Instrucţiunea de apel a unui subprogram are următorul format general
unde
nume reprezintă numele subprogramului
lista parametrilor actuali este formată dintr-o succesiune de expresii separate prin virgulă
Se utilizează instrucţiuni de apel atunci cacircnd subprogramul nu returnează nici o valoare sau cacircnd
nu se doreşte utilizarea valorii returnate de subprogram ci doar efectuarea prelucrărilor descrise de
subprogram
Icircn cazul icircn care se doreşte utilizarea valorii returnate de subprogram ca operand icircntr-o expresie
se va apela subprogramul icircn cadrul expresiei astfel
Icircn această situaţie lipseşte caracterul bdquordquo care marchează sfacircrşitul instrucţiunii de apel La apelul
unui subprogram valorile parametrilor actuali sunt atribuite icircn ordine parametrilor formali
corespunzători
Construirea apelului subprogramului
Dacă subprogramul este definit de utilizator el trebuie declarat prin prototip sau prin definirea
subprogramului icircnainte de blocul apelant
Este modul prin care subprogramul este pus icircn execuţie Apelul poate fi făcut ori de cacircte ori este
nevoie
Apelul poate fi făcut din funcţia rădăcină main () dintr-o altă funcţie sau din ea icircnsăşi prin
autoapelare (recursivitate)
Dacă subprogramul este standard (de sistem) trebuie inclus fişierul ce conţine subprogramul
utilizat
Atacirct procedurile cacirct şi funcţiile trebuie definite icircnainte de a fi apelate
Apelarea unei funcţii nu este o instrucţiune de sine stătătoare ea trebuie inclusă ca operant icircn
cadrul unei expresii
Transmiterea parametrilor efectivi la apelul unei funcţii se face prin copierea valorilor
parametrilor efectivi icircn parametrii formali care sunt variabile locale ale funcţiei Icircn acest fel funcţia
apelată lucrează cu duplicate ale variabilelor parametrilor efectivi şi nu poate modifica accidental
variabile din funcţia apelantă Compilatorul generează o secvenţă de atribuiri la parametrii
formaliicircnainte de efectuarea saltului la prima instrucţiune din funcţia apelată
O funcţie recursivă este o funcţie care se apelaeză pe ea icircnsăşi Există două tipuri de funcţii
recursive
Funcţii cu un singur apel recursiv ca ultimă instrucţiune care se pot rescrie sub formă
nerecursivă (iterativă)
Funcţii cu unul sau mai multe apeluri recursive a căror formă trebuie să folosească o stivă pentru
memorarea unor rezultate intermediare
Recursivitatea este posibilă deoarece la fiecare apel al funcţiei adresa de revenire variabilele
locle şi parametrii formali sunt memorate icircntr-o stivă iar la ieşire din funcţie se scot din stivă toate
datele puse la intrarea icircn funcţie
O funcţie recursivă trebuie să conţină cel puţin o instrucţiune if de obicei la icircnceput prin care se
verifică dacă este necesar un apel recursiv sau se iese din funcţie
Apelul unei funcţii care nu returnează nici o valoare are forma generală
unde
parametru efectiv = parametru actual = parametru real = parametru de apel
lista parametrilor efectivi = fie vidă fie o expresie sau mai multe despărţite prin virgulă
Pentru a apela o funcţie aceasta trebui mai icircntacirci definită Astfel apelul unei funcţii trebuie
precedat de definiţia funcţiei respective
17
O a doua posibilitate de apelare a funcţiei constă icircn scrierea prototipului funcţiei icircnainte ca acesta
să fie apelată
Prototipul funcţiei conţine informaţii asemănătoare cu cele din antetul funcţiei Pot lipsi numele
parametrilor formali (contează doar tipul şi ordinea acestora) icircn plus acesa este urmat de ldquordquo
Exemplu Apelul unei funcții ce nu returnează o valoare
Exemplu Apelul unei funcții ce returnează o valoare
16 Modularizarea programelor (Tipuri de variabile domeniul şi plasarea subprogramelor
Variabile globale şi locale Domeniul de vizibilitate)
Variabilele pot fi definite icircn C++ icircn orice poziţie a programului Locul unde a fost definită o
variabilă determină domeniul de vizibilitate a acesteia Acest domeniu icircncepe icircn locul unde variabila este
definită şi se sfacircrşeşte icircn locul de icircncheiere a blocului ce o conţine
Prin domeniul de vizibilitate (valabilitate) se intelege zona de program in care e valabila
declararea sau definirea unui identificator
Variabilele globale sunt declarate la icircnceputul programului icircn afara funcţiilor inclusv icircn afara
rădăcinii Acestea sunt vizibile şi pot fi utilizate icircn orice punct al programului Sunt iniţilizate icircn mod
automat cu zero Durata lor de viaţă este pe tot parcursul executării programului
Variabilele declarate icircntr-o funcţie se numesc variabile locale şi pot fi referite numai din funcţia
respectivă Sunt vizibile doar icircn interiorul funcţiei Nu sunt iniţializate automat Durata lor de viaţă este
18
pe tot parcursul executării funcţiei icircn care au fost definite Domeniul de valabilitate a unei variabile
locale este funcţia sau instrucţiunea compusă icircn care a fost definită
Icircn cazul icircn care există o variabilă locală care are acelaşi nume cu o variabilă globală aceste două
variabile se numesc variabile omonime Variabilele locale sunt prioritare variabilelor globale omonime
Exemplu
include ltiostreamgt
int z=8
void schimb(int x int ampy)
int s se poate folosi doar icircn acest subprogram este o variabilă locală
x=15 parametru de intrare transmis prin valoare
y=16 parametru de iesire transmis prin referință
z=9 variabila globala se transmit modificările
void main()
int a=2b=3
schimb(ab)
coutltlta=ltltaltlt b=ltltbltlt z=ltltz se va tipari a=2 b=16 z=9
Plasarea subprogramelor icircn cadrul programului
A defini un subprogram icircnseamnă al scrie efectiv după o anumită structură A declara un
subprogram icircnseamnă a-l anunţa Un subprogram nedeclarat nu poate fi folosit Definiţia unui
subprogram ţine loc şi de declaraţie
Orice program trebuie să conţină
Instrucţiuni imperative prin care se comandă executarea anumitor acţiuni
Declaraţii de variabile de funcţii etc necesare compilatorului dar fără efect la execuţie
Comentarii ignorate de compilator necesare utilizatorului
Instrucţiunile executabile sunt grupate icircn subprograme Icircn C++ trebuie să existe cel puţin o
funcţie ldquomainldquo cu care icircncepe execuţia unui program Celelalte funcţii sunt apelate din funcţia
ldquomainldquo sau din alte funcţii activate direct sau indirect de ldquomainldquo
Acoladele sunt necesare pentru a delimita definiţia unei funcţii care este un bloc de instrucţiuni
şi declaraţii Un program descrie procedurile de obţinere a unor rezultate pe baza unor date iniţiale şi
foloseşte rezultate intermediare Toate aceste date sunt memorate icircn variabile ale programului Pot exista
şi date constante ale căror valoari nu se pot modifica icircn cursul execuţiei Toate variabilele folosite icircntr-
un program trebuie definite sau declarate prin declaraţii ale limbajului de programare
Un program C este compus icircn general din mai multe functii dintre care functia main nu poate
lipsi deoarece cu ea icircncepe executia programului
Functiile pot face parte dintr-un singur fisier sursatilde sau din mai multe fisiere sursatilde Un fisier sursatilde
C este un fisier text care contine o succesiune de declaratii definitii de functii si eventual declaratii de
variabile
Antetul conţine tipul şi numele funcţiei şi o listatilde de argumente
Practic nu existatilde program care satilde nu apeleze functii din bibliotecile existente si care satilde nu continatilde
definitii de functii specifice aplicatiei respective
Motivele utilizatilderii de subprograme sunt multiple
Un program mare poate fi mai usor de scris de icircnteles si de modificat dacatilde este modular deci
format din module functionale relativ mici
Un subprogram poate fi reutilizat icircn mai multe aplicatii ceea ce reduce efortul de programare al
unei noi aplicatii
19
Un subprogram poate fi scris si verificat separat de restul aplicatiei ceea ce reduce timpul de
punere la punct a unei aplicatii mari (deoarece erorile pot apare numai la comunicarea icircntre
subprograme corecte)
Intretinerea unei aplicatii este simplificatatilde deoarece modificatilderile se fac numai icircn anumite
subprograme si nu afecteazatilde alte subprograme (care nici nu mai trebuie recompilate)
Utilizarea de functii permite dezvoltarea progresiva a unui program mare fie de jos icircn sus
(ldquobottom uprdquo) fie de sus icircn jos (ldquotop downrdquo) fie combinat
Exemplu Modularizarea unui program utilizacircnd subprograme Să se scrie un program care
pentru un număr citit de la tastatură va determina daca e număr prim perfect va face suma divizorilor și
va tipări pătratul lui
include ltiostreamgt
int sumad(int x)
int is=0
for(i=1iltxi++)
if (xi==0) s=s+i
return s
int prim(int x)
int is
s=0
for(i=2ilt=x2i++)
if((xi)==0) s=1
if (x==1) s=1
return s
void perfect(int x int sumint amprez)
if(sum==x) rez=0
else rez=1
void main()
int asumr
coutltltDati numarul cingtgta
if (prim(a)==0) coutltltaltlt este prim
else coutltltaltlt nu este prim
sum=sumad(a)
coutltltendlltltSuma divizorilor lui ltltaltlt este ltltsum
perfect(asumr)
if(r==0) coutltltendlltltaltlt este numar perfect
else coutltltendlltltaltlt nu este numar perfect
coutltltendlltltPatratul lui este ltltaa
Schema modulelor este
20
Modulul Sumad
subprogram de tip funcție
parametri de intrare numărul de tip icircntreg - x
parametri de ieșire nu are
scopul subprogramului calcularea sumei divizorilor unui număr și returnarea lui ca rezultat al
funcției (tip intreg)
Modulul Prim
subprogram de tip funcție
parametri de intrare numărul (tip intreg) - x
parametri de ieșire nu are
scopul subprogramului verificarea daca un număr este prim sau nu și returnarea ca rezultat al
funcției valoarea 0 dacă este prim și 1 dacă nu este prim
Modulul Perfect
subprogram de tip procedura
parametri de intrare numarul (tip intreg) - suma divizorilor (tip intreg) - sum
parametri de ieșire o variabilă a cărei valoare va fi 0 dacă este perfect sau 1 dacă numărul nu este
perfect - rez
scopul subprogramului compararea numărului cu suma divizorilor săi și atribuirea unei valori
corespunzătoare parametrului de ieșire
Capitolul 2
21 Alocarea memoriei (segmentul de date segmentul de stivă heap registrii)
Fiecărui program i se alocă trei zone distincte icircn memoria internă icircn care se găsesc memorate
variabilele programului
Segment de date
Segment de stivă
Heap
Există posibilitatea ca variabilele să fie memorate icircntr-un anumit registru al microprocesorului Icircn
acest caz timpul de acces la astfel de variabile este foarte mic deci se pot obţine programe optimizate
Moduri de alocare a memoriei
Statică variabile implementate icircn zona de date ndash globale Memoria este alocată la compilare icircn
segmentul de date din cadrul programului şi nu se mai poate modifica icircn cursul execuţiei
21
Variabilele externe definite icircn afara funcţiilor sunt implicit statice dar pot fi declarate static şi
variabile locale definite icircn cadrul funcţiilor
Auto variabile implementate icircn stivă ndash locale Memoria este alocată automat la activarea unei
funcţii icircn zona stivă alocată unui program şi este eliberată automat la terminarea funcţiei
Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Memoria se alocă icircn stiva ataşată programului
Dinamică variabile implementate icircn heap Memoria se alocă dinamic (la execuţie) icircn zona heap
ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii
de bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea
funcţiei free
Register variabile implementate icircntr-un registru de memorie
O variabilă se caracterizează prin 4 atribute Acestea sunt
clasa de memorare
vizibilitate
durata de viaţă
tipul variabilei
Clasa de memorare precizează locul unde este memorată variabila respectivă O variabilă poate
fi memorată icircn segmentul de date icircn cel de stivă icircn heap sau icircntr-un registru al microprocesorului
Vizibilitatea precizeză liniile textului sursă din care variabila respectivă poate fi accesată Există
Vizibilitate la nivel de bloc (instrucţiune compusă)
Vizibilitate la nivel de fişier ndash icircn cazul icircn care programul ocupă un singur fişier sursă
Vizibilitate la nivel de clasă - icircn cazul programării pe obiecte
Durata de viaţă reprezintă timpul icircn care variabila respectivă are alocat spaţiu icircn memoria
internă Există
Durata statică ndash variabila are alocat spaţiu icircn tot timpul execuţiei programului
Durata locală ndash variabila are alocat spaţiu icircn timpul icircn care se execută instrucţiunile blocului
respectiv
Durata dinamică ndash alocarea şi dezalocarea spaţiului necesar variabilei respective se face de
către programator prin operatori sau funcţii speciale
Atributele variabilelor globale sunt
Clasa de memorare ndash este segmentul de date
Vizibilitatea ndash icircn cazul icircn care declaraţiile acestora sunt icircnaintea tuturor funcţiilor acestea sunt
vizibile la nivelul icircntrgului program sau fişier Dacă anumite funcţii se află plasate icircnaintea
declaraţiilor acestor variabile atunci ele sunt vizibile doar pentru funcţiile care sunt plasate după
aceste declaraţii
Durata de viaţă ndash este statică Variabilele globale au spaţiu rezervat icircn tot timpul execuţiei
programului
Atributele variabilelor locale sunt
Clasa de memorare ndash este implicit segmentul de stivă Există posibilitatea ca acestea să fie
alocate icircn registrele microprocesorului caz icircn care declaraţia lor trebuie precedată de cuvacircntul
cheie ldquoregisterrdquo
Vizibilitatea ndash este la nivelul blocului icircn care au fost declarate
Durata de viaţă ndash este atacirct timp cacirct durează execuţia blocului respectiv
Clase de alocare a memoriei Auto Variabilele locale unui bloc (unei funcţii) şi parametrii formali sunt implicit din clasa auto
Durata de viaţă a acestor variabile este temporară memoria este alocată automat la activarea
22
bloculuifuncţiei icircn zona stivă alocată programului şi este eliberată automat la ieşirea din
blocterminarea funcţiei Variabilele locale nu sunt iniţializate Trebuie să le atribuim o valoare iniţială
Exemplu
int doi()
int x = 2
return x
int main()
int a
int b = 5
a = bdoi()
printf(ldquoa = dnrdquo a)
return 0
Conţinut stivă
(x) 2
(b) 5
(a) 10
Clase de alocare a memoriei Static Memoria este alocată la compilare icircn segmentul de date din cadrul programului şi nu se mai
poate modifica icircn cursul execuţiei Variabilele globale sunt implicit statice (din clasa static) Pot fi
declarate static şi variabile locale definite icircn cadrul funcţiilor folosind cuvacircntul cheie static O variabilă
sau o funcţie declarată (sau implicit) static are durata de viaţă egală cu cea a programului In consecinţă
o variabilă statică declarată icircntr-o funcţie icircşi păstrează valoarea icircntre apeluri succesive ale funcţiei spre
deosebire de variabilele auto care sunt realocate pe stivă la fiecare apel al funcţiei şi pornesc de fiecare
dată cu valoarea primită la iniţializarea lor (sau cu o valoare imprevizibilă dacă nu sunt iniţializate)
Exemplu
int f1()
int x = 1 Variabilă locală iniţializată cu 1 la fiecare apel al lui f1
int f2()
static int y = 99 Variabilă locală statică iniţializată cu 99 doar la primul apel al lui f2 valoarea
ei este reţinută pe parcursul apelurilor lui f2
int f()
static int nr_apeluri=0
nr_apeluri++
printf(funcţia f() este apelata pentru a d-a oaranldquo nr_apeluri)
return nr_apeluri
int main()
int i
23
for (i=0 ilt10 i++) f() f() apelata de 10 ori
printf(functia f() a fost apelata de d ori f()) 11 ori
return 0
Observația 1 Variabilele locale statice se folosesc foarte rar icircn practica programării (funcţia de
bibliotecă strtok este un exemplu de funcţie cu o variabilă statică) Variabilele statice pot fi iniţializate
numai cu valori constante (pentru că iniţializarea are loc la compilare) dar variabilele auto pot fi
iniţializate cu rezultatul unor expresii (pentru că iniţializarea are loc la execuţie) Observația 2 Toate variabilele externe (şi statice) sunt automat iniţializate cu valori zero
(inclusiv vectorii) Cuvacircntul cheie static face ca o variabilă globală sau o funcţie să fie privată(proprie)
unităţii unde a fost definită ea devine inaccesibilă altei unităţi chiar prin folosirea lui extern
Observația 3 Cantitatea de memorie alocată pentru variabilele cu nume rezultă din tipul
variabilei şi din dimensiunea declarată pentru vectori Memoria alocată dinamic este specificată explicit
ca parametru al funcţiilor de alocare icircn număr de octeţi
Memoria neocupată de datele statice şi de instrucţiunile unui program este icircmpărţită icircntre stivă şi
heap
Consumul de memorie stack (stiva) este mai mare icircn programele cu funcţii recursive (număr
mare de apeluri recursive)
Consumul de memorie heap este mare icircn programele cu vectori şi matrice alocate (şi realocate)
dinamic De observat că nu orice vector cu dimensiune constantă este un vector static un vector definit
icircntr-o funcţie (alta decacirct main) nu este static deoarece nu ocupă memorie pe toată durata de execuţie a
programului deşi dimensiunea sa este stabilită la scrierea programului Un vector definit icircntr-o funcţie
este alocat pe stivă la activarea funcţiei iar memoria ocupată de vector este eliberată automat la
terminarea funcţiei
Clase de alocare a memoriei register A treia clasă de memorare este clasa register pentru variabile cărora li se alocă registre ale
procesorului şi nu locaţii de memorie pentru un timp de acces mai bun
O variabilă declarată register solicită sistemului alocarea ei icircntr-un registru maşină dacă este
posibil
De obicei compilatorul ia automat decizia de alocare a registrelor maşinii pentru anumite
variabile auto din funcţii Se utilizează pentru variabile ldquofoarte solicitaterdquo pentru mărirea vitezei de
execuţie
Exemplu
register int i
for(i = 0 i lt N ++i)
hellip
se elibereaza registrul
Clase de alocare a memoriei extern
O variabilă externă este o variabilă definită icircn alt fişier Declaraţia extern icirci spune compilatorului
că identificatorul este definit icircn alt fişier sursă (extern) Ea este este alocată icircn funcţie de modul de
declarare din fişierul sursă
24
Exemplu
File1cpp
extern int i Declara aceasta variabila ca fiind definita in alt fisier
File2cpp
int i = 88 Definit aici
Alocarea dinamică a memoriei
Reamintim că pentru variabilele alocate dinamic memoria se alocă dinamic (la execuţie) icircn zona
heap ataşată programului dar numai la cererea explicită a programatorului prin apelarea unor funcţii de
bibliotecă (malloc calloc realloc) Memoria este eliberată numai la cerere prin apelarea funcţiei free
Principalele diferenţe icircntre alocarea statică şi cea dinamică sunt
La alocarea statică compilatorul alocă şi eliberează memoria automat ocupacircndu-se astfel de
gestiunea memoriei icircn timp ce la alocarea dinamică programatorul este cel care gestionează
memoria avacircnd un control deplin asupra adreselor de memorie şi a conţinutului lor
Entităţile alocate static sau auto sunt manipulate prin intermediul unor variabile icircn timp ce cele
alocate dinamic sunt gestionate prin intermediul pointerilor
Funcţiile standard pentru alocarea dinamica a memoriei sunt declarate icircn fişierele stdlibh şi
alloch
Alocarea memoriei de o dimensiune size octeţi se face astfel
void malloc(size_t size)
Alocarea memorie pentru nitems de dimensiune size octeţi şi iniţializarea zonei alocată
cu zerouri se face astfel
void calloc(int nitems size_t size)
Cele două funcţii au ca rezultat adresa zonei de memorie alocate (de tip void)Dacă cererea de
alocare nu poate fi satisfăcută pentru că nu mai exista un bloc continuu de dimensiunea solicitată atunci
funcţiile de alocare au rezultat NULL Funcţiile de alocare au rezultat void deoarece funcţia nu ştie
tipul datelor ce vor fi memorate la adresa respectivă
La apelarea funcţiilor de alocare se folosesc
Operatorul sizeof pentru a determina numărul de octeţi necesar unui tip de date
(variabile)
Operatorul de conversie cast pentru adaptarea adresei primite de la funcţie la tipul datelor
memorate la adresa respectivă (conversie necesară atribuirii icircntre pointeri de tipuri
diferite)
Exemple
aloca memorie pentru 30 de caractere
char str = (char) malloc(30)
aloca memorie ptr n icircntregi
int a = (int ) malloc( n sizeof(int))
aloca memorie ptr n icircntregi si initializeaza cu zerouri
int a= (int) calloc (n sizeof(int) )
25
Realocarea memoriei Realocarea unui vector care creşte (sau scade) faţă de dimensiunea
estimată anterior se poate face cu funcţia realloc care primeşte adresa veche şi noua dimensiune şi
icircntoarce noua adresă
void realloc(void adr size_t size)
Funcţia realloc realizează următoarele operaţii
alocă o zonă de dimensiunea specificată prin al doilea parametru
copiază la noua adresă datele de la adresa veche (primul parametru)
eliberează memoria de la adresa veche
Exemplu dublarea dimensiunii curente a unei anumite zone de la o anumită adresă
dublare dimensiune curenta a zonei de la adr a
a = (int )realloc (a 2n sizeof(int))
Observație Se va evita redimensionarea unui vector cu o valoare foarte mică de un număr mare de ori
o strategie de realocare folosită pentru vectori este dublarea capacităţii lor anterioare
Exemplu Exemplu de funcţie cu efectul funcţiei realloc dar doar pentru caractere
char ralloc (char p int size) p = adresa veche
char q q=adresa noua
if (size==0) echivalent cu free
free(p)
return NULL
q = (char) malloc(size) aloca memorie
if (q) daca alocare reusita
memcpy(qpsize) copiere date de la p la q
free(p) elibereaza adresa p
return q q poate fi NULL
Observație La mărirea blocului conţinutul zonei alocate icircn plus nu este precizat iar la micşorarea
blocului se pierd datele din zona la care se renunţă
Eliberarea memoriei Funcţia free are ca argument o adresă (un pointer) şi eliberează zona de la
adresa respectivă (alocată dinamic) Dimensiunea zonei nu mai trebuie specificată deoarece este
memorată la icircnceputul zonei alocate (de către funcţia de alocare)
void free(void adr)
Eliberarea memoriei prin free este inutilă la terminarea unui program deoarece icircnainte de
icircncărcarea şi lansarea icircn execuţie a unui nou program se eliberează automat toată memoria heap
Exemplu
char str
str=(char )malloc(10sizeof(char))
hellip
str=(char )realloc(str20sizeof(char))
26
hellip
free(str)
Observație Atenţie la definirea de şiruri icircn mod dinamic Şirul respectiv trebuie iniţializat cu adresa
unui alt şir sau a unui spaţiu alocat pe heap (adică alocat dinamic)
Exemplu Program care alocă spaţiu pentru o variabilă icircntreagă dinamică după citire şi tipărire spaţiul
fiind eliberat
include ltstdlibhgt
include ltstdiohgt
int main()
int pi
pi=(int )malloc(sizeof(int))
if(pi==NULL)
puts( Memorie insuficienta )
return 1 revenire din main
printf(valoare) citirea variabilei dinamice de pe heap de la adresa din pi
scanf(dpi)
pi=pi2 dublarea valorii
printf(val=dpi(adresa pe heap)=padr_pi=pn pi pi amppi) sizeof aplicat unor
expresii
printf(d d dnsizeof(pi) sizeof(pi) sizeof(amppi))
free(pi) eliberare spatiu
printf(pi(dupa elib)pnpi) nemodificat dar invalid
return 0
22 Implementarea structurilor dinamice de date (liste stive cozi arbori)
Pointerii sunt variabile care conţin adresa de memorie a unei alte variabile Din aceste
considerente pointerii se numesc şi variabile de adresă
Un pointer este o variabila care pastreaza adresa unei date nu valoarea datei Un pointer poate fi
utilizat pentru referirea diferitelor date si structuri de date Schimband adresa memorata in pointer pot fi
manipulate informatii situate la diferite locatii de memorie Pointerii permit de asemenea crearea de noi
variabile icircn timpul execuţiei programului prin alocare dinamică
Un pointer poate fi utilizat doar după iniţializare prin atribuirea adresei unei variabile sau prin
alocare dinamică
Memoria internă poate fi privita ca o serie de octeti Pentru a-i distinge acestia sunt numerotati
Numarul de ordine al unui octet se numeste adresa Adresa primului octet al variabilei se numeste adresa
variabilei Variabilele de tip pointer se caracterizeaza prin faptul ca valorile pe care le pot memora sunt
adrese ale altor variabile
Anumite variabile pot fi declarate dinamic Asta inseamna ca
Spatiul necesar memorarii este rezervat intr-un segment special acestui scop numit HEAP
In memorie se rezerva spatiu in timpul executarii programului atunci cand se utilizeaza un
anumit operator
Atunci cand variabila respectiva nu mai este utila spatiul din memorie este eliberat pentru afi
rezervat daca este cazul pentru alte variabile
Mecanismul alocarii dinamice este urmatorul
Se declara o variabila de tip pointer s-o numim P care permite memorarea unei adrese
Se aloca variabila dinamica prin operatorul NEW aplicat asupra unui tipiar rezultatul este
atribuit variabilei P In urma acestei operatii variabila P retine adresa variabilei alocate
27
Prin structură de date se icircnţelege un ansamblu de date caracterizat prin relaţiile existente icircntre ele
şi prin operaţiile care pot fi efectuate cu datele respective Structura de date este un concept abstract
Adică conceptul icircn sine nu precizează locul unde structura respectivă va fi memorată adică clasa de
memorare şi nici detaliile de implementare Structurile dinamice de date sunt date structurate ale căror
componente se alocă icircn mod dinamic Avantajele alocării dinamice sunt
memorie suplimentară pentru programe
posibilitatea de a utiliza această memorie
Alocarea dinamica a componentelor structurii impune un mecanism prin care o nouă componentă
apărută este legată icircn succesiune logică de corpul structurii deja format pacircnă atunci Rezultă că fiecare
componentă pe lacircngă informaţia propriu-zisă pe care o deţine trebuie să conţină şi o informaţie de
legatură cu componenta cu care se leagă logic icircn succesiune Această informaţie de legătură va fi adresa
componentei spre care se realizează succesiunea logică iar mecanismul se mai numeşte şi alocare
icircnlănţuită dupa adrese
Icircn HEAP structura respectivă va avea zone alocate componentelor sale icircn locurile găsite
disponibile care nu se succed icircntotdeauna icircn ordinea icircn care este realizată icircnlănţuirea logică
Icircn funcţie de tipul icircnlănţuirii realizate icircntre componente există urmatoarele tipuri de organizări
structuri liniare
liste simplu icircnlănţuite (liniare şi circulare)
liste dublu icircnlănţuite (liniare şi circulare)
structuri arborescente
structuri reţea
221 Liste liniare Lista simplu şi dublu icircnlănţuită Operaţii cu liste (crearea
adăugare eliminare parcurgere prelucrare nod)
O listă este o colecţie de elemente de informaţie (noduri) aranjate icircntr-o anumită ordine
Lungimea unei liste este numărul de noduri din listă Structura corespunzatoare de date trebuie să ne
permită să determinăm eficient care este primulultimul nod icircn structură şi care este
predecesorulsuccesorul (dacă există) unui nod dat De exemplu aşa arată cea mai simpla listă lista
liniară
O listă circulară este o listă icircn care după ultimul nod urmează primul deci fiecare nod are
succesor şi predecesor
O listă liniară este o colecţie de n noduri nge0 aflate icircntr-o relaţie de ordine
Operaţiile permise sunt
accesul la oricare nod al listei pentru citirea sau modificarea informaţiei conţinute de acesta
adăugarea unui nod indiferent de poziţia pe care o ocupă icircn listă
ştergere a unui nod indiferent de poziţia pe care o ocupă icircn listă
schimbarea poziţiei unui nod icircn cadrul listei
Structură liniară icircnseamnă faptul că fiecare nod cu excepţia ultimului are un nod succesor
adică care icirci urmează icircn listă şi cu excepţia primului nod are un singur predecesor adică care se află
imediat icircnaintea lui icircn listă
O listă liniară simplu icircnlănţuită este caracterizată prin faptul că relaţia de ordine definită pe
mulţimea elementelor este unică şi totală Ordinea elementelor pentru o astfel de listă este specificată
exclusiv printr-un cacircmp de informaţie care este parte componentă a fiecărui element şi indică elementul
28
următor conform cu relaţia de ordine definită pe mulţimea elementelor listei Deci fiecare element de
listă simplu icircnlănţuită are următoarea structură
Pe baza informaţiei de icircnlănţuire (păstrată icircn cacircmpul leg) trebuie să poată fi identificat următorul
element din listă Dacă există un ultim element icircn listă atunci lista se numeşte liniară Dacă nu există un
element care să conţină icircn cacircmpul informaţie valoarea null
Listele pot fi organizate sub formă statică de tablou caz icircn care ordinea este implicit dată de
tipul tablou unidimensional sau cel mai des sub formă de liste dinamice icircn care ordinea nodurilor este
stabilită prin pointeri Nodurile listelor dinamice sunt alocate icircn memoria heap Listele dinamice se
numesc liste icircnlănţuite putacircnd fi simplu sau dublu icircnlănţuite
Modelul listei simplu icircnlănţuite este prezentat icircn figura următoare
Fig 1 Model de listă simplu icircnlănţuită
Crearea unei liste simplu icircnlănţuite
O lista simplu icircnlănţuită poate fi creata icircn felul următor
bull Prin inserare la icircnceput
bull Prin inserare la sfacircrşit
bull Prin inserare ordonată
Crearea unei liste simplu icircnlănţuite se va face astfel
Iniţial lista este vidă
Se generează nodul de introdus
Se fac legăturile corespunzătoare
Exemplu Presupunem că la un moment dat lista este cea de mai jos iar v reţine adresa
primului element (adr1)
Dacă se citeşte un nou număr (de exemplu 4) atunci acesta se adaugă icircntr-o icircnregistrare aflată la
icircnceputul listei icircn următoarele etape
a) Se alocă spaţiu pentru noua icircnregistrare se completează cacircmpul numeric iar adresa următoare
este cea din v deci a primului element al listei
29
a) Variabila v va memora adresa noii icircnregistrări
Programul C++ utilizat pentru crearea unei liste simplu icircnlănțuite este
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
int nr
void Adaug(Nodamp v int nr)
Nod c=new Nod
c-gtinfo=nr
c-gtadr_urm=v
v=c
void Tip(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
coutltltnumar=cingtgtnr
while (nr)
Adaug(vnr)
coutltltnumar=cingtgtnr
Tip(v)
Un alt algoritm de creare a listei recursiv este prezentat mai jos De această dată lista cuprinde
informaţiile icircn ordinea icircn care acestea au fost introduse
30
include ltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod v
Nod Adaug()
Nod c
int nr
coutltltnumar cingtgtnr
if (nr)
c=new(Nod)
c-gtadr_urm=Adaug()
c-gtinfo=nr
return c
else return 0
void Tip(Nod v)
Nodc=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
main()
v=Adaug()
Tip(v)
Tipărirea informaţiilor icircn ordine inversă faţă de modul icircn care se găsesc icircn listă se face astfel
void Tip_inv(Nod v)
if (v)
Tip_inv (v-gtadr_urm)
coutltltv-gtinfoltltendl
Accesul la un nod al unei liste simplu icircnlănţuite Icircn funcţie de cerinţe nodurile listei pot fi
accesate secvenţial extrăgacircnd informaţia utilă din ele O problemă mai deosebită este găsirea unui nod
de o cheie dată şi apoi extragerea informaţiei din nodul respectiv Căutarea nodului după cheie se face
liniar el putacircnd fi prezent sau nu icircn listă O funcţie de căutare a unui nod de cheie ldquokeyrdquo returnează
adresa nodului respectiv icircn caz de găsire sau pointerul NULL icircn caz contrar
Inserarea unui nod icircntr-o listă simplu icircnlănţuită Nodul de inserat va fi generat la fel ca la
crearea unei liste se presupune că are pointerul p Dacă lista este vidă acest nod va fi singur icircn listă
Dacă lista nu este vidă inserarea se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul de cheie ldquokeyrdquo
- se inserează nodul de pointer p făcacircnd legăturile corespunzătoare
31
după un nod precizat printr-o cheie ldquokeyrdquo
- se caută nodul avacircnd cheia ldquokeyrdquo
- se inserează nodul de adresă p făcacircnd legăturile corespunzătoare
Exemplu Fiind dată o listă liniară se cere să se adauge la sfacircrşitul ei un nod cu o anumită informaţie
icircn exemplele noastre un număr icircntreg Se disting două cazuri
a) lista este vidă - v reţine 0 Să presupunem că vrem să adăugăm un nod cu informaţia 3 Se alocă
icircn HEAP nodul respectiv adresa sa va fi icircn v şi cum lista are un singur nod adresa primului nod
este şi adresa ultimului deci conţinutul lui v va coincide cu acela al lui sf
b) lista este nevidă Fie lista
Se adaugă un nod cu informaţia 6 Iniţial se alocă spaţiu pentru nod
Cacircmpul de adresă al ultimului nod cel care are adresa icircn sf va reţine adresa nodului nou creat
după care şi sf va reţine aceeaşi valoare
Observație Dacă n-am fi utilizat variabila sf pentru a reţine adresa ultimului nod ar fi fost
necesar să parcurgem icircntreaga listă pornind de la v pentru a obţine adresa ultimului
Programul C++ aferent este
void Adaugare(Nodamp v Nodamp sf int val)
Nod c
if (v==0)
v=new(Nod)
v-gtinfo=val
v-gtadr_urm=0
sf=v
else
c=new(Nod)
32
sf-gtadr_urm=c
c-gtinfo=val
c-gtadr_urm=0
sf=c
Ştergerea unui nod dintr-o listă simplu icircnlănţuită La ştergerea unui nod se vor avea icircn vedere
următoarele probleme lista poate fi vidă lista poate conţine un singur nod sau lista poate conţine mai
multe noduri
De asemenea se poate cere ştergerea primului nod a ultimului nod sau a unui nod dat printr-o
cheie ldquokeyrdquo
Ştergerea primului nod
Ştergerea ultimului nod
Ştergerea unui nod de cheie ldquokeyrdquo
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod după un altul de informaţie data Fie
lista din figura anterioară Dorim să adăugăm după nodul cu informaţia 3 un altul cu informaţia 5
Iniţial se identifică nodul după care se face adăugarea Icircn cazul de faţă acesta este primul Se alocă
spaţiu pentru noul nod Se completează adresa şi anume adresa nodului care urmează după cel de
informaţie 3
Apoi cacircmpul de adresă al nodului cu informaţia 3 va reţine adresa nodului nou creat
Observație Un caz aparte apare atunci cacircnd nodul de informaţie val este ultimul icircn listă Icircn acest caz sf
va reţine adresa nodului nou creat pentru că acesta va fi ultimul
Programul aferent este
void Inserare_dupa(Nod v Nodamp sf int val int val1)
Nod c=v d
while (c-gtinfo=val)
c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
33
if (d-gtadr_urm==0) sf=d
Exemplu Icircn acest exemplu ne propunem să inserarăm un nod icircnaintea altuia de informaţie data
Icircntrucacirct operaţia este asemănătoare cu precedenta prezentăm numai subprogramul care realizează
operaţia respective
void Inserare_inainte(Nodamp v int val int val1)
Nod cd
if (v-gtinfo==val)
d=new Nod
d-gtinfo=val1
d-gtadr_urm=v
v=d
else
c=v
while (c-gtadr_urm-gt
info=val) c=c-gtadr_urm
d=new Nod
d-gtinfo=val1
d-gtadr_urm=c-gtadr_urm
c-gtadr_urm=d
Ştergerea unei liste simplu icircnlănţuite Icircn acest caz se şterge icircn mod secvenţial fiecare nod
Exemplu Algoritmul este diferit icircn funcţie de poziţia icircn listă a nodului care va fi şters - dacă este primul
sau nu
a Nodul nu este primul Pentru nodul care va fi şters informaţia de adresă a predecesorului va
reţine adresa nodului succesor
Memoria ocupată de nodul care urmează a fi şters este eliberată
b Nodul este primul Fie lista
Variabila v va reţine adresa celui de-al doilea nod
34
Spaţiul ocupat de primul nod va fi eliberat
Programul icircn C++ este
void Sterg(Nodamp v Nodamp sf
int val)
Nod c man
if (v-gtinfo==val)
man=v
v=v-gtadr_urm
else
c=v
while (c-gtadr_urm-gtinfo
=val) c=c-gtadr_urm
man=c-gtadr_urm
c-gtadr_urm=man-gtadr_urm
if (man==sf) sf=c
delete man
Pentru a verifica modul de funcţionare a subprogramelor de mai sus este necesar să utilizăm un
altul care afişează lista liniară
void Listare(Nod v)
Nod c=v
while (c)
coutltltc-gtinfoltltendl
c=c-gtadr_urm
coutltltendl
Pentru a testa aplicația se utilizează următorul program
Nod vsf
int i
main()
for (i=1ilt=10i++)
Adaugare(vsfi)
Listare(v)
35
Inserare_dupa(vsf711)
Inserare_dupa(vsf1012)
Inserare_dupa(vsf113)
Listare(v)
Inserare_inainte(v1314)
Inserare_inainte(v115)
Listare(v)
Sterg(vsf15)
Sterg(vsf13)
Sterg(vsf12)
Listare(v)
O listă liniară dublu icircnlănţuită este caracterizată prin faptul că pe mulţimea elementelor sunt
definite două relaţii de ordine totală inverse una celeilalte icircnainte şi icircnapoi Rezultă două secvenţializări
ale listei Ordinea elementelor pentru o astfel de listă este specificată exclusiv prin două cacircmpuri de
informaţie care sunt parte componentă precedent conform cu relaţiile de ordine definite pe mulţimea
elementelor listei Deci fiecare element de listă dublu icircnlănţuită are următoarea structură
Pe baza informaţiilor de icircnlănţuire păstrate icircn cacircmpurile urm şi prec trebuie să poată fi
identificate următorul element din listă respectiv elementul precedent
Lista dublu icircnlănţuită este lista dinamică icircntre nodurile căreia s-a definit o dublă relaţie de
succesor si de predecesor
Modelul listei dublu icircnlănţuite este prezentat icircn figura următoare
Fig2 Model de listă dublu icircnlănţuită
Ca şi la lista simplu icircnlănţuită principalele operaţii sunt
crearea
accesul la un nod
inserarea unui nod
ştergerea unui nod
ştergerea listei
Lista dublu icircnlănţuită va fi gestionată prin pointerii prim şi ultim
Crearea unei liste dublu icircnlănţuite
Iniţial lista este vidă După alocarea de memorie şi citirea datelor icircn nod introducerea nodului de
pointer icircn listă se va face astfel
Accesul la un nod
Accesul la un nod se poate face
36
secvenţial icircnainte (de la bdquoprimrdquo spre bdquoultimrdquo)
secvenţial icircnapoi ( de la bdquoultimrdquo spre bdquoprimrdquo)
pe baza unei chei Căutarea unui nod de cheie dată key se va face identic ca la lista simplu
icircnlănţuită
Inserarea unui nod
Inserarea unui nod icircntr-o listă dublu icircnlănţuită se poate face astfel
icircnaintea primului nod
după ultimul nod
icircnaintea unui nod de cheie dată key
după un nod de cheie dată key
Ştergerea unui nod
Există următoarele cazuri de ştergere a unui nod din listă
ştergerea primului nod
ştergerea ultimului nod
ştergerea unui nod precizat printr-o cheie key
Ştergerea listei
Ştergerea icircntregii liste se realizează ştergacircnd nod cu nod
Exemplu Operațiile anterior prezentate sunt implementate icircn urmtorul program C++
include ltiostreamhgt
struct Nod
Nod as ad
int nr
Nod bsc
int nmi
void Creare (Nodamp b Nodamp s)
coutltltn= cingtgtn
b=new Nod
b-gtnr=n
b-gtas=b-gtad=0
s=b
void Addr(Nodamp s)
coutltltn= cingtgtn
Nod d=new Nod
d-gtnr=n
d-gtas=s
d-gtad=0
s-gtad=d
s=d
void Listare(Nodamp b)
Nod d=b
while (d)
coutltltd-gtnrltltendl
d=d-gtad
37
void Includ(int m Nod b)
Nod d=b e
while (d-gtnr=m) d=d-gtad
coutltltn= cingtgtn
e=new Nod
e-gtnr=n
e-gtas=d
d-gtad-gtas=e
e-gtad=d-gtad
d-gtad=e
void Sterg(int m Nod b)
Nod d=b
while (d-gtnr=m) d=d-gtad
d-gtas-gtad=d-gtad
d-gtad-gtas=d-gtas
delete d
main()
coutltltCreare lista cu o singura inregistr ltltendl
Creare (bs)
coutltltCate inregistrari se adauga cingtgtm
for (i=1ilt=mi++) Addr(s)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltIncludem la dreapta o inregistrare ltltendl
coutltltdupa care inregistrare se face includerea cingtgtm
Includ (mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
coutltltAcum stergem o inregistrare din interiorltltendl
coutltltCe inregistrare se sterge
cingtgtm
Sterg(mb)
coutltltAcum listez de la stanga la dreaptaltltendl
Listare(b)
222 Stive cozi Definire şi memorare utilizacircnd listele liniare Operaţii
(adăugareaeliminarea unui nod)
O stivă se defineşte ca o listă liniară simplu icircnlănţuită icircn care toate intrările şi ieşirile se fac pe la
un singur capăt al ei Stiva este o o structură de tip LIFO (Last In First Out) adică ultimul nod introdus
este primul scos Rezultă că icircnregistrarea de pe nivelul k reţine icircnregistrarea de pe nivelul k-1 Icircn cazul
stivei se reţine doar elementul din vacircrful stivei
38
Fig3 Model de stivă
Fiind o structură particulară a unei liste simplu icircnlănţuite operaţiile principale asupra unei stive
sunt
push = adăugare - pune un element pe stivă funcţia se realizează prin inserarea unui nod
icircnaintea primului
pop = eliminare - scoate elementul din vacircrful stivei funcţia se realizează prin ştergerea primului
nod
clear - ştergerea stivei
Numărul de noduri care pot fi memorate la un moment dat este mai mic decacirct icircn cazul alocării
dinamice icircnlănţuite icircn funcţie de gradul de ocupare al segmentului de date
Pe un anumit nivel se reţine de regulă o singură informaţie icircnsă este posibil să existe şi mai
multe informaţii pe un nivel
Exemplu Icircn acest exemplu se creează o stivă prin utilizarea unei liste liniare simplu icircnlănţuite
Adăugarea unui element icircn stivă se face cu subprogramul PUSH iar eliminarea cu subprogramul POP
Vacircrful stivei este reţinut de variabila v
include ltiostreamhgt
struct Nod
int info
Nod adr_inap
Nod v
int n
void Push (Nodamp vint n)
Nod c
if (v)
v= new Nod
v-gtinfo=n
v-gtadr_inap=0
else
c= new Nod
c-gtinfo=n
c-gtadr_inap=v
v=c
void Pop (Nodamp v)
Nod c
if (v)
coutltltstiva este vida
else
c=v
39
coutltltam scos
ltlt c-gtinfoltltendl
v=v-gtadr_inap
delete c
main()
Push(v1) Push(v2)
Push(v3)
Pop(v) Pop(v)
Pop(v) Pop(v)
O coadă este o listă pentu care toate inserările sunt făcute la unul din capete toate ştergerile
consultările modificările la celălalt capăt Coada este o structură de tip FIFO (First In First Out) adică
primul nod introdus este primul scos
Fig 4 Model de coadă
Operaţiile importante sunt
introducerea unui element icircn coadă - funcţia se realizează prin inserarea după ultimul nod
scoaterea unui element din coadă ndash funcţia se realizează prin ştergerea primului nod
ştergerea cozii ndash se şterge secvenţial fiecare nod
Exemplu Pentru a implementa o coadă ca o listă liniară simplu icircnlănțuită vom face cacircteva
precizări O variabilă v va reţine adresa elementului care urmează a fi scos (servit) O alta numită sf va
reţine adresa ultimului element introdus icircn coadă Figura următoare prezintă o coadă icircn care primul
element care urmează a fi scos are adresa icircn v iar ultimul introdus are adresa icircn sf
Programul C++ este
includeltiostreamhgt
struct Nod
int info
Nod adr_urm
Nod vsf
int n
void Pune(Nodamp vNodamp sfint n)
Nod c
if (v)
v=new Nod
40
v-gtinfo=n
v-gtadr_urm=0
sf=v
else
c=new Nod
sf-gtadr_urm=c
c-gtinfo=n
c-gtadr_urm=0
sf=c
void Scoate(Nodamp v)
Nod c
if (v)
coutltltcoada este
vidaltltendl
else
coutltltAm scos
ltltv-gtinfoltltendl
c=v
v=v-gtadr_urm
delete c
subprogram de Listare a elementelor aflate in coada
main()
Pune(vsf1) Pune(vsf2)
Pune(vsf3) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
Scoate(v) Listare(v)
223 Grafuri
Se numeste graf sau graf neorientat o pereche de multimi G = (AB) in care A este multimea
nodurilor (este finita si nevida) iar B e multimea relatiilormuchiilor
B = (xy) x apartine lui A y apartine lui A
Exemplu1 graf neorientat
unde A = 12345 B = (12)(13)(23)(25)
Caracteristici
Două noduri distincte pot fi unite prin cel mult o muchie
Nu există o muchie care uneşte un nod cu el icircnsuşi (o muchie uneşte două noduri distincte)
41
muchie icircn care extremităţile coincid se numeşte buclă
Un graf G se numeşte simplu dacă oricare două noduri ale sale sunt extremităţi pentru cel mult o
muchie
Un graf G = (VE) este finit dacă V şi E sunt finite
Se numeste graf orientat o multime ordonata G = (VE) in care V este multimea nodurilor (finita
si nevida) iar E este multimea arcelor
Exemplu2 graf orientat
unde V = 12345 E = (12)(21)(23)(31)(52)
Explicaţii
Daca (xy) apartine lui B atunci
x si y sunt noduri adiacente
x si y sunt extremitatile arcului (xy)
x si y sunt incidente cu (xy)
Icircn cazul grafurilor orientate
x este extremitatea initiala a (xy)
y este extremitatea finala a (xy)
u = (xy) v = (yz) =gt u si v sunt incidente
Exemplu
1 este adiacent cu 2 si 3
1 si 2 sunt extremitatile (12)
nodul 1 este incident cu (12)
(52) si (23) sunt incidente
Gradul unui nod numarul de muchii incidente cu el
d(x) - gradul nodului x
1 d(1) = 2
2 d(1) = 3
Pentru grafurile orientate se definesc
Gradul exterior al lui x d+(x) = numarul arcelor care pleaca din x
Gradul interior al lui x d-(x) = numarul arcelor care intra in x
Exemplu
pentru 2 d(1)=3 d+(1)=1 d
-(1)=2
Nodurile de grad 0 se numesc noduri izolate
Nodurile de grad 1 se numesc noduri terminale
Proprietati
d+(x) + d
-(x) = d(x)
Daca un graf are m muchii sau arce atunci d(x1) + d(x2) + + d(xn) = 2m
Daca un graf orientat are m arce
d+(x1) + d
+(x2) + + d
+(xn) = m
42
d-(x1) + d
-(x2) + + d
-(xn) = m
A Lanturi Drumuri
Pentru grafuri neorientate Se numeste lant o succesiune de noduri x1 xk cu proprietatea ca oricare doua noduri vecine
(xixi+1) apartin de B Icircn cadrul definiției x1 xk sunt extremitatile lantului Lungimea lantului este egala
cu numarul de muchii care il compun k-1 Daca nodurile din lant sunt distincte atunci lantul este
elementar
Exemplu 3 lanț ndash graf neorientat
unde
12314 - Lant neelementar (lungime 4)
1234 - Lant elementar (lungime 3)
123125 - Lant neelementar (lungime 5)
1235 - Nu este lant
Pentru grafuri orientate Se numeste lant o succesiune de arce u1 u2 uk cu proprietatea că oricare doua arce de pe
pozitii consecutive au un nod comun
Observatie nu conteaza ordinea de parcurgere
Se numeste drum o succesiune de noduri x1 x2 xk cu proprietatea ca (xixi+1) este arc
Observatie conteaza ordinea de parcurgere
Daca nodurile sunt distincte drumul se numeste elementar
Exemplu 4 lanț ndash graf orientat
unde
Lanturi (12)(23)(34) - Da
(12)(52)(23) - Da
(12)(21)(13) - Nu
(12)(23)(15)(52) - Nu
Drumuri 12312 - Drum neelementar
1234 - Drum elementar
3125 - Nu este drum
B Cicluri Circuite
Pentru grafuri neorientate
43
Se numeste ciclu intr-un graf neorientat un lant x1x2 xk si oricare 2 muchii (xixi+1) sunt
distincte
Daca un ciclu are toate nodurile distincte 2 cate 2 cu exceptia capetelor atunci el se numeste ciclu
elementar
Exemplu 5 ciclu ndash graf neorientat
unde
12341 - Ciclu elementar
23412 - Ciclu elementar
1234231 - Nu este ciclu
1234251 - Ciclu neelementar
Pentru grafuri orientate Se numeste circuit intr-un graf un drum x1x2 xk cu proprietatea ca x1 = xk si arcele (xixi+1) sa
fie distincte 2 cate 2
Un circuit in care toate nodurile sunt distincte cu exceptia capetelor se numeste circuit elementar
Exemplu 6 circuit ndash graf orientat
unde
1231 - Circuit elementar
2312 - Circuit elementar
123121 - Nu este circuit
2123152 - Circuit neelementar
Reprezentarea grafurilor in memorie
Acest lucru se face astfel
C1 Reprezentarea prin matrice de adiacenta
C2 Liste de adiacenta
C3 Vector de muchii
44
C4 Matrice arce-noduri
C1 Matricea de adiacenta
Pentru grafuri neorientate
a[ij] = 1 daca intre i si j este muchie
a[ij] = 0 altfel
Observatia 1 Pe diagonala principala toate elementele sunt 0 (nu avem bucle)
Observația 2 Matricea este simetrica fata de diagonala principala deci a[ij] = a[ji]
Pentru grafuri orientate
a[ij] = 1 daca exista arcul (ij)
a[ij] = 0 altfel
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf neorentiat
prin matricea de adiacență
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
int n numărul de noduri
45
bool ad[NMAX][NMAX] matricea de adiacență
bool find(Edge edge)
return ad[edgex][edgey]
void remove(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = false
void insert(Edge edge)
ad[edgex][edgey] = ad[edgey][edgex] = true
void neighbours(int node)
for (int j = 1 j lt= n j++)
if (ad[node][j])
cout ltlt j ltlt
cout ltlt n
int main()
n = 5
insert(Edge(1 2))
insert(Edge(1 3))
insert(Edge(1 4))
insert(Edge(4 5))
insert(Edge(3 4))
remove(Edge(3 4))
cout ltlt find(Edge(4 5)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(1)
neighbours(2)
neighbours(3)
neighbours(4)
neighbours(5)
return 0
C2 Lista de adiacenta Pentru fiecare nod se memoreaza o lista a vecinilor sai Pentru intregul graf este necesar un
vector de liste (P) in care Pi este adresa primului element al listei asociate lui i
Exemplu7
46
Exemplu 8
Observatie pentru grafurile orientate se memoreaza in lista lui i nodurile k pentru care exista arcul (ik)
Exemplu Mai jos se prezintă un program icircn C++ icircn vederea reprezentării unui graf orentiat prin
liste de adiacență
include ltvectorgt
include ltiostreamgt
using namespace std
const int NMAX = 618
struct Edge
int x y
Edge(int x int y)
this-gtx = x
this-gty = y
47
vectorltintgt ad[NMAX] lista de adiacență
int find(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
return j
return -1
void remove(Edge edge)
for (int j = 0 j lt (int) ad[edgex]size() j++)
if (ad[edgex][j] == edgey)
swap(ad[edgex][j] ad[edgex]back())
ad[edgex]pop_back()
return
void insert(Edge edge)
ad[edgex]push_back(edgey)
void neighbours(int node)
for (int j = 0 j lt (int) ad[node]size() j++)
cout ltlt ad[node][j] ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
neighbours(5)
return 0
C3 Vector de muchii
48
Exemplu Icircn cele ce urmează se prezintă un program icircn C++ icircn vederea reprezentării unui graf
orentiat prin vector de muchii
include ltiostreamgt
using namespace std
const int VMAX = 618
struct Edge
int x y
Edge(int x = 0 int y = 0)
this-gtx = x
this-gty = y
int m numărul de muchii
Edge edg[VMAX] vector de muchii
Funcția returnează poziția din vector unde se găsește edge
sau -1 dacă muchia nu există
int find(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
return i
return -1
void remove(Edge edge)
for (int i = 0 i lt m i++)
if (edg[i]x == edgex ampamp edg[i]y == edgey)
swap(edg[i] edg[m - 1])
m--
return
void insert(Edge edge)
edg[m++] = edge
void neighbours(int node)
for (int i = 0 i lt m i++)
if (edg[i]x == node)
cout ltlt edg[i]y ltlt
cout ltlt n
int main()
insert(Edge(5 1))
insert(Edge(5 2))
insert(Edge(5 4))
insert(Edge(5 3))
remove(Edge(5 3))
cout ltlt find(Edge(5 2)) ltlt n
cout ltlt find(Edge(2 5)) ltlt n
49
neighbours(5)
return 0
C4 Matricea noduri-arce
Este folosita in special pentru grafurile orientate
Exemplu 9
Matricea noduri-arce aferenta este
Metoda Breadth First ndash BF (icircn lăţime)
Pentru grafuri neorientate Exemplu10
x = 1
1 2 3 4 6 7 8 9 5
Se porneste de la un nod oarecare x
Se viziteaza toti vecinii directi ai nodului x daca nu au fost deja vizitati
Fiecare dintre nodurile vizitate la pasul anterior devine nod curent si este prelucrat la fel ca nodul
x
Structuri de date necesare pentru implementare sunt
Matrice de adiacenta (sau alte variante de reprezentare) a
Coada (in care se memoreaza in ordinea parcursa nodurile vizitate) c
p u - indicatorii primului si ultimului element din coada
Vectorul nodurilor vizitate v
v[i]=1 daca i a fost vizitat
v[i]=0 altfel
50
Parcurgerea BF se efectuează prin utilizarea structurii numită coadă avacircnd grijă ca un nod să fie
vizitat o singură dată Atunci cacircnd un nod a fost introdus icircn coadă se marchează ca vizitat
Exemplu Parcurgerea unui graf prin metoda Breadth First Search (BFS) utilizacircnd C++
include ltiostreamgt
include ltfstreamgt
include ltvectorgt
include ltqueuegt
using namespace std
ifstream fin(bfsin)
ofstream fout(bfsout)
const int NLIM = 100005
int N M S
int Distance[NLIM]
vector ltintgt Edge[NLIM]
queue ltintgt Q
void BFS()
int Node Next
while(Qempty())
Node = Qfront()
Qpop()
for(unsigned int i = 0 i lt Edge[Node]size() i++)
Next = Edge[Node][i]
if(Distance[Next] == -1)
Qpush(Next)
Distance[Next] = Distance[Node] + 1
void Read()
fin gtgt N gtgt M gtgt S
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
for(int i = 1 i lt= N i++)
Distance[i] = -1
Distance[S] = 0
Qpush(S)
BFS()
for(int i = 1 i lt= N i++)
fout ltlt Distance[i] ltlt
51
int main()
Read()
return 0
Pentru grafuri orientate
Observatie algoritmul se adapteaza astfel incat sa poata fi luati in considerare toti vecinii unui
nod
Exemplu 11
x = 1
1 2 3 4 5
Metoda Depth First ndash DF (icircn adacircncime)
Pentru grafuri neorientate
Exemplul 12
x = 1
1 2 4 5 10 9 7 8 6 3
Se porneste de la un nod oarecare x
Se alege primul vecin al lui x care nu a fost inca vizitat
Pentru nodul ales se reia procedeul
Daca un nod nu are nici un vecin nevizitat se revine la nodul vizitat anterior acestuia
Structuri de date necesare implementarii
Matrice de adiacenta (sau alte variante) a
Stiva s (in care se memoreaza nodurile in ordinea parcurgerii)
Daca se implementeaza varianta recursiva se va folosi stiva procesorului
Vectorul nodurilor vizitate v
Pentru grafuri orientate Exemplu 13
52
x = 10
10 4 2 1 3 6 8 7 9
Parcurgerea este similara punandu-se conditia de parcurgere a tuturor vecinilor unui nod
indiferent de sens
Exemplu Parcurgerea unui graf prin metoda Depth First Search (DFS) utilizacircnd C++
include ltfstreamgt
include ltvectorgt
using namespace std
ifstream fin(dfsin)
ofstream fout(dfsout)
const int NLIM = 100005
int N M
vector lt int gt Edge[NLIM]
bool beenThere[NLIM]
int answer
void DFS(int Node)
beenThere[Node] = true
for(unsigned int i = 0 i lt Edge[Node]size() i++)
int Next = Edge[Node][i]
if(beenThere[Next])
DFS(Next)
void Read()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y
fin gtgt x gtgt y
Edge[x]push_back(y)
Edge[y]push_back(x)
for(int i = 1 i lt= N i++)
53
if(beenThere[i])
answer += 1
DFS(i)
fout ltlt answer ltlt n
int main()
Read()
return 0
Tipuri de grafuri
1 Graf partial
Fie G=(AB) si G1=(A1B1) Spunem ca G1 este un graf partial al lui G daca A=A1 si B1 este
inclus sau egal cu B
Un graf partial se obtine dintr-un graf indepartand o parte dintre muchiile sale si pastrand
toate nodurile acestuia
Exemplu 14
2 Subgraful unui graf
54
Fie G=(AB) si G1=(A1B1) A1 inclus sau egal cu A B1 inclus sau egal cu B B1 = (xy)
oricare xy apartine A1 daca (xy) apartine de B =gt (xy) apartine de B1
Subgraful se obtine din graful initial selectand o parte din nodurile sale si o parte din nodurile
adiacente cu acesta
Exemplu 15
3 Graf complet
Un graf este complet daca oricare doua varfuri distince sunt adiacente
Exemplu 16
Un graf neorientat cu n noduri are n(n-1)2 muchii
Exista un singur graf complet neorientat cu n noduri
Exista mai multe grafuri orientate complete cu n noduri
4 Grafuri bipartite Fie G=(AB) neorientat G este bipartit daca exista doua multimi A1 si A2 astfel incat A1 cap
A2 = Oslash si A1 U A2 = A iar oricare muchie (xy) apartinand lui B are un capat in multimea A1
si celalalt in A2
55
Exemplu 17
Un graf bipartit este bipartit complet daca fiecare nod din multimea A1 este adiacent cu toate
nodurile din A2 si reciproc
Exemplu 18
5 Grafuri conexe Un graf este conex daca este format dintr-un singur nod sau daca intre oricare doua noduri ale
sale exista cel putin un lant
Pentru grafuri neorientate Exemplu 19
56
Pentru grafuri orientate Exemplu 20
Se numeste componenta conexa a unui graf un subgraf al sau care este conex si care este
maximal in raport cu aceasta proprietate (daca i se adauga un nod isi pierde aceasta proprietate)
Observatie pentru grafurile orientate nu se tine cont de orientarea arcelor
6 Grafuri tare conexe Un graf este tare conex daca ar un singur nod sau daca oricare ar fi (xy) exista drum de la x
la y si exista drum de la y la x
Determinarea componentelor tare conexe Se poate realiza prin 3 metode
1 Utilizand metoda DFBF
2 Utilizand matricea drumurilor
3 Algoritmul +-
O componenta tare conexa este un subgraf al sau care este tare conex si care este maximal in
raport cu aceasta proprietate
Observatie reunind toate arcele din componentele tare conexe se poate obtine o multime mai
mica decat multimea arcelor grafului initial
Se poate construi un graf al componentelor tare conexe in care fiecare componenta tare conexa
formeaza un nod iar arcele simuleaza legaturile dintre ele
Exemplu 21
Determinarea componentelor tare conexe utilizand matricea drumurilor
57
Exemplu 22
d(ij) = 1 daca exista drum de la i la j
d(ij) = 0 altfel
7 Grafuri hamiltoniene Lant hamiltonian lant elementar care contine toate nodurile grafului
Ciclu hamiltonian ciclu elementar care contine toate nodurile grafului
Graf hamiltonian graf care contine un ciclu hamiltonian
Exemplul 23
Conditii de suficientă
Teorema lui Dirac Fie G dat prin perechea (A B) Daca G are un numar de cel putin 3 varfuri astfel
incat gradul fiecarui nod respecta conditia d(x) ge n2 atunci graful este hamiltonian
Algoritmi de determinare a unei solutii Algoritmul utilizat este Backtracking care este adaptat in mod corespunzator
8 Grafuri euleriene Ciclu eulerian ciclu care trece prin toate muchiile unui graf exact o data
Graf eulerian graf care contine cel putin un ciclu eulerian
Exemplul 24
58
Conditii de suficienta
Teorema Fie un graf conex fara noduri izolate cu nge 3 noduri Graful este eulerian daca si numai daca
pentru oricare nod al sau x d(x) este par
Exemplu 25
Se porneste de la un nod oarecare si se construieste un ciclu
Se parcurg nodurile din ciclul determinat anterior daca exista un nod care mai are muchii
neincluse in ciclul anterior se construieste un nou ciclu provenind de la acest nod
Ciclul construit este inclus in ciclul initial in locul nodului gasit la pasul anterior
pas 1
o c1 1231
o c2 2472
pas 2
o c1 1247231
o c2 75107
pas 3
o c1 12475107231
o c2 78117
pas 4
o c1 124781175107231
o c2 7697
pas 5
o c1 124769781175107231
Drumuri maximeminime in graf Problemele de optim presupun că fiecare muchie a grafului are asociat un anumit cost (de
exemplu distanta intre doua orase i si y)
Aceste informatii se memoreaza in matricea costurilor
c(ij) = costul asociat muchiei (ij) c(ij) = +infin daca nu exista muchia (ij)
59
Observatie daca intereseaza un drum maxim in loc de +infin se memoreaza -infin sau o valoare
adecvata
Exista mai multe tipuri de probleme de optim
1 sursa unicadestinatii multiple
2 sursa multipladestinatii multiple
Algoritmi pentru drum minim cu sursa unica 1 Algoritmul lui Dijkstra
2 Algoritmul lui Lee
Algoritmul lui Dijkstra Se considera un graf orientat in care fiecare arc are asociat un anumit cost Dandu-se un nod x
oarecare se cere sa se determine drumurile de cost minim care pornesc de la nodul x si ajung la toate
celelalte noduri ale grafului
Observatie daca sunt mai multe noduri de acelasi cost minim intre x si y se va gasi unul dintre
ele
Observatie metoda folosita este metoda Greedy
Se utilizeaza urmatoarele structuri
s - vectorul nodurilor selectate
s[i]=1 daca nodul i este selectat
s[i]=0 altfel
d
d[i] = costul drumului minim de la x la y
d[i]=+infin daca nu exista drum de la x la i
t - vectorul de tativectorul predecesorilor
t[i]=predecesorul lui i in drumul de la x la i
t[i]=0 daca nu exista drum
Exemplu Algorimul Dijkstra ce determină lungimea cea mai scurtă de la un nod de start la toate
celelalte noduri ale grafului (funcționează doar pe grafuri orientate) este prezentat mai jos
include ltiostreamgt
include ltfstreamgt
include ltqueuegt
include ltvectorgt
using namespace std
ifstream fin(dijkstrain)
ofstream fout(dijkstraout)
const int NMax = 50005
const int oo = (1 ltlt 30)
int N M
int D[NMax]
bool InCoada[NMax]
vector lt pair ltintintgt gt G[NMax]
struct compara
bool operator()(int x int y)
return D[x] gt D[y]
60
priority_queueltint vectorltintgt comparagt Coada
void Citeste()
fin gtgt N gtgt M
for(int i = 1 i lt= M i++)
int x y c
fin gtgt x gtgt y gtgt c
G[x]push_back(make_pair(yc))
void Dijkstra(int nodStart)
for(int i = 1 i lt= N i++)
D[i] = oo
D[nodStart]=0
Coadapush(nodStart)
InCoada[nodStart] = true
while(Coadaempty())
int nodCurent = Coadatop()
Coadapop()
InCoada[nodCurent] = false
for(size_t i = 0 i lt G[nodCurent]size() i++)
int Vecin = G[nodCurent][i]first
int Cost = G[nodCurent][i]second
if(D[nodCurent] + Cost lt D[Vecin])
D[Vecin] = D[nodCurent] + Cost
if(InCoada[Vecin] == false)
Coadapush(Vecin)
InCoada[Vecin] = true
void Afiseaza()
for(int i = 2 i lt= N i++)
if(D[i] = oo)
fout ltlt D[i] ltlt
else
fout ltlt 0
int main()
61
Citeste()
Dijkstra(1)
Afiseaza()
224 Arbori
Un arbore este un graf neorientat conex şi fără cicluri Arborii reprezintă grafurile cele mai
simple ca structură din clasa grafurilor conexe ei fiind cel mai frecvent utilizaţi icircn practică Un arbore cu
n varfuri are n-1 muchii
Exemplu 26
Fie G = (VE) graf arbore Subgraful H = (V1E1) al lui G este un subarbore al lui G dacă H este
graf arbore
Un arbore este o multime de elemente numite noduri sau vacircrfuri pentru care
exista un nod cu destinatie speciala (radacina arborelui)
celelalte noduri sunt repartizate icircn nge0 seturi disjuncte A1 A2 An fiecare set constituind la
racircndul sau un arbore
Icircn structura ierarhica a arborelui fiecare nod (mai putin radacina) este subordonat unui alt nod
(relatie fiu-parinte) Daca un nod nu are fi el se numeste terminal (sau frunza)
Fie un graf neorientat G=(VE) unde V e mulţimea vacircrfurilor iar E cea a muchiilor sale
Următoarele afirmaţii sunt echivalente
G este arbore
G este un graf conex minimal cu această proprietate (dacă se elimină o muchie oarecare se
obţine un graf neconex)
G este un graf fără cicluri maximal cu această proprietate (dacă se adaugă o muchie se obţine un
graf care are măcar un ciclu)
Observații
Un arbore cu n ge 2 vacircrfuri conţine cel puţin două vacircrfuri terminale
Orice arbore cu n vacircrfuri are n-1 muchii
Fie G un graf neorientat Un graf parţial H al lui G cu proprietatea că H este arbore se numeşte
arbore parţial al lui G
Un graf neorientat G conţine un arbore parţial dacă şi numai dacă G este conex
Un graf neorientat care nu conţine cicluri se numeşte pădure
Fiind dat un graf neorientat conex se numeste arbore parţial al grafului un graf parţial cu
proprietatea că este arbore Intuitiv un arbore parţial este un arbore obţinut prin eliminarea unor muchii
din graf Un arbore parţial al unui graf neorientat conex poate fi definit ca un graf parţial conex cu număr
minim de muchii sau un graf parţial aciclic cu număr maxim de muchii
Exemplu 27
62
Corolar Un arbore cu n varfuri are n - 1 muchii
Exemplu 28
Daca alegem 2 ca fiind radacina reprezentarea arborelui pe nivele este
unde nodul 2 este tatal nodurilor 6 1 3 si 7 5 este fiul lui 6 4 este fiul lui 3 iar 8 este fiul lui 7
Nodurile 5 4 8 si 1 nu au nici un fiu Nodurile care nu au fii se mai numesc frunze sau noduri
terminale iar muchiile dintre noduri ramuri Nodurile 6 1 3 si 7 sunt frati Nodurile 6 1 3 si 7 sunt
urmasii lui 2 De asemenea nodurile 5 4 si 8 sunt urmasii lui 2 iar nodul 2 este stramosul tuturor
nodurilor (mai putin el insusi) 2 fiind radacina raborelui 2 adica radacina este singurul nod care nu are
tata
In general un nod al unui arbore poate avea un numar arbitrar de fii Daca orice nod al unui
arbore nu are mai mult de n fii atunci arborele se numeste arbore n-ar
Un arbore in care orice nod nu are mai mult de 2 fii se numeste arbore binar
Se numeste inaltime a unui arbore lungimea celui mai lung drum de la radacina la un nod
terminal din arbore Pentru arborele de mai sus inaltimea este 2 Se observă ca intre orice nod si radacina
exista exact un singur drum
Un arbore binar este un arbore in care orice nod are cel mult doi descendenti facandu-se
distincatie clara intre descendentul drept si descendentul stang Radacina unui arbore binar are doi
subarbori subarborele stang cel care are drept radacina fiul stang si subarborele drept cel care are ca
radacina fiul drept Orice aubarbore al unui arbore binar este el insusi arbore binar De exemplu arborele
de mai jos este un arbore binar radacina 10 are drept fiu stang nodul 4 iar fiu drept nodul 21 nodul 21
are subarborele stang format din nodul 15 si subarborele drept format din nodurile 23 si 28
Exemplu 29
63
Nota Un arbore binar poate fi si vid (adica fara nici un nod)
Un arbore binar pentru care orice nod neterminal are exact doi fii se numeste arbore plin (full)
Arborele binar este arborele icircn care un nod are cel mult doi fii Icircn aceasta situatie se poate vorbi
(pentru un arbore nevid) de cei doi subarbori (stacircng si drept) ai unui arbore
Schematic avem
Reprezentare
De obicei nodurile unui arbore in particular binar contin pe langa informatia corespunzatoare si
informatii despre cei doi fii stang si drept In calculator arborii binari se pot reprezenta in doua moduri
Reprezentarea secvențiala
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente a trei
vector diferiti INFO[i] ST[i] si DR[i] unde i este indicele asociat unui nod Cei trei vectori au
dimensiunea egala cu numarul de noduri din arbore De exemplu pentru arborele de mai sus daca
numerotam nodurile incepand cu nivelul 0 de la stanga la dreapta obtinem urmatorii vectori cu
conventia ca radacina este nodul 1
INFO= (10 4 21 1 9 15 23 28)
ST=(1 4 6 00 0 0 0)
DR = (3 5 7 0 0 0 8 0)
Reprezentarea inlantuita
Pentru fiecare nod al arborelui se precizeaza informatia si descendentii directi ca elemente ale
unei structuri definita astfel
unde T este presupus definit anterior (eventual printr-o definitie typedef) stang este pointer la
subarborele stang al nodului iar drept este pointer la subarborele drept al nodului
64
Pentru identificarea radacinii arborelui vom defini NODARB rad drept un pointer la radacina
arborelui Daca unul din subarbori este vid atunci pointerul la acel subarbore este NULL Pentru
arborele de mai sus reprezentarea inlantuita este
Traversare
De multe ori dorim sa accesam (vizitam) nodurile unei structuri (lista sau arbore) Pentru arbori
aceasta accesare examinare a unui nod sau mai exact examinarea tuturor nodurilor unui arbore se
numeste traversare si se poate face
in preordine intai vizitam radacina arborelui apoi subarborele stang urmat de subarborele drept
in inordine (simetrica) intai vizitam subarborele stang apoi radacina arborelui si apoi
subarborele drept
in postordine intai vizitam subarborele stang si subarborele drept si ultima data radacina
arborelui
Actiunea explicita de vizitare a unui nod depinde de scopul traversarii (de exemplu aflarea
numarului de elemente ale arborelui gasirea unei valori date in arbore) Pentru arborele de mai sus de
exemplu traversarile sunt
preordine 10 4 1 9 21 15 23 28
inordine (simetrica) 1 4 9 10 15 21 23 28
postordine 1 9 4 15 28 23 21
Arbori parţiali de cost minim
Fie G = ltX Vgt un graf neorientat conex unde X este multimea varfurilor si U este multimea
muchiilor Un arbore este un asemenea graf ce nu are cicluri Fiecare muchie are un cost pozitiv (sau o
lungime pozitiva) Pentru a gasi un arbore se pune problema sa gasim o submultime A inclusa in U
astfel incat toate varfurile din X sa ramina conectate atunci cand sunt folosite doar muchii din A Numim
arbore partial de cost minim acel arbore ce are multimea varfurilor X si a muchiilor A iar suma
lungimilor muchiilor din A este minima Cautam deci o submultime A de cost total minim care sa lege
printr-un drum oricare doua noduri din X Aceasta problema se mai numeste si problema conectarii
oraselor cu cost minim avand numeroase aplicatii
Graful partial ltX Agt este un arbore si este numit arborele partial de cost minim al grafului G
(minimal spanning tree) Un graf poate avea mai multi arbori partiali de cost minim
Observatii
In orice nod intra cel mult un arc
In nodul radacina nu intra nici un arc
Nodurile pot fi etichetate sau nu
Icircnaltimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent
1+maximul dintre icircnaltimile subarborilor sai
Exemplu 30 Arborele prezentat icircn figura de mai jos are icircnaltimea 5
65
Reprezentarea icircn memorie a arborilor poate fi statica sau dinamica Icircn cazul static arborii se pot
simula cu ajutorul tablourilor
Exemplu 31 Icircn tabloul arbore cu n componente arbore(i) (i=1n) reprezinta tatal nodului i
Astfel arborele din figura de mai sus se poate reprezenta sub forma
Avantajul acestei implementari este urmatorul fiecarui nod avacircnd cel mult un tata icirci atasam icircn
tablou o singura informatie (Luam arbore(i)=0 daca nodul i este radacina)
Datorita dinamismului structurilor modelate printr-un arbore varianta de implementare dinamica
este preferabila variantei statice In acest caz daca arborele este binar o celula va contine trei cacircmpuri
un cacircmp pentru memorarea informatiei specifice nodului (informatia utila) si doua cacircmpuri care contin
adresa radacinii subarborelui stacircng respectiv drept
Operatiile fundamentale asupra arborilor includ parcurgerea arborelui stergerea cautarea sau
adaugarea unui nod
Doua tipuri de parcurgere a unui arbore sunt folosite frecvent parcurgerea icircn latime si
parcurgerea icircn icircnaltime
In cazul parcugerii icircn latime se viziteaza si prelucreaza nodurile icircn ordinea radacina nodurile de
la stacircnga spre dreapta de pe primul nivel de pe al doilea nivel etc Astfel rezultatul parcurgerii icircn latime
a arborelui din figura este lista de noduri 1 2 5 6 3 4 7 8 9 10
Putem realiza pacurgerea icircn latime a unui arbore binar printr-un algoritm care utilizeaza o coada
drept element ajutator
Operaţii pe arbori binari
Operaţiile pe arbori se grupează icircn următoarele categorii
Operaţii de creare a arborilor binari Crearea arborilor binari presupune construirea icircn
memorie a unui arbore binar folosind informaţii din mediul extern sursele cele mai frecvente
fiind introducerea de la tastatura de către utilizator sau fişierele Algoritmii de creare ai unui
arbore binar presupun a cunoaşte relaţiile icircn care se află un nod cu celelate noduri din arbore O
metodă simplă de a specifica aceste relaţii este ca după crearea unui nod să se specifice fiul stacircng
şi fiul drept dacă ei există
Operaţii cu elemente (noduri) categorie din care cele mai importante sunt operaţiile de inserare
şi ştergere de noduri icircn şi din arbore Deoarece operaţia de inserare a unui nod necesită
specificarea relaţiei icircn care se află nodul respectiv cu celelate noduri din arbore iar ştergerea
unui nod implică formarea unor noi relaţii icircntre noduri aceste operaţii sunt uşor de definit in
cazul icircn care peste mulţimea informaţiilor din noduri există o relaţie de ordine
Traversări de arbori atacirct pentru prelucrarea informaţiei utile cacirct şi pentru căutare de informaţie
icircn arbore Cele mai frecvente moduri de traversare utilizate icircn cazul arborilor binari sunt
1 preordine traversarea se face prin rădăcina arborelui apoi se traversează subarborele
stacircng iar apoi subarborele drept
66
2 inordine traversarea se face icircncepacircnd cu subarborele stacircng apoi prin rădăcină iar apoi
se traversează subarborele drept
3 postordine traversarea se face icircncepacircnd cu subarborele stacircng apoi se traversează
subarborele drept iar apoi rădăcina
Algoritmul pentru operaţia de căutare icircntr-un arbore binar de căutare este următorul
1 Se compară cheia căutate cu cheia din radăcină
2 Dacă sunt egale algoritmul se incheie
3 Dacă valoarea cheii căutate este mai mică decacirct valoarea din rădacină atunci se va relua
algoritmul pentru subarborele stacircng Dacă nu există subarbore stacircng inseamnă că
informaţia căutată nu se găseşte in arbore
4 Altfel dacă valoarea cheii căutate este mai mare decacirct valoarea cheii din radacină se va
relua algoritmul pentru subarborele drept Dacă nu există subarbore drept inseamnă că
informaţia căutată nu se găseşte in arbore
Conversii şi stocare icircn fişier Conversiile şi stocarea icircn fişiere presupune traversarea arborilor şi
salvarea informaţiilor icircn alte structuri de date aflate icircn memorie sau icircn fişiere pe medii de stocare
Arbori binari de căutare
Se numeşte arborescenţă un arbore caracterizat astfel
are un vacircrf special numit rădăcină
celelalte noduri pot fi grupate icircn pgt=0 mulţimi disjuncte astfel icircncacirct fiecare dintre aceste mulţimi
să conţină un nod adiacent cu rădăcina iar subgrafurile generate de acestea să fie la racircndul lor
arborescenţe
Observații
1 Dacă o arborescenţă este formată dintr-un singur nod spunem că este formată doar din nodul
rădăcină
2 Dacă ordinea relativă a arborescenţelor are importanţă arborescenţa se numeşte se numeşte
arbore ordonat
Informaţia din fiecare nod este mai mare decacirct informaţia din nodul fiului stacircng şi mai mică sau
egală cu cea din nodul fiului drept Un astfel de arbore se poate reprezenta printr-o structură de date
icircnlănţuită icircn care fiecare nod este un obiect
Pe lacircngă un cacircmp cheie şi date adiţionale fiecare obiect nod conţine cacircmpurile stacircnga dreapta şi
p care punctează spre nodurile corespunzătoare fiului stacircng fiului drept şi respectiv părintelui nodului
Icircnt-un arbore binar de căutare cheile sunt icircntotdeauna astfel memorate icircncacirct ele satisfac
proprietatea arborelui binar de căutare
Fie x un nod dintr-un arbore binar de căutare Dacă y este un nod din subarborele stacircng al lui x
atunci cheie[y] cheie[x] Dacă y este un nod din subarborele drept al lui x atunci cheie[x] cheie[y]
Proprietatea arborelui binar de căutare ne permite să afişăm toate cheile icircn ordine crescătoare
parcurgicircnd nodurile arborelui icircn inordine
Exemple
67
Exemplu Principalele operații de bază aferente arborilor binari sunt prezentate icircn următoarea
bibliotecă
ifndef ARBORE_H
define ARBORE_H
un nod din arbore
struct NodArbore
informatia utila
TipArbore Date
legaturile catre subarbori
NodArbore Stanga Dreapta
constructor pentru initializarea unui nod nou
NodArbore(TipArbore date
NodArbore stanga = NULL NodArbore dreapta = NULL)
Date(date) Stanga(stanga) Dreapta(dreapta)
Arborele este manipulat sub forma unui pointer catre radacina
typedef NodArbore Arbore
Creaza un arbore vid
Arbore ArbCreare()
return NULL
Testeaza daca un arbore este vid
bool ArbEGol(Arboreamp arbore)
return arbore == NULL
68
Adauga un element intr-un arbore de cautare
void ArbAdauga(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
arbore = new NodArbore(date)
return
Cazul 2 arbore nevid
if (date lt arbore-gtDate)
daca exista subarborele stang
if (arbore-gtStanga = NULL)
inseram in subarbore
ArbAdauga(arbore-gtStanga date)
else
cream subarborele stang
arbore-gtStanga = new NodArbore(date)
if (date gt arbore-gtDate)
daca exista subarborele drept
if (arbore-gtDreapta = NULL)
inseram in subarbore
ArbAdauga(arbore-gtDreapta date)
else
cream subarborele drept
arbore-gtDreapta = new NodArbore(date)
Functie privata de stergere a unui nod
void __ArbStergeNod(Arboreamp legParinte)
salvam un pointer la nodul de sters
Arbore nod = legParinte
daca avem un subarbore drept
if (nod-gtDreapta = NULL)
facem legatura
legParinte = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
69
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
facem legatura la acesta
legParinte = nod-gtStanga
else
daca nu avem nici un subnod
legParinte = NULL
stergem nodul
delete nod
Sterge un nod dintr-un arbore de cautare
void ArbSterge(Arboreamp arbore TipArbore date)
Cazul 1 arbore vid
if (ArbEGol(arbore))
return
Cazul 2 stergere radacina
if (arbore-gtDate == date)
salvam un pointer la radacina
Arbore nod = arbore
daca avem un subarbore drept
if (nod-gtDreapta)
facem legatura
arbore = nod-gtDreapta
daca avem si un subarbore stang
if (nod-gtStanga)
cautam cel mai mic element din subarborele drept
Arbore temp = nod-gtDreapta
while (temp-gtStanga = NULL)
temp = temp-gtStanga
si adaugam subarborele stang
temp-gtStanga = nod-gtStanga
else
daca avem doar un subarbore stang
if (nod-gtStanga = NULL)
70
facem legatura la acesta
arbore = nod-gtStanga
else
daca nu avem nici un subnod
arbore = NULL
stergem vechea radacina
delete nod
return
Cazul 3 stergere nod in arbore nevid
cautam legatura la nod in arbore si stergem nodul (daca exista)
Arbore nodCurent = arbore
while (true)
if (date lt nodCurent-gtDate)
if (nodCurent-gtStanga == NULL)
break nodul nu exista
else
if (nodCurent-gtStanga-gtDate == date)
nodul de sters este descendentul stang
__ArbStergeNod(nodCurent-gtStanga)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtStanga
else
if (nodCurent-gtDreapta == NULL)
break nodul nu exista
else
if (nodCurent-gtDreapta-gtDate == date)
nodul de sters este descendentul drept
__ArbStergeNod(nodCurent-gtDreapta)
else
continuam cautarea in subarborele stang
nodCurent = nodCurent-gtDreapta
Cauta recursiv un nod in arborele de cautare
bool Cautare(Arboreamp arbore TipArbore info)
conditia de oprire din recursie
if (arbore == NULL)
return false
verificam daca am gasit nodul
if (arbore-gtDate == info)
return true
71
daca cheia este mai mica
if (arbore-gtDate lt info)
cautam in subarborele stang
return Cautare(arbore-gtStanga info)
else
altfel cautam in subarborele drept
return Cautare(arbore-gtDreapta info)
endif ARBORE_H
Capitolul 3
31 Tipuri de funcţii Metode predefinite
Un program scris icircn limbajul CC++ este un ansamblu de funcţii fiecare dintre acestea efectuacircnd
o activitate bine definită Din punct de vedere conceptual funcţia reprezintă o aplicaţie definită pe o
mulţime D (D=mulţimea domeniul de definiţie) cu valori icircn mulţimea C (C=mulţimea de valori
codomeniul) care icircndeplineşte condiţia că oricărui element din D icirci corespunde un unic element din C
Funcţiile comunică prin argumente ele primesc ca parametri (argumente) datele de intrare
efectuează prelucrările descrise icircn corpul funcţiei asupra acestora şi pot returna o valoare (rezultatul
datele de ieşire) Execuţia programului icircncepe cu funcţia principală numită main Funcţiile pot fi
descrise icircn cadrul aceluiaşi fişier sau icircn fişiere diferite care sunt testate şi compilate separat asamblarea
lor realizacircndu-se cu ajutorul linkeditorului de legături
O funcţie este formata din antet si corp
72
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
A Funcţii matematice (headerul ltmathhgt)
Funcţii aritmetice (valori absolute )
int abs(int x) Returnează un icircntreg care reprezintă valoarea absolută a argumentului
long int labs(long int x) Analog cu funcţia abs cu deosebirea că argumentul şi valoarea
returnată sunt de tip long int
double fabs(double x) Returnează un real care reprezintă valoarea absolută a argumentului
real
Exemplu Modul de utilizare a funcției abs () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
int x = -5
long y = -2371041
int a = abs(x)
long b = abs(y)
cout ltlt abs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt a ltlt endl
cout ltlt abs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt b ltlt endl
Icircn urma rulării obținem
abs (-5) = | -5 | = 5
abs (-2371041) = | -2371041 | = 2371041
Exemplu Modul de utilizare a funcției labs () Să se ruleze următorul program
include ltiostreamgt
73
include ltcstdlibgt
using namespace std
int main()
long int xy
x = -9999999L
y = 10000000L
cout ltlt labs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt labs(x) ltlt endl
cout ltlt labs( ltlt y ltlt ) = | ltlt y ltlt | = ltlt labs(y) ltlt endl
return 0
Icircn urma rulării obținem
labs(-9999999) = |-9999999| = 9999999
labs(10000000) = |10000000| = 10000000
Exemplu Modul de utilizare a funcției fabs () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = -1025 result
result = fabs(x)
cout ltlt fabs( ltlt x ltlt ) = | ltlt x ltlt | = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
fabs(-1025) = |-1025| = 1025
Funcţii de rotunjire
double floor(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mic sau egal cu x (rotunjire prin lipsă)
double ceil(double x) Returnează un real care reprezintă cel mai apropiat număr fără
zecimale mai mare sau egal cu x (rotunjire prin adaos)
Exemplu Modul de utilizare a funcției floor () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
74
x = -34251
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
x = 071
result = floor(x)
cout ltlt Floor of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Floor of 1025 = 10
Floor of -34251 = -35
Floor of 071 = 0
Exemplu Modul de utilizare a funcției ceil () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = ceil(x)
cout ltlt Ceil of ltlt x ltlt = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Ceil of 1025 = 11
Funcţii trigonometrice
double sin(double x) Returnează valoarea lui sin(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double cos(double x) Returnează valoarea lui cos(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [-1 1]
double tan(double x) Returnează valoarea lui tg(x) unde x este dat icircn radiani
Exemplu Modul de utilizare a funcției sin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 0439203 result
result = sin(x)
75
cout ltlt sin(x) = ltlt result ltlt endl
double xDegrees = 900
converting degrees to radians
x = xDegrees314159180
result = sin(x)
cout ltlt sin(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
sin(x) = 0425218
sin(x) = 1
Exemplu Modul de utilizare a funcției tan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
long double x = 099999 result
result = tan(x)
cout ltlt tan(x) = ltlt result ltlt endl
double xDegrees = 600
converting degree to radians and using tan() fucntion
result = tan(xDegrees314159180)
cout ltlt tan(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
tan(x) = 155737
tan(x) = 173205
Funcţii trigonometrice inverse
double asin(double x) Returnează valoarea lui arcsin(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat (icircn radiani) se află icircn intervalul [-pi2 pi2]
double acos(double x) Returnează valoarea lui arccos(x) unde x se află icircn intervalul [-1 1]
Numărul real returnat se află icircn intervalul [0 pi]
double atan(double x) Returnează valoarea lui arctg(x) unde x este dat icircn radiani Numărul
real returnat se află icircn intervalul [0 pi]
double atan2(double y double x) Returnează valoarea lui tg(yx) cu excepţia faptului ca
semnele argumentelor x şi y permit stabilirea cadranului şi x poate fi zero Valoarea returnată
se află icircn intervalul [-pipi] Dacă x şi y sunt coordonatele unui punct icircn plan funcţia
returnează valoarea unghiului format de dreapta care uneşte originea axelor carteziene cu
76
punctul faţă de axa absciselor Funcţia foloseşte deasemenea la transformarea coordonatelor
cartezine icircn coordonate polare
Exemplu Modul de utilizare a funcției asin () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 025 result
result = asin(x)
cout ltlt asin(x) = ltlt result ltlt radians ltlt endl
result in degrees
cout ltlt asin(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
asin(x) = 025268 radians
asin(x) = 144779 degrees
Exemplu Modul de utilizare a funcției atan () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 5774 result
result = atan(x)
cout ltlt atan(x) = ltlt result ltlt radians ltlt endl
Output in degrees
cout ltlt atan(x) = ltlt result18031415 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan(x) = 155348 radians
atan(x) = 890104 degrees
Exemplu Modul de utilizare a funcției atan2 () Să se ruleze următorul program
include ltiostreamgt
77
include ltcmathgt
using namespace std
int main()
double x = 100 y = -100 result
result = atan2(y x)
cout ltlt atan2(yx) = ltlt result ltlt radians ltlt endl
cout ltlt atan2(yx) = ltlt result1803141592 ltlt degrees ltlt endl
return 0
Icircn urma rulării obținem
atan2(yx) = -0785398 radians
atan2(yx) = -45 degrees
Funcţii exponenţiale şi logaritmice
double exp(double x)
long double exp(long double x) Returnează valoarea e
double log(double x) Returnează logaritmul natural al argumentului ( ln(x) )
double log10(double x) Returnează logaritmul zecimal al argumentului (lg (x) )
double pow(double baza double exponent) Returnează un real care reprezintă rezultatul
ridicării bazei la exponent ( )
double sqrt(double x) Returnează rădăcina pătrată a argumentului x
double hypot(double x double y) Funcţia distanţei euclidiene - returnează 22 yx deci
lungimea ipotenuzei unui triunghi dreptunghic sau distanţa punctului P(x y) faţă de origine
Exemplu Modul de utilizare a funcției exp () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 219 result
result = exp(x)
cout ltlt exp(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
exp(x) = 893521
Exemplu Modul de utilizare a funcției log () Să se ruleze următorul program
include ltiostreamgt
x
baza onentexp
78
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
x = -3591
result = log (x)
cout ltlt log(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log(x) = 256925
log(x) = nan
Exemplu Modul de utilizare a funcției log10 () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double x = 13056 result
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
x = -3591
result = log10(x)
cout ltlt log10(x) = ltlt result ltlt endl
return 0
Icircn urma rulării obținem
log10(x) = 111581
log10(x) = nan
Exemplu Modul de utilizare a funcției pow () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main ()
double base exponent result
79
base = 34
exponent = 44
result = pow(base exponent)
cout ltlt base ltlt ^ ltlt exponent ltlt = ltlt result
return 0
Icircn urma rulării obținem
34^44 = 218025
Exemplu Modul de utilizare a funcției sqrt () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 1025 result
result = sqrt(x)
cout ltlt Square root of ltlt x ltlt is ltlt result ltlt endl
return 0
Icircn urma rulării obținem
Square root of 1025 is 320156
Exemplu Modul de utilizare a funcției hypot () Să se ruleze următorul program
include ltiostreamgt
include ltcmathgt
using namespace std
int main()
double x = 21 y = 31 result
result = hypot(x y)
cout ltlt hypot(x y) = ltlt result ltlt endl
long double yLD resultLD
x = 352
yLD = 5232342323
hypot() returns long double in this case
resultLD = hypot(x yLD)
cout ltlt hypot(x yLD) = ltlt resultLD
return 0
80
Icircn urma rulării obținem
hypot(x y) = 374433
hypot(x yLD) = 630617
Funcţii de generare a numerelor aleatoare
int rand(void) ltstdlibhgt Generează un număr aleator icircn intervalul [0 RAND_MAX]
Exemplu Modul de utilizare a funcției rand () Să se ruleze următorul program
includeltiostreamgt
includeltcstdlibgt
using namespace std
int main()
int random = rand()
No srand() calls before rand() so seed = 1
cout ltlt Seed = 1 Random number = ltlt random ltlt endl
srand(5)
Seed = 5
random = rand()
cout ltlt Seed = 5 Random number = ltlt random ltlt endl
return 0
Icircn urma rulării obținem
Seed = 1 Random number = 41
Seed = 5 Random number = 54
B Funcţii de clasificare (testare) a caracterelor
Au prototipul icircn headerul ltctypehgt Toate aceste funcţii primesc ca argument un caracter şi
returnează un număr icircntreg care este pozitiv dacă argumentul icircndeplineşte o anumită condiţie sau
valoarea zero dacă argumentul nu icircndeplineşte condiţia
int isalnum(int c) Returnează valoare icircntreagă pozitivă daca argumentul este literă sau cifră
Echivalentă cu isalpha(c)||isdigit(c)
int isalpha(int c) Testează dacă argumentul este literă mare sau mică Echivalentă cu
isupper(c)|| islower(c)
int iscntrl(int c) Testează dacă argumentul este caracter de control (neimprimabil)
int isdigit(int c) Testează dacă argumentul este cifră
int isxdigit(int c) Testează dacă argumentul este cifră hexagesimală (0-9 a-f A-F)
int islower(int c) Testează dacă argumentul este literă mică
int isupper(int c) Testează dacă argumentul este literă mare
int ispunct(int c) Testează dacă argumentul este caracter de punctuaţie (caracter imprimabil
dar nu literă sau spaţiu)
int isspace(int c) Testează dacă argumentul este spaţiu alb ( n t v r)
int isprint(int c) Testează dacă argumentul este caracter imprimabil inclusiv blancul
Exemplu Modul de utilizare a funcției isalnum () Să se ruleze următorul program
81
include ltstdiohgt
include ltctypehgt
int main()
char c
int result
c = 5
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = Q
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = l
result = isalnum(c)
printf(When c is passed return value is dn c result)
c = +
result = isalnum(c)
printf(When c is passed return value is dn c result)
return 0
Icircn urma rulării obținem
When 5 is passed return value is 1
When Q is passed return value is 1
When l is passed return value is 1
When + is passed return value is 0
Exemplu Modul de utilizare a funcției isalpha () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = ad138kw+~$]qjj
int count = 0
for (int i=0 ilt=strlen(str) i++)
if (isalpha(str[i]))
count ++
cout ltlt Number of alphabet characters ltlt count ltlt endl
cout ltlt Number of non alphabet characters ltlt strlen(str)-count ltlt endl
return 0
Icircn urma rulării obținem
Number of alphabet characters7
82
Number of non alphabet characters12
Exemplu Modul de utilizare a funcției iscntrl () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = t
char ch2 = x
iscntrl(ch1)cout ltlt ch1 ltlt is a control charactercout ltlt ch1 ltlt is not a control character
cout ltlt endl
iscntrl(ch2)cout ltlt ch2 ltlt is a control charactercout ltlt ch2 ltlt is not a control character
return 0
Icircn urma rulării obținem
t is a control character
x is not a control character
Exemplu Modul de utilizare a funcției isdigit () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = hjpq910js4
cout ltlt The digit in the string are ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isdigit(str[i]))
cout ltlt str[i] ltlt
return 0
Icircn urma rulării obținem
The digit in the string are
9 1 0 4
Exemplu Modul de utilizare a funcției islower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
83
using namespace std
int main()
char str[] = This Program Converts ALL LowerCase Characters to UpperCase
for (int i=0 i lt strlen(str) i++)
if (islower(str[i]))
Converting lowercase characters to uppercase
str[i] = str[i] - 32
cout ltlt str
return 0
Icircn urma rulării obținem
THIS PROGRAM CONVERTS ALL LOWERCASE CHARACTERS TO UPPERCASE
Exemplu Modul de utilizare a funcției isupper () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = This Program Converts ALL UPPERCASE Characters to LOWERCASE
for (int i=0 iltstrlen(str) i++)
if (isupper(str[i]))
Converting uppercase characters to lowercase
str[i] = str[i] + 32
cout ltlt str
return 0
Icircn urma rulării obținem
this program converts all uppercase characters to lowercase
Exemplu Modul de utilizare a funcției ispunct () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
using namespace std
int main()
char ch1 = +
char ch2 = r
84
ispunct(ch1) cout ltlt ch1 ltlt is a punctuation character cout ltlt ch1 ltlt is not a punctuation
character
cout ltlt endl
ispunct(ch2) cout ltlt ch2 ltlt is a punctuation character cout ltlt ch2 ltlt is not a punctuation
character
return 0
Icircn urma rulării obținem
+ is a punctuation character
r is not a punctuation character
Exemplu Modul de utilizare a funcției isspace () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
using namespace std
int main()
char str[] = lthtmlgtnltheadgtntlttitlegtC++lttitlegtnltheadgtnlthtmlgt
cout ltlt Before removing whitespace characters ltlt endl
cout ltlt str ltlt endl ltlt endl
cout ltlt After removing whitespace characters ltlt endl
for (int i=0 iltstrlen(str) i++)
if (isspace(str[i]))
cout ltlt str[i]
return 0
Icircn urma rulării obținem
Before removing whitespace characters
lthtmlgt
ltheadgt
lttitlegtC++lttitlegt
ltheadgt
lthtmlgt
After removing whitespace characters
lthtmlgtltheadgtlttitlegtC++lttitlegtltheadgtlthtmlgt
Exemplu Modul de utilizare a funcției isprint () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
85
using namespace std
int main()
char str[] = Hellotallnhow are you
for (int i=0 iltstrlen(str) i++)
replace all non printable character by space
if (isprint(str[i]))
str[i] =
cout ltlt str
return 0
Icircn urma rulării obținem
Hello all how are you
C Funcţii de conversie a caracterelor (prototip icircn ltctypehgt)
int tolower(int c) Funcţia schimbă caracterul primit ca argument din literă mare icircn literă
mică şi returnează codul ASCII al literei mici Dacă argumentul nu este literă mare codul
returnat este chiar codul argumentului
int toupper(int c) Funcţia schimbă caracterul primit ca argument din literă mică icircn literă
mare şi returnează codul acesteia Dacă argumentul nu este literă mică codul returnat este
chiar codul argumentului
Exemplu Modul de utilizare a funcției tolower () Să se ruleze următorul program
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The lowercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(tolower(str[i]))
return 0
Icircn urma rulării obținem
The lowercase version of John is from USA is
john is from usa
Exemplu Modul de utilizare a funcției toupper () Să se ruleze următorul program
86
include ltcctypegt
include ltiostreamgt
include ltcstringgt
include ltcstdiogt
using namespace std
int main()
char str[] = John is from USA
cout ltlt The uppercase version of ltlt str ltlt is ltlt endl
for (int i=0 iltstrlen(str) i++)
putchar(toupper(str[i]))
return 0
Icircn urma rulării obținem
The uppercase version of John is from USA is
JOHN IS FROM USA
D Funcţii de conversie din şir icircn număr (de citire a unui număr dintr-un şir - prototip icircn
ltstdlibhgt)
long int atol(const char npr) Funcţia converteşte şirul transmis ca argument (spre care
pointează npr) icircntr-un număr cu semn care este returnat ca o valoare de tipul long int Şirul
poate conţine caracterele + sau - Se consideră că numărul este icircn baza 10 şi funcţia nu
semnalizează eventualele erori de depăşire care pot apare la conversia din şir icircn număr
int atoi(const char sir) Converteste şirul spre care pointeaza sir icircntr-un număr icircntreg
double atof(const char sir) Funcţia converteste şirul transmis ca argument icircntr-un număr
real cu semn (returnează valoare de tipul double) Icircn secvenţa de cifre din şir poate apare
litera e sau E (exponentul) urmată de caracterul + sau - şi o altă secvenţă de cifre Funcţia
nu semnalează eventualele erori de depăşire care pot apare
Exemplu Modul de utilizare a funcției atol () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char s[] = -114
double number
cout ltlt Number in String = ltlt s ltlt endl
number = atol(s)
cout ltlt Number in Long Int = ltlt number
return 0
Icircn urma rulării obținem
87
Number in String = -114
Number in Long Int = -114
Exemplu Modul de utilizare a funcției atof () Să se ruleze următorul program
include ltiostreamgt
include ltcstdlibgt
using namespace std
int main()
char numberString[] = -3240
double numberInDouble
cout ltlt Number in String = ltlt numberString ltlt endl
numberInDouble = atof(numberString)
cout ltlt Number in Double = ltlt numberInDouble
return 0
Icircn urma rulării obținem
Number in String = -3240
Number in Double = -324
E Funcţii de intrareieşire (prototip icircn ltstdiohgt)
Streamurile (fluxurile de date) implicite sunt stdin (fişierul dispozitivul standard de intrare)
stdout (fişierul dispozitivul standard de ieşire) stderr (fişier standard pentru erori) stdprn (fişier
standard pentru imprimantă) şi stdaux (dispozitivul auxiliar standard) De cacircte ori este executat un
program streamurile implicite sunt deschise automat de către sistem Icircn headerul ltstdiohgt sunt definite
şi constantele NULL (definită ca 0) şi EOF (sfacircrşit de fişier definită ca -1 CTRLZ)
int getchar(void) Citeşte un caracter (cu ecou) din fişierul standard de intrare (tastatură)
int putchar(int c) Afişează caracterul primit ca argument icircn fişierul standard de ieşire
(monitor)
char gets(char sir) Citeşte un şir de caractere din fişierul standard de intrare (pacircnă la
primul blank icircntacirclnit sau linie nouă) Returnează pointerul către şirul citit
int puts(const char sir) Afişează şirul argument icircn fişierul standard de ieşire şi adaugă
terminatorul de şir Returnează codul ultimului caracter al şirului (caracterul care precede
NULL) sau -1 icircn caz de eroare
int printf(const char format ) Funcţia permite scrierea icircn fişierul standard de ieşire (pe
monitor) a datelor icircntr-un anumit format Funcţia returnează numărul de octeţi (caractere)
afişaţi sau ndash1 icircn cazul unei erori
Exemplu Modul de utilizare a funcției getchar () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int ci=0
88
char str[100]
cout ltlt Enter characters Press Enter to stopn
do
c = getchar()
str[i] = c
i++
while(c=n)
cout ltlt str
return 0
Icircn urma rulării obținem
Enter characters Press Enter to stop
rtq paSd12 62 haQ
rtq paSd12 62 haQ
Exemplu Modul de utilizare a funcției putchar () Să se ruleze următorul program
include ltcstdiolt
int main()
for (int i=48 ilt58 i++)
Writes the equivalent character
putchar(i)
putchar( )
return 0
Icircn urma rulării obținem
0 1 2 3 4 5 6 7 8 9
Exemplu Modul de utilizare a funcției gets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
char str[100]
cout ltlt Enter a string
gets(str)
cout ltlt You entered ltlt str
89
return 0
Icircn urma rulării obținem
Enter a string Have a great day
You entered Have a great day
Exemplu Modul de utilizare a funcției puts () Să se ruleze următorul program
include ltcstdiogt
int main()
char str1[] = Happy New Year
char str2[] = Happy Birthday
puts(str1)
Printed on new line since n is added
puts(str2)
return 0
Icircn urma rulării obținem
Happy New Year
Happy Birthday
Exemplu Modul de utilizare a funcției printf () Să se ruleze următorul program
include ltcstdiogt
int main()
int x = 5
char my_name[] = Lincoln
printf(x = d n x)
printf(My name is s n my_name)
return 0
Icircn urma rulării obținem
x = 5
My name is Lincoln
include ltcstdiogt
int main()
char ch = a
float a = 50 b = 30
int x = 10
printf(3f 3f = 3f n abab)
printf(Setting width c n5ch)
90
printf(Octal equivalent of d is o nxx)
return 0
Icircn urma rulării obținem
5000 3000 = 1667
Setting width a
Octal equivalent of 10 is 12
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh)
Cacircnd un program este executat icircn mod automat se deschid următoarele fluxuri de date
predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier
care conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Funcţia fopen
Crează un flux de date icircntre fişierul specificat prin numele extern (nume_fişier) şi programul C
Parametrul mod specifică sensul fluxului de date şi modul de interpretare a acestora Funcţia returnează
un pointer spre tipul FILE iar icircn caz de eroare - pointerul NULL (prototip icircn stdioh)
FILE fopen(const char nume_fişier const char mod)
91
Parametrul mod este o constantă şir de caractere care poate conţine caracterele cu semnificaţiile
r flux de date de intrare deschidere pentru citire
w flux de date de ieşire deschidere pentru scriere (crează un fişier nou sau suprascrie
conţinutul anterior al fişierului existent)
a flux de date de ieşire cu scriere la sfacircrşitul fişierului adăugare sau crearea fişierului icircn
cazul icircn care acesta nu există
+ extinde un flux de intrare sau ieşire la unul de intrareieşire operaţii de scriere şi citire
asupra unui fişier deschis icircn condiţiile r w sau a
b date binare
t date text (modul implicit)
Exemple
r+ ndash deschidere pentru modificare (citire şi scriere)
w+ ndash deschidere pentru modificare (citire şi scriere)
rb ndash citire binară
wb ndash scriere binară
r+b ndash citirescriere binară
Exemplu Deschiderea unui fisier in mod scriere cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt w)
char str[20] = Hello World
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Exemplu Deschiderea unui fisier in mod citire cu fopen () Să se ruleze următorul program
include ltcstdiogt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt r)
if (fp)
while ((c = getc(fp)) = EOF)
putchar(c)
92
fclose(fp)
return 0
Icircn urma rulării obținem
Hello World
Exemplu Deschiderea unui fisier in mod anexă cu fopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
using namespace std
int main()
int c
FILE fp
fp = fopen(filetxt a)
char str[20] = Hello Again
if (fp)
putc(nfp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
fclose(fp)
Icircn urma rulării obținem
Se crează un fisier bdquofiletxtrdquo care intr-o linie noua textul Hello Again
Funcţia freopen (stdioh)
Asociază un nou fişier unui flux de date deja existent icircnchizacircnd legătura cu vechiul fişier şi
icircncercacircnd să deschidă una nouă cu fişierul specificat Funcţia returnează pointerul către fluxul de date
specificat sau NULL icircn caz de eşec (prototip icircn stdioh)
FILEfreopen(const charnume_fişconst charmodFILE flux_date)
Exemplu Modul de utilizare a funcției freopen () Să se ruleze următorul program
include ltcstdiogt
include ltcstdlibgt
int main()
FILE fp = fopen(test1txtw)
fprintf(fpsThis is written to test1txt)
if (freopen(test2txtwfp))
fprintf(fpsThis is written to test2txt)
else
93
printf(freopen failed)
exit(1)
fclose(fp)
return 0
Icircn urma rulării obținem
The following will be written to test1txt
This is written to test1txt
The following will be written to test2txt
This is written to test2txt
Funcţia open
Deschide fişierul specificat conform cu restricţiile de acces precizate icircn apel Returnează un
icircntreg care este un indicator de fişier sau -1 (icircn caz de eşec) (prototip icircn ioh)
int open(const char nume_fişier int acces [int mod])
Restricţiile de acces se precizează prin aplicarea operatorului | (disjuncţie logică la nivel de bit)
icircntre anumite constante simbolice definite icircn fcntlh cum sunt
O_RDONLY - citire
O_WRONLY - scriere
O_RDWR - citire şi scriere
O_CREAT - creare
O_APPEND - adăugare la sfacircrşitul fişierului
O_TEXT - interpretare CR-LF
O_BINARY - nici o interpretare
Restricţiile de mod de creare se realizează cu ajutorul constantelor
S_IREAD - permisiune de citire din fişier
S_IWRITE - permisiune de scriere din fişier eventual legate prin operatorul
ldquo|rdquo
Exemplu Modul de utilizare a funcției open () Să se ruleze următorul program
include ltunistdhgt
include ltfcntlhgt
int main()
int filedesc = open(testfiletxt O_WRONLY | O_APPEND)
if(filedesc lt 0)
return 1
if(write(filedescThis will be output to testfiletxtn 36) = 36)
94
write(2There was an error writing to testfiletxtn)
return 1
return 0
Funcţia creat
Crează un fişier nou sau icircl suprascrie icircn cazul icircn care deja există Returnează indicatorul de fişier
sau -1 (icircn caz de eşec) Parametrul un_mod este obţinut icircn mod analog celui de la funcţia de deschidere
(prototip icircn ioh)
int creat(const char nume_fişier int un_mod)
Exemplu Modul de utilizare a funcției creat () Să se ruleze următorul program
include ltiohgt
include ltsysstathgt
include ltstdiohgt
include ltstdlibhgt
void main()
int fp
fp = _creat(filedat S_IREAD|S_IWRITE)
if (fp == -1)
printf(Cannot create filedatn)
else
printf(Filedat successfully createdn)
Funcţia creatnew
Crează un fişier nou conform modului specificat Returnează indicatorul fişierului nou creat sau
rezultat de eroare (-1) dacă fişierul deja există (prototip icircn ioh)
int creatnew(const char nume_fişier int mod)
După cum se observă informaţia furnizată pentru deschiderea unui fişier este aceeaşi icircn ambele
abordări diferenţa constacircnd icircn tipul de date al entitaţii asociate fişierului Implementarea din ioh oferă
un alt tip de control la nivelul comunicării cu echipamentele periferice (furnizat de funcţia ioctrl) asupra
căruia nu vom insista deoarece desfăşurarea acestui tip de control este mai greoaie dar mai profundă
Funcţia fclose
Funcţia icircnchide un fişier deschis cu fopen şi eliberează memoria alocată (zona tampon şi
structura FILE) Returnează valoarea 0 la icircnchiderea cu succes a fişierului şi -1 icircn caz de eroare (prototip
icircn stdioh)
95
int fclose(FILE pf)
Exemplu Modul de utilizare a funcției fclose () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
FILE fp
fp = fopen(filetxtw)
char str[20] = Hello World
if (fp == NULL)
cout ltlt Error opening file
exit(1)
fprintf(fpsstr)
fclose(fp)
cout ltlt File closed successfully
return 0
Funcţia fcloseall
Icircnchide toate fluxururile de date şi returnează numărul fluxurilor de date icircnchise (prototip icircn
stdioh)
int fcloseall(void)
Exemplu Modul de utilizare a funcției fcloseall () Să se ruleze următorul program
includeltstdiohgt
int streams_closed
fopen(ONEtxtw)
fopen(TWOtxtw)
streams_closed = fcloseall()
if (streams_closed == EOF)
printf(Error)
else
printf(d Streams Were Closed streams_closed)
return 0
Funcţia close Icircnchide un indicator de fişier şi returnează 0 (icircn caz de succes) sau -1 icircn caz de eroare (prototip icircn
ioh)
96
int close(int indicator)
In general nu se scriu functii care sa aloce memorie fara sa o eliberezedeoarece apelarea repetata
a unor astfel de functii poate duce la consum inutilde memorie La fel nu se admite ca sarcina eliberarii
memoriei alocate sarevina celui care apeleaza functia
Exemplu Modul de utilizare a funcției close () Să se ruleze următorul program
include ltfstreamgt
int main ()
stdofstream ofs
ofsopen (testtxt stdofstreamout | stdofstreamapp)
ofs ltlt more lorem ipsum
ofsclose()
return 0
32 Modalităţi şi tehnici de utilizare a funcţiilor metodelor predefinite
Limbajul C poseda o biblioteca de functii C care pot fi utilizate in cadrul programului Informatii
despre functiile din biblioteca sunt precizate in niste fisiere de tip h ( headere) standard care sunt
adaugate programului printr-o directiva preprocesor de tip include
In cazul operatiilor de intrareiesire IE se specifica fisierul header standard stdioh cu ajutorul
directivei include ldquostdiohrdquosau include ltstdiohgt constructie ce nu este o instructiune C care se
introduce in cadrul functiilor nici un cuvint cheie al limbajului si nici nu se termina cu dar se scrie din
prima coloana In bibliotecile standard ale limbajului C si C++ exista functii predefinite care pot fi usor
folosite de catre utilizatori Apelul lor implica existenta prototipului lor
Pentru aceste functii standard exista anumite fisiere standard headere de tip h (stdioh
stringh etc) care contin prototipul unor functii inrudite Aceste fisiere headere se includ printr-o
directiva preprocesor
stdioh - contine functii de introducere - extragere a datelor Functiile utilizate pina in acest
moment (de ex getchar printf gets etc) opereaza cu fisierele standard de introducere si
extragere stdin(implicit tastatura) si stdout (implicit monitorul) Prin redirectare aceste fisiere
standard se pot asocia cu alte fisiere
Un fisier este o structura dinamica situata in memoria secundara (pe flopyy disk-uri sau hard
disk-uri ) numarul de elemente ale unui fisier este variabil chiar nul
Limbajul C permite operarea cu fisiere
de tip text - un astfel de fisier contine o succesiune de linii separate prin NL (n)
de tip binar - un astfel de fisier contine o succesiune de octeti fara nici o structura
Prelucrarea unui fisier presupune asocierea acestuia cu un canal de IE ( numit flux sau stream )
Exista doua canale predefinite care se deschid automat la lansarea unui program
stdin - fisier de intrare text este intrarea standard - tastatura
stdout - fisier de iesire text este iesirea standard - ecranul monitorului
Pentru a prelucra un fisier trebuie parcurse urmatoarele etape
se defineste o variabila de tip FILE pentru accesarea fisierului FILE este un tip structura
definit in stdioh care contine informatii referitoare la fisier si la tamponul de transfer de date
intre memoria centrala si fisier ( adresa lungimea tamponului modul de utilizare a fisierului
indicator de sfarsit de pozitie in fisier )
se deschide fisierul pentru un anumit mod de acces folosind functia de biblioteca fopen care
realizeaza si asocierea intre variabila fisier si numele extern al fisierului
se prelucreaza fisierul in citirescriere cu functiile specifice
97
se inchide fisierul folosind functia de biblioteca fclose
Practic nu exista program care sa nu apeleze functii din bibliotecile existentesi care sa nu contina
definitii de functii specifice aplicatiei respective In limbajul C exista numai functii dar pentru functiile
fara rezultat direct(asociat numelui functiei) s-a introdus tipul void Pentru o functie cu rezultatdirect
tipul functiei este tipul rezultatului
Argumentele folosite la apelul functiei se numesc argumente efective si potfi orice expresii
(constante functii etc) Argumentele efective trebuie sacorespunda ca numar si ca ordine (ca
semnificatie) cu argumentele formale (cuexceptia unor functii cu numar variabil de argumente Este
posibil ca tipul unui argument efectiv sa difere de tipul argumentului formal corespunzator cu conditia
ca tipurile sa fie compatibile la atribuire
Conversia de tip (intre numere sau pointeri) se face automat la fel ca si la atribuire
Tipul unei functii C poate fi orice tip numeric orice tip pointer orice tip structura (struct) sau
void In lipsa unei declaratii de tip explicite se considera ca tipul implicit al functiei este int Functia
main poate fi declarata fie de tip void fie de tip int explicit sau implicit
Variabilele definite intr-o functie pot fi folosite numai in functia respectiva cu exceptia celor
declarate extern Pot exista variabile cu aceleasi nume in functii diferite dar ele se refera la adrese de
memorie diferite O functie are in general un numar de argumente formale (fictive) prin care primeste
datele initiale necesare si poate transmite rezultatele functiei Aceste argumente pot fi doar nume de
variabile (nu orice expresii) cu tipul declarat in lista de argumente pentru fiecare argument in parte
Standardul limbajului C contine si o serie de functii care trebuie sa existe in toate implementarile
limbajului Declaratiile acestor functii sunt grupate in fisiere antet cu acelasi nume pentru toate
implementarile In afara acestor functii standard exista si alte functii specifice sistemului de operare
precum si functii utile pentru anumite aplicatii (grafica pe calculator baze de date aplicatii de retea sa)
Uneori aceleasi operatii se pot realiza cu functii universale sau cu functii dependente de sistem
obtineremodificare timp operatii cu directoare sa Utilizarea functiilor standard din biblioteci reduce
timpul de dezvoltare a programelor mareste portabilitatea lor si contribuie la reducerea diversitatii
programelor cu efect asupra usurintei de citire si de intelegere a lor Functiile de biblioteca nestandard
utilizate ar trebui marcate prin comentarii Informatii complete asupra functiilor de biblioteca pot fi
obtinute prin ajutor (Help) oferit de orice mediu IDE sau prin examinarea fisierelor antet de tip H
Cacircteva grupuri de functii standard utile
Functii standard de intrare-iesire pentru consola si fisiere
Functii de alocare memorie de conversie din caractere in binar (atoi atol atof) de sortare si
cautare (qsort bsearch) functii diverse (exit)
Functii standard matematice (cu rezultat si argumente double)
(abssqrtpowsincosexplog sa)
Functii standard pentru operatii cu siruri de caractere
Functii de verificare tip caractere si de conversie caractere
Functii pentru operatii cu timpi si date calendaristice
Functii (macrouri) pentru functii cu numar variabil de argumente
Functii standard de intrare-iesire stil Unix
Functii de intrare-iesire cu consola (ecranul si tastatura)
Functii pentru executie procese (taskuri)
In definirea functiilor se folosesc pointeri pentru
Transmiterea de rezultate prin argumente
Transmiterea unei adrese prin rezultatul functiei
O functie care trebuie sa modifice mai multe valori primite prin argumente sau care trebuie sa
transmita mai multe rezultate calculate de functie trebuie sa foloseasca argumente de tip pointer
Anumite aplicatii numerice necesita scrierea unei functii care sa poata apela o functie cu nume
necunoscut dar cu prototip si efect cunoscut Prin conventie in limbajul C numele unei functii neinsotit
98
de o lista de argumente (chiar vida) este interpretat ca un pointer catre functia respectiva (fara a se folosi
operatorul de adresare amp) Deci sin este adresa functiei sin(x) in apelul functiei listf
O eroare de programare care trece de compilare si se manifesta la executie este apelarea unei
functii fara paranteze compilatorul nu apeleaza functia si considera ca programatorul vrea sa foloseasca
adresa functiei
O functie este apelata prin nume urmat de o lista de argumente intre paranteze O metoda de a
comunica date intre functii este prin intermediul argumentelor functiei O functie fara argumente se
indica prin ( )
Acoladele includ instructiunile care alcatuiesc functia Un program C oricare i-ar fi
marimea consta din una sau mai multe functii care specifica operatiile efective de calculat care
trebuiesc facute
Orice mediu de programare este prevăzut cu una sau mai multe biblioteci de funcţii predefinite
Orice bibliotecă este formată din
fişierele header (conţine prototipurile funcţiilor declaraţiile de variabile)
biblioteca (arhiva) propriu-zisă (conţine definiţii de funcţii)
Pentru ca funcţiile predefinite să poată fi utilizate fişierele header icircn care se găsesc prototipurile
acestora trebuie inclus icircn funcţia (programul) apelant printr-o directivă preprocesor (exemplu include
ltstdiohgt) Deasemenea utilizatorul icircşi poate crea propriile headere proprii Pentru a putea utiliza
funcţiile proprii el trebuie să includă aceste headere icircn programul apelant (exemplu include
my_headerh)
Pentru funcţiile predefinite au fost create fişiere header orientate pe anumite numite tipuri de
aplicaţii De exemplu funcţiile matematice se găsesc icircn headerul ltmathhgt Headerul ltstdlibhgt care
conţine funcţii standard Headerul ltvalueshgt defineşte o serie de constante simbolice (exemplu
MAXINT MAXLONG) care reprezintă icircn principal valorile maxime şi minime ale diferitelor tipuri de
date
Icircn limbajul C operaţiile asupra fişierelor se realizează cu ajutorul unor funcţii din biblioteca
standard (stdioh) Transferurile cu dipozitivele periferice (tastatură monitor disc imprimantă etc) se
fac prin intermediul unor dispozitive logice identice numite stream-uri (fluxuri) şi prin intermediul
sistemului de operare Un flux de date este un fişier sau un dispozitiv fizic tratat printr-un pointer la o
structură de tip FILE (din header-ul stdioh) Cacircnd un program este executat icircn mod automat se
deschid următoarele fluxuri de date predefinite dispozitive logice (icircn stdioh)
stdin (standard input device) - dispozitivul standard de intrare (tastatura) - ANSII C
stdout (standard output device) - dispozitivul standard de ieşire (monitorul) - ANSII C
stderr (standard error output device) - dispozitivul standard de eroare (de obicei un fişier care
conţine mesajele de eroare rezultate din execuţia unor funcţii) - ANSII C
stdaux (standard auxiliary device) - dispozitivul standard auxiliar (de obicei interfaţa serială
auxiliară) - specifice MS-DOS
stdprn (standard printer) - dispozitivul de imprimare - specifice MS-DOS
Icircn abordarea limbajului C (impusă de stdioh) toate elementele care pot comunica informaţii cu
un program sunt percepute - icircn mod unitar - ca fluxuri de date Datele introduse de la tastatură formează
un fişier de intrare (fişierul standard de intrare) Datele afişate pe monitor formează un fişier de ieşire
(fişierul standard de ieşire) Sfacircrşitul oricărui fişier este indicat printr-un marcaj de sfacircrşit de fişier
(end of file) Icircn cazul fişierului standard de intrare sfacircrşitul de fişier se generează prin Ctrl+Z (^Z) (sub
MS-DOS) (sau Ctrl+D sub Linux) Acest caracter poate fi detectat prin folosirea constantei simbolice
EOF (definită icircn fişierul stdioh) care are valoarea -1 Această valoare nu rămane valabilă pentru
fişierele binare care pot conţine pe o poziţie oarecare caracterul rsquox1Arsquo
De obicei schimbul de informaţii dintre programe şi periferice se realizează folosind zone
tampon O zonă tampon păstrează una sau mai multe icircnregistrări Prin operaţia de citire icircnregistrarea
curentă este transferată de pe suportul extern icircn zona tampon care icirci corespunde programul avacircnd apoi
acces la elementele icircnregistrării din zona tampon Icircn cazul operaţiei de scriere icircnregistrarea se
construieşte icircn zona tampon prin program fiind apoi transferată pe suportul extern al fişierului Icircn cazul
99
monitoarelor icircnregistrarea se compune din caracterele unui racircnd De obicei o zonă tampon are lungimea
multiplu de 512 octeţi Orice fişier trebuie deschis inainte de a fi prelucrat iar la terminarea prelucrării
lui trebuie icircnchis
Fluxurile pot fi de tip text sau de tip binar Fluxurile de tip text icircmpart fişierele icircn linii separate
prin caracterul rsquonrsquo (newline=linie nouă) putacircnd fi citite ca orice fişier text Fluxurile de tip binar
transferă blocuri de octeţi (fără nici o structură) neputacircnd fi citite direct ca fişierele text
Prelucrarea fişierelor se poate face la două niveluri
Nivelul superior de prelucrare a fişierelor icircn care se utilizează funcţiile specializate icircn
prelucrarea fişierelor
Nivelul inferior de prelucrare a fişierelor icircn care se utilizează direct facilităţile oferite de sistemul
de operare deoarece icircn final sarcina manipulării fişierelor revine sistemului de operare Pentru a
avea acces la informaţiile despre fişierele cu care lucrează sistemul de operare foloseşte cacircte un
descriptor (bloc de control) pentru fiecare fişier
Ca urmare există două abordări icircn privinţa lucrului cu fişiere
abordarea implementată icircn stdioh asociază referinţei la un fişier un stream (flux de date) un
pointer către o structură FILE
abordarea definită icircn header-ul ioh (inputoutput header) asociază referinţei la un fişier un aşa-
numit handle (icircn cele ce urmează acesta va fi tradus prin indicator de fişier) care din punct de
vedere al tipului de date este in
Scopul lucrului cu fişiere este acela de a prelucra informaţia conţinută Pentru a putea accesa un
fişier va trebui să-l asociem cu unul din cele două modalităţi de manipulare Acest tip de operaţie se mai
numeşte deschidere de fişier Icircnainte de a citi sau scrie icircntr-un fişier (neconectat automat programului)
fişierul trebuie deschis cu ajutorul funcţiei fopen din biblioteca standard Funcţia primeşte ca argument
numele extern al fişierului negociază cu sistemul de operare şi retunează un nume (identificator) intern
care va fi utilizat ulterior la prelucrarea fişireului Acest identificator intern este un pointer la o structură
care conţine informaţii despre fişier (poziţia curentă icircn buffer dacă se citeşte sau se scrie icircn fişier etc)
Utilizatorii nu trebuie să cunoască detaliile singura declaraţie necesară fiind cea pentru pointerul de
fişier
După deschiderea unui fişier toate operaţiile asupra fişierului vor fi efectuate cu pointerul său
Operaţiile de citire şi scriere icircntr-un fişier text pot fi
intrăriieşiri la nivel de caracter (de octet)
intrăriieşiri la nivel de cuvacircnt (2 octeţi)
intrăriieşiri de şiruri de caractere
intrăriieşiri cu formatare
Comunicarea de informaţie de la un fişier către un program este asigurată prin funcţii de citire
care transferă o cantitate de octeţi (unitatea de măsură icircn cazul nostru) din fişier icircntr-o variabilă-program
pe care o vom numi buffer ea icircnsăşi avacircnd sensul unei icircnşiruiri de octeţi prin declaraţia void buf
Comunicarea de informaţie de la un program către un fişier este asigurată prin funcţii de scriere care
transferă o cantitate de octeţi dintr-o variabilă-program de tip buffer icircn fişier
Fişierele sunt percepute icircn limbajul C ca fiind implicit secvenţiale (informaţia este parcursă
succesiv element cu element) Pentru aceasta atacirct fluxurile de date cacirct şi indicatorii de fişier au asociat
un indicator de poziţie curentă icircn cadrul fişierului Acesta este iniţializat la 0 icircn momentul deschiderii
iar operaţiile de citire respectiv scriere se referă la succesiunea de octeţi care icircncepe cu poziţia curentă
Operarea asupra fiecărui octet din succesiune determină incrementarea indicatorului de poziţie
curentă
Prelucrarea unui fişier la nivel de caracter
Fişierele pot fi scrise şi citite caracter cu caracter folosind funcţiile putc (pentru scriere) şi getc
(citire)
100
Funcţia putc Funcţia putc returnează valoarea lui c (valoarea scrisă icircn caz de succes) sau ndash1 (EOF) icircn caz de
eroare sau sfacircrşit de fişier
int putc (int c FILE pf)
unde
c ndash este codul ASCII al caracterului care se scrie icircn fişier
pf ndash este pointerul spre tipul FILE a cărui valoare a fost returnată de funcţia fopen
Exemplu Modul de utilizare a funcției putc () Să se ruleze următorul program
include ltcstdiogt
include ltcstringgt
int main()
char str[] = Testing putc() function
FILE fp
fp = fopen(filetxtw)
if (fp)
for(int i=0 iltstrlen(str) i++)
putc(str[i]fp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia getc Funcţia citeşte un caracter dintr-un fişier (pointerul spre tipul FILE transmis ca argument) şi
returnează caracterul citit sau EOF la sfacircrşit de fişier sau eroare
int getc (FILE pf)
Exemplu Modul de utilizare a funcției getc () Să se ruleze următorul program
include ltcstdiogt
int main()
int c
FILE fp
fp = fopen(filetxtr)
if (fp)
101
while(feof(fp) == 0)
c = getc(fp)
putchar(c)
else
perror(File opening failed)
fclose(fp)
return 0
Prelucrarea unui fişier la nivel de cuvacircnt
Funcţiile putw şi getw (putword şi getword) sunt echivalente cu funcţiile putc şi getc cu
diferenţa că unitatea transferată nu este un singur octet (caracter) ci un cuvacircnt (un int)
int getw(FILE pf)
int putw (int w FILE pf)
Se recomandă utilizarea funcţiei feof pentru a testa icircntacirclnirea sfacircrşitului de fişier
Exemplu Modul de utilizare a funcțiilor getw () și putw () Să se ruleze următorul program
include ltstdiohgt
int main ()
FILE fp
int i=1 j=2 k=3 num
fp = fopen (testcw)
putw(ifp)
putw(jfp)
putw(kfp)
fclose(fp)
fp = fopen (testcr)
while(getw(fp)=EOF)
num= getw(fp)
printf(ldquoData in testc file is d nrdquo num)
fclose(fp)
return 0
Icircn urma rulării obținem
Datele din fisierul testc sunt 1 2 3
Prelucrarea unui fişier la nivel de şir de caractere
102
Icircntr-un fişier text liniile sunt considerate ca linii de text separate de sfacircrşitul de linie (n) iar icircn
memorie ele devin şiruri de caractere terminate de caracterul nul (0) Citirea unei linii de text dintr-un
fişier se realizează cu ajutorul funcţiei fgets iar scrierea icircntr-un fişier - cu ajutorul funcţiei fputs
Funcţia fgets este indentică cu funcţia gets cu deosebirea că funcţia gets citeşte din fişierul
standard de intrare (stdin) Funcţia fputs este indentică cu funcţia puts cu deosebirea funcţia puts scrie icircn
fişierul standard de ieşire (stdout)
Funcţia fputs
Funcţia scrie un şir de caractere icircntr-un fişier şi primeşte ca argumente pointerul spre zona de
memorie (buffer-ul) care conţine şirul de caractere (s) şi pointerul spre structura FILE Funcţia
returnează ultimul caracter scris icircn caz de succes sau -1 icircn caz de eroare
int fputs(const char s FILE pf)
Exemplu Modul de utilizare a funcției fputs () Să se ruleze următorul program
include ltcstdiogt
int main()
char str[] = Learning to program
FILE fp
fp = fopen(filetxtw)
if (fp)
fputs(strfp)
else
perror(File opening failed)
fclose(fp)
return 0
Funcţia fgets
Funcţia citeşte maximum dim-1 octeţi (caractere) din fişier sau pacircnă la icircntacirclnirea sfarşitului de
linie Pointerul spre zona icircn care se face citirea caracterelor este s Terminatorul null (0) este plasat
automat la sfacircrşitul şirului (buffer-lui de memorie) Funcţia returnează un pointer către buffer-ul icircn care
este memorat şirul de caractere icircn caz de succes sau pointerul NULL icircn cazul eşecului
char fgets(char s int dim FILE pf)
Exemplu Modul de utilizare a funcției fgets () Să se ruleze următorul program
include ltiostreamgt
include ltcstdiogt
using namespace std
int main()
int count = 10
char str[10]
FILE fp
fp = fopen(filetxtw+)
fputs(An example filen fp)
fputs(Filename is filetxtn fp)
103
rewind(fp)
while(feof(fp) == 0)
fgets(strcountfp)
cout ltlt str ltlt endl
fclose(fp)
return 0
Intrăriieşiri formatate
Operaţiile de intrareieşire formatate permit citirea respectiv scrierea icircntr-un fişier text
impunacircnd un anumit format Se utilizează funcţiile fscanf şi fprintf similare funcţiilor scanf şi printf
(care permit citireascrierea formatată de la tastaturămonitor)
Funcţia fscanf
int fscanf(FILE pf const char format )
Funcţia fprintf
int fprintf(FILE pf const char format )
Funcţiile primesc ca parametri ficşi pointerul (pf ) spre tipul FILE (cu valoarea atribuită la apelul
funcţiei fopen) şi specificatorul de format (cu structură identică celui prezentat pentru funcţiile printf şi
scanf) Funcţiile returnează numărul cacircmpurilor cititescrise icircn fişier sau -1 (EOF) icircn cazul detectării
sfacircrşitului fişierului sau al unei erori
Exemplu Modul de utilizare a funcției fscanf () Să se ruleze următorul program
include ltcstdiogt
int main ()
FILE fp
char name[50]
int age
fp = fopen(exampletxtw)
fprintf(fp s d Tim 31)
fclose(fp)
fp = fopen(exampletxtr)
fscanf(fp s d name ampage)
fclose(fp)
printf(Hello s You are d years oldn name age)
return 0
Icircn urma rulării obținem Hello Tim You are 31 years old
Exemplu Modul de utilizare a funcției fprintf () Să se ruleze următorul program
include ltcstdiogt
int main()
FILE fp
104
fp = fopen(exampletxtw)
char lang[5][20] = CC++JavaPythonMatlab
fprintf(fpTop 5 programming languagen)
for (int i=0 ilt5 i++)
fprintf(fp d sn i+1 lang[i])
fclose(fp)
return 0
Icircn urma rulării obținem
1 C
2 C++
3 Java
4 Python
5 Matlab
BIBLIOGRAFIE
Cărți
1 V Huţanu T Sorin ndash Manual de informatică EdLampS Soft Bucureşti 2006
2 M Milosescu ndash Manual de informatică Ed Didactică și Pedagocică Bucureşti 2011
3 D Oprescu LB Ienulescu ndash Manual de informatică Ed Niculescu 2006
4 B Overland ndash Ghid pentru icircncepători C++ Ed Corint Bucureşti 2006
5 E Cerchez M Şerban ndash Programarea icircn limbajul CC++ pentru liceu Ed Polirom Bucureşti
2007
Site-uri web
6 wwwcsutclujro
7 wwwlabscsuttro
8 wwwfacultateregielivero
9 wwwdidacticro
10 wwwinfoscience3xro
11 Carmen Ana Anton httpscarmenantonfileswordpresscom201510lectia-7-informatica-
subprogramepdf
12 httpandreiclubciscorocursuri1pccurs1Curs20820Docpdf
13 httpswwwprogramizcom
14 httpsinfogeniusroreprezentarea-grafurilor-cpp
15 httpstutoriale-penetparcugerea-adancime-dfs
16 httpasesoftmentorroStructuriDeDate06_Arborihtm