Sunteți pe pagina 1din 116

1.

Programarea i rezolvarea problemelor

Comportamentul uman i gndirea sunt caracterizate de secvene logice. Un individ nva cum s execute anumite comenzi. De asemenea, nva ce tipuri de comportamente poate atepta de la ali indivizi. La o scar mai larg, matematica lucreaz cu secvene logice de pai pentru rezolvarea problemelor i demonstrarea teoremelor. La fel, producia de mas nu poate exista fr succesiunile de operaii executate ntr-o anumit ordine. Atunci cnd ordonm un proces, l programm. Acest curs se refer la programarea unui aparat: calculatorul. Calculatorul este un dispoztiv programabil care poate pstra, regsi i procesa date. Aa cum un program descrie aciunile care trebuie executate pentru a atinge un scop, un program de calculator descrie paii pe care trebuie s i execute calculatorul pentru a rezolva o problem. n contextul acestui curs, atunci cnd vom vorbi de programare ne vom referi la programarea calculatorului. Un program de calculator este o list de instruciuni care trebuie urmate de calculator. Un calculator ne permite s realizm o serie de aciuni ntr-un mod mult mai rapid i mai precis dect o putem face fr ajutorul su. Pentru a l folosi, ns, trebuie s specificm ce dorim s facem, dar i n ce ordine. Aceste lucruri le facem prin programare.

1.1

Cum scriem un program?

Pentru a scrie un program trebuie s parcurgem dou faze: - rezolvarea problemei - implementarea.

Faza de rezolvare a problemei


1. Analiza nseamn nelegerea, definirea problemei i a soluiei ce trebuie dat; 2. Algoritmul presupune dezvoltarea unei secvene logice de pai care trebuie urmai pentru rezolvarea problemei; 3. Verificarea este parcurgerea pailor algoritmului pe mai multe exemple pentru a fi siguri c rezolv problema pentru toate cazurile.

Faza de implementare
1. Programul reprezint translatarea algoritmului ntr-un limbaj de programare 2. Testarea este etapa n care ne asigurm c instruciunile din program sunt urmate corect de calculator. n situaia n care constatm c sunt erori, trebuie s revedem algoritmul i programul pentru a determina sursa erorilor i pentru a le corecta. Aceste dou faze de dezvoltare a programului sunt urmate de faza de utilizare a programului care nseamn folosirea acestuia pentru rezolvarea problemelor reale, cele pentru care a fost conceput. Ulterior pot interveni modificri ale programului fie pentru a rspunde noilor cerine ale utilizatorilor, fie pentru corectarea erorilor care apar n timpul utilizrii i care nu au putut fi gsite n faza de testare. Calculatorul nu este inteligent. El nu poate analiza problema i nu poate s dea o soluie. Programatorul trebuie s analizeze problema, s dea soluia i apoi s o

Programarea calculatoarelor i limbaje de programare I

comunice calculatorului. Avantajul folosirii calculatorului este c el rezolv problemele rapid i fr erori, eliberndu-ne totodat de realizarea unor operaii repetitive i plictisitoare. Programatorul ncepe operaia de programare prin analiza problemei i dezvoltarea unei soluii generale numit algoritm. Algoritmul este o procedur care descrie paii ce trebuie parcuri pentru rezolvarea unei probleme ntr-un timp finit. Algoritmul este esenial pentru procesul de programare. Programul este de fapt un algoritm scris pentru calculator.

Un algoritm este o secven logic de aciuni. Folosim algoritmi n fiecare zi: reetele, instruciunile de folosire sunt exemple de algoritmi care nu sunt, ns, programe. Un exemplu de algoritm poate fi o succesiune de pai care trebuie urmai pentru a porni o main. Un alt exemplu este calculul sumei care trebuie pltit unui salariat ntr-o sptmn. 1. Verificarea sumei pltite pe or 2. Determinarea numrului de ore lucrate n timpul sptmnii 3. Dac numrul de ore este mai mic sau egal cu 40, se nmulete numrul de ore cu suma pltit pe or 4. Dac numrul de ore depete 40, atunci se scade 40 din numrul de ore lucrate, iar diferena de ore se nmulete cu 1,5 ori suma pltit pe or 5. Adun sumele de la punctele 3 i 4 i stabilete suma final. Dup dezvoltarea soluiei generale, programatorul poate testa algoritmul mental sau n scris. Dac algoritmul nu este corect, relum paii descrii mai devreme. Cnd programatorul este satisfcut de algoritm, poate s l translateze ntr-un program scris ntr-un limbaj de programare.

Programarea calculatoarelor i limbaje de programare I

Limbajul de programare este un set de reguli, simboluri i cuvinte speciale folosite pentru a construi un program. Limbajul C++ este o variant simplificat a limbii engleze i care are un set strict de reguli gramaticale. Datorit numrului mic de cuvinte disponibile, suntei obligai s scriei instruciuni simple i exacte. Codarea este translatarea algoritmului ntr-un limbaj de programare. Execuia este rularea programului pe calculator (running). Depanarea este faza de determinare i corectare a erorilor (debugging). Implementarea este combinaia dintre codarea i testarea algoritmului. O parte important a programrii este scrierea documentaiei. Documentaia este reprezentat de un text scris i de comentariile necesare nelegerii de ctre alte persoane a programului scris de noi. Dup scrierea programului, trebuie s transmitem calculatorului informaiile sau datele necesare rezolvrii problemei. Informaia este orice cunotin care poate fi comunicat, inclusiv idei abstracte sau concepte. Datele sunt informaii transpuse ntr-o form care poate fi neleas de calculator.

1.2

Ce este un limbaj de programare?

Programatorii scriu instruciuni n diverse limbaje de programare, unele care sunt nelese n mod direct de calculator, altele care necesit mai muli pai de translatare. n prezent exist sute de limbaje de programare care pot fi mprite n trei tipuri generale: 1. Limbaje main 2. Limbaje de asamblare 3. Limbaje de nivel nalt

Singurul limbaj de programare pe care calculatorul l poate executa n mod direct este un set primitiv de instruciuni numit limbaj main sau cod main. Acesta este limbajul natural al unui calculator i este definit de alctuirea hardware a fiecrui calculator n parte. Un anumit limbaj main poate fi folosit doar pentru un anumit tip de calculator. Limbajul main este alctuit din instruciuni codate binar i poate fi folosit direct de calculator. Limbajele main sunt greu de folosit de programatori, aa cum se poate vedea din urmtoarea seciune de program scris n limbaj main care adun o sum suplimentar de bani la suma de baz pe care o primete un angajat, rezultnd suma final. Exemplu +1300042774 +1400593419 +1200274027 3

Programarea calculatoarelor i limbaje de programare I

Pe msur ce calculatoarele au devenit tot mai populare, a devenit evident c limbajul main este greu de folosit, dezvoltarea aplicaiilor este foarte lent i probabilitatea de apariie a erorilor este foarte mare. n loc s se foloseasc numere pentru a programa calculatoarele, s-a trecut la folosirea unor abrevieri ale unor cuvinte din limba englez care reprezint operaii elementare pentru calculator. Aceste abreviaii formeaz baza limbajelor de asamblare. n acelai timp au fost dezvoltate programe de translatare sau asambloare pentru a converti programele scrise in limbaj de asamblare ctre programe n limbaj main. Secvena de instruciuni de mai jos realizeaz aceleai operaii ca cele din exemplul anterior, dar ntr-o manier mai clar dect echivalentul n limbaj main. Exemplu LOAD BASEPAY ADD OVERPAY STORE GROSSPAY n prezent se folosesc limbaje de nivel nalt, mult mai uor de folosit dect codul main i care accelereaz procesul de dezvoltare software. Un program numit compilator translateaz un program scris ntr-un limbaj de nivel nalt n limbaj main. Iat o variant scris ntr-un limbaj de nivel nalt a programului de mai sus. Exemplu grossPay = basePay + overTimePay Pentru a putea rula un program de nivel nalt pe un calculator, pe acesta trebuie s existe un compilator adaptat limbajului dar i calculatorului. Programul surs este un program scris ntr-un limbaj de nivel nalt. Programul obiect este versiunea n limbaj main a programului surs i se obine n urma compilrii. Compilarea i execuia sunt dou procese distincte.

Instruciunile dintr-un limbaj de programare reflect operaiile pe care le poate realiza un calculator: - transferarea datelor dintr-un loc n altul - citirea datelor de la un dispozitiv de intrare (ex. tastatura) i transmiterea lor ctre un dispozitiv de ieire (ex. ecran) - stocarea i aducerea datelor din memorie sau alte dispozitive de memorare - compararea a dou date pentru stabilirea egalitii sau a inegalitii - operaii aritmetice

Programarea calculatoarelor i limbaje de programare I

Scurt istoric al limbajelor C i C++


Limbajul C++ a evoluat din limbajul C care, la rndul su, a avut la baz alte dou limbaje de programare, BCPL i B. Limbajul BCPL a fost dezvoltat n 1967 de Martin Richards ca limbaj pentru scrierea sistemelor de operare i a compilatoarelor. Ken Thompson a modelat multe elemente ale BCPL n limbajul B pe care l-a folosit pentru scrierea uneia dintre primele versiuni ale limbajului UNIX la Bell Laboratories n 1970. Aceste dou limbaje de programare nu foloseau tipuri de dat, fiecare dat avea aceeai dimensiune n memorie, iar tratarea unei date ca ntreg sau real era n responsabilitatea programatorului. Limbajul C a fost dezvoltat din limbajul B la Bell Laboratories n 1972 de Dennis Ritchie. Iniial a fost folosit pentru dezvoltarea sistemului de operare UNIX, iar astzi majoritatea sistemelor de operare sunt scrise n C i C++. Limbajul C++ este o extensie a lui C i a fost creat la nceputul anilor 1980 de Bjarne Stroustroup tot la Bell Laboratories. Are toate elementele limbajului C, dar ofer posibilitatea programrii orientate pe obiecte. Obiectele sunt, n principiu, componente software reutilizabile care modeleaz elemente din lumea real. S-a dovedit c folosirea unei abordri modulare, a design-ului i a implementrii orientate pe obiecte poate face ca grupurile de dezvoltare s fie mult mai productive dect atunci cnd se folosesc alte tehnici de programare, cum ar fi programarea procedural.

Biblioteca standard C++


Programele C++ constau din elemente numite clase i funcii. Putei programa fiecare pies de care avei nevoie pentru a alctui un program. Dar cei mai muli programatori folosesc avantajul oferit de bogata colecie de clase i funcii oferite de biblioteca standard C++. De aceea, nvarea limbajului C++ nseamn, pe de o parte nvarea limbajului n sine i, pe de alt parte, deprinderea modului n care se pot folosi clasele i funciile din biblioteca standard C++. Aceste clase i funcii sunt, de regul, oferite odat cu compilatorul, dar exist multe biblioteci suplimentare care sunt realizate de companii software independente. Unele dintre acestea pot fi descrcate n mod liber de pe Internet. Avantajul crerii propriilor noastre funcii i clase este c vom ti exact cum lucreaz, dar dezavantajul este timpul consumat i efortul depus pentru proiectarea, dezvoltarea i ntreinerea noilor clase i funcii pentru a opera corect i eficient.

Programarea structurat
Dup anii 1960, cnd aplicaiile au nceput s devin din ce n ce mai complexe i cnd costurile de dezvoltare au nceput s devin foarte mari, lumea a realizat c acest proces este mai complex dect s-a estimat iniial. Activitile de cercetare n domeniu au rezultat n evoluia ctre programarea structurat, o abordare disciplinat n scrierea programelor care au devenit mai clare, mai uor de testat, de corectat i de modificat.

Structuri de program
Limbajele de programare folosesc anumite structuri pentru a transpune algoritmii n programe.

Programarea calculatoarelor i limbaje de programare I

Secvena

Secvena este o serie de declaraii executate una dup alta. Selecia (deciza)

IF condiie THEN declaraie1 ELSE declaraie2 Bucla (repetiia sau iteraia)

WHILE condiie DO declaraie Subprogramul (procedura, funcia, subrutina)

Programarea calculatoarelor i limbaje de programare I

Subprogramul poate fi o combinaie a structurilor anterioare. Ne permite scrierea separat a unor pri din program i apoi asamblarea lor ntr-o form final.

Programarea orientat pe obiecte


Evoluiile pozitive n dezvoltarea software au nceput s apar odat cu folosirea programrii structurate. n anii 1980, tehnologia programrii orientate pe obiecte a nceput s fie folosit pe scar larg n proiectarea i dezvoltarea software. Tehnologia obiectelor este, n principiu, o schem de mpachetare care permite crearea unitilor software cu o semnificaie proprie. Acestea sunt focalizate pe pri specifice ale unei aplicaii. Se pot crea obiecte pentru date, pentru pli, pentru facturi, obiecte video, obiecte fiier etc. De fapt, orice substantiv poate fi transpus ntr-un obiect. Trim ntr-o lume de obiecte. Exist n jurul nostru maini, avioane, oameni, animale, cldiri etc. naintea apariiei limbajelor orientate pe obiecte, limbajele de programare (de ex. FORTRAN, Pascal, Basic, C) erau focalizate pe aciuni (verbe) i nu pe obiecte (substantive). O lume a obiectelor trebuie transpuns, puin forat, ntro lume a aciunilor. Paradigma orientrii pe obiecte prin limbaje cum ar fi Java sau C++ a fcut posibil ca programarea s devin o prelungire a realitii. Acesta este un proces mai natural dect programarea procedural i conduce la creteri semnificative de productivitate. Una dintre problemele majore ale programrii procedurale este ca unitile de program nu modeleaz foarte firesc entiti ale lumii reale i, de aceea, nu pot fi reutilizate n mod facil. Fiecare nou proiect n programarea procedural presupunea scrierea codului de la zero, lucru care nseamn o risip de timp, bani i resurse umane. Tehnologia obiectelor face ca entitile create ntr-un proiect (obiectele), dac sunt corect concepute, s poat fi folosite i n proiecte viitoare. Pe de alt parte, este remarcabil faptul c uneori nu reutilizarea codului este marele avantaj al programrii orientate pe obiecte, ci faptul c programele sunt mult mai uor de neles, organizat, de ntreinut, de modificat sau de corectat. Elementele de programare structurat sunt noiuni cheie n programarea orientat pe obiecte. Fiecare clas este o unitate care ncorporeaz structuri de program.

1.3

Ce este un calculator?

Un calculator (computer) este un dispozitiv capabil s realizeze calcule i s ia decizii logice cu viteze de milioane sau miliarde de ori mai mari dect oamenii. Aceasta nseamn c unei persoane i trebuie cteva milioane de secunde pentru a face calculele pe care le poate face un calculator ntr-o secund. Calculatorul proceseaz date sub controlul unor niruiri de instruciuni numite programe de calculator. Aceste programe dirijeaz calculatorul s realizeze secvene de aciuni care au fost specificate de persoane numite programatori. Un calculator este alctuit din diverse dispozitive, cum ar fi tastatura, mouse-ul, discurile, memoria, CD-ROM-ul sau microprocesorul, toate acestea fiind numite generic hardware. Programele de calculator care ruleaz pe calculator sunt numite software. Costurile hardware-ului au scazut foarte mult n ultimii ani pn la punctul n care un calculator personal a devenit foarte accesibil ca pre. Din pcate, costurile pe care le implic dezvoltarea software au crescut n tot acest timp pe msur ce aplicaiile au devenit din ce n ce mai complexe. n acest curs i n cel din semestrul urmtor vom studia metode de dezvoltare software cunoscute prin a cror utilizare se

Programarea calculatoarelor i limbaje de programare I

pot reduce costurile: programarea structurat, dezvoltarea top-down, funcionalizarea, programarea orientat pe obiecte, programarea generic.

Organizarea unui calculator


Se poate programa i fr a ti prea multe despre alctuirea intern a calculatorului. Cunoaterea prilor componente ajut, ns, la nelegerea efectului fiecrei instruciuni din program. Calculatorul are 4 componente de baz: 1. Unitatea de memorare este o colecie de celule care stocheaz datele. Fiecare astfel de celul are o adres. Aceste celulue se numesc celule de memorie sau locaii de memorie. 2. Unitatea central de procesare (CPU) este cea care urmrete instruciunile din program. Are dou componente: a. Unitatea aritmetico-logic (ALU) care realizeaz operaiile aritmetice i logice b. Unitatea de control care controleaz aciunile celorlalte componente astfel nct instruciunile s se execute n ordinea corect 3. Dispozitivele de intrare/ieire (I/O) accept date care vor fi procesate i le prezint pe cele care au fost procesate 4. Dispozitivele auxiliare de stocare pstreaz datele i dup oprirea calculatorului Dispozitivele periferice sunt cele de intrare/ieire i cele auxiliare. Toate aceste componente sunt cunoscute sub numele de hardware. Programele care permit hardware-ului s funcioneze se numesc software. Pe lng programele scrise de noi, existun set de programe numite software de sistem care simplific interfaa dintre calculator i utilizator. n aceast categorie intr compilatoarele, sistemele de operare sau editoarele de text. Sistemul de operare coordoneaz toate resursele calculatorului. El poate rula compilatorul, poate rula programe obiect, poate executa comenzi de sistem Editorul este un program interactiv folosit pentru crearea i modificarea programelor surs sau a datelor.

1.4

Tehnici de rezolvare a problemelor

Adesea n viaa de zi cu zi suntem pui n situaia de a urma algoritmi. n faza de rezolvare a unei probleme de programare va trebui s proiectm algoritmi. Este important s ne punem ct mai multe ntrebri pn cnd nelegem exact ce avem de fcut. Folosirea soluiilor existente ntotdeauna trebuie s evitm s reinventm roata. Dac exist o soluie, atunci s o folosim. Dac am rezolvat o problem similar nainte, trebuie doar s repetm soluia pentru c n programare exist probleme care apar foarte des (ex. calculul unui minim sau al unui maxim). Dac avem la dispoziie o secven de cod care rezolv o parte a problemei noastre, putem s o folosim. Aceast metod se numete software reuse i este elementul central n programarea orientat pe obiecte. Divide et impera Adeseori este mult mai uor s rezolvm o problem dac o mprim n subprobleme mai mici. De altfel, metoda descompunerii unui program n funcii sau tehnica programrii orientate pe obiecte se bazeaz pe acest principiu. Dificultatea de a ncepe Programatorii se confrunt adesea cu o mare dificultate: se gsesc n faa unei foi albe i nu tiu cum s nceap. Privesc 8

Programarea calculatoarelor i limbaje de programare I

problema i li se pare foarte complicat. Pentru a depi acest moment, rescriei problema cu propriile voastre cuvinte. ncercai s o descompunei n subprobleme individuale n loc s o analizai global. Acest lucru v va ajuta s extragei componente mai uor de rezolvat. De asemenea, acest lucru v va ajuta s sintetizai mai uor algoritmul de rezolvare a problemei.

2. Sintaxa i semantica C++


n acest curs vom vedea care sunt principalele reguli i simboluri care fac din C++ un limbaj de programare. Vom vedea, de asemenea, ce pai trebuie parcuri pentru a scrie un program simplu i a-l face s ruleze pe un calculator.

2.1

Structura unui program C++

Subprogramele permit scrierea separat a unor pri din program care, apoi, se asambleaz n programul final. n C++, subprogramele se numesc funcii, iar un program C++ este o colecie de clase i funcii. Fiecare program C++ trebuie s conin o funcie care se numete main. Aceasta poate fi privit ca o funcia main funcie master pentru celelalte funcii din program. Cnd main este programat s execute subprogramul corespunztor functie Patrat spunem c main apeleaz sau invoc funcia Patrat. funcia Patrat Dup ce Patrat ncheie execuia tuturor instruciunilor, ea transmite (sau ntoarce) controlul napoi ctre funcia main care i continu execuia. Urmtorul exemplu este un program C++ care funcia Cub este alctuit din 3 funcii: main, Patrat i Cub. Detaliile nu sunt importante. #include <iostream> using namespace std; int Patrat(int); int Cub(int); int main() { cout << "Patratul lui 27 este " << Patrat(27) << endl; cout << "si cubul lui 27 este " << Cub(27) << endl; return 0; } int Patrat(int n) { return n*n; } int Cub(int n) { return n*n*n; }

Programarea calculatoarelor i limbaje de programare I

n fiecare dintre funcii, acoladele { i } marcheaz nceputul i sfritul instruciunilor care trebuie executate. Ceea ce se gsete ntre acolade se numete corpul funciei sau bloc de instruciuni. Execuia unui program C++ ncepe ntotdeauna cu prima instruciune din funcia main. n programul de mai sus, aceasta este cout << "Patratul lui 27 este " << Patrat(27) << endl; Aceasta este o instruciune care produce afiarea unor informaii pe ecranul calculatorului. Detalii vom afla puin mai trziu, tot n acest capitol. Pe scurt, instruciunea tiprete dou elemente. Primul este mesajul Patratul lui 27 este Cel de-al doilea este o valoare obinut prin apelul (invocarea) funciei Patrat cu valoarea 27. Aceast funcie realizeaz ridicarea la ptrat i trimite rezultatul, 729, napoi ctre apelant (funcia invocatoare), adic funcia main. Acum main continu execuia tiprind valoarea 729 dup care trece la instruciunea urmtoare. Similar, a doua instruciune din funcia main tiprete mesajul si cubul lui 27 este dup care invoc funcia Cub. Aceasta ntoarce rezultatul 19683, care este tiprit. Rezultatul final va fi Patratul lui 27 este 729 si cubul lui 27 este 19683 Atat Patrat ct i Cub sunt exemple de funcii care returneaz o valoare. O astfel de funcie transmite o singur valoare ctre funcia apelant. Cuvntul int aflat la nceputul primei linii a funciei Patrat arat c funcia ntoarce o valoare ntreag (un numr ntreg). S revenim la funcia main. Prima ei linie este int main() Cuvntul int indic faptul c main este o funcie care ntoarce o singur valoare, un numr ntreg. Dup tiprirea ptratului i a cubului lui 27, main execut instruciunea return 0; pentru a ntoarce valoarea 0 ctre apelant. Dar cine apeleaz funcia main? Rspunsul este: sistemul de operare. Acesta ateapt o valoare (exit status) de la main dup ce aceasta i ncheie execuia. Prin convenie, valoarea returnat 0 nseam c totul a decurs OK. O alt valoare (1, 2 etc.) nseamn c s-a petrecut ceva nedorit.

2.2

Sintax i semantic

Vom ncepe acum s intrm n detalii legate de programarea n C++. Un limbaj de programare este un set de reguli, simboluri i cuvinte speciale folosite pentru a scrie un program. Regulile sunt valabile att pentru sintax (gramatic), ct i pentru semantic (semnificaie). Sintaxa este un set de reguli care definesc exact ce conbinaii de litere, numere i simboluri pot fi folosite ntr-un limbaj de programare. Nu se accept ambiguiti. Vom vedea c nclcarea oricrei reguli a limbajului, de exemplu scrierea incorect a unui cuvnt sau uitarea unei virgule pot genera erori de sintax (syntax errors) i codul surs nu poate fi compilat pn la corectarea lor. Semantica este un set de reguli care determin semnificaia instruciunilor scrise ntr-un limbaj de programare.

Programarea calculatoarelor i limbaje de programare I

abloane sintactice
n acest curs vom folosi abloane ca i exemple generice de construcii sintactice n C++. Cel mai frecvent vom folosi abloane asemntoare celui pentru funcia main: int main() Funcia main { instruciune } Acest ablon arat c funcia main ncepe cu cuvntul int urmat de cuvntul main i o pereche de paranteze rotunde. Prima linie a oricrei funcii numete heading. Acest heading este urmat de o acolad care marcheaz nceputul unei liste de instruciuni corpul funciei. n final, acolada nchis indic sfritul funciei. Cele 3 puncte indic faptul c instruciunea poate fi urmat de 0 sau mai multe alte instruciuni.

Denumirea elementelor programului: identificatorii


n C++ identificatorii sunt nume asociate funciilor, claselor sau datelor i sunt folosite pentru a referi funcii, clase sau date. Identificatorii sunt alctuii din litere (A-Z, a-z), cifre (0-9) i caracterul underscore (_), dar trebuie s nceap cu o liter sau cu underscore. Exemplu Identificatori coreci: J9, GetData, sum_of_squares Identificatori incoreci: 40Hours - nu poate ncepe cu o cifr Get Data - nu poate conine spaiu box-22 - nu poate conine pentru c este simbol matematic int - cuvntul int este predefinit n C++ Cuvntul int este cuvnt rezervat. Acestea sunt cuvinte care au o utilizare special n C++ i nu pot fi utilizate drept identificatori definii de programator. Este foarte util ca identificatorii s fie sugestivi i uor de citit. Exemplu PRINTTOPPORTION fa de PrintTopPortion

2.3

Date i tipuri de date

De unde ia programul datele de care are nevoie pentru a lucra? Datele sunt pstrate n memoria calculatorului. Fiecare locaie are o adres unic pe care o referim atunci cnd dorim s stocm sau s aducem date. Adresa fiecrei locaii de memorie este un numr binar. n C++ folosim identificatori pentru a denumi locaiile de memorie. Acesta este unul dintre avantajele limbajelor de programare de nivel nalt: ne elibereaz de grija de a gestiona locaiile de memorie la care sunt pstrate datele i instruciunile. n C++ fiecare dat trebuie s fie de un anumit tip. Tipul de dat determin modul n care datele sunt reprezentate n interiorul calculatorului i operaiile care se pot realiza asupra lor. C++ definete un set de tipuri standard de date, pe care le vom descrie mai jos. De asemenea, programatorul i poate defini propriile tipuri de date. Tipurile standard sunt organizate astfel: Tipuri simple Tipuri integrale char

Programarea calculatoarelor i limbaje de programare I

short int long enum Tipuri reale float double long double Tipuri adres pointer referin Tipuri structurate tablou (array) struct union class

Tipurile integrale
Se numesc aa pentru c se refer la valori ntregi. Despre tipul enum nu discutm n acest capitol. ntregii sunt secvene de una sau mai multe cifre. Nu se admite virgula. n multe cazuri, semnul preced un ntreg. Exemplu 22 16 1 -378 -912 Atunci cnd cuvntul rezervat unsigned preced un tip de dat, valoarea ntreag poate fi doar pozitiv sau 0. Exemplu unsigned int Tipurile de dat char, short, int i long reprezint ntregi de diferite dimensiuni cu mai muli sau mai puini bii. celula de memorie char celula de memorie short celula de memorie int celula de memorie long n general, cu ct sunt mai muli bii n celula de memorie, cu att mai mari sunt valorile ntregi care pot fi memorate acolo. Dimensiunile acestor celule de memorie sunt dependente de main. Pe unele calculatoare, o dat de tip int se poate gsi n intervalul -32768 ... +32767, iar pe altele ntre -2147483648 ... +2147483647. Cnd programul ncearc s calculeze o valoare mai mare dect valoarea maxim, rezultatul este un integer overflow. Un ntreg care ncepe cu cifra 0 este considerat ca fiind scris n baza 8. Exemplu 015 este 158 Tipul char. Acesta este cel mai mic tip de dat care poate fi folosit pentru a reprezenta valori ntregi. Se obinuiete folosirea acestui tip de dat atunci cnd se dorete o economie de memorie i se folosesc ntregi mici. ns tipul char, n mod tipic, se folosete pentru a descrie date care constau dintr-un caracter alfanumeric (liter, cifr sau simbol special). Exemplu 'A' 'a' '8' '2' '+' '$' ' ' 4

Programarea calculatoarelor i limbaje de programare I

Fiecare caracter este cuprins ntre apostroafe, astfel nct C++ face diferena dintre data de tip caracter '8' i valoarea ntreag 8, pentru c cele dou sunt pstrate n mod diferit n interiorul calculatorului. Nu sunt obinuite operaii de adunare a caracterului 'A' cu caracterul 'B', de scdere a caracterului '3' din caracterul '8', ns aceste caractere se pot compara. Caracterul 'A' este ntotdeauna mai mic dect 'B', 'B' mai mic dect 'C' etc. Fiecare set de caractere definete o astfel de secven.

