curs programare modularĂ

104
1 UNIVERSITATEA DIN PETROȘANI CURS PROGRAMARE MODULARĂ CONF. DR. ING. STOICUȚA OLIMPIU - COSTINEL 2019

Upload: others

Post on 21-Nov-2021

24 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: 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

Page 2: CURS PROGRAMARE MODULARĂ

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

Page 3: CURS PROGRAMARE MODULARĂ

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

Page 4: CURS PROGRAMARE MODULARĂ

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

Page 5: CURS PROGRAMARE MODULARĂ

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

Page 6: CURS PROGRAMARE MODULARĂ

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

Page 7: CURS PROGRAMARE MODULARĂ

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

Page 8: CURS PROGRAMARE MODULARĂ

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

Page 9: CURS PROGRAMARE MODULARĂ

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

Page 10: CURS PROGRAMARE MODULARĂ

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

Page 11: CURS PROGRAMARE MODULARĂ

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

Page 12: CURS PROGRAMARE MODULARĂ

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

Page 13: CURS PROGRAMARE MODULARĂ
Page 14: CURS PROGRAMARE MODULARĂ
Page 15: CURS PROGRAMARE MODULARĂ
Page 16: CURS PROGRAMARE MODULARĂ
Page 17: CURS PROGRAMARE MODULARĂ
Page 18: CURS PROGRAMARE MODULARĂ
Page 19: CURS PROGRAMARE MODULARĂ
Page 20: CURS PROGRAMARE MODULARĂ
Page 21: CURS PROGRAMARE MODULARĂ
Page 22: CURS PROGRAMARE MODULARĂ
Page 23: CURS PROGRAMARE MODULARĂ
Page 24: CURS PROGRAMARE MODULARĂ
Page 25: CURS PROGRAMARE MODULARĂ
Page 26: CURS PROGRAMARE MODULARĂ
Page 27: CURS PROGRAMARE MODULARĂ
Page 28: CURS PROGRAMARE MODULARĂ
Page 29: CURS PROGRAMARE MODULARĂ
Page 30: CURS PROGRAMARE MODULARĂ
Page 31: CURS PROGRAMARE MODULARĂ
Page 32: CURS PROGRAMARE MODULARĂ
Page 33: CURS PROGRAMARE MODULARĂ
Page 34: CURS PROGRAMARE MODULARĂ
Page 35: CURS PROGRAMARE MODULARĂ
Page 36: CURS PROGRAMARE MODULARĂ
Page 37: CURS PROGRAMARE MODULARĂ
Page 38: CURS PROGRAMARE MODULARĂ
Page 39: CURS PROGRAMARE MODULARĂ
Page 40: CURS PROGRAMARE MODULARĂ
Page 41: CURS PROGRAMARE MODULARĂ
Page 42: CURS PROGRAMARE MODULARĂ
Page 43: CURS PROGRAMARE MODULARĂ
Page 44: CURS PROGRAMARE MODULARĂ
Page 45: CURS PROGRAMARE MODULARĂ
Page 46: CURS PROGRAMARE MODULARĂ
Page 47: CURS PROGRAMARE MODULARĂ
Page 48: CURS PROGRAMARE MODULARĂ
Page 49: CURS PROGRAMARE MODULARĂ
Page 50: CURS PROGRAMARE MODULARĂ
Page 51: CURS PROGRAMARE MODULARĂ
Page 52: CURS PROGRAMARE MODULARĂ
Page 53: CURS PROGRAMARE MODULARĂ
Page 54: CURS PROGRAMARE MODULARĂ
Page 55: CURS PROGRAMARE MODULARĂ
Page 56: CURS PROGRAMARE MODULARĂ
Page 57: CURS PROGRAMARE MODULARĂ
Page 58: CURS PROGRAMARE MODULARĂ
Page 59: CURS PROGRAMARE MODULARĂ
Page 60: CURS PROGRAMARE MODULARĂ
Page 61: CURS PROGRAMARE MODULARĂ
Page 62: CURS PROGRAMARE MODULARĂ
Page 63: CURS PROGRAMARE MODULARĂ
Page 64: CURS PROGRAMARE MODULARĂ
Page 65: CURS PROGRAMARE MODULARĂ
Page 66: CURS PROGRAMARE MODULARĂ
Page 67: CURS PROGRAMARE MODULARĂ
Page 68: CURS PROGRAMARE MODULARĂ
Page 69: CURS PROGRAMARE MODULARĂ
Page 70: CURS PROGRAMARE MODULARĂ
Page 71: CURS PROGRAMARE MODULARĂ
Page 72: CURS PROGRAMARE MODULARĂ
Page 73: CURS PROGRAMARE MODULARĂ
Page 74: CURS PROGRAMARE MODULARĂ
Page 75: CURS PROGRAMARE MODULARĂ
Page 76: CURS PROGRAMARE MODULARĂ
Page 77: CURS PROGRAMARE MODULARĂ
Page 78: CURS PROGRAMARE MODULARĂ
Page 79: CURS PROGRAMARE MODULARĂ
Page 80: CURS PROGRAMARE MODULARĂ
Page 81: CURS PROGRAMARE MODULARĂ
Page 82: CURS PROGRAMARE MODULARĂ
Page 83: CURS PROGRAMARE MODULARĂ
Page 84: CURS PROGRAMARE MODULARĂ
Page 85: CURS PROGRAMARE MODULARĂ
Page 86: CURS PROGRAMARE MODULARĂ
Page 87: CURS PROGRAMARE MODULARĂ
Page 88: CURS PROGRAMARE MODULARĂ
Page 89: CURS PROGRAMARE MODULARĂ
Page 90: CURS PROGRAMARE MODULARĂ
Page 91: CURS PROGRAMARE MODULARĂ
Page 92: CURS PROGRAMARE MODULARĂ
Page 93: CURS PROGRAMARE MODULARĂ
Page 94: CURS PROGRAMARE MODULARĂ
Page 95: CURS PROGRAMARE MODULARĂ
Page 96: CURS PROGRAMARE MODULARĂ
Page 97: CURS PROGRAMARE MODULARĂ
Page 98: CURS PROGRAMARE MODULARĂ
Page 99: CURS PROGRAMARE MODULARĂ
Page 100: CURS PROGRAMARE MODULARĂ
Page 101: CURS PROGRAMARE MODULARĂ
Page 102: CURS PROGRAMARE MODULARĂ
Page 103: CURS PROGRAMARE MODULARĂ
Page 104: CURS PROGRAMARE MODULARĂ