Tipurile reale (virgul mobil)


Aceste tipuri de date se utilizeaz pentru a reprezenta numere reale. Numerele reprezentate n virgul mobil au parte ntreag i o parte fracionar, separate de un punct. Exemplu 18.0 127.54 .8 0.57 Numrul 0.57 nu este octal. Aceast regul este valabil doar pentru numere ntregi. Aa cum tipurile integrale din C++ au diferite dimensiuni, la fel se ntmpl i cu tipurile reale. n ordine cresctoare, acestea sunt float, double i long double. Valorile reprezentate n virgul mobil pot avea un exponent, ca n notaia tiinific (un numr este scris ca o valoare nmulit cu 10 ridicat la putere). n loc de 3.504x1012, n C++ scriem 3.504e12. e nseamn exponent al bazei 10. Numrul dinaintea lui e nu trebuie s includ n mod obligatoriu punctul zecimal. Dintre tipurile de dat reale, cel mai folosit este float care, adeseori, este suficient. Valoarea maxim oferit de tipul float este, n general, n jur de 3.4e+38. Calculatoarele nu pot reprezenta ntotdeauna numerele n virgul mobil. Datorit faptului c memorarea se face n form binar, multe valori reale pot fi doar aproximate n acest sistem. De aceea, nu trebuie s ne mire c, pe unele calculatoare, de exemplu 4.8 va fi afiat 4.7999998, fr a fi vorba de o eroare de programare.

2.4

Declaraiile

Identificatorii pot fi utilizai pentru a denumi constante sau variabile, adic locaii de memorie al cror coninut se permite s fie modificat. Cum spunem calculatorului ce reprezint un identificator? Declaraia este o instruciune care asociaz un nume (un identificator) cu o dat, o funcie sau un tip de dat, astfel nct programatorul poate s se refere la acest element prin nume. Este la fel cum o definiie dintr-un dicionar asociaz un nume unei descrieri a elementului la care ne referim prin acest nume. De exemplu, declaraia int empNum; Anun c empNum este numele unei variabile al crei coninut este de tip int. Cnd declarm o variabil, compilatorul alege o locaie de memorie i o asociaz cu identificatorul pstrnd aceast asociere la dispoziia programului. Orice identificator dintr-un program trebuie s fie unic n domeniul ei. n C++ un identificator trebuie s fie declarat nainte de a fi folosit. Dac declarm un identificator ca fiind o constant i ncercm mai trziu s i modificm valoarea, compilatorul detecteaz aceast inconsisten i semnaleaz eroare. 5

Programarea calculatoarelor i limbaje de programare I

Constantele i variabilele poart numele generic de date.

2.5

Variabilele

Variabila este o locaie de memorie referit printr-un identificator i care pstreaz valoarea unei date ce poate fi modificat. Numele simbolic asociat variabilei se numete identificatorul variabilei sau numele variabilei. Identificatorul variabilei empNum (locaia de memorie 0x22ff74) Variabila

10533
Valoarea

(int)

Tipul de dat

Declararea variabilei nseamn specificarea att a numelui ei ct i a tipului de dat. O declaraie se termina ntotdeauna cu ; Exemplu int empNum; Se pot declara mai multe variabile de acelai tip ntr-o singur declaraie. Exemplu int studentCount, maxScore, sumOfScores; Aceasta declaie este identic cu: int studentCount; int maxScore; int sumOfScores; Declararea fiecrei variabile ntr-o instruciune separat ne permite s adugm comentarii pentru nelegerea declaraiilor la o parcurgere ulterioar a programului. Exemplu float payRate; //Employees pay rate Comentariile sunt precedate de // i sunt ignorate de compilator.

2.6

Constantele

Toate numerele, ntregi sau reale, sunt constante. La fel, caracterele (cuprinse ntre ' ') i secvenele de caractere sau string-urile, irurile (cuprinse ntre ). Exemplu 16 32.3 'A' Boys n C++ ca i n matematic, o constant este un element al crui valoare nu se schimb niciodat. Folosim constante ca pri ale unor expresii aritmetice. Putem scrie o instruciune care adun constantele 5 i 6 i plaseaz rezultatul n variabila numit sum. Numim valoare literal (sau literal) orice valoare constant din program. O alternativ la literale sunt constantele simbolice (sau constante cu nume). Acestea sunt locaii de memorie referite printr-un identificator care pstreaz date ce pot fi modificate. De exemplu, n loc s folosim literalul 3.14159 folosim constanta simbolic PI. Acest lucru face programul mai uor de citit i de modificat.

Programarea calculatoarelor i limbaje de programare I

Declararea constantelor n C++ ncepe cu cuvntul rezervat const, iar semnul = se aeaz ntre identificator i valoarea literal. Exemplu const char BLANK = ' '; const float PI = 3.14159; const int MAX = 20; De regul, constantele simbolice se scriu cu litere mari pentru a le distinge mai uor de variabile la citirea programului.

2.7
Asignarea

Aciuni: instruciuni executabile

Valoarea unei variabile poate fi schimbat prin asignare. Exemplu quizScore = 10; Valoarea 10 este asignat variabilei quizScore, adic valoarea 10 va fi stocat n locaia de memorie numit quizScore. Semantica operatorului de asignare = este pstreaz, stocheaz. Orice valoare anterioar stocat la acea locaie de memorie se pierde, fiind nlocuit de noua valoare. ntr-o instruciune de asignare, doar o variabil poate aprea n stnga operaiei. Asignarea nu este ca o ecuaie matematic ( x+y=z+4 ). Avnd declaraiile: int num; int alpha; float rate; char ch; putem face urmtoarele asignri: alpha = 2856; rate = 0.36; ch = 'B'; num = alpha; Asignarea ch = Hello; nu este corect pentru c ch este o variabil de tip char iar Hello este un string. Pentru o mai mare lizibilitate a programelor pe care le scriem, vom respecta urmtoarele reguli: - Folosim iniiala mic pentru numele de variabile Exemplu lengthsInYards hours - Folosim iniiala mare pentru numele de funcii sau clase Exemplu Cub(27) MyDataType - Folosim litere mari pentru constante simbolice Exemplu UPPER_LIMIT PI Expresia din dreapta operatorului de asignare este evaluat i valoarea ei este stocat n variabila aflat n stnga operaturului. O expresie este alctuit din constante, variabile i operatori. Exemplu alpha+2 rate-6.0 alpha*num 7

Programarea calculatoarelor i limbaje de programare I

Operatorii admii ntr-o expresie depind de tipurile de date ale constantelor i ale variabilelor din expresie. Operatorii matematici sunt: + Plus unar - Minus unar + Adunare - Scdere * Multiplicare / mprire real (rezultat real) sau mprire ntreag (rezultat ntreg) % Modulo (restul mpririi) Operatorii unari folosesc un singur operand. Cei binari folosesc doi operanzi. Exemplu -54 +259.65 -rate O constant fr semn este pozitiv. mprirea ntreag este ctul obinut la mprirea a dou numere ntregi. Operaia modulo reprezint restul acestei mpriri i se aplic doar numerelor ntregi. Exemplu 6/2 3 7/2 3 6%2 0 7%2 1 mprirea n virgul mobil (real) genereaz rezultat real, iar operanzii trebuie s fie reali. Exemplu 7.0/2.0 3.5 Exemplu Expresie Valoare Expresie Valoare 3+6 9 7.0/0.0 eroare 3.4+6.9 10.3 7/0 eroare 2*3 6 7%0 eroare 8.0/-2.0 -4.0 8/9 0 5%2.3 Eroare Pentru c n expresii pot aprea i variabile, urmtoarele asignri sunt valide: alpha = num + 6; num = alpha * 2; num = num + alpha; num = 6 % alpha; n cazul instruciunii num = num + alpha; valorile lui num i alpha sunt adunate, iar rezultatul este pstrat n num, nlocuind vechea valoare pstrat aici. Acest exemplu arat diferena dintre egaliatea matematic i asignare. n matematic num = num + alpha este adevrat doar cnd alpha este 0. Instruciunea de asignare num = num + alpha; este valid pentru orice alpha.

Programarea calculatoarelor i limbaje de programare I

Incrementarea i decrementarea
Pe lng operatorii aritmetici, C++ ofer operatorii de incrementare i decrementare. ++ Incrementare -- Decrementare Acetia sunt operatori unari. Pentru operanzi ntregi i reali, efectul este de adugare a valorii 1, respectiv de scdere a valorii 1 din operand. Exemplu int num = 8; num++; //num va avea valoarea 9 Acelai efect poate fi obinut prin num = num + 1; Operatorii ++ i -- pot fi att operatori prefix: ++num; ct i operatori postfix: num++; C++ permite folosirea lui ++ i -- n interiorul unor expresii mai lungi: Exemplu alpha = num++ * 3;

Afiarea
Tiprirea rezultatelor se face n C++ folosind o variabil special numit cout mpreun cu operatorul de inserie (<<). Exemplu cout << Hello; Aceast instruciune afieaz caracterele Hello la dispozitivul standard de ieire, de obicei ecranul. Variabila cout este predefinit n C++ i semnific un flux de ieire. Acesta poate fi imaginat ca o secven nesfrit de caractere care merg ctre dispozitivul de ieire. Operatorul de inserie << (put to) folosete 2 operanzi. Cel din stnga este o expresie flux (stream) (de exemplu cout). n dreapta se afl irul sau expresia al crei rezultat trebuie afiat. Exemplu cout << The answer is; cout << 3 * num; De remarcat faptul c operatorul << arat sensul n care circul datele: dinspre expresie sau string nspre streamul de ieire. Operatorul << poate fi folosit de mai multe ori ntr-o singur instruciune de afiare: Exemplu cout << The answer is << 3 * num; i rezultatul este acelai. Exemplu int i = 2; int j = 6; Instruciune Ce se tiprete cout << i; 2 cout << i = << i; i = 2 cout << j: << j << i: << i; j:6 i:2 Dac dorim s afim un ir care conine caracterul trebuie s plasm semnul \ naintea : 9

Programarea calculatoarelor i limbaje de programare I

Exemplu cout << Al \Butch\ Jones; iar pe ecranul calculatorului va aprea: Al Butch Jones n mod obinuit, mai multe instruciuni de ieire succesive afieaz rezultatele continuu, pe aceeai linie: cout << Hi; cout << there; genereaz Hithere Pentru a tipri pe linii separate scriem: cout << Hi << endl; cout << there << endl; i obinem: Hi There Identificatorul endl (end line) este un element special din C++: este un manipulator. Este suficient de tiut acum c endl permite terminarea unei linii i continuarea scrierii pe linia urmtoare.

2.8

Construirea unui program

Vom vedea cum asamblm diferitele elemente prezentate pn acum pentru a construi un program. Un program C++ este format din clase i funcii, una dintre funcii numindu-se obligatoriu main. Un program poate conine declaraii n afara oricrei funcii. Modelul unui program este: Declaraie ... Definiie de clas Definiie de clas ... Definiie de funcie Definiie de funcie ... O definiie de funcie se construiete dup urmtorul model: Heading { Instruciune ... } Vom da exemplu de un program cu o singur funcie, funcia main. //**************************************** //Temperaturi.cpp //Acest program calculeaza temperatura de //la jumatatea dintre punctul de inghet si //cel de fierbere a apei //**************************************** #include <iostream> using namespace std;

10

Programarea calculatoarelor i limbaje de programare I

const float INGHET = 0.0; //Punctul de inghet al apei const float FIERBERE = 100.0; //Punctul de fierbere int main() { float temperaturaMedie;

//Pastreaza rezultatul medierii //dintre INGHET si FIERBERE

cout << "Apa ingheata la " << INGHET << " grade"; cout << " si fierbe la " << FIERBERE << " de grade." << endl; temperaturaMedie = INGHET + FIERBERE; temperaturaMedie = temperaturaMedie / 2.0; cout << "Temperatura medie este de "; cout << temperaturaMedie << " grade." << endl; } return 0;

Programul ncepe cu un comentariu care explic ce face programul. Urmeaz #include <iostream> care insereaz coninutul fiierului iostream n programul nostru. El conine informaiile necesare lucrului cu stream-uri de intrare i de ieire, ca de exemplu cout. Urmeaz declararea constantelor INGHET i FIERBERE. Restul programului este definiia funciei main. Mai nti heading-ul urmat de {}. Aceste acolade informaz compilatorul c main este o funcie. Corpul funciei cuprinde declararea variabilei tempMedie urmat de o serie de alte instruciuni executabile. Funcia main returneaz 0. De notat aranjarea liniilor de program, spaierea i folosirea comentariilor.

Blocuri (instruciuni compuse)


Corpul unei funcii este un exemplu de bloc: { Instruciune ... } Un bloc conine 0 sau mai multe instruciuni cuprinse ntre {}. O instruciune se termin obligatoriu cu ; Exist i instruciunea vid: ; Obinuim ca instruciunile dintr-un bloc s le deplasm puin spre dreapta pentru claritatea programului. De asemenea, putem opta pentru gruparea instruciunilor prin separarea grupurilor cu rnduri libere.

2.9

Preprocesorul C++

Imaginai-v c suntei n rolul compilatorului de C++ i vi se prezint urmtorul program: int main() 11

Programarea calculatoarelor i limbaje de programare I

} Recunoatei identificatorul int ca fiind cuvnt rezervat C++ i main ca fiind numele unei funcii care trebuie s existe n mod obligatoriu. Dar cout i endl? Nu au fost declarate ca i variabile sau constante i nu sunt cuvinte rezervate. Vei afia urmtorul mesaj de eroare: In function int main(): Line 3: cout undeclared Line 3: endl undeclared Pentru a corecta eroarea, programatorul trebuie s insereze la nceputul programului linia #include <iostream> Ea spune c ntreg coninutul fiierului iostream va fi inserat n program. El conine declaraiile lui cout i endl. Instruciunea #include nu este interpretata de compilatorul C++, ci de un program numit preprocesor. El acioneaz ca un filtru i precede faza de compilare. O linie care ncepe cu # se numete directiv de preprocesare. Program surs Expandarea programului surs Compilator C++

cout << "Happy Birthday" << endl; return 0;

Preprocesor

La preprocesare sunt posibile urmtoarele aciuni: Includerea altor fiiere n fiierul care urmeaz s fie compilat Definirea constantelor simbolice i a macrourilor (macrourile sunt specifice limbajului C i nu le vom prezenta) Compilarea condiional

Directiva de preprocesare #include


Aceast directiv de preprocesare produce includerea n codul surs a unei copii a fiierului specificat. Ea are dou forme. Prima form este cea n care se folosesc semnele < > ca n directiva #include <iostream> Acestea indic faptul c de referim la un fiier din biblioteca standard iar preprocesorul caut acel fiier n directorul standard pentru includere. n varianta #include consum.dat Ghilimelele arat c fiierul consum.dat se gsete n acelai director cu fiierul surs.

Directiva de preprocesare #define: constante simbolice


Prin directiva de preprocesare #define se pot defini constante simbolice. Exemplu #define PI 3.14159 Proprocesorul va nlocui toate apariiile lui PI din textul care urmeaz declaraiei cu valoarea 3.14159. Dac dorim s modificm valoarea constantei, aceasta poate fi 12

Programarea calculatoarelor i limbaje de programare I

modificat prin nlocuirea valorii din directiva de preprocesare. Diferenele dintre constantele definite prin cuvntul cheie const i constantele definite prin directive de preprocesare sunt c primele au un tip de dat i c sunt vizibile n faza de debugging. Odat nlocuit o constant de ctre preprocesor, doar valoarea sa va mai fi vizibil. Directiva de preprocesare #define DEBUG n care lipsete valoarea asociat constantei terge orice apariie a identificatorului DEBUG din fiierul surs. Identificatorul rmne definit i poate fi testat prin directivele #if defined sau #ifdef. Exist 5 constante simbolice predefinite: Constanta simbolic Descrierea __LINE__ Numrul liniei curente din fiierul surs __FILE__ Numele fiierului surs __DATE__ Data la care a fost compilat fiierul surs __TIME__ Ora la care a fost compilat fiierul surs __STDC__ Constanta ntreag 1 Directiva de preprocesare #undef aplicat unei constante simbolice sau unui macro definite prin #define realizeaz tergerea definiiei.

Compilarea condiional
Compilrile condiionate permit programatorilor s controleze execuia directivelor de preprocesare i a compilrii programului surs. Compilrile condiionale pot fi realizate prin folosirea directivelor de preprocesare #ifndef i #ifdef. Codul #ifndef NULL #define NULL 0 #endif verific dac NULL a fost deja definit n program, iar daca daca nu, o definete. Compilarea condiional se folosete de regul pentru debugging. Se folosesc instruciuni de afiare care tipresc valorile unor variabile i care confirm fluxul corect al programului. Aceste afiri care nu mai sunt necesare dupa ce programul a fost corectat sunt, de regul, ncadrate de directive condiionale de preprocesare. Exemplu #ifdef DEBUG cout << "Variabila x = " << x << endl; #endif

13

3. Expresii aritmetice, apeluri de funcii i ieiri


n acest curs vom discuta n detaliu despre scrierea expresiilor aritmetice i despre formatarea ieirilor. Vom vedea, de asememea, cum putem folosi bibliotecile de funcii funcii scrise anterior i care fac parte din orice sistem C++.

3.1

Expresii aritmetice

Vom studia modul n care pot fi scrise expresii care conin mai mult de un operator i care folosesc operanzi care au diferite tipuri de dat.

Reguli de preceden
Expresiile aritmetice sunt formate din constante, variabile, operatori i paranteze. Ordinea n care sunt realizate operaiile este stabilit conform regulilor de preceden. Cele 5 operaii aritmetice de baz i parantezele sunt ordonate n felul urmtor: Cea mai nalt preceden ( ) * / % + Cea mai sczut preceden Exemplu tempMedie = INGHET + FIERBERE / 2.0 n acest exemplu, mai nti se efectueaz mprirea FIERBERE / 2.0, iar apoi rezultatul este adunat cu INGHET. Folosind parantezele, se poate schimba ordinea de evaluare a expresiei. Exemplu tempMedie = (INGHET + FIERBERE) / 2.0 Mai nti sunt evaluate subexpresiile din paranteze, iar apoi urmm precedena operatorilor. Atunci cnd apar n aceeai expresie mai muli operatori cu aceeai preceden, ordinea de grupare sau asociativitatea este de la stnga la dreapta. Expresia int1 int2 + int3 este echivalent cu (int1 int2) + int3 i nu cu int1 (int2 + int3). Un alt exemplu: (float1 + float2) / float1 * 3.0 Se evalueaz mai nti parantezele, apoi rezultatul se mparte la float1, iar n final se realizeaz multiplicarea cu 3.0.

Conversii implicite i explicite


Valorile ntregi i cele reale sunt stocate n mod diferit n memorie. Modelul din memorie al biilor care reprezint constanta 2 nu arat ca modelul din memorie al biilor care reprezint constanta 2.0. Problema este ce se ntmpl cnd folosim un ntreg i un real n aceeai expresie sau ntr-o asignare.

Programarea calculatoarelor i limbaje de programare I

Instruciuni de asignare
Dac facem declaraiile int unInt; float unFloat; atunci variabila unInt poate pstra doar valori ntregi, iar variabila unFloat doar valori n virgul mobil. Instruciunea de asignare unFloat =12; pare c ncarc valoarea ntreag 12 n variabila unFloat. ns calculatorul refuz s stocheze altceva dect valori de tip float n variabila unFloat. Compilatorul insereaz, n acest caz, dou noi instruciuni care mai nti convertesc valoarea 12 n 12.0 i apoi stocheaz 12.0 n variabila unFloat. Aceast transformare automat a unei valori dintr-un tip de dat n alt tip de dat se numete conversie implicit (type coercion, forare de tip). Instruciunea unInt = 4.8; provoac de asemenea o forare de tip. Cnd un numr real este asignat unei variabile ntregi, partea fracionar este trunchiat. Ca rezultat, lui unInt i se asigneaz valoarea 4. Adeseori, n conversiile implicite sunt implicate expresii ntregi. Pstrarea rezultatului unei expresii cu rezultat de tip ntreg ntr-o variabil real (float) nu provoac pierderi de informaie. Stocarea rezultatului unei expresii reale ntr-o variabil ntreag conduce la trunchierea prii fracionare. Pentru a clarifica programul i pentru a evita erorile putem folosi conversia explicit (type casting). n C++o operaie de cast const din precizarea tipului de dat pe care dorim s l aib rezultatul urmat, ntre paranteze, de expresia pe care dorim s o convertim. Exemplu unFloat = float(3 * unInt + 2); unInt = int(5.2 / unFloat altFloat); Urmtoarele dou instruciuni produc rezultate identice: unInt = unFloat + 8.2; unInt = int(unFloat + 8.2); Diferena constnd n claritatea programului i eliminarea erorilor de la compilare.

Scrierea expresiilor aritmetice


Pn acum am vorbit doar despre combinarea diferitelor tipuri de dat n operaia de asignare. Este, de asemenea, posibil combinarea datelor de diferite tipuri n expresii. Exemplu unInt * unFloat 4.8 + unInt 3 ntotdeauna, cnd ntr-o expresie apar variabile de tip ntreg i variabile de tip float apar conversii implicite dup cum urmeaz: 1. ntregul este forat temporar la o valoare real 2. Se efectueaz operaia 3. Rezultatul este real S analizm a doua instruciune din exemplul anterior, n care varianta unInt conine valoarea 2. Operatorul + are operanzi de tipuri diferite, de aceea valoarea lui unInt este forat la 2.0. Aceast conversie este temporar i nu afecteaz

Programarea calculatoarelor i limbaje de programare I

valoarea 2 stocat n unInt. Se efectueaz adunarea, iar rezultatul este 6.8. Scderea are de asemenea, doi operanzi de tipuri diferite: 6.8 i 3. Valoarea 3 este forat la 3.0, se execut scderea i rezultatul este numrul real 3.8. n interiorul expresiilor se pot folosi conversiile explicite de tip pentru a reduce riscul de apariie al erorilor i pentru claritate: Exemplu float(unInt) * unFloat 4.8 + float(unInt 3) Conversiile explicite de tip nu se fac, ns, doar pentru claritate. S calculm media mai multor numere. Suma lor este stocat n sum si numrul lor este stocat n count. Avem urmtoarele declaraii: int sum; int count; float average; Valoarea medie se gsete astfel: average = sum / count; //eroare Dac sum este 6.0 i count este 80, rezultatul va fi 0.0. De ce? Expresia din dreapta operatorului = conine doi operanzi ntregi. n aceast situaie, mprirea este de tip ntreg, deci rezultatul este 0. Apoi, rezultatul este convertit la valoarea real 0.0 nainte de a fi stocat n average. Pentru a corecta rezultatul, modificm ultima instruciune astfel: average = float(sum) / float(count); mprirea va fi real, iar rezultatul va fi 0.75. Pn acum ne-am referit doar la tipurile int i float. Conversiile se pot aplica i valorilor char, short sau double.

3.2

Apeluri de funcii i biblioteci de funcii

Apeluri de funcii
n capitolul anterior am vzut un program care coninea trei funcii: main, Patrat i Cub. Toate trei returnau cate o valoare. Patrat si Cub returneaz valori ctre funciile apelante, iar main ntoarce o valoare ctre sistemul de operare. n instruciunea cout << si cubul lui 27 este << Cub(27) << endl; secvena Cub(27) este un apel de funcie sau invocare de funcie. Calculatorul oprete temporar execuia funciei main i pornete funcia Cub. Cnd Cub i ncheie execuia tuturor instruciunilor, calculatorul revine la main din punctul n care a fost oprit. n apelul funciei Cub, numrul 27 se numete parametru sau argument. Parametrii creeaz posibilitatea unei funcii s lucreze cu diferite valori. Astfel, putem scrie cout << Cub(4); cout << Cub(16); Modelul sintactic al unui apel de funcie este NumeleFunciei(ListDeParametri) Lista de parametri este mecanismul prin care funciile comunic una cu cealalt.

Programarea calculatoarelor i limbaje de programare I

Unele funcii, de exemplu Patrat sau Cub, au un singur parametru n lista de parametri. Alte funcii, de exemplu main, nu au niciun parametru n list. Exist funcii cu doi, trei sau mai muli parametri n list, separai prin virgul. Funciile care ntorc o valoare pot fi utilizate n expresii n acelai fel n care se folosesc constantele sau variabilele. Valoarea calculat de funcie nlocuiete apelul funciei n expresie. Exemplu unInt = Cub(2) * 10; //unInt va pastra valoarea 80 ntr-o expresie, un apel de funcie are cea mai mare preceden. Consideraii referitoare la apelurile de funcii: Apelurile de funcii sunt folosite n expresii. Nu apar ca instruciuni de sine-stttoare; Funcia calculeaz o valoare (un rezultat) care poate fi folosit apoi ntr-o expresie; Funcia ntoarce exact un rezultat nu mai multe, nici mai puine. Funcia Cub ateapt s i se dea, s i se transmit un parametru de tip int. Dac primete un parametru de tip float, compilatorul realizeaz o forare implicit a tipului de dat. Exemplu Cub(6.9) calculeaz 63 i nu 6.93 Pn acum am folosit doar constante literale ca i parametri ai funciei Cub. Acetia, ns, pot fi i variabile sau constante simbolice i, n general, expresii avnd un tip potrivit cu cel al parametrului. n instruciunea alfa = Cub(int1 * int1 + int2 * int2); expresia care reprezint lista de parametri este evaluat prima, i numai dup aceea rezultatul este transmis funciei. De exemplu, dac int1 conine 3 i int2 conine 5, atunci parametrul transmis funciei Cub este 34. O expresie din lista de parametri a funciei poate include i apeluri de funcii. Putem rescrie apelul precedent folosind funcia Patrat: alfa = Cub(Patrat(int1) + Patrat(int2));

Biblioteci de funcii
Anumite calcule, cum ar fi rdcina ptrat, sunt foarte des foloste n programe. De aceea, limbajul C++ include o bibliotec standard care este o colecie de funcii prescrise care realizeaz anumite operaii. Fiierul Funcia Tipul Tipul Rezultatul header parametrilor rezultatului <stdlib.h> abs(i) int Int Valoarea absolut a lui i <math.h> cos(x) double double Cosinusul lui x (x n radiani) <math.h> fabs(x) double double Valoarea absolut a lui x <math.h> pow(x, y) double double x y . Dac x=0.0, y trebuie s fie pozitiv. Dac x<0.0, y trebuie s fie ntreg

Programarea calculatoarelor i limbaje de programare I

Pentru a folosi o bibliotec de funcii, trebuie s plasm directiva #include la nceputul programului, specificnd fiierul header dorit. Exemplu

Funcii void
Pn acum am discutat despre funcii care ntorc o valoare. Dac studiem urmtoarea definiie de funcie, observm c ea ncepe cu cuvntul void n loc de int sau double: void Calcul(...) { ... } Acesta este un exemplu de funcie care nu ntorcea nicio valoare ctre funcia apelant. Ea realizeaz doar o aciune i apoi revine- Acestea sunt funcii care nu ntorc nicio valoare sau funcii void. n multe limbaje de programare aceste funcii se mai numesc i proceduri. Spre deosebire de funciile care ntorc o valoare, acestea se apeleaz ntr-o singur instruciune de sine-stttoare: Exemplu Calcul(plataPeOra, ore); Din punctul de vedere al apelantului, aceste funcii arat ca o comand: ExecutaAsta(x, y, z); FaAsta();

3.3

Formatarea ieirilor

Formatarea ieirilor unui program nseamn modul n care se poate controla apariia pe ecran sau la imprimant a rezultatelor programelor. Dac variabilele i, j i k au valorile 15, 2 i 6, atunci instruciunea cout << Rezultate: << i << j << k; produce irul de caractere Rezultate: 1526 Fr spaii ntre numere, rezultatul este dificil de interpretat.

Spaierea vertical
Am vzut deja c pentru aceasta se folosete manipulatorul endl. O secven de instruciuni de instruciuni de ieire continu s scrie pe linia curent pn cnd endl termin linia. S vedem ce afieaz urmtoarele instruciuni: cout << Formatarea << endl; cout << endl; cout << iesirilor. << endl; Prima instruciune produce afiarea pe ecran a irului de caractere Formatarea, iar endl provoac trecerea pe rndul urmtor. Urmtoarea instruciune produce o nou trecere pe rndul urmtor a cursorului. A treia instruciune tiprete cuvntul iesirilor i termin linia. Rezultatul este: Formatarea iesirilor.

Programarea calculatoarelor i limbaje de programare I

Instruciunile de mai sus sunt echivalente cu : cout << Formatarea << endl << endl; cout << iesirilor. << endl; sau cu cout << Formatarea << endl << endl << iesirilor. << endl; sau cu cout << Formatarea << endl << endl << iesirilor. << endl; Ultimul exemplu arat c o instruciune C++ poate fi scris pe mai multe linii. Compilatorul urmrete apariia semnului ; i nu sfritul fizic al liniei.

Inserarea spaiilor ntr-o linie


Pentru a controla spaierea orizontal se obinuiete introducerea unor spaii suplimentare. Pentru a preveni afiarea numerelor 15, 2 i 6 n forma Rezultate: 1526 putem tipri cte un singur caracter (constant tip char) ntre numere: cout << Rezultate: << i << << j << << k; Aceast instruciune va afia: Rezultate: 15 2 6 Dac dorim afiarea unor spaii mai mari, putem opta pentru folosirea irurilot constante care conin spaii: cout << Rezultate: << i << << j << << k; Aceast instruciune afieaz: Rezultate: 15 2 6 Pentru a produce ieirea: * * * * * * * * * putem folosi urmtoarele instruciuni: cout << * * * * endl; cout << * * * * * << endl; Pentru ca spaiile s fie tiprite pe ecran, ele trebuie incluse ntre apostrafe sau ghilimele. Remarcm c instruciunea: cout << * << *; are urmtorul efect: ** pentru c spaiile care sunt n afara apostroafelor nu sunt luare n considerare la tiprire.

Manipulatori
Am folosit de multe ori pn acum manipulatorul endl pentru a termina o linie afiat pe ecran. n C++, un manipulator este o entitate care se comport ca o funcie, dar se folosete ca o dat. Ca funcie el produce o aciune, iar ca dat poate fi plasat ntr-o serie de operaii de inserie: cout << unInt << endl << unFloat; Manipulatorii se folosesc numai n instruciuni de intrare sau de ieire. Bibliotecile standard C++ ofer o serie ntreag de manipulatori, iar noi vom studia trei dintre ei: endl, setw i setprecision. Pentru a l folosi pe endl trebuie s includem fiierul header iostream. Pentru ceilali manipulatori trebuie s includem, n plus, i fiierul header iomanip. Exemplu 6

Programarea calculatoarelor i limbaje de programare I

#include <iostream> #include <iomanip> using namespace std; int main() { int unInt = 2; cout << setw(5) << unInt; } Manipulatorul setw (set width) permite stabilirea numrului de coloane folosite pentru urmtoarea afiare. El se aplic doar numerelor i string-urilor, nu i datelor de tip char. Parametrul lui setw este o expresie ntreag numit specificaie a dimensiunii cmpului. Numrul de coloane stabilite pentru afiare se numete cmp. Data afiat va fi aliniat la dreapta, iar poziiile cmpului rmase astfel libere vor fi umplute cu spaii. Exemplu int a = 33; int b = 7132; cout << setw(4) << a << setw(5) << b << setw(7) << "Salut";
cmpurile au fost completate cu spaii; acestea au fost marcate prin

337132Salut

337132Salut cout << setw(1) << a cmpurile au fost mrite automat << setw(4) << b la dimensiunea datei afiate << setw(5) << "Salut"; Stabilirea dimensiunii cmpului afecteaz doar urmtorul element afiat. Dup aceea, dimensiunea este resetat la 0, ceea ce nseamn c dimensiunea va fi extins la attea coloane cte sunt necesare. Exemplu int a = 33; int b = 7132; cout << "Salut" << setw(5) << a << b; afieaz Salut 337132 La specificarea dimensiunii cmpului pentru numerele reale, trebuie s inem cont c punctul zecimal ocup i el o poziie. Valoarea 4.85 ocup 4 coloane, nu 3. Exemplu float x = 4.85; cout << setw(4) << x << endl 4.85 << setw(6) << x << endl 4.85 << setw(3) << x << endl; 4.85 Exist cteva observaii care trebuie fcute n legtur cu afiarea numerelor reale. 1. Numerele foarte mari sunt afiate implicit n form tiinific. Exemplu

Programarea calculatoarelor i limbaje de programare I

123456789.5 este afiat 1.23457+008 2. Dac numrul afiat este ntreg, nu se va tipri ca numr real. Exemplu 95.0 este afiat 95 Pentru a evita aceste formate implicite, naintea afirii oricrui numr real trebuie s includem urmtoarele dou instruciuni: cout.setf(ios::fixed, ios::floatfield); cout.setf(ios::showpoint); Aceste instruciuni folosesc noiuni mai avansate de C++ care nu pot fi explicate acum n detaliu. setf este o funcie void asociat stream-ului cout (punctul dintre cout i setf este strict necesar). Prima instruciune ne asigur c numerele reale vor fi tiprite n form zecimal i nu tiinific. Cea de-a doua instruciune specific faptul c punctul zecimal va fi tiprit ntotdeauna, chiar i pentru numere ntregi. Momentan vom utiliza aceste instruciuni n aceast form. Aceste setri rmn valabile pn la o nou modificare a lor. Adeseori dorim s controlm numrul de zecimale afiate, de exemplu pe 12.8 s l tiprim 12.80 sau pe 16.38753 s l tiprim 16.39. Pentru aceasta trebuie s folosim manipulatorul setprecision. Exemplu cout << setprecision(3) << x; Parametrul lui setprecision stabilete numrul de zecimale cu care va fi tiprit un numr real. Exemplu float x = 310.0; cout.setf(ios::fixed, ios::floatfield); cout.setf(ios::showpoint); cout << setw(10) 310.00 << setprecision(2) << x; cout << setw(7) 310.00000 << setprecision(5) << x; x=4.827; 4.83 cout << setw(6) << setprecision(2) << x;

4. Intrrile n program. Scrierea aplicaiilor


Un program are nevoie de date pentru a opera. Pn acum am scris programe n care valorile datelor se gsesc chiar n program, n constante simbolice sau literale. Dac dorim s modificm o dat, trebuie s facem o mic modificare n program, s l recompilm i s l executm din nou. n acest capitol vom vedea cum putem introduce date n program chiar n timpul rulrii lui. Odat ce am aflat cum s introducem date n program, s procesm datele i s afim rezultatele, putem s ne gndim la scrierea unor programe mai complicate. Pentru aceasta avem nevoie de o abordare ceva mai organizat. n finalul capitolului vom discuta despre descompunerea n module i despre programarea orientat pe obiecte.

4.1

Transmiterea datelor ctre programe

Unul dintre marile avantaje ale calculatorului este c un program poate fi folosit cu diverse seturi de date. Pentru aceasta trebuie s separm datele de program pn n momentul execuiei. Anumite instruciuni din program copiaz valori n variabilele din program. Dup stocarea acestor valori n variabile, programul poate s le foloseasc n calcule. Procesul de plasare a unor valori dintr-o mulime de date n variabile din program se numete intrare (input). ntr-o terminologie mai larg, calculatorul se spune c citete date din exterior n variabile. Datele pot proveni dintr-un fiier, de la tastatur etc. Dispozitivul standard de intrare este tastatura.

Stream-uri de intrare i operatorul de extracie >>


n C++, conceptul de stream este esenial. Putem gndi un stream de ieire ca pe o secven infinit de date care circul dinspre program nspre un dispozitiv de ieire. Un stream de intrare este o secven infinit de caractere care pornete de la un dispozitiv de intrare i este dirijat ctre program. Fiierul header iostream conine, printre altele, definiiile a dou tipuri de date: istream i ostream. Aceste tipuri de date reprezint stream-uri de intrare i stream-uri de ieire. Acest fiier header mai conine dou declaraii care arat aproximativ astfel: istream cin; ostream cout; Cele dou declaraii arat c cin este un obiect de tip istream i cout este un obiect de tip ostream. n mod explicit, cin este asociat cu tastatura, iar cout cu display-ul. Am vzut c trimiterea unor valori ctre cout se face folosind operatorul de inserie <<: cout << 3 * pret; Similar, putem citi date din cin folosind operatorul de extracie >>: cin >> cost; Operatorul de extracie >> are doi operanzi. n stnga se gsete un stream, n cel mai simplu caz cin, iar n dreapta se gsete o variabil avnd un tip predefinit (char, int, float etc.). Putem folosi operatorul de mai multe ori ntr-o instruciune: cin >> lungime >> latime;

Programarea calculatoarelor i limbaje de programare I

fiind echivalent cu: cin >> lungime; cin >> latime; Trebuie s fim ateni atunci cnd folosim cei doi operatori, pentru c cin poate fi folosit doar n combinaie cu >>, iar cout doar cu <<. Dac ntr-o instruciune de afiare putem folosi constante, variabile i chiar expresii foarte complicate, ntr-o instruciune de intrare nu putem folosi dect nume de variabile. Aceasta pentru c o instruciune de intrare trebuie s precizeze clar unde se stocheaz valoarea unei date de intrare. Doar numele de variabile refer locaii de memorie ale cror valori pot fi modificate n timpul execuiei unui program. Atunci cnd introducem o dat de la tastatur, trebuie s ne asigurm c tipul introdus i cel ateptat de program se potrivesc. Un numr ntreg este forat automat la un numr real. Operaia invers, ns, poate conduce la rezultate eronate. Atunci cnd extrage valori dintr-un stream, operatorul >> ignor orice spaiu de la nceput. De asemenea, ignor caracterul care marcheaz sfritul liniei. Apoi, operatorul >> procedeaz la extragerea valorilor din stream-ul de intrare. Dac data ateptat este un char, intrarea se ntrerupe dup primul caracter. Dac este vorba de un int sau double, intrarea se ntrerupe la primul caracter care nu se potrivete ca tip de dat, cum ar fi un spaiu. Exemplu int i, j, k; char ch; float x; Instruciunea Data Coninutul variabilei dup intrare cin >> i; 32 i = 32 cin >> i >> ch >> x; 25 A 16.9 i = 25 ch = A x = 16.9 cin >> i >> j >> x; 12 8 i = 12 j = 8 Programul ateapt al treilea numr cin >> i >> x; 46 32.4 15 i = 46 x = 32.4 15 este pstrat pentru o intrare ulterioar

Caracterul newline
Fiecare linie are un caracter invizibil care marcheaz sfritul su caracterul newline. Pentru a determina urmtoarea valoare de intrare, operatorul >> trece peste caracterul newline, n cazul n care acesta exist. Atunci cnd utilizm tastatura, caracterul newline este introdus prin apsarea lui Return sau Enter. Programul poate genera un newline folosind manipulatorul endl ntr-o instruciune de ieire. n C++ putem s ne referim la caracterul newline folosind simbolurile \n. Dei \n const din dou caractere, el se refer la unul singur caracterul newline. Aa cum putem pstra litera A n variabila ch de tip char prin instruciunea char ch = A; tot aa putem pstra caracterul newline ntr-o variabil: 2

Programarea calculatoarelor i limbaje de programare I

ch = \n; Exemplu Cnd avem o secven de citiri, putem introduce valorile n mai multe feluri: cin >> i; 25 A 16.9\n 25\n 25A16.9\n cin >> ch; A\n Citirea se oprete cnd cin >> x; 16.9\n tipul de dat nu mai corespunde Dup citire, variabilele vor avea urmtoarele valori: i = 25 ch = A x = 16.9

Citirea datelor de tip caracter folosind instruciunea get


Am vzut c operatorul >> ignor orice spaiu aprut n stream-ul de intrare. S presupunem c ch1 i ch2 sunt variabile de tip char i c programul execut instruciunea cin >> ch1 >> ch2; Dac stream-ul de intrare este R 1 Atunci operatorul de extracie va stoca R n ch1, ignor spaiul i apoi pstreaz 1 n ch2. Ce se ntmpl, ns, dac am fi dorit, de fapt, s introducem trei caractere: R, spaiu i 1? Cu operatorul de extracie acest lucru nu este posibil. Vom folosi funcia get care ncarc urmtorul caracter fr a ignora spaiile. Acest apel de funcie arat astfel: cin.get(ch); Specificm numele istream-ului, adic cin, apoi punem semnul . (punct) urmat de numele funciei i lista ei de parametri. Apelul funciei get folosete sintaxa apelului funciilor void i nu a celor care ntorc o valoare. Acest apel de funcie este, deci, o instruciune de sine stttoare. Parametrul funciei get trebuie s fie o variabil. Acesta este un exemplu prin care se apeleaz o funcie din clasa istream funcia get pentru un obiect al acestei clase obiectul cin. Putem folosi urmtoarele trei apeluri ale lui get: cin.get(ch1); cin.get(ch2); cin.get(ch3); sau cin >> ch1; cin.get(ch2); cin >> ch3; Dac stream-ul de intrare este tot R 1 atunci variabilele ch1, ch2 i ch3 vor stoca ch1 = R; ch2 = ; ch3 = 1; Exemplu

Programarea calculatoarelor i limbaje de programare I

cin >> ch1; cin >> ch2; cin >> ch3;

A B\n CD\n

ch1 = A; ch2 = B; ch3 = C; ch1 = A; ch2 = ; ch3 = B; ch1 = A; ch2 = B; ch3 = \n;

cin.get(ch1); A B\n cin.get(ch2); CD\n cin.get(ch3); cin >> ch1; A B\n cin >> ch2; CD\n cin.get(ch3);

Ignorarea caracterelor folosind funcia ignore


Funcia ignore este asociat cu tipul de dat istream, fiind o funcie definit n aceast clasa istream. Este folosit pentru a sri peste caractere din stream-ul de intrare. Este o funcie cu doi parametri, iar un exemplu de apel este urmtorul: cin.ignore(200, \n); Primul parametru este o expresie int, iar al doilea una char. Acest apel al funciei spune calculatorului s ignore urmtoarele 200 de caractere de intrare sau s sar pn ntlnete caracterul newline, n funcie de care dintre ele apare prima. Exemplu cin >> i >> j; 957 34 1235\n i = 957; cin.ignore(100, \n); 128 96\n j = 34; cin >> k; k = 128; cin >> ch; cin.ignore(100, B); cin >> i; cin.ignore(2, \n); cin >> ch; A 22 B 16 C 19\n ch = A; i = 16;

ABCDEF\n

ch = C;

Intrri i ieiri interactive


Un program interactiv este unul prin care utilizatorul comunic direct cu calculatorul. Multe dintre programele scrise de noi vor fi interactive, dnd i o anumit etichet codului. Atunci cnd dorim s cerem utilizatorului s introduc o dat n program, este util s i afim nainte un mesaj prin care s i explicm ce trebuie s introduc. De asemenea, programul ar trebui s tipreasc toate datele introduse pentru ca utilizatorul s se poat verifica. Aceasta se numete tiprire n ecou. Programul de mai jos arat cum poate fi scris un cod interactiv. Exemplu #include <iostream> #include <iomanip> using namespace std; int main() { int codNumeric;

Programarea calculatoarelor i limbaje de programare I

int cantitate; double pretUnitar; double pretTotal; cout << "Introduceti comandat:" << endl; cin >> codNumeric; codul numeric al produsului

cout << "Introduceti cantitatea:" << endl; cin >> cantitate; cout << "Introduceti produs:" << endl; cin >> pretUnitar; pretul unitar pentru acest

pretTotal = cantitate * pretUnitar; cout.setf(ios::fixed, ios::floatfield); cout << << << pretUnitar << cout << "Produsul " << codNumeric ", cantitatea " << cantitate ", pretul unitar " << setprecision(2) " lei " << endl; "Total: " << pretTotal << endl;

<<

return 0; } n timpul rulrii acestui program se va tipri cte un text care va arta utilizatorului care este urmtoarea valoare ateptat. Exemplu Introduceti codul numeric al produsului comandat 4671 Introduceti cantitatea: 10 Introduceti pretul unitar pentru acest produs: 272.55 Produsul 4671, cantitatea 10, pretul unitar 272.55 lei Total: 2725.50 Volumul informaiei afiate depinde de cel care va folosi programul. Dac el este destinat unor persoane nefamiliarizate cu calculatorul, mesajele vor fi mai detaliate. Dac programul este folosit frecvent de aceeai persoan, putem da mesaje mai scurte sau se pot introduce mai multe valori pe aceeai linie.

Intrri i ieiri neinteractive


Dei tindem s dm exemple de programe interactive, multe dintre programele folosite n viaa real sunt neinteractive. Acestea sunt programe care prelucreaz cantiti mari de date, greu de introdus interactiv fr erori. Pentru acest tip de programe, datele se pstreaz n fiiere de date pregtite anterior. Aceasta permite verificare i corectarea datelor nainte de rularea programului.

Programarea calculatoarelor i limbaje de programare I

4.2

Intrri i ieiri din fiiere

Fiierul este o zon pe un suport de stocare, de exemplu hard disc, desemnat printr-un nume, care pstreaz o colecie de date, de exemplu codul programului scris cu un editor. Un program poate citi datele dintr-un fiier n aceeai manier n care le citete de la tastatur. Se poate scrie n fiier la fel cum se scrie pe ecran. Atunci cnd programul ajunge s lucreze cu cantiti mari de date, este de preferat s folosim fiierele. Acestea pot fi scrise cu un editor de texte, corectate i salvate pe disc. Totodat, nu suntem obligai s introducem toate datele dintr-o singur dat.

Modul de utilizare a fiierelor


Pentru a folosi un fiier n operaiile I/O trebuie s parcurgem patru pai. 1. Cerem preprocesorului s includ fiierul header fstream; 2. Folosim instruciuni de declarare pentru a declara stream-urile; 3. Pregtim fiierele pentru citire i scriere prin instruciunea open; 4. Specificm numele stream-ului n fiecare instruciune de citire sau de scriere. Includerea fiierului header fstream Vom modifica programul care msoar consumul unui autoturism la 100 km pentru ca s citeasc datele dintr-un fiier. Exemplu #include <fstream> using namespace std; int main() { float float float float float float float

cant1; cant2; cant3; cant4; indicatiePlecare; indicatieSosire; litriPerKm;

ifstream inConsum; ofstream outConsum; inConsum.open("incons.dat"); outConsum.open("outcons.dat"); inConsum >> cant1 >> cant2 >> cant3 >> cant4 >> indicatiePlecare >> indicatieSosire; litriPerKm = (cant1 + cant2 + cant3 + cant4)*100.0/ (indicatieSosire - indicatiePlecare); outConsum << "Consumul este " << litriPerKm << " litri per km." << endl;

Programarea calculatoarelor i limbaje de programare I

return 0; } Mai nti folosim directiva de preprocesare #include <fstream> Prin acest header se definesc dou noi tipuri de date, ifstream i ofstream. Cele dou tipuri de dat reprezint, primul, un stream de caractere provenind de la un fiier, iar al doilea un stream de caractere care conduce ctre un fiier. Toate operaiile valabile pentru un istream: >>, get sau ignore sunt valabile i pentru tipul ifstream. Operaiile folosite pentru un ostream: <<, endl, setw, setprecision se pot folosi i pentru un ofstream. Declararea stream-urilor pentru fiiere Obiectele de tip stream se declar la fel ca orice variabil Exemplu int unInt; float unFloat; ifstream unFisier; ofstream altFisier; Obiectele cin i cout nu trebuie declarate n fiecare program pentru c ele sunt declarate n fiierul iostream, citirile de la tastatur i scrierile pe ecran fiind operaii frecvente. Spre deosebire de ele, stream-urile pentru lucrul cu fiiere trebuie declarate n program pentru c fiecare aplicaie folosete propriile fiiere de date. Pentru programul nostru declarm dou stream-uri: ifstream inConsum; ofstream outConsum; De notat c ifstream se utilizeaz doar pentru fiiere de intrare, iar ofstream doar pentru fiiere de ieire. Prin intermediul unui obiect ifstream se pot face doar citiri, iar prin intermediul unui obiect ofstream se pot face doar scrieri. Deschiderea fiierelor Trebuie s pregtim acum fiierele pentru citire sau scriere, deci le deschidem. Ne propunem s citim din stream-ul tip fiier inConsum i s scriem n streamul outConsum. Deschidem fiierele folosind urmtoarele instruciuni: inConsum.open("incons.dat"); outConsum.open("outcons.dat"); Funcia open are un singur argument cuprins ntre ghilimele. Prima instruciune este un apel al funciei open asociate cu tipul de dat ifstream, iar a doua apeleaz funcia open asociat cu ofstream. Funcia open asociaz variabila stream din program cu un fiier fizic pe disc. Prima instruciune realizeaz o conexiune ntre obiectul inConsum i fiierul incons.dat. A doua instruciune leag obiectul outConsum de fiierul outcons.dat. inConsum este conectat cu incons.dat aa cum cin este legat de tastatur. Mai departe, aciunea depinde de tipul de stream. Pentru un fiier de intrare, funcia open poziioneaz markerul de citire pe primul element din fiier. Pentru un fiier de ieire, funcia verific dac acesta exist. Dac exist, terge vechiul coninut al su. Dac nu exist, l creeaz. Apoi markerul de scriere este aezat pe prima poziie.

Programarea calculatoarelor i limbaje de programare I

Folosim noiunile de marker de citire sau de scriere pentru a reprezenta locul din care se va citi dintr-un stream de intrare, respectiv se va scrie ntr-un stream de ieire. Exemplu inConsum outConsum 23.2 _ 17.4 19.8 16.7 22451 23544 Deschiderea fiierelor trebuie realizat naintea oricrei utilizri a lor deoarece funcia open le pregtete pentru citire sau scriere. Specificarea stream-urilor n instruciuni I/O Pentru citirea i scrierea din fiier nu va trebui dect s nlocuim obiectele cin i cout cu obiectele de tip stream de fiier declarate mai devreme. Exemplu inConsum >> cant1 >> cant2 >> cant3 >> cant4 >> indicatiePlecare >> indicatieSosire; outConsum << "Consumul este " << litriPerKm << " litri per km." << endl; Cel mai interesant este c C++ folosete o sintax uniform pentru operaiile I/O, indiferent dac este vorba despre fiiere sau dispozitive I/O.

4.3

Erori de citire

La citirea datelor de la tastatur sau dintr-un fiier pot aprea erori. S presupunem c programul ne cere s introducem un numr ntreg, iar noi introducem caractere. Operaia de intrare eueaz datorit datelor de intrare invalide. n terminologia C++ stream-ul cin intr ntr-o stare de eroare - fail state. Dac un stream intr ntr-o astfel de stare, orice alt operaie ulterioar asupra sa este anulat. Din pcate, calculatorul nu oprete programul i nu d niciun mesaj de eroare n astfel de situaii. De cele mai multe ori erorile de intrare apar din cauza nepotrivirii tipurilor de dat. Exemplu int i = 10; int j = 20; int k = 30; cin >> i >> j >> k; cout << i: << i << j: << j << k: << k; Dac tastm 1234.56 7 89 programul afieaz i: 1234 j: 20 k: 30 Un alt motiv pentru care un stream intr n fail state este incercarea de deschidere a unui fiier de intrare care nu exist. S presupunem c avem pe disc fiierul myfile.dat i scriem urmtoarele instruciuni care i propun s lucreze cu acest fiier: ifstream inFisier; inFisier.open(myfil.dat); 8

Programarea calculatoarelor i limbaje de programare I

inFisier >> i >> j >> k; Datorit scrierii incorecte a numelui de fiier, inFisier va intra n fail state. Ca urmare, variabilele i, j i k vor avea nite valori nedeteminate. Un stream de citire dintr-un fiier intr n fail state atunci cnd se citete din fiier dincolo de caracterul EOF, cel care marcheaz finalul fiierului.

4.4

Scrierea aplicaiilor

Problemele pe care le-am prezentat pn acum au fost simple i uor de programat. n acest moment putem scrie i aplicaii mai complexe i de aceea trebuie s vedem cum putem concepe corect o aplicaie. Vom vorbi despre descompunerea funcional i despre proiectarea orientat pe obiecte.

Descompunerea funcional
Aceast este o tehnic de dezvoltare a unei pri a unui program sau chiar a unui program de dimensiuni reduse prin care problema este mprit n subprobleme mai uor de rezolvat, soluii care creeaz o soluie global a ntregii probleme. Printr-o astfel de descompunere crem o structur ierarhic numit structur arborescent. Rezolvarea problemei Pas I Pas II Pas III abstract Nivelul 0

pas abstract Subproblema I Pas A Pas B Subproblema II Pas C Pas D Subproblema III Pas E Pas F Nivelul 1

Subproblema A Pas 1

Subproblema B Subproblema C Pas 2 Pas 3 Pas 4 Pas 5 Pas 6 Subproblema 2 Pas a Pas b Pas c 9

Subproblema F Pas 7 Pas 8 Nivelul 2

pas concret

Nivelul 3 concret

Programarea calculatoarelor i limbaje de programare I

Paii haurai conin suficiente detalii pentru a putea fi implementai n C++. Cei nehaurai trebuie descompui n continuare. Fiecare celul este un modul. Modulele sunt elemente de baz ntr-o descompunere funcional. Pentru conceperea unui modul trebuie s parcurgem urmtorii pai: 1. S schim o soluie a problemei 2. S descriem paii majori 3. Dac un pas este suficient de simplu pentru a putea fi implementat, nu mai necesit descompuneri ulterioare 4. Dac pasul poate fi gndit ca o serie de pai mai mici, este nc un pas abstract

Proiectarea orientat pe obiecte


Descompunerea funcional poate fi privit ca o metod de gsire a soluiei unei probleme cu accent pe algoritmi i aciunile care trebuie realizate. Datele, n acest caz, joac un rol secundar. Proiectarea orientat pe obiecte se focalizeaz pe entiti (obiecte) i operaiile posibile asupra acestor entiti. Exemplu O problem bancar poate avea nevoie de un obiect contBancar cu operaiile asociate DeschideCont, ScrieCec i CreeazaDepozit. Obiectul contBancar const din date numarCont i balantaCurenta. Primul pas n proiectarea orientat pe obiecte este identificarea obiectelor din problem i a operaiilor asociate. Soluia final va fi exprimat n termeni de obiecte i operaii. Datele joac aici un rol determinant. n C++ operaiile asociate cu o clas sunt scrise ca funcii i se numesc funcii membre. O funcie membr este apelat prin numele unui obiect al clasei urmat de un punct i de numele funciei cu lista de parametri. Exemplu contBancar.DeschideCont(1000, tip1); Datele care compun obiectul se numesc date membre. Instanele unei clase se numesc obiecte, n timp ce instanele tipurilor de date predefinite cum ar fi int se numesc variabile. Proiectarea orientat pe obiecte conduce la un program care folosete o colecie de obiecte. Fiecare obiect rspunde de o parte din soluie, iar obiectele comunic ntre ele prin apelarea funciilor membre. Proiectarea orientat pe obiecte se preteaz la scrierea proiectelor mari din urmtoarele trei motive: 1. Obiectele dintr-un program modeleaz obiecte din problema de rezolvat; 2. Este posibil furnizarea i utilizarea de biblioteci de clase scrise de diverse firme sau persoane independente; 3. Se folosete un concept fundamental numit motenire care permite adaptarea unei clase la particularitile problemei fr a modifica codul scris anterior. Pentru crearea unei soluii software optime se urmeaz un proces detaliat pentru obinerea unei analize a cerinelor sistemului (requirments) i proiectarea acestuia (design) astfel nct s satisfac cerinele. Programatorii experimentai cunosc faptul c orict de simpl ar fi problema pe care o au de rezolvat, timpul

10

Programarea calculatoarelor i limbaje de programare I

petrecut cu analiza i proiectarea poate salva foarte mult timp care se poate pierde cu dezvoltarea unui sistem greit conceput. Unified Modeling Language (UML) Exist multe procedee pentru analiza i proiectarea orientat pe obiecte. Pentru totate acestea se folosete un limbaj grafic de comunicare a rezultatelor numit Unified Modeling Language (UML). Prima versiune a acestui limbaj a fost lansat n anul 1996 de ctre James Rumbaugh, Grady Booch i Ivar Jacobson de la Rational Software Corporation. n acelai timp, organizaia non-profit Object Management Group (OMG) a lansat, la iniiativa companiilor HP, IBM, Microsoft, Oracle i Rational Software, o propunere de creare a unui limbaj unic de modelare. UML a fost limbajul adoptat de OMG care, ncepnd cu anul 1997 asigur revizuirea permanent a sa. UML este o schem de reprezentare grafic pentru modelarea sistemelor orientate pe obiecte. Una dintre cele mai atractive caracteristici ale sale este flexibilitatea. UML este extensibil i independent de multele procese de analiz i proiectare orientate pe obiecte. Tehnologia obiectelor a devenit indispensabil n industria software, iar UML este din ce n ce mail folosit. Specificaiile complete ale UML sunt disponibile la http://www.uml.org.

11

5. Condiii, expresii logice i structuri de control pentru selecie


Pn acum, instruciunile din programele scrise de noi se executau n ordinea n care erau scrise. S presupunem acum c dorim s verificm validitatea unor date de intrare i apoi s facem un calcul sau s afim un mesaj de eroare, dar s nu le facem pe amndou. Pentru aceasta va trebui s punem o ntrebare i, bazndu-ne pe rspuns, s alegem una sau alta dintre variantele de evoluie a programului. Instruciunea if ne permite s executm instruciunile ntr-o alt ordine dect cea fizic.

5.1

Ordinea de execuie a intruciunilor

La un moment dat, calculatorul se gsete sub controlul unei instruciuni. Dup ce aceast instruciune este executat, controlul este trecut urmtoarei instruciuni. n mod obinuit execuia este secvenial.

Instruciunea 1

Instruciunea 2

Instruciunea 3

Acolo unde dorim ca execuia s nu mai fie secvenial, introducem structuri de control. Atunci cnd vrem ca un program s aleag ntre dou aciuni alternative, folosim o structur de control al seleciei. Vom face o presupunere care poate fi adevrat sau fals. Dac este adevrat, calculatorul execut o intruciune. Dac este fals, execut alt instruciune.

Adevrat

Presupunere

Fals

Instruciunea 1A

Instruciunea 1B

De exemplu, programul poate testa la un moment dat dac un angajat va fi pltit pentru ore suplimentare lucrate. Pentru aceasta, el va testa presupunearea c

Programarea calculatoarelor i limbaje de programare I

angajatul a lucrat mai mult de 40 de ore ntr-o sptmn, iar dac este fals calculeaz suma obinuit.

5.2

Condiii i expresii logice

Pentru a pune o ntrebare n C++, facem o presupunere care poate fi adevrat sau fals. Calculatorul evalueaz presupunearea pentru a constata dac este adevrat sau fals.

Expresii logice
n C++ presupunerile iau forma expresiilor logice sau booleene. O astfel de expresie este alctuit din valori logice i operaii. Datele booleene Versiunile actuale ale limbajului de programare C++ includ un tip de dat numit bool al crui domeniu de valori este format din constantele literale true i false. Exemplu const bool checkValue = true; bool dataOK; dataOK = false; Pentru compatibilitatea cu versiunile mai vechi ale standardului C++ n care acest tip de dat nu exista, valoarea true poate fi, de asemenea reprezentat, de orice valoare nenul, iar false poate fi reprezentat de valoarea 0. Exemplu const bool checkValue = 1; bool dataOK; dataOK = 0; Valorile booleene false sunt tiprite n mod implicit ca valoare 0, iar valorile true sunt tiprite ca valoare 1. Operatorul de inserie n stream << tiprete variabilele de tip bool ca ntregi. Exemplu bool dataOK; dataOK = false; cout << dataOK << endl; 0 Manipulatorul boolalpha seteaz stream-ul de ieire ca s afieze valorile de tip bool prin irurile true i false. Exemplu bool dataOK; dataOK = false; cout << boolalpha << dataOK << endl; false Operatori relaionali Unei variabile booleene i poate fi asignat rezultatul comparrii a dou valori. Exemplu bool maiMicDecat; int i, j; cin >> i >> j; maiMicDecat = ( i < j ); Variabilei maiMicDecat i se asigneaz true cnd i < j. Comparnd dou valori, prespunem c o relaie, de exemplu mai mic dect, exist ntre ele. Dac relaia exist ntr-adevr, presupunerea este adevrat. Dac nu, este fals.

Programarea calculatoarelor i limbaje de programare I

n C++ putem testa urmtoarele relaii: Operator Exemplu Semnificaie relaional == x == y x este egal cu y != x != y x este diferit de y > x > y x este mai mare dect y < x < y x este mai mic dect y >= x >= y x este mai mare sau egal dect y <= x <= y x este mai mic sau egal dect y n C++, rezultatul unei comparaii poate fi true sau false. Exemplu Dac x are valoarea 5 i y are valoarea 10, urmtoarele relaii sunt adevrate: x == 5 x != y y > x x < y y >= x x <= y Dac x este M i y este R, expresiile de mai sus sunt de asemenea true deoarece sunt comparate codurile ASCII ale caracterelor. n acest cod, literele mari sunt aezate inaintea celor mici. Exemplu M < R i m < r sunt true m < R este false Trebuie s fim ateni la tipurile de dat ale valorilor pe care le comparm. Cel mai sigur este s comparm int cu int, double cu double etc. Dac nu, apar forrile implicite de tip. Ca i n cazul expresiilor aritmetice, cel mai sigur, n aceste situaii este s folosim cast explicit pentru a ne face inteniile cunoscute: nrDouble >= double(nrInt) sau nrDouble >= static_cast<double>(nrInt) De asemenea, valorile char trebuie comparate doar cu valori char. Exemplu 0 < 9 sau 0 < 9 sunt corecte 0 < 9 se realizeaz cu o forare de tip i rezultatul nu este cel ateptat Se pot folosi operatori relaionali nu doar pentru a compara valori ale variabilelor sau constantelor, ci i pentru a compara valori ale expresiilor aritmetice. Exemplu Dac avem int x = 17, y = 2; atunci urmtoarea expresie este adevrat: x + 3 == y * 10 O eroare des ntlnit n scrierea programelor C++ este confuzia ntre operatorii = i ==. Folosirea operatorului == pentru asignare sau a operatorului = pentru a testa egalitatea sunt erori logice.

Programarea calculatoarelor i limbaje de programare I

Operatori logici n matematic, operatorii logici AND, OR i NOT acioneaz asupra expresiilor logice care joac rolul de operanzi. C++ folosete simboluri speciale pentru fiecare dintre aceti operatori. Operator logic Exemplu Semnificaie && x && y AND logic || x || y OR logic ! !x NOT logic Pentru ca rezultatul operaiei AND (&&) s fie true, ambii operanzi trebuie s fie true. Tabelul de adevr pentru operatorul && este: Expresia 1 Expresia 2 Expresia 1 && Expresia 2 false false false false true false true false false true true true Operaia OR (||) cere ca cel puin un operand s fie true pentru ca rezultatul s fie true. Tabelul de adevr pentru operatorul || este: Expresia 1 Expresia 2 Expresia 1 || Expresia 2 false false false false true true true false true true true true Operatorul NOT (!) precede o singur expresie logic i d un rezultat opus valorii logice a expresiei. Tabelul de adevr pentru operatorul ! este: Expresia 1 ! Expresia 1 false True true False NOT ne d o metod de a inversa sensul unei presupuneri. Exemplu !(oreLucrate > 40) este echivalent cu oreLucrate <= 40 n unele expresii prima form este mai clar, n altele cea de-a doua. Exemple Conform regulilor lui De Morgan avem: !( a == b ) a != b !( a == b || a == c ) a != b && a != c !( a == b && c > d ) a != b || c <= d

Evaluri condiionale
Evaluarea expresiilor logice se face de la stnga la dreapta, aceasta oprindu-se atunci cnd este cunoscut valoarea ntregii expresii. ntrebarea care se pune este cum poate calculatorul s tie dac valoarea unei expresii este true sau false dac nu a evaluat-o pn la capt. O operaie AND este true dac ambii operanzi sunt true. Dac n expresia i == 1 && j > 2 i are valoarea 10, subexpresia din stnga va fi false, deci expresia nu mai poate avea dect valoarea false.

Programarea calculatoarelor i limbaje de programare I

Evaluarea unei expresii OR se oprete cnd una dintre subexpresii este true, deci rezultatul este true.

Precedena operatorilor
Am discutat despre precedena operatorilor n evaluarea expresiilor aritmetice. Regulile de preceden se aplic i operatorilor logici i relaionali. Lista operatorilor i precedena lor este urntoarea: Cea mai nalt preceden ( ) ! * / % + < <= > >= == != && || = Cea mai sczut preceden Parantezele rotunde au prioritate asupra oricrei operaii. Acestea trebuie folosite ntotdeauna n pereche.

Operatori relaionali pentru tipuri reale


Operatorii relaionali pot fi folosii pentru orice tipuri de dat. Vom prezenta cazul valorilor double. Ca regul general, nu studiai egalitatea a dou numere reale pentru c, datorit micilor erori care apar la calculele cu numere reale, dou astfel de valori nu sunt ntotdeauna egale. Pentru instruciunile float oTreime, x; oTreime = 1.1 / 3.0; x = oTreime + oTreime + oTreime; Ne ateptm ca x s aib valoarea 1.1, dar probabil c nu va fi aa. Prima asignare 1 va stoca n variabila oTreime o aproximare lui , probabil 0.366667. Cea de-a 3 doua asignare va stoca in variabila x o valoare egal cu 3*0.366667. Dac vom compara x cu 1.1 rezultatul va fi false. Diferena dintre valorile lui x i 1.1 este un numr foarte mic, de exemplu 2.38419e-008. n loc s testm egalitatea, putem considera c, dac diferena dintre cele dou numere este foarte mic, putem considera c numerele sunt egale. Putem nlocui testul de egalitate cu fabs(x-1.1) < 0.00001

5.3

Instruciunea if

Am vzut cum se scriu expresiile logice, vom vedea acum cum putem afecta fluxul secvenial al programului. Instruciunea if permite ramificarea fluxului normal. Putem pune o ntrebare i n funcie de condiiile existente putem realiza o aciune sau alta.

Programarea calculatoarelor i limbaje de programare I

Structura IF-THEN-ELSE
n C++ instruciunea if poate fi folosit n dou variante: forma IF-THEN-ELSE sau forma IF-THEN. O vom analiza mai nti pe prima. Sintaxa formei IF-THEN-ELSE este if(expresie) Instruciunea 1A else Instruciunea 1B Expresia din paranteze poate avea orice tip simplu de dat. Aproape ntotdeauna aceasta va fi o expresie logic. Dac valoarea expresiei este nenul sau true, calculatorul execut Instruciunea 1A. Dac valoarea expresiei este 0 sau false, se execut Instruciunea 1B. Instruciunea 1A se numete clauza then, iar Instruciunea 1B se numete clauza else. if (expresie)
false true

Instruciunea 1A Instruciunea 1B

Instruciunea 2 De notat c instruciunea if folosete doar cuvintele rezervate if i else, chiar dac structura se numete IF-THEN-ELSE. Exemple if(oreLucrate <= 40) plata = sumaPeOra * oreLucrate; else plata = sumaPeOra * (40.0 + (oreLucrate 40.0) * 1.5); cout << plata; n limbaj natural, aceast instruciune spune urmtorul lucru: Dac numrul de ore lucrate este mai mic sau egal dect 40, atunci calculeaz suma obinuit de plat i apoi treci la instruciunea de tiprire. Dac numrul de ore lucrare este mai mare dect 40, atunci calculeaz suma normal i suma suplimentar , apoi treci la instruciunea de tiprire.

Blocuri
Pentru a evita mprirea la zero ntr-o expresie, s presupunem c atunci cnd numitorul este egal cu 0 facem dou lucruri: tiprim un mesaj de eroare i ncrcm valoarea 9999 n variabila rezultat. Trebuie, aadar, s realizm dou instruciuni pe aceeai ramur, ns sintaxa instruciunii if ne limiteaz la una singur. S ne amintim c orice compilator C++ trateaz blocul { ... } ca o singur instruciune. Folosind o pereche de acolade pentru a ncadra secvena de instruciuni, instruciunile vor deveni un singur bloc.

Programarea calculatoarelor i limbaje de programare I

Exemplu if(numitor != 0) rezultat = numarator / numitor; else { cout << Impartirea la 0 nu este permisa. << endl; rezultat = 9999; } Putem folosi blocuri de instruciuni pe ambele ramuri ale unui IF-THEN-ELSE. Exemplu if(numitor != 0) { rezultat = numarator / numitor; cout << Impartirea este realizata corect. << endl; } else { cout << Impartirea la 0 nu este permisa. << endl; rezultat = 9999; } Dup acolada care nchide blocul nu se pune niciodat semnul ;

Forma IF-THEN
Uneori dorim s realizm o aciune cnd se ndeplinete o condiie, dar s nu se ntmple nimic atunci cnd condiia este fals. Putem lsa ramura else vid. Exemplu if(a <= b) c = 20; else ; Mai simplu, putem s nu scriem deloc ramura else. Ajungem, astfel, la forma IF-THEN: if(Expresie) Instruciune Putem rescrie instruciunea din exemplul anterior astfel: if(a <= b) c = 20; Ca i la forma IF-THEN, ramura din IF-THEN poate fi un bloc de instruciuni. S presupunem c avem de completat un formular pentru efectuarea unei pli. Conform instruciunilor, linia 23 din formular trebuie sczut din linia 17, iar rezultatul trebuie trecut pe linia 24. Dac rezultatul este negativ, pe linia 24 se trece rezultatul 0 i se bifeaz csua 24A. n C++ aceast cerin se implementeaz astfel: rezultat = linia17 linia23; if( rezultat < 0.0 ) { cout << Bifeaza casuta 24A << endl; rezultat = 0.0; } linia24 = rezultat; Ce se ntmpl dac uitm s folosim acoladele?

Programarea calculatoarelor i limbaje de programare I

rezultat = linia17 linia23; if( rezultat < 0.0 ) cout << Bifeaza casuta 24A << endl; rezultat = 0.0; linia24 = rezultat; n ciuda inteniilor noastre, compilatorul trateaz clauza then ca una cu o singur instruciune. Dac rezultatul este negativ, calculatorul execut instruciunea de afiare, seteaz rezultatul cu 0 i apoi l pstreaz n linia24. Pn acum totul este bine. Dac rezultatul este pozitiv, calculatorul ignor clauza then i execut urmtoarea instruciune dup instruciunea if. De aceea, rezultatul este setat cu 0, ceea ce contravine inteniilor noastre. Concluzia este c alinierea instruciunilor nu influeneaz n niciun fel compilatorul.

Instruciuni if imbricate
Nu exist restricii asupra tipului de instruciuni pe care le poate conine o ramur a unui if. Prin urmare, o ramur a unui if poate conine un alt if. Cnd folosim o astfel de construcie, spunem c am creat o structur de if imbricat. IF azi este smbt sau duminic IF plou Rmi n cas ELSE Iei la plimbare ELSE Mergi la coal n general, orice problem care implic multi-ramificare poate fi codat prin instruciuni if imbricate. Pentru a afia numele unei zile din sptmn putem folosi o serie de instruciuni if neimbricate. Exemplu if(zi == 1) cout << Luni; if(zi == 2) cout << Marti; if(zi == 3) cout << Miercuri; if(zi == 4) cout << Joi; if(zi == 5) cout << Vineri; if(zi == 6) cout << Sambata; if(zi == 7) cout << Duminica; Acest cod poate fi rescris cu instruciuni if imbricate. Exemplu if(zi == 1) cout << Luni; else 8

Programarea calculatoarelor i limbaje de programare I

if(zi == 2) cout << Marti; else if(zi == 3) cout << Miercuri; else if(zi == 4) cout << Joi; else if(zi == 5) cout << Vineri; else if(zi == 6) cout << Sambata; else if(zi == 7) cout << Duminica; Este mult mai eficient aceast variant pentru c implic mai puine comparaii. Structura care implementeaz ramificarea multipl se numete IF-THENELSE-IF. Putem realinia structura de mai sus pentru a evita deplasarea continu spre dreapta datorit alinierilor convenite. Exemplu if(zi == 1) cout << Luni; else if(zi == 2) cout << Marti; else if(zi == 3) cout << Miercuri; else if(zi == 4) cout << Joi; else if(zi == 5) cout << Vineri; else if(zi == 6) cout << Sambata; else if(zi == 7) cout << Duminica;

Folosirea greit a lui else


La folosirea instruciunilor if imbricate pot aprea confuzii n legtur cu perechile if-else: crui if i aparine un else? S presupunem c dac nota unui student este mai mic dect 5 dorim s tiprim Respins, dac nota lui este ntre 5 i 6 s tiprim Admis, iar dac nota este mai mare dect 6 s nu tiprim nimic. Putem coda aceast problem astfel: if(media < 6.0) if(media < 5.0) cout << "Respins" << endl; else cout << "Admis" << endl; Cum tim crui if i aparine un else? O regul din C++ spune c, n absena acoladelor, un else este asociat ntotdeauna instruciunii if cea mai apropiat care

Programarea calculatoarelor i limbaje de programare I

nu are nc un else drept pereche. Noi obinuim s aliniem codul astfel nct s reflectm aceast mperechere. Ne propunem s rescriem codul de mai sus n aa fel nct instruciunea else s fie asociat primului if. if(media < 6.0) if(media < 5.0) cout << "Respins" << endl; else cout << "Admis" << endl; Aceast versiune nu rezolv problema aa cum dorim noi pentru c simpla aliniere a instruciunilor nu este suficient. Conform regulii, ultimul else va fi mperecheat cu al doilea if. Pentru o soluie corect, plasm cel de-al doilea if ntr-un bloc de instruciuni. if(media < 6.0) { if(media < 5.0) cout << "Respins" << endl; } else cout << "Admis" << endl; Perechea de acolade indic faptul c cea de-a doua instruciune if este complet, iar else trebuie s aparin primului if.

5.4

Testarea strii stream-urilor de intrare / ieire

n capitolul trecut am prezentat conceptul de stream de intrare i de stream de ieire n C++. Am artat c sunt situaii n care un stream poate intra in fail state: - date de intrare invalide; - tentativa de a citi un fiier dincolo de EOF; - intenia de a deschide pentru citire un fiier inexistent. C++ ofer un mecanism de a determina cnd un stream este in fail state. n expresii logice se poate folosi efectiv numele stream-ului ca i cum ar fi o variabil boolean. Exemple if(cin) ... if(!inFile) ... La testarea strii unui stream rezultatul poate fi nenul, adic ultima operaie I/O asupra stream-ului a avut succes sau nul, cnd ultima operaie I/O asupra streamului a euat.

5.5

Proiectarea orientat pe obiecte

Proiectarea este etapa care trebuie parcurs n procesul de dezvoltare a unui proiect cu scopul de a depi bariera pe care o impune complexitatea sa. O metod de proiectare ne ajut s mprim proiectul n module de dimensiuni mai mici, care pot fi rezolvare mai uor. Proiectarea orientat pe obiecte este una dintre metodele de proiectare a aplicaiilor, fiecare avnd propriile avantaje i dezavantaje. n proiectarea orientat

10

Programarea calculatoarelor i limbaje de programare I

pe obiecte problemele sunt modelate prin obiecte. Acestea se caracterizeaz prin comportament, adic realizeaz aciuni i prin stri care se modific prin aciuni. Exemplu Un autoturism poate fi tratat ca un obiect. Acesta are o stare n care prespunem c motorul este pornit i un comportament - pornirea autoturismului - care i schimb starea din motor oprit n motor pornit.

Abstractizarea
n proiectarea orientat pe obiecte, complexitatea problemelor este tratat prin abstractizare. Abstractizarea nseamn eliminarea elementelor nerelevante i amplificarea elementelor eseniale. Un exemplu de abstractizare este descrierea funcionrii unui autoturism. Dorim s nvm pe cineva s conduc autoturismul folosindu-ne de abstractizare. Amplificm elementele eseniale nvndu-l pe elevul nostru cum se pornete maina i cum se mnuiete volanul. Eliminm detaliile legate de funcionarea motorului i nu i vom spune c, pentru ca maina s funcioneze, combustibilul este pompat din rezervor ctre motor unde printr-o scnteie acesta se aprinde, are loc o mic explozie care mpinge un piston care, la rndul su, face ca motorul s se mite. Problemele au, n general, mai multe nivele de abstractizare. Putem discuta despre un autoturism din punctul de vedere al oferului, plasndu-ne la un nivel nalt de abstractizare. Atunci cnd vom discuta cu un mecanic auto, acesta va avea nevoie de informaii detaliate despre funcionarea motorului sau a instalaiei electrice i atunci ne vom afla la un nivel mai sczut de abstractizare, devenind puin mai concrei. Se poate vorbi, totui, despre abstractizare i la nivelul mecanicului auto. Mecanicul testeaz sau ncarc bateria automobilului fr s se uite n interiorul acesteia, acolo unde au loc reacii chimice complexe. Pe de alt parte, un proiectant al bateriei este interesat de aceste detalii, dar nu i de modul n care curentul generat de baterie face ca aparatul de radio s funcioneze. Observm astfel c putem analiza detalii cum ar fi proiectarea bateriei sau a aparatului de radio. Analiza se face pe module care pot fi mai uor de descris, dar prin abstractizare i ignorarea detaliilor putem s privim ntregul autoturism ca un modul.

11

6. Bucle
n capitolul trecut am vzut cum putem selecta diferite instruciuni pentru execuie folosind instruciunea if. O bucl este o structur de control care provoac executarea unei instruciuni sau a unei secvene de instruciuni n mod repetat. Instruciunile se execut atta timp ct sunt ndeplinite una sau mai multe condiii. Vom descrie diferite tipuri de bucle i vom vedea cum se pot implementa acestea folosind instruciunea while. Vom prezenta, de asemenea, buclele imbricate.

6.1

Instruciunea while

Aceast instruciune, ca i if, testeaz o condiie. Sintaxa ei este urmtoarea: while(expresie) Instruciune Exemplu while(valIn != 25) cin >> valIn; Instruciunea care se execut n mod repetat se numete corpul buclei. Condiia din instruciunea while poate fi o expresie de orice tip de dat. Aproape ntotdeauna ea este o expresie logic. Instruciunea while din exemplul de mai sus spune urmtorul lucru: Dac valoarea expresiei este true (nenul), execut corpul buclei iar apoi revino i testeaz din nou expresia. Dac expresia este false (zero), treci de corpul buclei. Dac expresia este fals de la nceput, corpul nu se execut niciodat. n figura de mai jos artm n mod schematic modul de execuie a instruciunii while.

while (expresie)
false true

Instruciune

Instruciunea 2 Corpul buclei poate fi i un bloc, fapt care ne permite s executm mai multe instruciuni n mod repetat. Exemplu while(expresie) { ... } Blocul se execut pn cnd expresia devine fals.

Programarea calculatoarelor i limbaje de programare I

6.2

Fazele de execuie a unei bucle

1. Intrarea n bucl. Este punctul n care programul ajunge la prima instruciune din interiorul buclei. 2. Iterarea. De fiecare dat cnd se execut corpul buclei, spunem c facem cte o trecere prin bucl. Aceast trecere se numete iteraie. 3. Testul buclei. Reprezint punctul n care se evalueaz expresia din instruciunea while. n urma acestei evaluri se poate lua decizia de a se ncepe o nou iteraie sau de a trece la instruciunea imediat urmtoare buclei. 4. Condiia de terminare. Este condiia care provoac ieirea din bucl, trecndu-se la prima instruciune de dup bucl. Aceast condiie apare n instruciunea while. 5. Ieirea din bucl. ntr-o bucl while, ieirea din bucl apare cnd expresia din instruciunea while este false sau 0. n acest moment, se ntrerupe repetarea corpului buclei. Dei condiia de terminare poate deveni valid n mijlocul corpului buclei, iteraia curent este executat pn la capt i numai dup aceea calculatorul verific din nou expresia din instruciunea while.

6.3

Implementarea buclelor folosind instruciunea while

n rezolvarea problemelor se pot ntlni dou tipuri majore de bucle: - bucla controlat de un contor; - bucla controlat de un eveniment. Dac n timpul unui antrenament sportiv vi se cere s alergai de 3 ori n jurul stadionului, este vorba de o bucl controlat de contor. Dac, n schimb, vi se cere s alergai pn cnd vei auzi sunetul fluierului, avem de a face cu o bucl controlat de un eveniment.

Bucla controlat de un contor


O astfel de bucl folosete o variabil numit variabil de control al buclei. naintea buclei ea este iniializat, adic i se atribuie o valoare iniial. Apoi, n fiecare iteraie a buclei ea trebuie incrementat. Exemplu int contorBucla = 1; //initializare while(contorBucla <= 10) //test { ... //actiune care se repeta contorBucl++; //incrementare } n acest exemplu, contorBucla este variabila de control al buclei. Ea este iniializat cu valoarea 1 nainte de intrarea n bucl. Instruciunea while testeaz expresia contorBucla <= 10 i execut bucla atta timp ct expresia este adevrat. Ultima instruciune a buclei incrementeaz variabila contorBucla. Variabilele folosite n acest fel se numesc contoare. La folosirea acestor bucle, programatorul trebuie s urmreasc iniializarea contorului naintea instruciunii while. Trebuie, de asemenea, s urmareasc dac

Programarea calculatoarelor i limbaje de programare I

n interiorul buclei valoarea lui se modific n aa fel nct la un moment dat condiia s devin fals. O bucl din care programul nu poate iei deloc se numete bucl infinit. Aceast situaie apare atunci cnd n program se omite incrementarea contorului.

Bucla controlat de un eveniment


Pentru aceast categorie de bucle condiia de terminare depinde de un eveniment care poate aprea n timpul execuiei corpului buclei. Vom studia dou tipuri de bucle controlate de evenimente: - bucla controlat de o valoare de semnalizare (valoare santinel); - bucla controlat de sfritul unui fiier (EOF). Bucla controlat de o valoare de semnalizare (valoare santinel) Aceste bucle se folosesc n special atunci cnd se prelucreaz volume mari de date. La fiecare iteraie se citete i se prelucreaz cte un set de date. Anumite valori dintre cele citite vor semnaliza ncheierea buclei while. Bucla while i continu execuia atta timp ct valorile citite nu sunt cele de semnalizare (santinel). Exemplu int luna, ziua; cin >> luna >> ziua; //citeste primul set de date while(!(luna == 2 && ziua == 31)) { ... //procesare cin >> luna >> ziua; //urmatorul set de date } Este bine ca valorile santinel s fie dintre cele care nu apar n mod obinuit ntre datele valide de intrare. nainte de intrarea n bucl este citit primul set de date. Dac nu este vorba despre valorile santinel, ele sunt procesate. La sfritul buclei se citete urmtorul set de date, revenindu-se apoi la nceputul buclei. Bucla se execut pn la citirea valorilor santinel. Acestea nu sunt prelucrate i conduc la ieirea din bucl. Exemplu Atunci cnd prelucrm date de tip char putem folosi caracterul newline ca valoare santinel: char inChar; cin.get(inChar); while(inChar != \n) { cout << inChar; cin.get(inChar); } Ce se ntmpl dac nu introducem valoare santinel? Un program interactiv ne va cere n continuu noi valori. Dac intrrile n program se fac dintr-un fiier i datele se epuizeaz naintea apariiei valorii santinel, stream-ul intr in fail state. O greeal frecvent n urma creia programul poate avea o evoluie nedorit este folosirea neintenionat a operatorului = n locul lui ==. Exemplu char inChar, valSemnal; cin >> inChar >> valSemnal; while(valSemnal = 1) 3

Programarea calculatoarelor i limbaje de programare I

//din greseala am folosit = in loc de == ... cin >> inChar >> valSemnal;

} Aceast eroare creeaz o bucl infinit. Expresia din instruciunea while este o asignare i nu o expresie logic. Calculatorul evalueaz valoarea variabilei valSemnal dup asignare. Aceasta va fi 1 i va fi interpretat ca fiind true. Astfel, expresia testat n exemplul de mai sus stocheaz valoarea 1 n valSemnal nlocuind valoarea care tocmai a fost citit. Pentru c expresia este tot timpul true, bucla nu se ntrerupe niciodat. Bucla controlat de sfritul unui fiier (EOF) Dup ce programul citete i ultimele date din fiierul de intrare, calculatorul ajunge la sfritul fiierului (EOF, end of file). n acest moment starea stream-ului este normal. Dar dac ncercm s citim o nou dat, stream-ul intr n fail state. Putem folosi acest comportament n avantajul nostru n buclele while n care se citete un numr necunoscut de valori. Starea de eroare a stream-ului poate fi interpretat ca valoare santinel pentru c numele stream-ului poate aprea ntr-o expresie logic la fel ca o variabil booleean. ntr-un astfel de test, rezultatul este true dac ultima operaie de intrare/ieire a avut succes, sau este false dac aceasta a euat. S presupunem c avem un fiier de date care conine valori ntregi. Putem scrie: int inVal; inData >> inVal; while(inVal) { cout << inVal << endl; inData >> inVal; } Dac fiierul de date conine numerele 10, 20 i 30, primele 3 citiri se vor realiza corect. Chiar i dup citirea lui 30 starea stream-ului este normal. Dac dorim s citim dup sfritul fiierului, ns, stream-ul va intra n stare de eroare. Aceasta nseamn c valoarea expresiei logice din while va fi false provocnd ieirea din bucl. Trebuie spus c orice eroare de citire conduce la intrarea stream-ului n fail state. Similar, se poate folosi i stream-ul de intrare cin. n sistemele UNIX, combinaia de taste CTRL-D, iar n sistemele DOS combinaia de taste CTRL-Z au semnificaia EOF pentru intrri interactive.

6.4
Pentru despre -

Operaii n bucl
a avea sens, o bucl trebuie s realizeze o operaie. Vom discuta contorizare; nsumare; pstrarea unei valori anterioare.

Programarea calculatoarelor i limbaje de programare I

Contorizarea
O operaie comun este memorarea numrului de iteraii executate. Programul care urmeaz citete i numr caracterele dintr-o propoziie, pn la apariia punctului. Exemplu #include <iostream> using namespace std; int main() { char inChar; int count = 0; //initializarea contorului cin.get(inChar); //citirea primului caracter while(inChar != '.') { count++; //incrementarea contorului cin.get(inChar); //citirea urmatorului caracter } cout << "Propozitia are " << count << " caractere" << endl; return 0; } Dup terminarea buclei, count va conine cu 1 mai puin dect numrul de caractere citite, adic nu numr i valoarea santinel (.). Facem o prim citire naintea buclei pentru c aceasta este controlat de un caracter de semnalizare. O variabil care numr cte iteraii se execut se numete contor de iteraii . n exemplul nostru, variabila count este un contor de iteraii.

nsumarea
O alt operaie care se poate implementa cu ajutorul buclelor este nsumarea unui set de valori. Exemplu #include <iostream> using namespace std; int main() { int numar; int suma = 0; int contor = 1; while(contor <= 5) { cin >> numar; suma = suma + numar; contor++; } cout << "Suma este " << suma << endl; return 0; }

Programarea calculatoarelor i limbaje de programare I

Iniializm suma cu 0 nainte de nceputul buclei. Atunci cnd se execut prima dat instruciunea suma = suma + numar; se adaug valoarea curent a variabilei suma la valoarea variabilei numar pentru a forma noua valoare a variabilei suma. Dup executarea buclei, variabila suma va conine suma celor 5 valori citite, contor va conine valoarea 6 i numar ultima valoare citit.

Pstrarea unei valori anterioare


Avem uneori nevoie n program de o valoare anterioar a unei variabile. S presupunem c dorim s scriem un program care contorizeaz numrul de operatori != dintr-un fiier surs C++. Va trebui s numrm de cte ori apare semnul ! urmat de =. De fiecare dat vom citi din fiierul de intrare un caracter pstrnd ultimele dou valori n dou variabile diferite. La fiecare iteraie valoarea curent devine valoare anterioar i apoi se citete o nou valoare. Bucla se termin cnd se ajunge la EOF. Exemplu #include <iostream> #include <fstream> using namespace std; int main() { int contor = 0; char carAnterior; char carCurent; ifstream inFisier; inFisier.open("main.cpp"); inFisier.get(carAnterior); inFisier.get(carCurent); while(inFisier) { if(carCurent == '=' && carAnterior == '!') contor++; carAnterior = carCurent; inFisier.get(carCurent); } cout << contor << " operator(i) != au fost gasiti in fisier" << endl; return 0;

} Contorul din acest exemplu este un contor de evenimente. El este o variabil care se incrementeaz atunci cnd apare un anumit eveniment. Este iniializat cu valoarea 0 spre deosebire de contorul de iteraii din exemplul precedent care este iniializat cu 1.

Programarea calculatoarelor i limbaje de programare I

6.5

Instruciuni while imbricate

Am studiat n capitolul anterior modul n care se pot scrie instruciunile if imbricate. Este posibil s folosim i instruciuni while imbricate. Exemplu Ne propunem s numrm cte caractere ; sunt pe fiecare linie dintr-un fiier. #include <iostream> #include <fstream> using namespace std; int main() { ifstream inFisier; inFisier.open("main.cpp"); char inChar; inFisier.get(inChar); while(inFisier) { int contorPunctVirgula = 0; while(inChar != '\n') { if(inChar == ';') contorPunctVirgula++; inFisier.get(inChar); } cout << contorPunctVirgula << endl; inFisier.get(inChar); } return 0; } S notm c am omis prima citire pentru bucla interioar. Aceasta a fost deja fcut naintea buclei exterioare. Dac o fceam, primul caracter citit s-ar fi pierdut nainte de a-l testa. ablonul sintactic al buclelor imbricate este: Iniializarea_buclei_exterioare while(condiia_buclei_exterioare) { ... Iniializarea_buclei_interioare while(condiia_buclei_interioare) { Procesarea_i_actualizarea_buclei_interioare } ... Actualizarea_buclei_exterioare } Fiecare bucl are propria iniializare, testare i actualizare. Se poate ca bucla extern s nu fac nicio procesare. Pe de alt parte, bucla interioar poate fi o mic parte a procesrii realizate de bucla exterioar. 7

7. Funcii
Am folosit deja funcii C++ atunci cnd amintrodus rutinele din bibliotecile standard, amintind de sqrt i abs. Vom analiza n detaliu modul n care programatorul poate s i scrie o funcie, alta dect main.

7.1

Funcii void

Limbajul C++ folosete dou tipuri de subprograme: funcii void i funcii care ntorc o valoare. Vom discuta despre prima categorie. Am vzut c unele funcii complexe pot fi implementate ca i colecii de module, multe dintre acestea putnd fi transpuse sub forma unor funcii void.

Scrierea modulelor ca funcii void


n principiu, o astfel de funcie arat ca funcia main, cu deosebirea c headerul su folosete cuvntul cheie void n locul lui int. n plus, o funcie void nu conine nicio instruciune de tipul return 0; aa cum se ntmpl n cazul lui main, deci nu ntoarce nicio valoare ctre apelant. Vom studia un program simplu care folosete funcii void. Acest program tiprete textul ************* ************* Welcome Home! ************* ************* ************* ************* Iat cum putem schia un program care s tipreasc acest mesaj: Nivelul 0 main Tiprete dou linii de asteriscuri Tiprete textul Welcome Home! Tiprete patru linii de asteriscuri Nivelul 1 Tipareste2Linii Tiprete textul ************* Tiprete textul ************* Tipareste4Linii Tiprete textul ************* Tiprete textul ************* Tiprete textul ************* Tiprete textul ************* Dac modulele de la nivelul 1 sunt scrise ca funcii void, atunci programul se va scrie astfel: #include <iostream> using namespace std;

Programarea calculatoarelor i limbaje de programare I

void Tipareste2Linii();//prototip de functie void Tipareste4Linii();//prototip de functie int main() { Tipareste2Linii();//apel de functie cout << "Welcome Home!" << endl; Tipareste4Linii();//apel de functie return 0; } void Tipareste2Linii() //Aceasta functie tipareste doua linii de asteriscuri { cout << "*************" << endl; cout << "*************" << endl; } void Tipareste4Linii() //Aceasta functie tipareste patru linii de asteriscuri { cout << "*************" << endl; cout << "*************" << endl; cout << "*************" << endl; cout << "*************" << endl; } Se observ similaritatea dintre funcia main i descrierea de la nivelul 0. Cele dou funcii care fac parte din corpul funciei main sunt fara parametri. Fiecare dintre definiiile acestor funcii este format din header-ul funciei urmat de un bloc de instruciuni care alctuiesc corpul funciei. Header-ul unei funcii void ncepe cu cuvntul cheie void care semnaleaz c nu ntoarce nicio valoare. Implicit, n corpul unei astfel de funcii nu va aprea nicio instruciune return urmat de o valoare. Se poate folosi, totui, instruciunea return; care ncheie execuia unei funcii void. Definiiile funciilor pot aprea n orice ordine, deci main ar fi putut aprea dup cele dou funcii. Cele dou declaraii dinaintea funciei main se numesc prototipuri de funcii. Ele sunt necesare pentru c regulile din C++ impun declararea unui identificator naintea folosirii sale. Cei doi identificatori sunt Tipareste2Linii i Tipareste4Linii folosii n main.

7.2

Sintaxa i semantica funciilor void

Apelul funciilor (invocarea)


Un apel de funcie ntr-un program nseamn execuia corpului funciei apelate. ablonul sintactic apelului unei funcii este urmtorul: NumeFuncie(ListParametriActuali); Parametrii dintr-un apel de funcie se numesc parametri actuali. Parametrii care apar n header-ul funciei se numesc parametri formali. Conform sintaxei C++, lista

Programarea calculatoarelor i limbaje de programare I

de parametri poate s fie vid. Lista de parametri actuali din ablonul sintactic al apelului de funcie este subliniat pentru ca poate s lipseasc. Dac lista conine doi sau mai muli parametri, acetia trebuie separai prin virgul: Expresie, Expresie ... n momentul apelului unei funcii, parametrii actuali sunt transmii parametrilor formali conform poziiei lor, de la stnga la dreapta, iar apoi controlul este transferat primei instruciuni din corpul funciei. Cnd se ncheie i execuia ultimei instruciuni din funcie, controlul este transmis punctului n care s-a fcut apelul.

Declaraii i definiii de funcii


Pentru c n C++ orice identificator trebuie declarat nainte de a fi folosit, i funciile trebuie s respecte aceast regul. O declaraie de funcie anun compilatorul despre numele funciei, tipul de dat al valorii returnate (poate fi i void) i tipurile datelor folosite n lista de parametri. Programul din exemplul de mai sus conine dou declaraii de funcii care nu sunt nsoite de corpul funciilor. Acestea sunt prototipuri de funcii. Dac declaraiile sunt nsoite i de corpul funciei, atunci este vorba despre definiii de funcii. Toate definiiile sunt declaraii, dar nu toate declaraiile sunt definiii. Prototipuri de funcii Pentru a satisface cerina ca orice identificator s fie declarat nainte de a fi folosit, programatorii C++ plaseaz prototipurile funciilor chiar naintea definiiei funciei main, la nceputul programului. Prototipul de funcie de mai numete n unele limbaje i declaraie forward. Pentru o funcie void, ablonul sintactic al unei declaraii este: void NumeFuncie(ListParametriFormali); Prototipul nu este nsoit de corpul funciei, lista de parametri formali este opional i declaraia se ncheie cu ; Lista parametrilor formali este opional i are urmtoarea form: TipData & NumeVariabil, TipDat & NumeVariabil ... Ampersantul & este ataat tipului de dat i este opional. ntr-un prototip de funcie, lista parametrilor formali trebuie s specifice tipurile de dat ale parametrilor, dar numele lor poate s lipseasc. Exemplu void Traiectorie(int, double); sau void Traiectorie(int viteza, double unghi); Numele parametrilor sunt utili pentru explicitarea funciei, dar compilatorul le ignor. Definiii de funcii Aa cum am vzut deja, definiia unei funcii const din header-ul funciei i corpul funciei care este, de fapt, un bloc. ablonul sintactic al unei definiii de funcie este: void NumeFuncie(ListParametriFormali) { Instruciune ... } Header-ul funciei nu se ncheie cu ; ca la prototipurile de funcii. Sintaxa listei de parametri din definiie difer de cea folosit n prototip, n sensul c aici trebuie specificate numele tuturor parametrilor formali. 3

Programarea calculatoarelor i limbaje de programare I

TipData & NumeVariabil, TipDat & NumeVariabil ...

7.3

Variabile locale

Deoarece corpul unei funcii este un bloc, orice funcie poate include declaraii de variabile n interiorul ei. Aceste variabile se numesc variabile locale pentru c sunt accesibile doar n interiorul blocului n care sunt declarate. n contrast cu variabilele locale sunt variabilele declarate n afara tuturor funciilor i accesibile lor i se numesc variabile globale. Variabilele locale ocup spaiu de memorie doar pe timpul execuiei funciei. Cnd funcia i ncheie execuia, variabilele locale sunt terse din memoria calculatorului. Acesta este motivul pentru care la fiecare apel al unei funcii variabiilele locale pornesc cu valori nedefinite, deci trebuie iniializate n interiorul funciei. Fiind terse din memorie dup ncheierea apelului, valorile variabilelor locale nu se pstreaz ntre dou apeluri de funcii.

7.4

Parametri

Atunci cnd se execut o funcie, ea folosete parametrii actuali care i-au fost transmii prin apelul su, innd, ns, cont de natura parametrilor formali. Limbajul C++ accept dou tipuri de parametri formali: parametri valoare i parametri referin. Parametrii referin sunt cei pentru care tipul de dat este nsoit, n lista parametrilor formali, de semnul &. Funcia primete adresa de memorie a parametrului actual. Din declaraiile parametrilor valoare lipsete semnul &, iar funcia primete o copie a valorii parametrului actual. Tip de parametru Folosire Parametru actual Apare n apelul funciei. Parametrii corespunztori pot fi att valoare ct i referin Parametru valoare formal Apare n header-ul funciei. Primete o copie a valorii pstrate n parametrul actual corespunztor Parametru referin formal Apare n header-ul funciei. Primete adresa parametrului actual corespunztor Numrul parametrilor actuali din apelul unei funcii trebuie s fie egal cu numrul parametrilor formali din header-ul funciei. De asemenea, tipurile datelor trebuie s corespund. Exemplu void ShowMatch(double num1, int num2, char letter); Header: ShowMatch(varDouble, varInt, varChar); Apel: Dac tipurile de dat nu se potrivesc, compilatorul ncearc s aplice operaiile de cast. Pentru a evita aceste conversii implicite de tip, se pot aplica i conversii explicite.

Parametrii valoare
Deoarece parametrii valoare primesc copii ale parametrilor actuali, se pot folosi constante, variabile i expresii n apelul unei funcii. Cnd funcia se ncheie, coninutul parametrilor valoare este ters, la fel cum se ntmpl n cazul variabilelor locale. Diferena dintre parametrii valoare i variabilelel

Programarea calculatoarelor i limbaje de programare I

locale este c cele din urm sunt nedefinite la startul funciei, n timp ce primele sunt iniializate automat cu valorile corespunztoare ale parametrilor actuali.

Parametrii referin
Parametrii valoare nu pot fi folosii pentru a transmite informaie ctre codul apelant pentru c valorile lor se pierd la finalul execuiei funciei. Pentru aceasta se folosesc parametrii referin. Prin intermediul parametrilor referin funciei i se permite s modifice valoarea parametrului actual. Dac parametrul unei funcii este de tip referin, se transmite ctre funcie locaia (adresa de memorie) i nu valoarea parametrului. Exist, astfel, o singur copie a informaiei, folosit att de apelant ct i de funcia apelat. Numele parametrului actual i cel al parametrului formal devin sinonime pentru aceeai variabil. Ceea ce va lsa funcia n acea locaie va fi regsit de funcia apelant. Spre deosebire de cazul parametrilor valoare, ctre parametrii referin pot fi transmise doar nume de variabile, nu i de constante sau expresii. Exemplu S presupunem c variabila y este de tip double si variabila i este de tip int Header: void Functie2(double val, int& contor); Apeluri corecte: Functie2(y, i); Functie2(9.81, i); Functie2(4.9*sqrt(y), i); Apel incorect: Functie2(y, 3); O alt diferen important ntre parametrii valoare i cei referin apare la verificarea potrivirii tipurilor de dat ntre parametrii actuali i cei formali. Dac n primul caz se realizeaz conversii implicite, pentru valorile referin lucrurile sunt diferite. De aceast dat compilatorul copiaz valoarea parametrului actual ntr-o variabil temporar de tip corect i transmite aceast variabil temporar parametrului formal. Cnd funcia se ncheie, variabila temporar este tears din memorie i modificrile din funcie nu se mai reflect n codul apelant. Exemplu #include <iostream> using namespace std; int PatratPrinValoare(int); void PatratPrinReferinta(int&); int main() { int x = 2; int z = 4; cout << "x= " << x << " inainte de PatratPrinValoare\n" << "Valoarea returnata de PatratPrinValoare: " << PatratPrinValoare(x) << endl << "x= " << x << " dupa PatratPrinValoare\n" << endl; cout << "z= " << z << " inainte de PatratPrinReferinta" << endl; PatratPrinReferinta(z); cout << "z= " << z << " dupa de PatratPrinReferinta" << endl;

Programarea calculatoarelor i limbaje de programare I

return 0; } int PatratPrinValoare(int a) { a = a * a; return a; } void PatratPrinReferinta(int& b) { b = b * b; } Parametrul referin este un nume alternativ pentru argumentul corespondent. Apelul funciilor care au parametri valoare este asemntor celui pentru funcii cu parametri referin atunci cnd parametrii sunt variabile. n ambele situaii se specific doar numele variabilei, ns compilatorul stabilete pe baza tipurilor parametrilor formali care dintre cele dou mecanisme de transmitere a parametrilor va fi invocat.

7.5

Funcii recursive

Programele pe care le-am scris pn acum sunt structurate sub forma unor funcii care apeleaz alte funcii ntr-o manier ierarhic. n unele aplicaii, este util ca funciile s se poat apela ele nsele. O funcie recursiv este o funcie care se autoapeleaz. Rezolvarea problemelor prin recursie presupune scrierea unei funcii care este apelat recursiv. Aceast funcie rezolv doar cazul cel mai simplu, numit i cazul de baz. Dac funcia este apelat pentru cazul de baz, ea returneaz un simplu rezultat. Dac este apelat pentru un caz mai complex, ea divide problema n dou module conceptuale: o parte pe care funcia poate s o rezolve i o alt parte pe care nu poate s o rezolve. Pentru ca recursia s fie fezabil, partea care nu poate fi rezolvat imediat trebuie s fie asemntoare problemei originale, dar s fie un caz mai simplu al acesteia. Aceast nou problem este o variant a celei originale, iar funcia va lansa o nou copie a sa pentru a o trata. Este vorba, deci, de un apel recursiv sau de un pas de recursie. Pasul de recursie trebuie s includ i o instruciune return pentru c rezultatul su va fi combinat cu partea pe care funcia poate s o rezolve pentru a forma rezultatul final care este transmis codului apelant. Pasul de recursie se execut n timp ce apelul original este nc activ, acesta nefiind finalizat. Un apel recursiv poate s genereze la rndul su alte apeluri recursive pe msur ce noua problem se divide la rndul su n dou alte noi probleme. Pentru ca recursia s aib finalitate, de fiecare dat funciile se apeleaz pe ele nsele cu versiuni din ce n ce mai simple ale problemei originale, aceast secven trebuind s fie conceput n aa fel nct s convearg ctre cazul de baz. La acest punct, funcia recunoate cazul de baz i se declaneaz o serie de return-uri n secven invers apelurilor, pn la apelul original. Factorialului unui numr ntreg nenegativ n, notat prin n! este dat de produsul n(n-1) (n-2) ...1 cu 0!=1. Aceast valoare se poate calcula iterativ (nerecursiv) folosind o bucl while: int factorial = 1;

Programarea calculatoarelor i limbaje de programare I

int counter = n; while(counter >= 1) { factorial = factorial * counter; counter--; } O definiie recursiv a acestei operaii este dat prin relaia n! = n (n-1)! Pentru 5! putem scrie: 5! = 5 4 3 2 1 5! = 5 (4 3 2 1) 5! = 5 (4!) Apelurile recursive i valorile returnate de fiecare dintre ele sunt urmtoarele: Valoarea final 120 5! 5! Returneaz 5!=120 5 * 4! 5 * 4! Returneaz 4!=24 4 * 3! 4 * 3! Returneaz 3!=6 3 * 2! 3 * 2! Returneaz 2!=2 2 * 1! 2 * 1! Returneaz 1 1 1

Programul urmtor calculeaz factorialul unui numr n citit de la tastatur: #include <iostream> #include <iomanip> using namespace std; int Factorial(int); int main() { int n; cout << "Introduceti numarul pentru care se calculeaza factorialul: "; cin >> n; cout << n << "! = " << Factorial(n) << endl; return 0; }

Programarea calculatoarelor i limbaje de programare I

int Factorial(int val) { if(val <= 1) //cazul de baza return 1; else return val * Factorial(val - 1); }

Recursie i iteraie
Vom compara caracteristicile recursivitii i ale iteraiei. Ambele tehnici se bazeaz pe cte o structur de control. Iteraia folosete o structur repetitiv, iar recursivitatea o structur de selecie. Recursivitatea implementeaz repetiia prin apelurile repetate ale aceleiai funcii. n ambele cazuri este nevoie de un test de terminare. Iteraia se termin atunci cnd testul buclei devine fals, n timp ce recursia se termin atunci cnd se ajunge la cazul de baz. Iteraia modific un contor care controleaz o bucl. Recursivitatea produce versiuni ale problemei originale pn cnd se ajunge la cazul de baz. Att iteraia ct i recursia pot degenera n bucle infinite dac testul de final nu este scris corect. Recursia are, ns, dezavantajul c invoc n mod repetat mecanismul de apel al funciilor care presupune ocuparea suplimentar a spaiului de memorie i timp de procesor pentru ca la fiecare apel se creeaz un nou set al parametrilor formali. Recursivitatea este, totui, un mecanism important din punct de vedere al ingineriei software pentru c permite structurarea ntr-o maniera mult mai judicioas a anumitor secvene de program. Atunci cnd suntem pui n situaia de a alege ntre cele dou tehnici, va trebui, aadar, s gsim echilibrul ntre performanele aplicaiei i dorina de a scrie un program corect din punct de vedere conceptual.

8. Tablouri
Tablourile (arrays) reprezint un tip important de structur de date i sunt colecii de obiecte de acelai tip reunite sub un singur nume. Uneori este necesar s referim anumite variabile ca un singur grup pentru c este dificil s definim i s folosim individual fiecare variabil. De exemplu, dac dorim s tiprim un set de date n ordine invers celei n care au fost introduse, trebuie s le citim i s le salvm pe toate nainte de a putea face prima tiprire. Dac avem de a face cu 1000 de valori, trebuie s declarm 1000 de variabile ca s le stocm, s scriem 1000 de instruciuni de citire i 1000 de instruciuni de tiprire. Tabloul este un tip de dat care ne permite s programm mai uor operaii asupra grupurilor de valori de acelai tip.

8.1

Tipuri de dat simple i tipuri de dat structurate

O valoare creia i este asociat un tip simplu de dat este un element singular care nu poate fi mprit mai departe n pri componente. De exemplu, o valoare de tip int este un numr ntreg i nu mai poate fi descompus. n contrast cu acesta, o valoare avnd un tip de dat structurat este o colecie de elemente. ntreaga colecie este referit printr-un singur nume i fiecare component poate fi accesat individual. Un tip de dat structurat este ifstream pe care l-am folosit pentru citirea valorilor dintr-un fiier. Atunci cnd declarm inFile de tip ifstream, inFile nu reprezint o singur valoare. El reprezint ntreaga colecie de date din fiier. Fiecare coomponent, ns, poate fi accesat individual printr-o operaie de intrare. Tipurile simple de date sunt elemente componente pentru tipurile structurate. Un tip de dat structurat pune mpreun mulimea de valori componente i impune un aranjament specific asupra valorilor. Aa cum am vzut deja n clasificarea pe care am fcut-o deja, C++ ofer mai multe tipuri de dat structurate: Tipuri structurate tablou (array) struct union class n acest capitol vom discuta despre tablouri.

8.2

Tablouri unidimensionale

S relum exemplul de citire i afiare n ordine invers a 1000 de valori. O variant de implementare a sa este: #include <iostream> using namespace std; int main() { int val0; int val1; ... int val999; cin >> val0;

Programarea calculatoarelor i limbaje de programare I

cin >> val1; ... cin >> val999; cout << val999 << endl; ... cout << val1 << endl; cout << val0 << endl; return 0; } Programul are peste 3000 de linii i folosete 1000 de variabile cu nume asemntoare. Ar fi mult mai comod dac numrul din componena numelor variabilelor ar fi, de fapt, un contor pe care s l putem folosi pentru a citi i scrie bucle while n care contorul s ia valori ntre 0 i 999. #include <iostream> using namespace std; int main() { int val[1000]; //declararea tabloului int contor = 0; while(contor < 1000) { cin >> val[contor]; contor++; } contor = 999; while(contor >= 0) { cout << val[contor] << endl; contor--; } return 0; } n acest program am declarat val ca fiind un tablou unidimensional, adic o colecie de variabile, toate de acelai tip, n care prima parte a numelui variabilelor este acelai, iar ultima parte este un indice cuprins ntre []. n acest exemplu, valoarea stocat n contor se numete indice. Declararea tabloului este similar cu declaraia unei variabile simple, cu o singur excepie: trebuie declarat i dimensiunea tabloului. Numrul de componente se declar ntre []. Declaraia int val[1000]; creeaz un tablou cu 1000 de componente, toate de tip int. Prima are indicele 0, a doua are indicele 1, iar ultima are indicele 999.

Declararea tablourilor
Un tablou unidimensional este o colecie structurat de componente (elemente) care pot fi fi accesate individual specificnd poziia componentei printr-un indice o constant ntreag. ablonul sintactic al unei declaraii de tablou unidimensional este urmtorul: TipDat NumeTablou[ExpresieConstInt];

Programarea calculatoarelor i limbaje de programare I

Componentele unui tablou pot avea aproape orice tip de dat, dar ne vom limita n acest capitol doar la tipurile atomice. Expresia dintre [] este o constant ntreag. Poate fi un literal sau o constant simbolic. Ea trebuie s fie strict mai mare dect 0 i definete numrul de componente ale tabloului. Dac valoarea este n, domeniul indicilor va fi ntre 0 i n-1, nu ntre 1 i n. Exemplu int val[4]; Prin aceast declaraie, compilatorul rezerv ntr-o zon compact de memorie 4 locaii de tip int: val val[0] val[1] val[2] val[3]

Accesarea componentelor individuale


Pentru a folosi componentele individuale scriem numele tabloului urmat de o expresie indice ntre []. Expresia specific numrul componentei accesate i poate fi att constant ct i variabil ori o expresie mai complicat. Oricare ar fi, ns, forma indicelui, acesta trebuie s fie o valoare ntreag. Cea mai simpl form a expresiei indice este o constant. Exemplu int val[4]; val val[0] = -2; val[0] -2 val[1] = 4; val[1] 4 val[2] = 18; val[2] 18 val[3] = -199; val[3] -199 Spre deosebire de declaraii, expresiile indice pot fi i variabile sau expresii ntregi. Exemplu #include <iostream> #include <iomanip> using namespace std; int main() { int i, n[10]; //declararea tabloului i = 0; while(i < 10) { n[i] = 0; //initializarea elementelor taboului i++; } cout << "Element" << setw(13) << "Valoare" << endl; i = 0; while(i < 10) { cout << setw(7) << i << setw(13) << n[i] << endl; 3

Programarea calculatoarelor i limbaje de programare I

i++; } return 0; } Dac i este 0, atunci se folosete n[0], cnd i este 1, atunci se folosete n[1] etc. Fiecare component a unui tablou poate fi tratat exact ca o variabil simpl. Exemplu int val[4]; val[0] = -2; cin >> val[2]; cout << val[1]; double x = sqrt(val[2]); double y = 6.8 * val[2] + 7.5;

Indicii din afara limitelor


S considerm declaraia double alfa[100]; pentru care domeniul valid al indicilor este 0 99. Ce se ntmpl dac executm instruciunea alfa[i] = 62.4; cnd i < 0 sau i > 99 ? Rezultatul este c se acceseaz locaii de memorie din afara tabloului. C++ nu verific ncadrarea indicilor ntre limite i aceasta este rspunderea programatorului. Algoritmii de procesare a tablourilor folosesc adeseori bucle pentru a parcurge elementele. Exemplu int i = 0; while(i < 10) { alfa[i] = 0.0; i++; } Aceeai bucl se poate scrie i ntr-o a doua variant n care variabila de control se compar cu limita superioar a domeniului indicilor. Exemplu int i = 0; while(i <= 9) { alfa[i] = 0.0; i++; } Prima variant este preferat pentru c valoarea cu care se compar variabila de control este aceeai cu dimensiunea tabloului, fiind mai sugestiv.

Iniializarea tablourilor
n limbajul C++ este permis iniializarea unei variabile odat cu declararea acesteia. Exemplu 4

Programarea calculatoarelor i limbaje de programare I

int i = 0; Elementele unui tablou pot fi, de asemenea, iniializate n instruciunea de declarare prin adugarea unei liste de valori separate prin virgul, plasate ntre acolade. Exemplu #include <iostream> #include <iomanip> using namespace std; int main() { int n[10] = {32, 2, 64, 18, 95, 14, 90, 70, -60, 37}; cout << "Element" << setw(13) << "Valoare" << endl; int i = 0; while(i < 10) { cout << setw(7) << i << setw(13) << n[i] << endl; i++; } return 0; } n acest fel, n[0] este iniializat cu valoarea 32, n[1] cu 2 etc. ntre acolade trebuie s se gseasc cel puin o valoare. Dac se nscriu prea multe valori, compilatorul semnaleaz o eroare. Dac sunt mai puine valori n list, restul sunt iniializate cu valoarea 0. O facilitate a limbajului C++ este aceea prin care se permite omiterea dimensiunii tabloului atunci cnd declaraia i iniializarea se fac n aceeai instruciune. Exemplu int n[] = {32, 2, 64, 18, 95, 14, 90, 70, -60, 37}; Compilatorul stabilete dimensiunea tabloului la numrul de elemente dintre acolade. Pentru declararea dimensiunii unui tablou se pot folosi i constante simbolice. Programul din exemplul urmtor va citi numerele dintr-un tablou de valori ntregi i va afia sub form grafic segmente ale cror lungimi sunt proporionale cu aceste valori. Acest grafic se numete histogram. Exemplu #include <iostream> #include <iomanip> using namespace std; int main() { const int dim = 5; int val[dim] = {19, 3, 15, 7, 11}; cout << "Element" << setw(13) << "Valoare" << setw(17) << "Histograma" << endl; 5

Programarea calculatoarelor i limbaje de programare I

int i = 0; while(i < dim) { cout << setw(7) << i << setw(13) << val[i] << setw(9); int j = 0; while(j < val[i]) { cout << '*'; j++; } cout << endl; i++; } return 0; }

Tabourile cu valori tip char


Tablourile pot avea orice tip de dat. Vom discuta acum despre stocarea irurilor de caractere (string-uri) n tablouri de tip char. Am vzut c pentru a afia un string pe ecran putem folosi o instruciune de tiprire prin care textul este inserat n stream-ul de ieire. Exemplu cout << caractere; Un string precum caractere este, de fapt, un tablou de valori de tip char. Aceste tablouri au cteva caracteristici speciale care le difereniaz de celelalte tipuri de tablouri. Un tablou de caractere poate fi iniializat printr-un string literal. Exemplu char clasament[] = primul; Prin aceast declaraie, elementele tabloului clasament sunt iniializate cu valorile caracterelor individuale din stringul literal primul. Dimensiunea tabloului clasament este determinata de compilator prin lungimea sirului la care se adaug automat un caracter special numit caracterul null care se numete terminator de ir. Astfel, tabloul clasament va avea ase elemente. Caracterul null are codul ASCII 0 iar reprezentarea sa ca i constant literal este \0. Toate string-urile se termin cu acest caracter. Un tablou de caractere care reprezint un string trebuie declarat ntotdeauna suficient de mare ca s cuprind caracterele irului i terminatorul de ir. Tablourile de caractere pot fi iniializate i prin constante individuale printr-o list de iniializare. Exemplu char clasament[] = {p, r, i, m, u, l, \0}; Putem accesa componentele individuale ale unui tablou de caractere prin folosirea indicilor. n felul acesta, clasament[0] este caracterul p, clasament[1] este caracterul r etc.

Programarea calculatoarelor i limbaje de programare I

Putem introduce valori de la tastatur direct ntr-un tablou de tip char folosind cin i >>. Exemplu char sir[20]; cin >> sir; Prin prima instruciune, tabloul sir este declarat ca un ir de caractere care poate s stocheze 19 caractere i terminatorul de ir. A doua instruciune citete un string de la tastatur adugndu-i automat caracterul null. Este rspunderea programatorului s stabileasc dimensiunea tabloului n care se vor pstra aceste caractere. Manipulatorul setw poate fi folosit n acest caz pentru a ne asigura c numrul de caractere citite din stream-ul cin nu va depi dimensiunea tabloului n care sunt transferate acestea. Exemplu Vor fi citite 19 caractere de la tastatur dup care se adaug automat caracterul null. cin >> setw(20) >> sir;

8.3

Transmiterea tablourilor ca parametri de funcii

Dac dorim s transmitem o variabil ca parametru unei funcii i dorim ca funcia s nu poat s i modifice valoarea atunci variabila trebuie transmis prin valoare i nu prin referin. De la aceast regul fac excepie stream-urile. Tot o excepie o reprezint i tablourile pentru c n C++ acestea sunt transmise ntotdeauna prin referin. Pentru ca o variabil simpl sau un obiect s fie transmis prin referin trebuie ataat semnul & tipului de dat din lista de parametri formali. Fiind transmise ntotdeauna prin referin, pentru declararea tablourilor nu se folosete &. Cnd un tablou este transmis ca parametru, i se transmite de fapt adresa de baz care este adresa de memorie a primului su element. n acest fel funcia l va putea localiza n memoria calculatorului i i va putea accesa elementele. Exemplu void ModificaTablou(int b[], int dimensiune) { int j = 0; while(j < dimensiune) { b[j] *= 2; j++; } } n acest exemplu am folosit operatorul *= care este un operator abreviat. n C++ se pot folosi urmtorii operatori aritmetici de asignare: Operator Exemplu Explicaie += a += 7 a = a + 7 -= b -= 6 b = b 6 *= c *= 5 c = c * 5 /= d /= 4 d = d / 4 %= e %= 3 e = e % 3

Programarea calculatoarelor i limbaje de programare I

Operatorul %= se poate aplica doar variabilelor ntregi. n lista parametrilor formali, declararea unui tablou nu include i dimensiunea sa ntre []. Dac se include dimensiunea, compilatorul o ignor. Compilatorului i este necesar doar informaia referitoare la natura parametrului, adic faptul c este vorba despre un tablou, i la tipul componentelor sale. Acesta este motivul pentru care trebuie adugat un al doilea parametru al funciei prin care se precizeaz numrul de componente. n prototipul unei funcii care are parametri de tip tablou nu este necesar prezena numelor parametrilor formali. De fapt aceast regul este valabil pentru orice funcie, indiferent de tipul parametrilor si. Exemplu void ModificaTablou(int [], int); Programul urmtor ilustreaz diferena ntre trimiterea unui tablou i a unui element al unui tablou ca parametri de funcii. Exemplu #include <iostream> #include <iomanip> using namespace std; void ModificaTablou(int [], int); void ModificaElement(int); int main() { const int dimTablou = 5; int i, a[dimTablou] = {0, 1, 2, 3, 4}; cout << "Efectele transmiterii unui tablou " << "prin referinta:" << "\n\nValorile tabloului original:\n"; i = 0; while(i < dimTablou) { cout << setw(3) << a[i]; i++; } cout << endl; //Tablou transmis prin referinta ModificaTablou(a, dimTablou); cout << "Valorile modificate sunt:\n"; i = 0; while(i < dimTablou) { cout << setw(3) << a[i]; i++; } 8

Programarea calculatoarelor i limbaje de programare I

cout << << << << <<

"\n\n\n" "Efectele transmiterii unui element " "al tabloului prin valoare:" "\n\nValoarea lui a[3] este " a[3] << '\n';

ModificaElement(a[3]); cout << "Valoarea lui a[3] este " << a[3] << endl; return 0; } //In functia ModificaTablou, "b" este un alt nume //pentru tabloul original "a" void ModificaTablou(int b[], int dimensiune) { int j = 0; while(j < dimensiune) { b[j] *= 2; j++; } } //In functia ModificaElement, "e" este o copie a //elementului a[3] transmis prin valoare void ModificaElement(int e) { cout << "Valoarea in functia ModificaElement este " << (e *= 2) << endl; } Programul tiprete pe ecran urmtoarele rezultate: Efectele transmiterii unui tablou prin referinta: Valorile tabloului original: 0 1 2 3 4 Valorile modificate sunt: 0 2 4 6 8

Efectele transmiterii unui element al tabloului prin valoare: Valoarea lui a[3] este 6 Valoarea in functia ModificaElement este 12 Valoarea lui a[3] este 6

Programarea calculatoarelor i limbaje de programare I

Atunci cnd se apeleaz funcia ModificaTablou, i se transmite funciei o copie a adresei de memorie a tabloului a, iar modificrile asupra tabloului b vor fi de fapt modificri ale tabloului a. Funcia ModificaElement are un parametru de tip valoare, o modificare asupra lui e neavnd niciun efect asupra parametrului actual a[3]. Pot aprea situaii n programele noastre cnd o funcie nu trebuie s poat modifica elemente ale unui tablou care i este transmis ca parametru. Deoarece tablourile sunt transmise ntotdeauna prin referin, modificarea valorilor dintr-un tablou este dificil de controlat. Limbajul C++ ofer mecanismul implementat prin cuvntul cheie const care se poate folosi pentru a mpiedica modificarea valorilor elementelor unui tablou printr-o funcie. Dac parametrul tablou al unei funcii este de tip const, elementele sale devin constante n interiorul funciei i orice intenie de modificare a valorilor lor este interpretat de compilator ca o eroare de sintax. Exemplu #include <iostream> using namespace std; void IncearcaSaModificeTablou(const int []); int main() { int a[] = {10, 20, 30}; IncearcaSaModificeTablou(a); cout << a[0] << ' ' << a[1] << ' ' << a[2] << endl; return 0; } void IncearcaSaModificeTablou(const int b[]) { b[0] /= 2; //eroare b[1] /= 2; //eroare b[2] /= 2; //eroare } Compilatorul semnaleaz eroare pentru c b[0], b[1] i b[2] sunt nemodificabile.

8.4

Tablouri multidimensionale

Tabourile n C++ pot avea mai multe dimensiuni. O modalitate comun de a le folosi este prin matrici cu linii i coloane, fiind vorba n acest caz despre tablouri cu dou dimensiuni. Pentru a identifica un element al unei astfel de matrici trebuie s specificm doi indici: primul reprezint linia iar al doilea reprezint coloana. Tablourile multidimensionale pot avea i mai mult de dou dimensiuni. Tablourile multidimensionale pot fi iniializate odat cu declararea lor n mod asemntor cu iniializarea tablourilor unidimensionale. De exemplu, un tablou bidimensional b[2][2] poate fi declarat i iniializat prin instruciunea int b[2][2] = { {1,2}, {3,4} }; Valorile sunt grupate pe linii, ntre acolade. Astfel, 1 i 2 sunt valorile iniiale pentru b[0][0] i b[0][1], iar 3 i 4 sunt valorile iniiale pentru b[1][0] i b[1][1].

10

Programarea calculatoarelor i limbaje de programare I

Dac nu sunt suficiente valori pentru o linie, elementele care rmn sunt iniializate cu valoarea 0. n felul acesta, declaraia int b[2][2] = { {1}, {3,4} }; iniializeaz b[0][0] cu 1, b[0][1] cu 0, b[1][0] cu 3 i b[1][1] cu 4. Programul urmtor demonstreaz folosirea matricilor multidimensionale. Exemplu ##include <iostream> using namespace std; void TiparesteTablou(int [][3]); int main() { int array1[2][3] = {{1,2,3}, {4,5,6}}; int array2[2][3] = {1,2,3,4,5}; int array3[2][3] = {{1,2}, {4}}; cout << "Valorile din array1 pe randuri sunt:" << endl; TiparesteTablou(array1); cout << "Valorile din array2 pe randuri sunt:" << endl; TiparesteTablou(array2); cout << "Valorile din array3 pe randuri sunt:" << endl; TiparesteTablou(array3); return 0; } void TiparesteTablou(int a[][3]) { int i = 0; while(i < 2) { int j = 0; while(j < 3) { cout << a[i][j] << ' '; j++; } cout << endl; i++; } } Programul apeleaz funcia TiparesteTablou pentru a tipri coninutul fiecruia dintre cele trei tablouri multidimensionale. Atunci cnd se transmit tablouri unidimensionale ca parametri de funcii, indicele nu trebuie precizat. n cazul tablourilor multidimensionale, se ignor doar valoarea primului indice, urmtorii trebuind s aib valori. Compilatorul folosete aceste dimensiuni pentru a determina poziia n memorie a elementelor tabloului multidimensional. Toate elementele unui astfel de tablou sunt aezate fizic n locaii consecutive, mai nti cele de pe primul 11

Programarea calculatoarelor i limbaje de programare I

rnd urmate imediat de elementele de pe al doilea rnd etc. Aceast poziionare n memorie este ilustrat i de modul n care sunt iniializate componentele tabloului array2 din exemplul anterior. Valorile sunt asignate elementelor de pe primul rnd, apoi celor de pe al doilea rnd, iar array2[1][2] primete valoarea 0.

12

9. Alte structuri de control


n capitolele precedente am vzut care instruciuni C++ implementeaz secvenele, seleciile, buclele i subprogramele. n unele cazuri am introdus mai multe metode de a implementa o structur, ca n cazul instruciunii IF-THEN sau IF-THEN-ELSE. n acest capitol, vom introduce cinci noi instruciuni utile n programare. Prima, switch, face mai uoar scrierea structurilor de selecie care au mai multe ramuri. Urmtoarele dou instruciuni, for i do-while faciliteaz implementarea anumitor tipuri de bucle. Ultimele dou instruciuni, break i continue, sunt folosite de asemenea n bucle i instruciuni de selecie.

9.1

Instruciunea switch

Aceasta este o instruciune pentru implementarea structurilor de control cu ramificare multipl. Valoarea expresiei switch determin ramura care se execut. Instruciunea switch const dintr-o serie de etichete case i o variant default. Programul de mai jos folosete instruciunea switch pentru a contoriza numrul de calificative din categoriile foarte bine, bine, satisfctor i insuficient pe care le-a primit un elev de-a lungul unui an colar. Vom codifica aceste calificative prin literele F foarte bine, B bine, S satisfctor i I insuficient. #include <iostream> using namespace std; int main() { char calificativ; int nrF = 0,//numarul nrB = 0,//numarul nrS = 0,//numarul nrI = 0;//numarul cout << << << <<

calificativelor calificativelor calificativelor calificativelor

F B S I

foarte bine bine satisfacator insuficient

"Introduceti litera corespunzatoare" "calificativului." << endl "Tastati caracterul EOF pentru a incheia." endl;

while((calificativ = cin.get()) != EOF) { switch(calificativ) { case 'F': case 'f': ++nrF; break; case 'B': case 'b': ++nrB; break;

Programarea calculatoarelor i limbaje de programare I

case 'S': case 's': ++nrS; break; case 'I': case 'i': ++nrI; break; case '\n': case '\t': case ' ': break; default://orice alt caracter cout << "Ati introdus o litera incorecta." << " Introduceti un nou calificativ." << endl; } } cout << << << << << <<

"\n\nTotalurile pentru fiecare calificativ" " sunt:" "\nFoarte bine: " << nrF "\nBine: " << nrB "\nSatisfacator: " << nrS "\nInsuficient: " << nrI << endl;

return 0; } n acest program, utilizatorului i se cere s introduc literele asociate calificativelor. Bucla while care implementeaz operaia de citire i prelucrare a datelor este scris astfel: while((calificativ = cin.get()) != EOF) { ... } Testul buclei const dintr-o asignare urmat de o comparaie. Asignarea (calificativ = cin.get()) dintre parantezele rotunde este executat prima. Funcia cin.get() citete un caracter din stream-ul de intrare. Codul ASCII al acestui caracter este, apoi, asignat variabilei calificativ. Valoarea acestei variabile este comparat cu EOF. Cnd caracterul este diferit de EOF, calculatorul trece la execuia instruciunii switch. Cuvntul rezervat switch este urmat, ntre paranteze, de o expresie integral (ex. char, int) a crei valoare este comparat cu fiecare etichet case care trebuie s fie ntotdeauna o expresie constant integral. Presupunnd c am introdus litera F corespunztoare calificativului foarte bine, F este comparat cu fiecare case. Dac exist o potrivire a valorilor (case F:), se execut secvena de instruciuni corespunztoare acestui case. n exemplul nostru, se incrementeaz variabila nrF. S notm c, spre deosebire de alte structuri de control, nu este nevoie s folosim blocuri de instruciuni pentru a delimita secvenele asociate unui case. Instruciunea break are ca efect transferul controlului programului la prima instruciune de dup instruciunea switch. Dac omitem instruciunea break, atunci se execut toate instruciunile care urmeaz ramurii selectate. Dac nu apare nicio 2

Programarea calculatoarelor i limbaje de programare I

potrivire a valorilor ntre expresia din switch i etichetele case, se execut varianta default. Sintaxa instruciunii switch permite gruparea mai multor etichete case: case 'I': case 'i': nsemnnd c pentru ambele cazuri se realizeaz acelai set de aciuni. O constant poate aprea o singur dat n instruciunea switch pentru c n caz contrar compilatorul genereaz o eroare de sintax. De asemenea, se poate folosi o singur etichet default. Instruciunea switch are acelai efect ca instruciunea IF-THEN-ELSE, fapt ilustrat prin scrierea n dou variante a structurii din programul anterior. Exemplu switch(calificativ) { case 'F': case 'f': ++nrF; break; case 'B': case 'b': ++nrB; break; case 'S': case 's': ++nrS; break; case 'I': case 'i': ++nrI; break; case '\n': case '\t': case ' ': break; default://alt caracter cout << "Ati introdus o litera incorecta." << " Introduceti un nou calificativ." << endl; } sau if(calificativ == 'F' || calificativ == 'f') ++nrF; else if(calificativ == 'B' || calificativ == 'b') ++nrB; else if(calificativ == 'S' || calificativ == 's') ++nrS; else if(calificativ == 'I' || calificativ == 'i') ++nrI; else if(calificativ == '\n' || calificativ == '\t' || calificativ == ' ') { } 3

Programarea calculatoarelor i limbaje de programare I

else cout << "Ati introdus o litera incorecta." << " Introduceti un nou calificativ." << endl;

9.2

Instruciunea do-while

Aceasta este o instruciune de control al buclelor n care condiia de final se testeaz la sfritul buclei, garantnd faptul c bucla se parcurge cel puin o dat. ablonul sintactic al instruciunii while este do { Instruciune 1 Instruciune 2 ... Instruciune n } while (expresie); Bucla cuprins ntre do i while se execut atta timp ct expresia din while este nenul sau true. do Instruciune while (expresie)
false true

Instruciune

while (expresie)
false true

S comparm o bucl while i una do-while care realizeaz aceeai aciune: gsesc primul punct din stream-ul de intrare. char inputChar; char inputChar; cin >> inputChar; do while(inputChar != '.') cin >> inputChar; cin >> inputChar; while(inputChar != '.'); Soluia while are nevoie de o prim citire astfel nct inputChar s aib o valoare nainte de intrarea n bucl. Acest lucru nu este necesar pentru soluia dowhile datorit faptului c bucla este executat o dat nainte de a se evalua condiia. Putem folosi structura do-while pentru a implementa o bucl controlat de un contor dac tim n avans c bucla este parcurs cel puin o dat. Urmtorul exemplu prezint dou bucle care nsumeaz ntregii de la 1 la n. #include <iostream> #include <iostream> using namespace std; using namespace std; int main() { int n = 10; int sum = 0; int contor = 1; while(contor <= n) int main() { int n = 10; int sum = 0; int contor = 1; do

Programarea calculatoarelor i limbaje de programare I

{ sum += contor; contor++; } cout << "Suma este " << sum << endl;

{ sum += contor; contor++; } while(contor <= n); cout << "Suma este " << sum << endl;

return 0; return 0; } } Dac n este un numr pozitiv, ambele versiuni sunt echivalente. Dar dac n este 0 sau negativ, cele dou bucle conduc la rezultate diferite. n versiunea while, valoarea final a lui sum este 0 pentru c bucla nu se execut niciodat. n varianta do-while, sum are valoarea 1 pentru c bucla se execut o dat. Instruciunea while testeaz condiia nainte de executarea corpului buclei i se numete bucl pretest, n timp ce instruciunea do-while se numete bucl posttest.

9.3

Instruciunea for

Aceast instruciune simplific scrierea buclelor controlate de contor, n C++ instruciunea for fiind form mai compact a buclei while. Exemplu for(int contor = 1; contor <= n; contor++) cout << contor << endl; int contor = 1; while(contor <= n) { cout << contor << endl; contor++; } Ambele instruciuni tipresc numerele de la 1 la n. Instruciunea for din exemplul de mai sus iniializeaz variabila de control al buclei, contor, cu valoarea 1. Atta timp ct valoarea lui contor este mai mic sau egal cu n execut instruciunea de afiare i incrementeaz contor. Corespondena dintre componentele instruciunii for i cele ale lui while este marcat prin caracterele aldine. Instruciunea for plaseaz iniializarea dinaintea buclei pe prima poziie dintre parantezele rotunde. Testul buclei este pe a doua poziie, iar incrementarea variabilei de contor este pe ultima poziie. Pe prima poziie se gsesc aciunile care se execut nainte de prima iteraie, iar pe ultima poziie sunt cele care se execut la sfritul fiecrei iteraii. Dac este vorba de mai mult de o aciune, atunci acestea sunt separate prin virgul. Ca i buclele while, i buclele do-while sau for pot fi imbricate. Exemplu #include <iostream> using namespace std; int main() {

Programarea calculatoarelor i limbaje de programare I

for(int num = 1; num <= 5; num++) { for(int numToPrint =1; numToPrint <= num; numToPrint++) cout << numToPrint; cout << endl; } return 0; } Rezultatul rulrii acestui program este 1 12 123 1234 12345

9.4

Instruciunile break i continue

Instruciunea break introdus odat cu instruciunea switch poate fi folosit i n bucle. Ea provoac ieirea imediat din cel mai interior switch, while, dowhile sau for n care apare. Una dintre cele mai frecvente situaii n care se folosete este ieirea din buclele infinite. Exemplu #include <iostream> #include <math.h> using namespace std; int main() { int num1, num2; int contor = 1; while(true) { cin >> num1; if(!cin || (num1 >= 100)) break; cin >> num2; if(!cin || (num2 <= 50)) break; cout << sqrt(static_cast<double>(num1+num2)) << endl; contor++; if(contor > 10) break; } return 0; } Pentru exemplul acesta am fi putut folosi o bucl for de la 1 la 10. n tot cazul, bucla este controlat de contor i de eveniment, deci preferm bucla while.

Programarea calculatoarelor i limbaje de programare I

Aceast bucl conine 3 puncte distincte de ieire. Unii programatori se opun acestui stil de programare susinnd c face codul mai greu de urmrit. Rescriind codul, vom vedea ca prima variant este, totui, mai clar. Exemplu #include <iostream> #include <math.h> using namespace std; int main() { bool num1Valid = true; bool num2Valid = true; int num1, num2; int contor = 1; while(num1Valid && num2Valid && contor<=10) { cin >> num1; if(!cin || (num1 >= 100)) num1Valid = false; else { cin >> num2; if(!cin || (num2 <= 50)) num2Valid = false; else { cout << sqrt(static_cast<double>(num1+num2)) << endl; contor++; } } } return 0; } O alt instruciune care afecteaz fluxul unui program C++ este continue. Ea termin iteraia curent i se folosete numai n bucle. Exemplu #include <iostream> #include <math.h> using namespace std; int main() { double valIntrare; for(int i = 1; i <= 5; i++) { cin >> valIntrare; if(valIntrare < 0) continue; cout << sqrt(valIntrare) << endl; } 7

Programarea calculatoarelor i limbaje de programare I

return 0; } Dac valIntrare este negativ, se trece la urmtoarea iteraie. i aceast secven de program poate fi rescris. Exemplu #include <iostream> #include <math.h> using namespace std; int main() { double valIntrare; for(int i = 1; i <= 5; i++) { cin >> valIntrare; if(valIntrare >= 0) cout << sqrt(valIntrare) << endl; } return 0; } Aadar, diferena dintre break i continue este c break ntrerupe imediat bucla curent, iar continue ntrerupe iteraia curent.

9.5 Proiectarea orientat pe obiecte abstractizarea n programare


Clase i obiecte
S ne imaginm c dorim s realizm imagini formate din ptrate. Fiecare ptrat este un obiect care este caracterizat prin atributele de culoare i poziie. Este caracterizat, de asemenea, printr-un comportament pentru c, printre altele putem s i schimbm culoarea i s il redesenm. Fiecare ptrat este diferit, dar are proprieti i comportamente comune cu alte ptrate. De aceea, putem s abstractizm aceste elemente comune: toate ptratele au acelai comportament i au acelai set de atribute. Abstractiznd, ignorm valorile particulare ale atributelor, adic nu inem cont de faptul c un ptrat este rou sau altul este verde, sau c unul este aezat n mijlocul ecranului iar altul intr-un col al su. Prin abstractizare am realizat o clas. O clas este o mulime de obiecte care au o structur comun i care au comportamente comune. Clasele reprezint abloane ale obiectelor pentru c dac dorim s introducem n imaginea noastr un nou ptrat, folosim clasa ptrat patricularizndu-i proprietile. Reprezentarea UML a unei clase se face printr-un dreptunghi mprit n trei seciuni. Patrat culoare : int varfuri : Punct[] nrVarfuri : int = 4 schimbaCuloarea(c : int) : void deseneaza() : void 8

Programarea calculatoarelor i limbaje de programare I

Prima parte conine numele clasei. Partea din mijloc conine atributele clasei. Descrierea unui atribut este format din numele atributului, tipul su i valoarea iniial. Tipul depinde de limbajul de programare n care se va face implementarea. Putem folosi tipuri primitive de date, aa cum am facut pentru atributele culoare i nrVarfuri, sau tipuri de date definite prin alte clase, cum a fost definit atributul varfuri. A treia seciune conine comportamentele sau operaiile ca nume de funcii. Numele operaiei este urmat ntre paranteze rotunde de lista de parametri separai prin virgul. Numele unui parametru este urmat de tipul acestuia. Lista poate s fie vid dac operaia nu folosete parametri. Ultimul element al descrierii unei operaii este tipul valorii returnate care este precedat de semnul :.

Motenirea
S ne imaginm c pe lng ptrate ne propunem s folosim i triunghiuri n imaginile pe care le construim. Triunghi culoare : int varfuri : Punct[] nrVarfuri : int = 3 schimbaCuloarea(c : int) : void deseneaza() : void Comparnd cele dou clase, observm c sunt cteva diferene ntre ele pentru c ptratele au patru vrfuri, n timp ce triunghiurile au doar trei. Totodat, modul n care sunt desenate aceste dou figuri difer. Pe de alt parte, sunt i similariti. De exemplu, putem stabili culoarea ambelor figuri i ambele figuri pot fi redesenate, chiar dac modul de desenare este diferit. Pe baza asemnrilor, dorim s abstractizm aceste clase eliminnd detaliile specifice fiecrei forme i amplificnd faptul c ambele au culoare i pot fi desenate. La un Forma culoare : int varfuri : Punct[] nrVarfuri : int schimbaCuloarea(c : int) : void deseneaza() : void

Patrat nrVarfuri : int = 4 deseneaza() : void 9

Triunghi nrVarfuri : int = 3 deseneaza() : void

Programarea calculatoarelor i limbaje de programare I

nivel mai nalt de abstractizare, am putea s gndim un desen ca fiind alctuit din forme, iar desenarea intregii imagini s se poat face desennd pe rnd fiecare form. Dorim s eliminm detaliile nerelevante, deci nu suntem interesai de faptul c o form este un ptrat sau c o alt form este un triunghi, atta timp ct pentru fiecare dintre ele vom concepe ulterior cte o metod particular de desenare. Pentru a face acest lucru, vom muta elementele comune ntr-o nou clas pe care o numim Forma. Aceast form de abstractizare se numete motenire. Clasele de la nivelul de jos se numesc subclase sau clase derivate. Ele motenesc elemente care definesc starea i comportamentul de la clasa de la nivelul superior numit i superclas sau clas de baz. Prin acest mecanism, un obiect de tip Triunghi va avea o culoare, o operaie schimbaCuloarea(c : int), un tablou de varfuri, o operaie deseneaza(), iar nrVarfuri va fi trei. O clas poate moteni urmtoarele trei tipuri de elemente: - starea, de exemplu culoare - operaiile, de exemplu schimbaCuloarea(c : int) - interfaa unei operaii: Clasa Forma conine interfaa operaiei deseneaza() pentru c interfaa deseneaza() pentru Triunghi i Patrat este aceeai. Implementarea operaiei rmne n subclase, ea fiind diferit pentru fiecare dintre ele. Atunci cnd abstractizm interfaa unei operaii lsnd implementarea n subclase spunem c avem de a face cu o operaie polimorfic. Aceast metod de abstractizare plaseaz elementele importante care definesc starea, comportamentul i interfeele n clasele de baz. Putem recurge la abstractizare i altfel, combinnd obiecte n interiorul unui nou obiect. Un exemplu sugestiv este cel al autoturismului privit ca un obiect care folosete o baterie, un aparat de radio, un motor dar i alte obiecte oferind o interfa simpl pentru conducerea sa. Exist diverse moduri de abstractizare iar gsirea celei mai potrivite pentru o problem este cheia proiectrii orientate pe obiecte. Atunci cnd, ca metod de proiectare, se folosete abordarea orientrii pe obiecte, putem folosi abstractizarea pentru a mpri o problem n subprobleme mai uor de rezolvat. Lucrnd la un nivel de abstractizare potrivit, rezolvarea poate fi mult mai uoar.

10

10. Scope. Lifetime. Namespace


Pe msur ce un program devine tot mai complex, numrul de identificatori crete. Unii identificatori sunt declarai n interiorul blocurilor, alii n afara lor. Vom studia n acest capitol regulile C++ care permit unei funcii s acceseze identificatori declarai n afara propriului bloc. Termenul scope definete domeniul de accesibilitate al identificatorilor. Lifetime este perioada de existen a unui identificator.

10.1

Scope domeniul de accesibilitate al unui identificator

Variabilele locale sunt cele declarate n interiorul unui bloc de instruciuni. Corpul unei funcii este un bloc de instruciuni, iar variabilele declarate ntr-o funcie sunt variabile locale pentru acea funcie. Variabilele locale nu pot fi accesate n afara blocului n care au fost declarate. Aceast regul se aplic i constantelor. Domeniul de accesibilitate, scope, este regiunea din program n care se poate folosi un identificator. C++ definete patru categorii de domenii pentru un identificator: 1. domeniul local este domeniul unui identificator declarat ntr-un bloc, din punctul declaraiei pn la sfritul blocului; 2. domeniul global (domeniul fiier) este domeniul unui identificator declarat n afara oricrei funcii sau clase, din punctul declaraiei pn la sfritul fiierului care conine codul; 3. domeniul clas se refer la tipurile de date introduse prin intermediul claselor; 4. al patrulea domeniu de existen este n legtur cu instruciunea go to. n C++, numelor de funcii le corespunde domeniul global sau domeniul clas. Funciile declarate n afara claselor sunt funcii globale. Odat declarat o astfel de funcie, ea poate fi folosit de orice alt funcie din program. Variabilele globale i constantele globale sunt declarate n afara tuturor funciilor sau claselor. Atunci cnd o funcie declar un identificator local cu acelai nume ca cel global, identificatorul local este cel folosit n funcie. Acest principiu se numete precedena numelui sau ascunderea numelui. Exemplu #include <iostream> using namespace std; void Functie(double); const int a = 17;//constanta globala int b;//variabila globala int c; int main() { b = 4;//b: global c = 6; Functie(42.8); return 0; }

Programarea calculatoarelor i limbaje de programare I

void Functie(double c)//c: local { double b;//b: local b = 2.3; //b: local cout << "a = " << a << endl;//a: global cout << "b = " << b << endl;//b: local cout << "c = " << c << endl;//c: local } Acest program va afia : a = 17 b = 2.3 c = 42.8

Reguli pentru domeniul de accesibilitate


Cnd scriem programe C++, declarm rar variabile globale. Atunci cnd le folosim, trebuie s tim exact cum manipuleaz C++ aceste declaraii. Suplimentar accesului global i local, aceste reguli arat ce se ntmpl dac ntr-un bloc includem alt bloc. Identificatorii declarai ntr-un bloc sunt nelocali pentru blocurile interioare lui. Spre deosebire de acetia, identificatorii globali sunt nelocali pentru toate blocurile din program. Dac un bloc acceseaz identificatori din afara sa, este vorba de acces non-local. Iat regulile referitoare la domeniul de accesibilitate : 1. Un nume de funcie care nu este declarat ntr-o clas are acces global. Definiiile de funcii nu pot fi imbricate n alte funcii; 2. Domeniul de accesibilitate al unui parametru formal este identic cu cel al unei variabile locale declarate n cel mai exterior bloc al corpului unei funcii; 3. Domeniul unei constante sau variabile globale se extinde din punctul declaraiei pn la sfritul fiierului cu excepia cazului din regula 5; 4. Domeniul unei constante sau al unei variabile locale este din punctul declaraiei pn la sfritul blocului, inclusiv blocurile interioare lui, cu excepia cazurilor din regula 5; 5. Domeniul de accesibilitate al unui identificator nu include blocurile interioare lui care conin o declaraie a unui identificator local cu acelai nume. Exemplu Acest program ilustreaz regulile de accesibilitate din C++. #include <iostream> using namespace std; void Bloc1(int, char&); void Bloc2(); int a1 = 1; char a2 = 'a'; int main() { //Blocul 1 Bloc1(a1, a2); Bloc2(); return 0; }

Programarea calculatoarelor i limbaje de programare I

void Bloc1(int a1, char& b2) { //Blocul 1 //Parametrii a1 si b2 ai functiei fac si ei //parte din acest bloc int c1 = 2; int d2 = 3; cout << "Bloc1: c1 + d2 = " << c1 + d2 << endl; } void Bloc2() { //Blocul 2 int a1 = 4; int b2; cout << "Bloc2: b2="; cin >> b2; if(b2-a1) { //Blocul 3 int c1 = 6; int b2; cout << "Bloc2: b2="; cin >> b2; cout << "Bloc2: b2-c1 = " << b2-c1 << endl; } } n acest exemplu, ntr-un bloc poate fi referit un identificator dintr-un alt bloc care l cuprinde i pe primul. De exemplu, n blocul 3 putem folosi un identificator declarat n blocul 2. Nu putem folosi, ns, n blocul 3 un identificator declarat n blocul 1 pentru c ar nsemna un acces din exterior n interior. Sunt permise doar accese de la un nivel interior spre unul exterior. Parametrii formali ai funciei Bloc1() sunt considerai ca aparinnd blocului 1. Numele funciei, ns, se gsete la nivelul global. S ne nchipuim fiecare bloc din programul de mai sus ca fiind nconjurat de cte un contur. Aceste contururi ar reprezenta pereii unor camere. Pereii sunt oglinzi cu partea care reflect orientat n afar, dar transpareni din interior. Dac suntem n camera Blocul 3, vom putea vedea toate declaraiile variabilelor din camera Blocul 2, dar i pe cele globale. Nu putem s vedem ce este n Blocul 1. Dac suntem n camera Blocul 2, nu putem vedea declaraiile din Blocul 3, dar nici pe cele din Blocul 1. Datorit acestei analogii, n mod frecvent se folosete termenul de vizibil pentru a descrie domeniile de acces. Astfel, variabila a2 este vizibil n tot programul, iar variabila c1 este vizibil doar n blocul 3. Trebuie s observm c variabila a1 este declarat n dou locuri n program. Datorit precedenei numelui, Blocul 2 i Blocul 3 acceseaz variabila a1 declarat n Blocul 2, i nu pe a1 declarat la nivel global. n mod similar, domeniul de accesibilitate al variabilei b2 declarat n Blocul 2 nu include i Blocul 3 deoarece acolo este declarat o alt variabil cu acelai nume, b2. Compilatorul lucreaz astfel atunci cnd este vorba de precedena numelor. Cnd o instruciune folosete un identificator, caut o declaraie local. Dac 3

Programarea calculatoarelor i limbaje de programare I

identificatorul nu este local, trece la un nivel superior pn gsete declaraia, dup care se oprete. Dac ajunge la nivelul global, incluznd i identificatorii inserai cu directiva #include i identificatorul nu este scris greit, compilatorul genereaz mesajul de eroare UNDECLARED IDENTIFIER.

Declaraii i definiii de variabile


Terminologia C++ face diferena ntre o declaraie de funcie i o definiie de funcie. Limbajul C++ aplic aceeai terminologie i variabilelor. Toate declaraiile de variabile pe care le-am folosit pn acum au fost i definiii. S vedem la ce se refer aceast afirmaie. C++ permite scrierea unui program multifiier, adic a unui program pentru care codul este mprit n mai multe fiiere. Se folosete cuvntul rezervat extern pentru a referi o variabil global localizat n alt fiier. Dac var1 este o variabil de tip ntreg, o declaraie a sa este urmtoarea: int var1; care are ca efect rezervarea de ctre compilator a unei locaii de memorie de tip int. Pe de alt parte, declaraia extern int var1; arat c variabila este global i este declarat n alt fiier, motiv pentru care nu mai trebuie rezervat spaiu de memorie pentru ea. Fiierele header conin declaraii externe pentru variabilele importante folosite n programe. Exemplu n fiierul header iostream exist declaraiile extern istream cin; extern ostream cout; extern ostream cerr; extern ostream clog; care permit referirea n program a lui cin i cout ca variabile globale. n terminologia C++, instruciunea extern int var1; este o declaraie a variabilei var1. Pe de alt parte, instruciunea int var1; este att declaraie ct i definiie. O variabil sau o funcie pot fi declarate de oricte ori, dar pot fi definite doar o dat. Spre deosebire de variabilele globale, folosirea constantelor globale este acceptabil. Pentru c valorile lor nu pot fi schimbate, folosirea lor nu are comportamente neateptate de genul celor care apar atunci cnd modificarea unei valori a unei variabile globale produce efecte nedorite ntr-o alt funcie. Exist dou avantaje ale folosirii constantelor globale: - uurina modificrii valorii sale - consistena. Dac dorim s modificm valoarea unei constante globale folosit de mai multe ori n program, trebuie s modificm doar declaraia ei. Declarnd o constant i folosind-o n program, suntem siguri c peste tot folosim aceeai valoare. Nu trebuie s nelegem c vom declara toate constantele globale. Dac o constant este folosit doar ntr-o funcie, este lipsit de sens s o declarm global, ci local.

Programarea calculatoarelor i limbaje de programare I

10.2

Lifetime perioada de existen a unei variabile

Perioada de existen a unei variabile este perioada de timp n care un identificator are alocat o zon de memorie. Exemple Variabilele locale rmn alocate din momentul intrrii n funcie pn la incheierea acesteia, cnd sunt dealocate. Perioada de existen a variabilelor globale coincide cu perioada de existen a programului. Putem face observaia c domeniul de accesibilitate este un element legat de faza de compilare, n timp ce perioada de existen este legat de faza de execuie. n C++ exist mai multe categorii de variabile determinate de perioada de existen: - variabilele automatice care sunt alocate la intrarea ntr-un bloc i dealocate la ieirea din bloc; - variabilele statice care rmn alocate pe durata ntregului program. Variabilele globale sunt de tip static. Toate variabilele declarate ntr-un bloc sunt automatice. Dac folosim cuvntul cheie static atunci cnd declarm o variabil, aceasta devine static i perioada ei de existen nu se va ncheia odat cu terminarea blocului. Ea va fi dispobili de la un apel al unei funcii la altul. Exemplu #include <iostream> using namespace std; void a(); void b(); void c(); int x = 1;//variabila globala int main() { int x = 5;//variabila locala functiei main cout << "variabila locala x din functia main " << "are valoarea " << x << endl; { //un nou bloc int x = 7; cout << "variabila locala x din blocul " << "interior functiei main are valoarea " << x << endl; } cout << "variabila locala x din functia main " << "are valoarea " << x << endl; a();//foloseste o variabila automatica locala x b();//foloseste o variabila statica locala x c();//foloseste variabila globala x a();//reinitializeaza variabila automatica locala x b();//variabila statica locala x retine valoarea

Programarea calculatoarelor i limbaje de programare I

c();//variabila globala x retine valoarea anterioara cout << "variabila locala x din functia main " << "are valoarea " << x << endl; return 0; } void a() { int x = 25;//variabila locala automatica //se initializeaza la fiecare apel //al functiei a() cout << endl << "variabila locala x are valoarea " << x << " la intrarea in functia a()" << endl; ++x; cout << "variabila locala x are valoarea " << x << " inainte de iesirea din functia a()" << endl; } void b() { static int x = 50;//variabila statica locala //este initializata doar la primul apel //al functiei b() cout << << ++x; cout << << } void c() { cout << endl << "variabila globala x are valoarea " << x << " la intrarea in functia c()" << endl; x *= 10; cout << "variabila globala x are valoarea " << x << " inainte de iesirea din functia c()" << endl; } Rezultatul rulrii programului este urmtorul: variabila locala x din functia main are valoarea 5 variabila locala x din blocul interior functiei main are valoarea 7 variabila locala x din functia main are valoarea 5 variabila locala x are valoarea 25 la intrarea in functia a() variabila locala x are valoarea 26 inainte de iesirea din functia a() endl << "variabila locala statica x are valoarea " x << " la intrarea in functia b()" << endl; "variabila locala statica x are valoarea " << x " inainte de iesirea din functia b()" << endl;

Programarea calculatoarelor i limbaje de programare I

variabila locala statica x are valoarea 50 la intrarea in functia b() variabila locala statica x are valoarea 51 inainte de iesirea din functia b() variabila globala x are valoarea 1 la intrarea in functia c() variabila globala x are valoarea 10 inainte de iesirea din functia c() variabila locala x are valoarea 25 la intrarea in functia a() variabila locala x are valoarea 26 inainte de iesirea din functia a() variabila locala statica x are valoarea 51 la intrarea in functia b() variabila locala statica x are valoarea 52 inainte de iesirea din functia b() variabila globala x are valoarea 10 la intrarea in functia c() variabila globala x are valoarea 100 inainte de iesirea din functia c() variabila locala x din functia main are valoarea 5 Valoarea variabilei statice locale x din funcia b() este reinut de la un apel la altul al funciei, spre deosebire de valoarea variabilei locale automatice x din funcia a() care se pierde. n general, se prefer declararea unei variabile locale statice dect folosirea unei variabile globale. Ca i o variabil global, ea rmne alocat pe durata ntregului program, ns este accesibil doar n interiorul funciei n care a fost declarat. Variabilele statice fie globale, fie locale, sunt iniializate o singur dat, atunci cnd programul parcurge prima dat aceste instruciuni. Aceste variabile trebuie iniializate ca si constantele.

10.3

Namespaces (spaii de nume)

Un program folosete identificatori inclui n diverse domenii de accesibilitate. Uneori, o variabil dintr-un domeniu se suprapune peste o variabil cu acelai nume din alt domeniu. Suprapunerea numelor poate, uneori, s pun probleme, mai ales atunci cnd se folosesc n acelai program mai multe biblioteci care utilizeaz la nivel global identificatori cu aceleai nume. O astfel de situaie conduce, de regul, la erori de compilare. Limbajul C++ rezolv aceast problem prin spaiile de nume (namespaces). Fiecare namespace definete un domeniu n care sunt plasai identificatorii. Pentru a folosi un membru al unui namespace, acesta trebuie s fie precedat de numele namespace-ului: nume_namespace::membru Alternativ, se poate folosi declaraia using naintea identificatorului care este folosit. n mod obinuit, declaraiile using sunt plasate la nceputul fiierului n care sunt folosii membrii namespace-ului. De exemplu, instruciunea using namespace nume_namespace; 7

Programarea calculatoarelor i limbaje de programare I

la nceputul unui fiier surs speficic faptul c membrii lui nume_namespace pot fi folosii n fiier fr a preceda fiecare identificator cu numele namespace-ului i operatorul domeniu ::. Exemplu #include <iostream> using namespace std; int var = 98; namespace Exemplu { const double PI = 3.14159; const double E = 2.71828; int var = 8; void TiparesteValori(); namespace Interior { enum Ani {FISCAL1 = 2004, FISCAL2, FISCAL3}; } } namespace { double d = 88.22; } int main() { cout << "d= " << d; cout << "\n(global) var = " << var; cout << "\nPI = " << Exemplu::PI << "\nE = " << Exemplu::E << "\nvar = " << Exemplu::var << "\nFISCAL3 = " << Exemplu::Interior::FISCAL3 << endl; Exemplu::TiparesteValori(); return 0; } void Exemplu::TiparesteValori() { cout << "\nIn TiparesteValori():\n" << "var = " << var << "\nPI = " << PI << "\nE = " << E << "\nd = " << d << "\n(global) var = " << ::var << "\nFISCAL3 = " << Interior::FISCAL3 << endl; } Programul afieaz pe ecran urmtorul rezultat: d= 88.22 (global) var = 98 8

Programarea calculatoarelor i limbaje de programare I

PI = 3.14159 E = 2.71828 var = 8 FISCAL3 = 2006 In TiparesteValori: var = 8 PI = 3.14159 E = 2.71828 d = 88.22 (global) var = 98 FISCAL3 = 2006 Instruciunea using namespace std; informeaz compilatorul c va fi folosit namespace-ul std. ntreg coninutul fiierului header iostream este definit ca parte a lui std. Aceast declaraie presupune c unii membri ai lui std vor fi folosii n mod frecvent n program. Programatorul are, astfel, posibilitatea, s acceseze toi membrii acestui namespace i s scrie instruciuni ntr-o variant mai concis: cout << "d= " << d; dect scriind std::cout << "d= " << d; Fr instruciunea de declarare a lui std, fiecare cout i endl ar trebui s fie precedat de std::. Prin declaraiile namespace Exemplu { const double PI = 3.14159; const double E = 2.71828; int var = 8; void TiparesteValori(); namespace Interior { enum Ani {FISCAL1 = 2004, FISCAL2, FISCAL3}; } } se definesc namespace-urile Exemplu i Interior. Primul dintre ele conine constantele PI i E, variabila var, funcia TiparesteValori() i namespace-ul intitulat Interior care este declarat n interiorul su. Acesta, la rndul su, conine o enumerare. Aceasta reprezint un tip de dat introdus prin cuvntul cheie enum urmat de un identificator i este un set de constante ntregi reprezentate de identificatorii dintre acolade. Valorile acestor constante ncep cu 0 i sunt incrementate apoi cu 1, cu excpia cazului n care se specific altfel. n exemplul de mai sus, constantele FISCAL1, FISCAL2 i FISCAL3 au valorile 2004, 2005 i 2006. Un namespace poate fi declarat la nivel global sau n interiorul unui alt namespace. Prin secvena namespace 9

Programarea calculatoarelor i limbaje de programare I

{ double d = 88.22; } se declar un namespace fr nume care conine variabila d. Un namespace fr nume ocup ntotdeauna domeniul global, cel din care fac parte i variabilele globale. Accesul la membrii namespace-ului se face prin prefixarea acestora: cout << "\nPI = " << Exemplu::PI << "\nE = " << Exemplu::E << "\nvar = " << Exemplu::var << "\nFISCAL3 = " << Exemplu::Interior::FISCAL3 << endl; PI, E i var sunt membri ai lui Exemplu i sunt precedai de Exemplu::. Apartenena membrului var trebuie precizat pentru c altfel ar fi tiprit variabila global cu acelai nume. FISCAL3 trebuie precedat de Exemplu::Interior:: pentru c face parte din namespace-ul Interior care este declarat n namespace-ul Exemplu. Funcia TiparesteValori() este membru al lui Exemplu i poate accesa n mod direct ceilali membri ai namespace-ului. Variabila global var este accesat prin folosirea operatorului domeniu unar ::, iar FISCAL3 este accesat prin folosirea naintea sa a lui Interior::. Cuvntul rezervat using poate fi folosit i pentru a permite accesul la membri individuali dintr-un namespace. Instruciunea using Exemplu::PI; ar permite folosirea n instruciunile care urmeaz a identificatorului PI fr ca acesta s mai fie precedat de Exemplu::. Aceste declaraii se fac doar atunci cnd sunt folosii n mod intens ntr-un program un numr limitat de identificatori dintr-un namespace.

10

11. Pointeri
Pointerii reprezint caracteristica cea mai puternic a limbajului de programare C++. n capitolele precedente am vzut cum se pot scrie funcii ale cror parametri sunt transmii prin referin. Mecanismul transmiterii parametrilor prin intermediul pointerilor este o extensie a transmiterii prin referin. Am vzut, de asemenea, c tablourile sunt colecii de elemente de acelai tip care sunt stocate n locaii succesive de memorie. Vom arta n acest capitol c exist o strns relaie ntre pointeri i tablouri i vom studia modul n care se pot manipula tablourile prin intermediul pointerilor.

11.1

Variabilele de tip pointer

Variabilele de tip pointer stocheaz adrese de memorie. Pot, de exemplu, s pstreze adrese de memorie ale altor variabile care, la rndul lor, conin alte valori. n acest sens, un nume de variabil refer direct o valoare, iar un pointer refer indirect o valoare. Referirea unei valori printr-un pointer se numete indirectare. count 7 count refer direct o variabil a crei valoare este 7

countPtr

count 7 countPtr refer indirect o variabil a crei valoare este 7

Pointerii, ca orice alt variabil, trebuie declarai nainte de a fi folosii. Exemplu int *countPtr, count; Prin aceste declaraii, variabila countPtr este de tip int*, adic este pointer ctre o valoare ntreag. Variabila count este de tip ntreg i nu pointer la ntreg. Fiecare variabil declarat ca pointer este precedat de un asterisc *. Exemplu double *x, *y; Att x ct i y sunt pointeri ctre valori de tip double. Aceste variabile pot pstra adrese de memorie ale unor valori de tip double. Pot fi declarai pointeri ca s pointeze ctre variabile de orice tip de dat. Este indicat ca pointerii s fie iniializai fie odat cu declaraia acestora, fie printr-o instruciune de asignare. Un pointer poate fi iniializat cu 0, NULL sau cu o adres de memorie. Un pointer cu valoarea 0 sau NULL nu pointeaz ctre nicio zon de memorie. Constanta NULL este declarat n fiierul header <iostream> i n alte cteva fiiere din biblioteca standard. Iniializarea prin valoarea NULL este echivalent cu iniializarea prin valoarea 0, dar n C++ se prefer cea de-a doua variant. ntregul 0 este convertit automat ctre o adres de tipul pointerului.

Programarea calculatoarelor i limbaje de programare I

Valoarea 0 este singurul ntreg care poate fi asignat direct unei variabile pointer fr o conversie prealabil. Pointerii constani sunt cei al cror coninut nu se poate modifica. Un astfel de pointer trebuie iniializat n instruciunea de declarare. Exemplu int x; const int *xPtr = &x;

11.2

Operatori pentru pointeri

Operatorul adres & este unar i returneaz adresa operandului su. Exemplu int y = 5; int *yPtr; yPtr = &y; Prin ultima instruciune, adresa de memorie a variabilei y este ncrcat n variabila pointer yPtr. n urma acestei asignri, vom spune c yPtr pointeaz ctre y. yPtr y 5

Exemplu #include <iostream> using std::cout; using std::endl;

int main() { int a; int *aP; a = 7; aP = &a; cout << "Adresa lui a este " << &a << "\nValoarea lui aP este " << aP; cout << "\n\nAdresa lui a este " << a << "\nValoarea lui *aP este " << *aP; cout << "\n\nOperatorii * si & sunt inversi unul altuia. " << "\n&*aP = " << &*aP << "\n*&aP = " << *&aP << endl; cout << "\n\nAdresa lui aP este " << &aP << endl; return 0; } Acest program afieaz pe ecran urmtorul rezultat: Adresa lui a este 0x22ff74 Valoarea lui aP este 0x22ff74 Adresa lui a este 7 Valoarea lui *aP este 7 2

Programarea calculatoarelor i limbaje de programare I

Operatorii * si & sunt inversi unul altuia. &*aP = 0x22ff74 *&aP = 0x22ff74 Adresa lui aP este 0x22ff70 n figura de mai jos reprezentm variabila aP care presupunem c este localizat n memorie la adresa 22FF70 i variabila a care se gsete la adresa 22FF74. aP 22FF70 22FF74 22FF74 a 7

Operandul operatorului adres trebuie s fie un lvalue (left value), adic o entitate cruia i poate fi asignat o valoare, de exemplu valoarea unei variabile. Operatorul adres nu poate fi aplicat constantelor sau expresiilor al cror rezultat nu poate fi referit. Operatorul * numit operator de indirectare sau de derefereniere returneaz un sinonim sau un alias pentru obiectul asupra pointeaz operandul su. Instruciunea cout << *aP << endl; tiprete valoarea variabilei a care este 7, n acelai fel n care o face instruciunea cout << a << endl; Folosirea lui * n acest mod se numete dereferenierea unui pointer. Un pointer derefereniat poate fi folosit n partea stng a unei instruciuni de asignare: *aP = 5; Prin aceast operaie, valoarea 5 este asignat variabilei a. Un pointer derefereniat poate fi folosit n diverse operaii: cin >> *aP; Pointerul derefereniat este un lvalue.

11.3

Transmiterea prin pointeri a parametrilor funciilor

n C++ se pot folosi pointerii i operatorul de indirectare pentru a simula transmiterea parametrilor prin referin. Exemplu #include <iostream> using std::cout; using std::endl; int cubPrinValoare(int); void cubPrinReferinta(int*); int main() { int val = 5; cout << "Valoarea numarului este " << val; cubPrinValoare(val); cout << "\nValoarea dupa apelul cubPrinValoare(val) este "

Programarea calculatoarelor i limbaje de programare I

<< val; val = 5; cout << "\n\nValoarea numarului este " << val; cubPrinReferinta(&val); cout << "\nValoarea dupa apelul cubPrinReferinta(&val) " << "este " << val << endl; return 0; } int cubPrinValoare(int n) { return n * n * n; } void cubPrinReferinta(int *nPtr) { *nPtr = *nPtr * *nPtr * *nPtr; } Acest program afieaz: Valoarea numarului este 5 Valoarea dupa apelul cubPrinValoare(val) este 5 Valoarea numarului este 5 Valoarea dupa apelul cubPrinReferinta(&val) este 125 Mecanismul prin care valoarea parametrului actual val este modificat prin funcia cubPrinReferinta(&val) este similar celui prin care parametrul actual este modificat la transmiterea parametrilor prin referin. Se transmite adresa de memorie a variabilei care iniializeaz parametrul formal nPtr. Modificarea valorii la care pointeaz nPtr nseamn modificarea valorii de la adresa &val.

11.4

Aritmetica pointerilor

Pointerii pot fi folosii n expresii aritmetice, asignri i comparaii, ns nu toi operatorii pot avea pointeri ca operanzi. Asupra pointerilor pot fi realizate operaii de incrementare (++), decrementare (--), adugare a unui ntreg (+ sau +=), scdere a unui ntreg (- sau -=) i scdere a unui pointer din alt pointer. S presupunem c declarm tabloul int v[5] al crui prim element este plasat de compilator la adresa de memorie 22FF50. Pentru un calculator pe care ntregii sunt reprezentai pe 4 bytes, cele cinci elemente ale tabloului sunt plasate la adresele de memorie din figura de mai jos. 22FF50 22FF54 v[0] v[1] v[2] v[3] 22FF60 v[4]

vPtr

Programarea calculatoarelor i limbaje de programare I

Pointerul vPtr poate fi iniializat cu adresa primului element al taboului folosind una dintre instruciunile urmtoare: int *vPtr = v; vPtr = &v[0]; Adresa celui de-al doilea element al tabloului este &v[1]. Spre deosebire de operaiile matematice n care adunarea 22FF50+2 are ca rezultat valoarea 22FF52, n aritmetica pointerilor adugarea unui ntreg la o adres de memorie are ca rezultat o nou adres de memorie. Aceasta este egal cu adresa iniial la care se adaug un numr de locaii de memorie egal cu valoarea ntregului nmulit cu dimensiunea obiectului la care refer pointerul. Exemplu vPtr += 2; are ca rezultat valoarea 22FF58, adic 22FF50 + 2 * 4 pentru c am presupus c ntregii sunt stocai pe 4 bytes. n urma acestei operaii, vPtr va pointa ctre v[2]. 22FF50 22FF54 22FF60 v[0] v[1] v[2] v[3] v[4]

vPtr Pentru un pointer ctre double, aceeai operaie are ca rezultat valoarea 22FF60 pentru c la 22FF50 se adaug 2 * 8 bytes. Programul de mai jos prezint operaiile care se pot efectua asupra pointerilor. #include <iostream> using namespace std; int main() { int v[5]; int *vPtr = &v[0]; cout << "Adresa lui v[0] este " << &v[0] << endl; cout << "Adresa stocata in vPtr este " << vPtr << endl; vPtr += 2; cout << "\nAdresa stocata in vPtr dupa operatia vPtr += 2" << " este " << vPtr; vPtr -= 4; cout << "\nAdresa stocata in vPtr dupa operatia vPtr -= 4" << " este " << vPtr; vPtr++; cout << "\nAdresa stocata in vPtr dupa operatia vPtr++" << " este " << vPtr; ++vPtr; cout << "\nAdresa stocata in vPtr dupa operatia ++vPtr" << " este " << vPtr; 5

Programarea calculatoarelor i limbaje de programare I

vPtr--; cout << "\nAdresa stocata in vPtr dupa operatia vPtr--" << " este " << vPtr; --vPtr; cout << "\nAdresa stocata in vPtr dupa operatia vPtr" << " este " << vPtr; int *v2Ptr = &v[4]; cout << "\nRezultatul operatiei v2Ptr-vPtr este " << v2Ptr-vPtr << endl; return 0; } Acest program afieaz urmtorul rezultat: Adresa lui v[0] este 0x22ff50 Adresa stocata in vPtr este 0x22ff50 Adresa stocata in vPtr dupa operatia Adresa stocata in vPtr dupa operatia Adresa stocata in vPtr dupa operatia Adresa stocata in vPtr dupa operatia Adresa stocata in vPtr dupa operatia Adresa stocata in vPtr dupa operatia Rezultatul operatiei v2Ptr-vPtr este vPtr += 2 este 0x22ff58 vPtr -= 4 este 0x22ff48 vPtr++ este 0x22ff4c ++vPtr este 0x22ff50 vPtr-- este 0x22ff4c -vPtr este 0x22ff48 6

Un pointer poate fi asignat altui pointer doar dac cei dou au acelai tip. n caz contrar, trebuie aplicat o operaie de conversie pentru ca pointerul din dreapta asignrii s fie adus la tipul pointerului din stnga. Excepie de la aceast regul n face pointerul void* care este un tip generic i poate reprezenta orice tip de pointer fr a mai fi nevoie de cast. Pe de alt parte, pointerul void* nu poate fi dereferiniat pentru c numrul de bytes corespunztor lui nu poate fi determinat de compilator.

11.5

Pointeri i tablouri

Tablourile i pointerii sunt, n limbajul C++, n strns legtur. Un nume de tablou poate fi interpretat ca un pointer constant, iar pointerii pot fi indexai ca i tablourile. Pentru tabloul v[5] am declarat variabila pointer vPtr pe care am iniializat-o cu v, adresa primului element al tabloului. Elementul v[3] poate fi referit i prin expresiile pointer *(vPtr + 3) *(v + 3) Valoarea 3 din aceste expresii se numete offset la pointer, iar o astfel de expresie care acceseaz un element al unui tablou se numete notaie offset sau notaie pointer. Fr paranteze, expresia *vPtr + 3 ar fi adunat valoarea 3 la expresia *vPtr, adic la v[0]. Pentru pointeri se pot folosi indici la fel ca i pentru tablouri. Exemplu 6

Programarea calculatoarelor i limbaje de programare I

cout << vPtr[1]; n schimb, numele unui tablou este un pointer constant i nu poate fi folosit n operaii care i-ar schimba coninutul. Exemplu v += 3; este o operaie invalid pentru c ar modifica valoarea pointerului care reprezint tabloul printr-o operaie de aritmetic a pointerilor.

Tablouri de pointeri
Tablourile pot conine pointeri. Se pot crea structuri de date care sunt formate din string-uri. Fiecare intrare ntr-un astfel de tablou este un string, dar n C++ un string este, de fapt, un pointer la primul su caracter. Astfel, fiecare intrare ntr-un astfel de tablou este un pointer ctre primul caracter al unui string. Exemplu const char *semn[4] = {"Pica", "Cupa", "Caro", "Trefla"}; Prin aceast instruciune, am declarat un tablou de patru pointeri la char. Vom folosi tabloul semn pentru a reprezenta un pachet de cri de joc. semn[0] semn[1] semn[2] semn[3] 'P' 'i' 'c' 'a' '\0' 'C' 'u' 'p' 'a' '\0' 'C' 'a' 'r' 'o' '\0' 'T' 'r' 'e' 'f' 'l' 'a' '\0'

Cele patru valori, "Pica", "Cupa", "Caro", "Trefla", sunt pstrate n memoria calculatorului ca iruri de caractere terminate prin NULL. n tabloul semn sunt pstrate doar adresele de memorie ale primelor caractere din fiecare ir, iar irurile au lungimi diferite. Dac am fi optat pentru varianta unui tablou bidimensional n care fiecare rnd reprezint un tip i fiecare coloan reprezint o liter din fiecare tip, ar fi trebuit s fixm numrul de coloane ale tabloului la dimensiunea maxim pe care o poate avea un ir. Pentru iruri de dimensiuni mari, memoria neutilizat ar fi fost, astfel, destul de mare. Vom ilustra folosirea tablourilor de string-uri prezentnd un program care simuleaz amestecarea unui pachet de 52 de cri de joc i distribuirea acestora. Folsim un tablou bidimensional cu 4 linii i 13 coloane numit pachet pentru a reprezenta pachetul de 52 de cri de joc. Dam 11 apte Patru Nou Pop 12 Valet 10 ase Cinci Zece 9 Trei Opt 7 Doi 1 As 0 Pica 0

Cupa 1 Caro 2 Trefla 3 Linia 0 corespunde semnului "Pica", linia 1 corespunde semnului "Cupa", linia 2 corespunde semnului "Caro", iar linia 3 semnului "Trefla". Coloanele corespund valorilor nscrise pe fiecare carte. Coloanele de la 0 pn la 9 sunt asociate crilor 7

Programarea calculatoarelor i limbaje de programare I

de la as pn la 10, coloana 10 este asociat valeilor, coloana 11 damelor i coloana 12 popilor. Numele semnelor vor fi pstrate n tabloul semn, iar valorile crilor vor fi stocate n tabloul valoare. Celula marcat, pachet[1][4], reprezint un cinci de cup. Acest pachet de cri virtual poate fi amestecat astfel: Iniializm tabloul pachet cu valori 0. Alegem apoi la ntmplare o linie i o coloana. Inserm valoarea 1 n celula pachet[linie][coloana] pentru a arta c aceasta este prima carte din pachetul amestecat. Continum acest proces insernd aleator valorile 2, 3 ... 52 n tabloul pachet pentru a arta care carte se va gsi pe poziia 2, 3 ... 52. Pentru c tabloul pachet se umple progresiv cu valori, este posibil ca n timpul derulrii acestui algoritm o carte s fie selectat din nou. n acest caz, selecia este ignorat i se alege un nou linie si o noua coloana pn cnd este gsit o carte care nu a fost selectat. Acest algoritm de amestecare a pachetului de cri are dezavantajul c poate s se deruleze pentru o perioad nedefinit de timp dac este aleas n mod repetat o carte care a fost deja selectat. Pentru a mpri prima carte, cutm n tabloul pachet celula pachet[linie][coloana] care conine valoarea 1. Vom afia, aadar, numele crii alese care va fi format din semn[linie] i valoare[coloana]. Algoritmul de implementare a soluiei acestei probleme este urmtorul: Iniializarea tabloului semn Iniializarea tabloului valoare Iniializarea tabloului pachet Pentru fiecare dintre cele 52 de cri Alege aleator o poziie din tabloul pachet Atta timp ct poziia a fost deja aleas Alege aleator o poziie din tabloul pachet nregistreaz numrul de ordine al crii n poziia din tabloul pachet Pentru fiecare dintre cele 52 de cri Pentru fiecare poziie din tabloul pachet Dac celula curent din tabloul pachet conine valoarea corect Tiprete numele crii Programul care implementeaz aceast problem este urmtorul: #include <iostream> using std::cout; using std::ios; #include <iomanip> using std::setw; void Amesteca(int[][13]); void Imparte(const int[][13], const char *[], const char *[]); int main() { 8

Programarea calculatoarelor i limbaje de programare I

const char *semn[4] = {"Pica", "Cupa", "Caro", "Trefla"}; const char *valoare[13] = {"As", "Doi", "Trei", "Patru", "Cinci", "Sase", "Sapte", "Opt", "Noua", "Zece", "Valet", "Dama", "Popa"}; int pachet[4][13] = {0}; srand(time(0)); Amesteca(pachet); Imparte(pachet, semn, valoare); return 0; } void Amesteca(int lPachet[][13]) { int linie, coloana; for(int carte=1; carte<=52; carte++) { do{ linie = rand()%4; coloana = rand()%13; } while(lPachet[linie][coloana] != 0); lPachet[linie][coloana] = carte; } } void Imparte(const int lPachet[][13], const char *lSemn[], const char *lValoare[]) { for(int carte=1; carte<=52; carte++) for(int linie=0; linie<=3; linie++) for(int coloana=0; coloana<=12; coloana++) if(lPachet[linie][coloana] == carte) cout << setw(6) << lValoare[coloana] << " de " << setw(6) << lSemn[linie] << (carte%2==0?'\n':'\t'); } Programul afieaz lista crilor dup ce acestea au fost amestecate: Patru de Pica As de Trefla Noua de Cupa Valet de Pica Doi de Pica Trei de Pica Opt de Cupa Zece de Caro Valet de Caro Cinci de Caro Popa de Pica Trei de Caro Popa de Cupa Opt de Trefla As de Cupa Sase de Trefla Popa de Caro Dama de Cupa 9

Programarea calculatoarelor i limbaje de programare I

As de Caro Opt de Pica Noua de Trefla Valet de Cupa Doi de Trefla Noua de Pica Patru de Caro Sapte de Caro Trei de Trefla Sase de Caro Zece de Pica As de Pica Valet de Trefla Zece de Cupa Dama de Trefla Doi de Caro Cinci de Pica Cinci de Cupa Sapte de Pica Opt de Caro Patru de Trefla Sapte de Cupa Cinci de Trefla Sase de Cupa Noua de Caro Patru de Cupa Dama de Caro Sase de Pica Doi de Cupa Trei de Cupa Sapte de Trefla Dama de Pica Zece de Trefla Popa de Trefla Instruciunea carte%2==0?'\n':'\t' folosete operatorul ternar ?: care are ablonul sintactic condiie ? instruciune1 : instruciune2 Atunci cnd condiia este adevrat, se execut instruciune1, iar n caz contrar se execut instruciune2.

10

12. Template-uri de funcii. Tratarea excepiilor


Template-urile (abloanele) reprezint una dintre cele mai puternice caracteristici ale limbajului C++. Acestea permit definirea, printr-un singur segment de cod, a unei ntregi game de funcii suprancrcate template de funcie sau a unei ntregi serii de clase template de clas. n acest capitol vom discuta cazul funciilor. Putem scrie generic, de exemplu, un singur template de funcie pentru ordonarea unui ir de elemente, iar funcia poate fi apelat pentru valori de diverse tipuri: int, double etc. Tratarea excepiilor este o metod care permite programatorilor s scrie programe mai robuste, mai clare i cu un grad mai mare toleran la erori. Programele pot fi concepute astfel nct s detecteze i s trateze erorile (excepiile) care pot aprea n timpul rulrii, evitnd astfel ntreruperile neplanificate ale aplicaiilor.

12.1

Template-uri de funcii

Funciile suprancrcate realizeaz, de obicei, operaii similare pentru diferite tipuri de date. Dac operaiile sunt identice pentru toate tipurile de date, acestea pot fi realizate mai compact folosind template-uri de funcii. Programatorul trebuie s scrie doar o singur definiie de template de funcie. Bazndu-se pe tipurile argumentelor date explicit sau rezultate din apeluri ale acestor funcii, compilatorul genereaz funcii separate n codul obiect pentru a manipula corespunztor fiecare tip de apel. n limbajul C, acest lucru poate fi realizat folosind macrouri create prin directiva de preprocesare #define. Este, ns, o metod care poate produce efecte nedorite i care nu permite compilatorului s fac verificri de tip. Toate definiiile de template-urile de funcii ncep prin cuvntul cheie template urmat de lista parametrilor formali de tip ai template-ului de funcie plasat ntre < >. Fiecare parametru formal de tip trebuie s fie precedat de cuvntul cheie class sau de typename. Parametrii formali de tip din definiia unui template de funcie sunt utilizai pentru a specifica tipurile argumentelor funciei, tipul valorii returnate de funcie i pentru a declara variabile n funcie. Definiia funciei este, astfel, cea a unei funcii obinuite. Cuvintele cheie class sau typename folosite pentru a specifica parametrii de tip ai template-ului au semnificaia de orice tip de dat predefinit sau definit prin program. Programul de mai jos definete funcia PrintArray care tiprete componentele unor tablouri. #include <iostream>

Programarea calculatoarelor i limbaje de programare I

<iostream> using std::cout; using std::endl; template< class T > void PrintArray(const T *array, const int count) { for(int i = 0; i < count; i++) cout << array[i] << " "; cout << endl; } int main() { const int aCount = 5, bCount = 7, cCount = 6; int a[aCount] = {1, 2, 3, 4, 5}; double b[bCount] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7}; char c[cCount] = "HELLO"; cout << "Tabloul a contine: " << endl; PrintArray(a, aCount); cout << "Tabloul b contine: " << endl; PrintArray(b, bCount); cout << "Tabloul c contine: " << endl; PrintArray(c, cCount); return 0; } Acest program afieaz: Tabloul a contine: 1 2 3 4 5 Tabloul b contine: 1.1 2.2 3.3 4.4 5.5 6.6 7.7 Tabloul c contine: H E L L O Funcia template PrintArray declar un singur parametru formal de tip cu numele T. Identificatorul T poate fi nlocuit cu orice alt identificator valid. Prin T se definete generic tipul tabloului ale crui componente urmeaz s fie tiprite. Atunci cnd compilatorul detecteaz n program un apel al funciei PrintArray, tipul argumentului generic este nlocuit cu tipul parametrului actual i se creeaz o funcie pentru tiprirea tabloului cu acest tip. Noua funcie este apoi compilat. n exemplul de mai sus, sunt instaniate trei funcii PrintArray, cu argumente de tip int, double i char. Exemplu Instanierea pentru tipul de dat int este: void PrintArray(const int *array, const int count) { for(int i = 0; i < count; i++)

Programarea calculatoarelor i limbaje de programare I

cout << array[i] << " "; cout << endl; } Fiecare parametru formal de tip din definiia unui template de funcie apare, n mod normal, cel puin o dat n lista de parametri ai funciei cel puin o dat. Numele unui parametru formal de tip poate fi folosit o singur dat n lista de parametri ai header-ului unui template. n programul de mai sus, funcia PrintArray este apelat de trei ori cu argumentele a de tip int*, b de tip double* i c de tip char* pentru primul parametru. Apelul PrintArray(a, aCount); face ca apariiile lui T n funcia PrintArray s fie transformate n int i compilatorul instaniaz funcia PrintArray pentru T de tip int. n mod asemntor se ntmpl i pentru apelurile PrintArray(b, bCount); PrintArray(c, cCount); n acest program, mecanismul template-urilor face ca programatorul s nu mai fie nevoit s scrie trei funcii suprancrcate cu prototipurile void PrintArray(const int *, const int); void PrintArray(const double *, const int); void PrintArray(const char *, const int);

12.2

Tratarea excepiilor

Exist mai multe tehnici prin care pot fi tratate erorile care apar n timpul rulrii unui program. Cea mai folosit este aceea de a introduce n program secvene de cod adaptate prevenirii unor posibile situaii nedorite. Erorile sunt tratate n locul n care apar n program. Avantajul acestei abordri este c persoana care citete codul poate vedea modalitatea de prelucrare a erorii n vecintatea codului i poate determina dac metoda de tratare a excepiei este implementat corect. Dezavantajul este c prin aceast schem codul este oarecum poluat cu secvene de procesare a erorilor i devine mult mai greu de citit de un programator care este concentrat pe aplicaia nsi. Aceast metod face codul mult mai greu de neles i de meninut. Tratarea excepiilor n C++ permite programatorilor s nlture partea de tratare a excepiilor din secvena principal de program. Stilul C++ de tratare a excepiilor permite interceptarea tuturor excepiilor sau a tuturor excepiilor de un anumit tip. Aceasta face programul mult mai robust, reducnd probabilitatea de ntrerupere neplanificat a programului. Tratarea excepiilor se folosete n situaia n care programul poate s i continue rularea dup ce depete eroarea care provoac excepia.

Tratarea excepiilor folosind try, throw i catch


Tratarea excepiilor n C++ este o metod care se aplic atunci cnd funcia care detecteaz o eroare nu o i trateaz. Ea doar genereaz sau arunc excepia (throw). Nu se garanteaz c excepia va fi i tratat n afara funciei. Pentru aceasta, trebuie specificat o secven de cod care detecteaz sau prinde excepia i o trateaz. Programatorul trebuie s includ ntr-un bloc try codul care ar putea genera o eroare generatoare a unei excepii. Blocul try este urmat de unul sau mai multe

Programarea calculatoarelor i limbaje de programare I

blocuri catch. Fiecare bloc catch specific tipul excepiei pe care o poate detecta i trata. Dac excepia se potrivete cu tipul parametrului unuia dintre blocurile catch, se execut codul acelui catch. Dac nu este identificat niciun bloc catch care s trateze eroarea, se apeleaz funcia predefinit terminate care la rndul ei apeleaz n mod implicit funcia predefinit abort pentru ntreruperea programului. Dac ntr-un bloc try nu se genereaz nicio excepie, programul ruleaz ignornd blocurile catch asociate lui. Exemplu #include <iostream> #include <stdexcept> using std::cin; using std::cout; using std::endl; using std::runtime_error; double Fractie(int numarator, int numitor) { if(numitor == 0) throw runtime_error("Numitorul este 0"); return static_cast<double>(numarator)/numitor; } int main() { int numar1, numar2; double rezultat; cout << "Introduceti doi intregi (EOF pentru a termina): "; while(cin >> numar1 >> numar2) { try { rezultat = Fractie(numar1, numar2); cout << "Valoarea fractiei este: " << rezultat << endl; } catch(runtime_error e) { cout << "A aparut urmatoarea exceptie: " << e.what() << endl; } cout << "Introduceti doi intregi (EOF pentru a termina): "; } cout << endl; return 0; } Un exemplu de rulare a acestui program este urmtorul: Introduceti doi intregi (EOF pentru a termina): 3 4 Valoarea fractiei este: 0.75 Introduceti doi intregi (EOF pentru a termina): 5 0

Programarea calculatoarelor i limbaje de programare I

A aparut urmatoarea exceptie: Numitorul este 0 Introduceti doi intregi (EOF pentru a termina): CTRL-D Programul de mai sus calculeaz rezultatul mpririi a dou numere ntregi. Dac numitorul este 0, este declanat o excepie i se semnaleaz eroarea. Programul conine un bloc try care cuprinde codul care ar putea genera o excepie. Operaia de mprire a celor dou numere este implementat prin funcia Fractie care genereaz o excepie prin instruciunea throw atunci cnd numitorul este 0. Clasa runtime_error face parte din biblioteca standard i este declarat n fiierul header stdexcept. Este derivat din clasa de baz exception care face parte tot din biblioteca standard, fiind declarat n fiierul header exception i care este folosit pentru a declana excepii n C++. Declanarea excepiilor cu ajutorul lui runtime_error permite adugarea unui mesaj care poate fi citit prin metoda what(). Limbajul C++ ofer o ntreag gam de clase derivate din exception, destinate diverselor tipuri de excepii care se pot declana ntr-un program. La rndul su, programatorul poate s i defineasc propriile sale clase pentru declanarea excepiilor, derivndu-le, spre exemplu, din exception sau din clasele derivate din ea. n exemplul de mai sus, blocul try este urmat imediat de un catch care conine codul de tratare a excepiei de tip runtime_error. Atunci cnd ntr-un bloc try este generat o excepie, ea va fi tratat de blocul catch care este destinat acelui tip particular de excepie. Aadar, blocul catch din exemplu va trata doar excepiile de tip runtime_error. Dac n timpul execuiei codul din blocul try nu genereaz nicio excepie, toate blocurile catch asociate acestui try vor fi ignorate, iar execuia cotinu cu prima instruciune care urmeaz dup acestea.

Generarea unei excepii throw


Cuvntul cheie throw se folosete pentru a indica faptul c a aprut o excepie. Un throw specific, n mod obinuit, un operand. Operandul lui throw poate fi de orice tip. Dac este un obiect, l vom numi obiect excepie. Putem folosi, ns, i obiecte care nu sunt destinate tratrii excepiilor, i chiar i expresii de orice tip. Imediat ce a fost declanat o excepie, aceasta va fi detectat de cel mai apropiat secven de cod destinat tratrii excepiei respective. Secvenele de tratare a excepiilor generate ntr-un bloc try sunt listate imediat dup blocul try. Ca regul general pe care este bine s o urmm atunci cnd scriem programe, excepiile trebuie declanate doar n interiorul blocurilor try. O excepie care apare n afara unui try poate conduce la terminarea programului.

Tratarea unei excepii catch


Tratarea excepiilor se face prin blocuri catch. Fiecare astfel de bloc const din cuvntul cheie catch urmat, ntre paranteze rotunde, de tipul excepiei i, opional, de un nume de parametru. ntre acolade se gsete codul care trateaz excepia. Atunci cnd este detectat o excepie, se execut codul dintre acolade. Blocul catch definete un domeniu de accesibilitate propriu. Dac parametrul are nume, atunci el poate fi invocat n blocul catch. Dac nu are nume, el este 5

Programarea calculatoarelor i limbaje de programare I

specificat numai n scopul potrivirii dintre blocul catch i tipul excepiei creia i este destinat. O excepie care nu este detectat apeleaz n mod implicit funcia terminate() din biblioteca standard care, la rndul ei, termin programul prin apelul funciei abort(). Programatorul poate schimba comportamentul funciei terminate() facnd ca aceasta s apeleze o alt funcie. Numele noii funcii va fi trimis ca parametru funciei set_terminate. Un catch care este urmat ntre parantezele rotunde de trei puncte trateaz toate excepiile. Exemplu catch(...) Aceast opiune este plasat, de obicei, ca o ultim variant ntr-o serie de blocuri catch. Un bloc try urmat de mai multe blocuri catch are un comportament similar instruciunii switch care folosete o ramur case n funcie de valoarea expresiei testate.

Generarea unei noi excepii n catch


Uneori, tratarea unei excepii nu poate fi fcut n blocul catch care a detectat-o i atunci se poate decide ca excepia s fie transmis mai departe, lasnd tratarea ei pe seama altei secvene de cod (rethrowing an exception). Instruciunea throw; lanseaz din nou excepia. Exemplu #include <iostream> using std::cout; using std::endl; #include <exception> using std::exception; void ThrowException() { //Lanseaza o exceptie si o prinde imediat try { cout << "Functia ThrowException" << endl; throw exception(); //Genereaza exceptia } catch(exception e) { cout << "Exceptia tratata in ThrowException" << endl; throw; } cout << "Acest text nu este tiparit"; } int main() { try { 6

Programarea calculatoarelor i limbaje de programare I

ThrowException(); cout << "Acest text nu este tiparit"; } catch(exception e) { cout << "Exceptia tratata in main" << endl; } cout << "Programul continua dupa catch in main" << endl; return 0; } Acest program afieaz: Functia ThrowException Exceptia tratata in ThrowException Exceptia tratata in main Programul continua dupa catch in main Funcia ThrowException este apelat n blocul try din main. n blocul try din funcia ThrowException este generat o excepie de tip exception care este detectat imediat de blocul catch asociat acestui try. Aici, excepia este relansat, fiind tratat n blocul catch din main.

Specificarea excepiilor
Lista excepiilor care pot fi generate de o funcie se poate specifica astfel: int g(double h) throw(a, b, c) { //corpul functiei } Prin aceast list se restrnge gama excepiilor care pot fi declanate de funcia g la tipurile a, b, c. Dac funcia genereaz o alt excepie dect cele listate, se apeleaz automat funcia unexpected din biblioteca standard care, la rndul ei apeleaz funcia terminate. Comportmenul funciei unexpected poate fi modificat asemntor modului n care se intervine asupra funciei terminate, dar apelnd, de data aceasta, funcia set_unexpected. O funcie pentru care nu exist o astfel de list poate genera orice excepie.

S-ar putea să vă placă și