P. 1
Curs C++

Curs C++

|Views: 4,046|Likes:
Published by howhigh85200

More info:

Published by: howhigh85200 on Oct 25, 2010
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as DOC, PDF, TXT or read online from Scribd
See more
See less

08/18/2013

pdf

text

original

Sections

  • Structura unui program C++
  • Tipuri de date
  • Constante
  • Declararea variabilelor si a constantelor cu nume
  • Clase de memorie
  • Intrari/iesiri standard
  • Operatorii limbajului C++
  • Instructiunile limbajului C++
  • Definirea si apelarea functiilor
  • Transferul parametrilor
  • Clase de functii
  • Functii recursive
  • Declararea pointerilor
  • Operatii cu pointeri
  • Structuri dinamice de date
  • Liste liniare simplu inlantuite
  • Liste liniare dublu inlantuite
  • Liste circulare
  • Declararea variabilelor sir de caractere
  • Functii de intrare pentru siruri de caractere
  • Functii pentru prelucrarea sirurilor de caractere
  • Nivelul superior de prelucrare a fisierelor in C
  • Nivelul superior de prelucrare a fisierelor in C++
  • Conceptele de baza ale programarii orientate pe obiecte
  • Declararea claselor
  • Construirea si distrugerea obiectelor
  • Functii friend
  • Supraincarcarea si redefinirea operatorilor
  • Mostenirea
  • Poliformismul

Limbajul C

++
Tipuri de date, variabile, constante, functii de I/E
Structura unui program C++
Limbajul C, aparut la inceputul anilor 70, a fost creat de Denis Ritchie si Brian Keringhan si are urmatoarele caracteristici: • Este dotat cu un set puternic de operatori (inclusiv operatori de acces la nivel de bit); • Efectueaza controale sumare asupra atribuirilor, executand de cele mai multe ori si conversiile necesare; • Permite folosirea numai a subprogramelor de tip functie, dar o functie poate fi apelata la fel ca o procedura; • Este orientat pe pointeri; • Imbina caracteristicile limbajelor de ansamblare cu cele ale limbajelor de nivel inalt. Limbajul C++, creat de Bjarne Stroustrup, poate fi privit ca o extensie a limbajului C++ care permite programarea pe obiecte. Un program C++ este alcatuit exclusiv din functii, dintre care una este principala si are numele main. Deci, fiecare program contine cel putin o functie si anume functia main. De exemplu, programul urmator afiseaza textul inclus intre ghilimele:
#include <stdio.h> void main() { printf(“Acesta este textul ce se va afisa”); }

Prima linie de cod indica faptul ca se va apela o functie, printf (afiseaza o informatie), care se gaseste in fisierul stdio.h. Un astfel de fisier se numeste fisier header (de aici si extensia .h). Fisierele header grupeaza prototipurile functiilor inrudite care pot fi apelate in C++. De exemplu fisierul header stdio.h contine prototipurile functiilor de citire/scriere. Cuvantul rezervat void din fata numelui functiei main indica faptul ca functia nu va returna nici un rezultat. Deoarece, o functie contine cel putin o instructiune bloc (numita si instructiune compusa) este obligatoriu includerea unei perechi de acolade. Fiecare instructiune dintr-un program C++ trebuie sa se termine cu caracterul punct si virgula (;). Limbajul C (C++) nu recunoaste sfarsitul liniei ca terminator. Aceasta inseamna ca nu exista restrictii in ceea ce priveste pozitia unei instructiuni in linie. De asemenea, pe o linie se pot plasa doua sau mai multe instructiuni. In procesul de transformare a textului sursa in program executabil apare o faza noua, numita preprocesare, care se executa inaintea compilarii. In faza de preprocesare fisierele header precizate sunt incluse in textul sursa. Din punct de vedere sintactic, un program C++ este o secventa de caractere ASCII, format din unitati lexicale separate prin spatii. Unitatile lexicale pot fi grupate astfel: • Cuvinte cheie – cuvinte care au un rol bine stabilit; ele nu pot fi folosite in alt context. if, while, for, do, break, continue etc. sunt cuvinte cheie care desemneaza instructiuni ale limbaului C (C++) si ele nu pot fi utilizate in alte scopuri (de exemplu, ca identificatori). • Identificatori – se folosesc pentru a denumi variabilele, functii etc. Identificatorii se formeaza cu ajutorul literelor mari si mici ale alfabetului (inclusiv liniuta de subliniere) si a cifrelor. Trebuie sa inceapa cu o litera si sa nu corespunda unui cuvant rezervat. Limbajul face deosebirea dintre literele mari si mici folosite intr-un identificator. Un identificator poate avea un numar nelimitat de caractere dar numai primele 32 caractere 1

sunt semnificative. De exemplu, x, y, pf, factorial, pi, sort_asc sunt identificatori corecti, in timp ce 1sort nu este un identificator valid deoarece incepe cu o cifra. • Constante – sunt valori numerce (5, 25, 56.7), caractere (‘A’, ‘c’, ‘x”) si siruri de caractere (“Introduceti un numar”, “Rezultatele sunt;’), care nu se pot modifica pe timpul prelucrarilor; • Operatori – reprezinta simboluri sau cuvinte care precizeaza operatia care se va executa. De exemplu, + este simbolul operatiei de adunare, * este simbolul operatiei de inmultire, ++ este simbolul operatiei de incrementare, sizeof este operatorul pentru determinarea spatiului de memorie necesar unei variabile etc. Un program poate contine si comentarii, care pot fi plasate oriunde in textul sursa, respecatand urmatoarele reguli: • Pentru a crea un comenatriu care sa ocupe una sau mai multe linii, se incadreaza textul intre perechile de caractere ‘/*’ si ‘*/’. De exemplu:
/* Acesta este o linie de comentariu */

Pentru a introduce un comentariu intr-o linie de program, la sfarsitul acesteia, se plaseaza in fata textului caracterele ‘//’.
printf(“Rezultatul”); // Comentariu pe linia de program

Tipuri de date
Una din primele intrebari pe care trebuie sa ni le punem in legatura cu un limbaj de programare, se refera la tipurile de date utilizabile pentru reprezentarea obiectelor ce intervin in problemele pe care le rezolvam. Aceste obiecte pot fi de complexitate diferita, pornind insa intordeauna de la elemente simple, de baza (numere, caractere, valori de adevar), care pot fi organizate in structuri din ce in ce mai complexe. De aceea raspunsul la intrebarea noastra va contine doua parti: cea referitoare la tipurile de date predefinite (fundamentale) ale limbajului si cea privind mecanismele ce sunt oferite pentru reprezentarea valorilor unor obiecte de alta natura decat cele direct reprezentabile prin intermediul tipurilor predefinite. Principalele tipuri de date predefinite (fundamentale) in limbajul C (C++) sunt tipurile aritmetice: intregi si reale. Aceasta optiune se bazeaza pe faptul ca entitatile de alta natura (caractere, valori de adevar, culori, stari etc.) pot fi codificate numeric, iar operatiile specifice lor pot fi reprezentate prin prelucrari, simple sau compuse, efectuate asupra codurilor numerice adoptate. Astfel, pentru reprezentarea valorilor logice se recurge la o conventie foarte simpla si clara: fals se codifica prin valoarea intreaga 0, iar adevarat – printr-o valoare intreaga nenula (in cazul evaluarii unei expresii relationale sau logice, aceasta valoare este 1). Limbajul C++ are o deosebita putere de reprezentare, datorita mecanismelor pe care le ofera pentru declararea de tipuri derivate, pornind de la cele fundamentale. Astfel, pot fi declarate: • Tipuri structurate – tablouri, structuri, uniuni; • Tipuri functie - un tip functie este caracterizat atat prin tipul rezultatului furnizat, cat si prin numarul si tipul argumentelor necesare pentru obtinerea rezultatului; • Tipuri pointer - ofera posibilitatea de adresare indirecta la entitati de diferite tipuri, deoarece o valoare de tip pointer reprezinta adresa unei entitati definite in program.

Declaratii de tip
Tratarea corecta a unei entitati presupune cunoasterea tipului sau. Daca, in cazul constantelor explicite, tipul poate fi dedus din modul in care sunt ele precizate (de exemplu 5 este o constanta intreaga, 3.5 este o constanta reala, ‘a’ este o constanta caracter), in cazul entitatilor identificate prin nume simbolice (variabile, functii, constante simbolice), tipul lor trebuie precizat anterior primei utilizari printr-o declaratie sau definitie corespunzatoare. Printr-un tip de data se intelege un ansamblu format din trei elemente:  multimea valorilor admise;  modalitatea de reprezentare a acestora in memoria interna; 2

 setul de operatii permise cu valorile respective. Declaratiile de variabile sunt de cele mai multe ori, definitii, deoarece pe langa asocierea unui nume determina si alocarea spatiului de memorie necesar pastrarii valorilor. Definitia unei variabile poate fi completata prin specificarea unei valori de initializare. Momentul initializarii variabilelor este insa conditionat de clasa de memorare a acestora. O declaratie se poate referi la una sau mai multe variabile precizand clasa de memorare, tipul de data, identificatorii si eventual valorile de initializare ale acestora, folosind urmatoarea sintaxa:
[clasa_memorare] tip variabila [=valoare] [,variabila [=valoare]…]

Intr-o declaratie C++ se poate preciza variabile de tipuri diferite, insa derivate din acelasi tip de baza. De exemplu,
char c=’A’, *p, s[10]

se refera la trei variabile de tipuri diferite: caracterul c, pointerul p si tabloul s de cel mult 10 caractere.

Tipul aritmetic
In limbajul C++, ca de altfel in majoritatea limbajelor de programare, pentru a reprezenta valorile numerice exista mai multe tipuri de date predefinite (numite si tipuri fundamentale), care se pot grupa in doua categorii: 1. Tipuri intregi – Se folosesc pentru reprezentarea numerelor intregi. Tipurile predefinite din limbajul C++ ce pot fi utiliza pentru a reprezenta valorile intregi sunt:  unsigned char (caracter fara semn) – ocupa 8 biti si poate lua valori cuprinse intre 0 si 255;  char (caracter) – ocupa 8 biti si poate avea valori intre –128 si 127;  unsigned int (intreg fara semn) – ocupa 16 biti si poate lua valori intre 0 si 65535;  short int (intreg scurt) – ocupa 16 biti si permite valori intre –32768 si 32767;  int (Intreg) – ocupa de regula 16 biti (lungimea poate diferi de la o implementare la alta) si ia valori intre –32768 si 32767;  unsigned long (intreg lung fara semn) – ocupa 32 de biti si ia valori intre 0 si 4294967295;  long (intreg lung cu semn) - ocupa 32 biti (4 bytes) si poate avea valori cuprinse intre –2147483648 si 2147483647.
Nota: Caracterele sunt considerate tot numere intregi, deoarece in memorie ele sunt reprezentate prin valorile numerice ale codului ASCII.

2. Tipuri reale – Sunt folosite la reprezentarea numerelor reale. Tipurile fundamentale din limbajul C++ care pot fi utilizate pentru a reprezenta valorile reale sunt:  float (virgula mobila simpla precizie) – ocupa 32 biti (4 bytes) si ia valori intre 3.4x10-38 si 3.4x1038;  double (virgula mobila dubla precizie) – ocupa 64 biti (8 bytes) si poate avea valori intre 1.7x10-308 si 1.7x10308;  long double (virgula mobila extinsa) – ocupa 80 biti (10 bytes) si permite valori intre 3.4x10-4932 si 1.1x104932.

Tipul enumerare
Tipurile enumerare sunt cazuri particulare ale tipurilor intregi. Ele se folosesc pentru a realiza o reprezentare comoda si sugestiva a obiectelor ale caror valori se pot identifica printr-un numar finit de nume simbolice. De exemplu, unitatile de masura ale unui produs pot fi: metru, litru, gram, bucata; mijlocul de transport pentru o deplasare poate fi: trenul, avionul, autocarul, autoturismul. Valorile constante identificate prin nume simbolice specifice problemei trebuie codificate prin entitati admise de limbajul de programare, deci prin constante numerice. Insa prin utilizarea directa a codificarii numerice se pierde din claritatea asigurata de folosirea numelor simbolice. Solutia acestei probleme este oferita de tipurile enumerare, care in esenta, 3

luna are valoarea 3. care specifica identificatorii pentru valorile numerice asociate de compilator. char etc. float. variabilele tipului enumerare sunt tratate ca simple date de tip int. 2. Dupa aceasta declaratie. Daca aceasta lista lipseste. martie. iunie. septembrie. conform declaratiei de mai sus. cuvantul Real poate fi folosit pentru a identifica date de tip float. atunci variabilele tipului enumerare vor fi precizate ulterior. • nume_tip – reprezinta identificatorul care se atribuie tipului precizat prin tip. se poate inlocui cu una mai sugestiva: luna = martie. octombrie. care poate lipsi. Asignarea unui nume pentru tipurile de date In limbajul C (C++) se poate atribui un nume unui tip. • lista_nume_constante – reprezinta o lista de constante simbolice. Exemple: 1. deoarece. atunci pentru a putea folosi acest tip este necesar sa declaram o variabila de forma: enum Boolean exista. De exemplu. Deci declaratia: Real x. Fie declaratia: typdef float Real. iulie. dar in acest caz va fi prezent cel putin un nume de variabila in lista_variabile. sau exista = true. numarul lunii poate fi inlocuit prin denumirea lunii respective. Daca in program exista declaratia: typedef int Intreg.declara o multime de constante simbolice carora li se asociaza coduri numerice de tip intreg. Dupa atribuirea unui nume tipului de date. incepand cu 0. noiembrie. indiferent daca el este un tip predefinit sau definit de utilizator: Pentru aceasta se foloseste o constructie de forma: typedef tip nume_tip. este echivalenta cu: float x. august. februarie. In C. 4 . true}. In continuare se pot folosi expresii de forma: exista = false. Pentru a declara un tip enumerare se foloseste sintaxa: enum [nume_tip] {lista_nume_constante} [lista_variabile]. decembrie} luna. mai. aprilie. Avand declaratia: enum {ilegal. o atribuire de forma: luna = 3. 2. la fel cum se folosesc in declaratii cuvintele cheie ale tipurilor predefinite: int. separate prin virgula. Exemple: 1. • lista_variabile – reprezinta o lista formata din una sau mai multe nume de variabile ale tipului enumerare precizat prin nume_tip. unde: • tip – este fie un tip predefinit (fundamental) fie un tip definit de utilizator. unde: • nume_tip – reprezinta identificatorul tipului enumerare. ianuarie. Fie declaratia: enum Boolean {false. identificatorul respectiv se poate folosi pentru a declara variabile ale acelui tip. Nota: Compilatorul C nu controleaza datele de tip enumerare si din acest motiv se pot scrie expresii care sa nu corespunda scopului pentru care a fost definit tipul de date.

Constante sir de caractere. 0x7FA este constanta hexazecimala 7FA. De exemplu. 141(8) si 61(16) si folosind o secventa escape. Ele sunt declarate plasand in fata numarului caracterele 0X sau 0x. De exemplu. aprilie. Boolean exista. in timp ce valorile cuprinse intre 32768 si 2147463647 sunt memorate folosind tipul long. Constante intregi. In continuare. martie. true} Boolean. De exemplu. valorile cuprinse intre 0 si 32767 sunt memorate folosind tipul int. septembrie. ‘5’ etc.  Octale (in baza 8). Mecanismul implicit tine cont de valoarea constantei. Mecanismul explicit consta in a forta ca o anumita constanta sa fie memorata intr-un anumit tip de data intreaga folosind un sufix. octombrie. “Acest sir contine secventa escape \n care va determina scrierea constantei pe doua linii”. se va folosi sufixul L sau l. Constantele caracter pot fi declarate si folosind o secventa escape. Ele se clasifica astfel:  Zecimale.atunci declaratia: Intreg y. Constante caracter.  In cazul caracterelor care nu au simboluri grafice. constanta ‘a’ poate fi scrisa sub forma \141 sau \x61 (in cazul folosirii sistemului hexazecimal. Se declara precedand numarul de un 0 nesemnificativ. De exemplu. Sirul nul (numit si sir de lungime zero) este sirul care nu contine nici un caracter. ‘A’. februarie. este echivalenta cu: int x.  Hexazecimale (in baza 16).este un operator unar). litera ‘a’ are codul ASCII 97(10). La declararea tipului enumerare se poate folosi cuvantul cheie typedef astfel: typedef enum {lista_nume_constante} nume_tip. De exemplu. Se compun dintr-o succesiune de mai multe caractere incluse intre ghilimele. 1. si se foloseste in urmatoarele cazuri:  Cand vrem sa folosim codul ASCII in octal sau hexazecimal al caracterului in locul caracterului propriu-zis. Constantele sir de caractere pot contine si secvente escape. variabilele tipului enumerare pot fi declarate folosind urmatoare: nume_tip lista_variabile. “” (este sirul nul). sunt constante caracter. decembrie} DenLuna. De exemplu. Secventa escape incepe cu caracterul backslash (\). 12(8) sau A(16)) poate fi declarat \12 sau \xA. constanta newline poate fi scrisa si sub forma \n. Observatie: O constanta intreaga este pozitiva. “Acesta este o constanta sir de caractere”. typedef enum {false. ianuarie. Constanta de tip caracter reprezinta un caracter incadrat intre apostrofuri. 5 . De exemplu. iulie. iunie. in C++ exista si notatii speciale sub forma unei secvente escape. noiembrie. caracterul newline (codul 10(10). care va fi evaluata. avem de fapt o expresie constanta (. 0143 reprezinta constanta octala 143. 3. Constanta este memorata dupa un mecanism implicit (fara interventia programatorului) sau explicit. Daca se foloseste semnul – in fata unei constante. De exemplu. De exemplu. daca vrem ca valoarea 234 sa fie memorata intr-un tip long. Constante In limbajul C++ exista mai multe tipuri de constante. Folosind acest mod de declarare. Pentru anumite caractere. august. mai. valoarea este precedata de caracterul ‘x’). DenLuna luna. exemplele prezentate mai sus pot fi scrise astfel: typedef enum {ilegal. adica se va scrie 234L sau 234l. 2. dar in continuare se vor prezenta doar cele corespunzatoare tipurilor definite (fundamentale).

pe langa date constante.7e10. Cel mai simplu mod de referire a unei date variabile este acela de a denumi data respectiva. care isi pot modifica valorile pe timpul executiei programului. de exemplu: int x = 25 * 3.5. nume2 … . se spune ca ea este o data izolata. y = 1.este tipul de data al variabilelor. Unei date izolate ii corespunde un tip.14f sunt constante reale.  float pi = 4. 3.valoarea de initializare a variabilei. Pentru a forta memorarea intr-un tip float se foloseste sufixul F sau f.1415.4. atunci ele pot fi grupate pentru a fi manipulate mai usor. lit = 65. unde: • tip data . Observatie: La declararea variabilelor. Numele datei izolate se spune ca reprezinta o variabila simpla. Grupului de date i se asociaza un nume care va fi folosit la adresarea atat a intregului grup cat si a elementelor ce compun grupul respectiv. Prima variabila este initializata cu codul caracterului D.1415. iar pentru tipul long double sufixul L sau l.numele variabilelor. Ele se memoreaza implicit folosind tipul double. De exemplu. Corespondenta dintre numele unei variabile si tipul ei se stabileste printr-o declaratie. iar a doua cu valoarea 65 (codul ASCII al literei A). Variabila x nu este initializata. Numele datei va permite accesul la valoarea ei si schimbarea valorii atunci cand este necesar. In cazul in care o data declarata nu are legaturi cu alte date (de exemplu. S-au declarat doua variabile de tip caracter ok si lit. S-au declarat variabilele x si y de tip intreg. 1. • constanta . in timp ce variabila y este initializata cu 1.  char ok = ’D’. nume2[= constanta]] …. Constante reale. in locul constantelor de initializare se pot folosi expresii constante (expresii formate din constante si operatori). 6 .754. Declararea variabilelor si a constantelor cu nume Intr-un program. dar nu si tipul ei. 3. • nume1. Variabila pi de tip real simpla precizie a fost initializata cu valoarea 3. In cursul executiei programului se pot modifica valorile unei variabile simple. Numele grupului de date reprezinta o variabila structurata. Exemple:  int x. legaturi de ordine). Daca intre datele ce se prelucreaza se poate satbili o legatura. se folosesc si date variabile. 1. Variabile simple Declararea unei variabile simple se realizeaza respectand urmatoarea sintaxa: tip_data nume1 [= constanta][.

Deci vect[0] are ca valoare valoarea primului element al tabloului. adica adresa lui vect[0]. elementele se vor referi prin: vect[0]. dmat[0][19] pentru elementele din prima linie a tabloului. unde: • tip_comun . dmat[1][0]. o multime ordonata de intregi. variabilele structurate pot fi clasificate in tablouri. in cazul primului exemplu (care este un vector). Un tablou. care se compune din numele tabloului urmat de valorile indicilor. Referirea la elementele unui tablou se face folosind o variabila cu indici.Variabile structurate De foarte multe ori. …. In cazul celui de-al doilea exemplu (care este o matrice) referirea se va face astfel: dmat[0][0]. structuri si reuniuni. acestei variabile i se aloca 20 bytes (10 * 2 bytes). intr-un program este necesar sa prelucram grupuri de date. O astfel de grupa se spune ca formeaza un tablou (numit si masiv). trebuie declarat inainte de a fi utilizat. vect este un simbol a carei valoare este adresa primului sau element. defineste un tablou bidimensional de tip double. Atentie: In limbajul C (C++) valoarea inferioara a indicilor este intotdeauna egala cu 0. dmat[0][1]. iar tablourile cu doua dimensiuni se numesc matrici. respectiv dmat[9][0]. Exemple: • int vect[10]. Tablourile unidimensionale se mai numesc si vectori. In functie de modul de stabilire a legaturilor dintre datele ce vor forma un grup. inclus intre paranteze drepte. sunt necesari mai multi indici pentru referirea lor. care este primul element al tabloului. dmat[9][19] pentru elementele din ultima linie a tabloului. dmat[1][19] pentru elementele din linia a doua a tabloului. limitaI-1. iar vect are ca valoare adresa acestui element. 1. numele tabloului si numarul maxim de elemente din fiecare dimensiune. iar daca pentru la referirea elementelor se folosesc mai multi indici. iar cea superioara este mai mica cu o unitate fata de numarul maxim de elemente precizat pentru dimensiunea respectiva. 7 . la fel ca orice variabila simpla. …. …. …. adica al I-lea indice poate lua valorile 0. Tablouri Cel mai simplu procedeu ce se poate utiliza la gruparea datelor de acelasi tip il constituie considerarea lor ca formand o multime ordonata de elemente. Deci. Declaratia de tablou trebuie sa indice tipul comun al elementelor sale. Deci o declaratie de tablou trebuie sa fie de forma: tip_comun nume_tablou[limita1][limita2]…[limitaN]. De exemplu. reprezinta un tablou de intregi.specifica tipul comun al elementelor tabloului. Limitele superioare pot fi constante sau expresii constante. vect[9]. vect[1]. se spune ca tabloul este n-dimensional. Tipul comun al elementelor unui tablou este si tipul tabloului. …. defineste tabloul vect de 10 elemente de tip int. • nume_tablou – indica numele tabloului. fiecare indice fiind reprezentat printr-o expresie inclusa intre paranteze drepte. caruia i se poate asocia un nume. In memorie. El reprezinta o matrice de 10 linii si 20 coloane fiecare si ocupa 800 bytes (10 * 20 * 4 bytes). 2. Atunci cand elementele care se grupeaza intr-un tablou sunt ele insele tablouri. Daca se foloseste un singur indice pentru referirea la elementele unui tablou. • double dmat[10][20]. se spune ca tabloul este unidimensional. • limitaI – reprezinta numarul maxim de elemente de pe dimensiunea I. care pot fi referite folosind indici.

c. c[40]. care se pot initializa cu siruri de caractere incluse intre ghilimele. ce se va folosi la referirea acestora. Expresiile folosite pentru initializare trebuie sa fie constante. Exemple:  Declaratorul int vect[5] = {34. De exemplu. o Un caz aparte este cel al tablourilor de caractere. In acest caz datele respective formeaza o structura. este echivalent cu char tabCh[5] = {’B’. inclusiv caracterul ‘\0’ (terminatorul de sir atasat automat de compilator) initializeaza cate un element al tabloului. care va reprezenta de fapt un nume de tip de data definit de utilizator. separate prin virgule si incluse intre acolade.9}. Cand intr-un program este necesara declararea mai multe tablouri avand aceeasi dimensiune si acelasi tip comun de date este indicat sa se declare mai intai un tip utilizator folosind cuvantul cheie typedef si apoi sa declaram variabile ale tipului respectiv. caz in care se foloseste un declarator de forma: nume_tablou[limita1]…[limitaN] = valoare_initiala. dar interpretarea depinde de tipul de baza al tabloului. Aceste trei date nu trebuie sa fie neaparat de acelasi tip.2. Vector a. realizand-se astfel o grupare de date neomogene din punct de vedere al tipului. 75}.3}. daca ea se preciza prin denumire. {4. Un exemplu simplu de structura este data calendaristica. ziua si anul pot fi intregi (tipul int).  Declaratorul int matrix[3][3] = {{1. b.2. descriere_membru_2.8. Relatia de legatura dintre datele grupate intr-o structura este definita de utilizator cu scopul de a simplifica manipularea respectivei grupe de date pe timpul prelucrarilor. in timp ce luna poate fi de tip nenumeric (caracter). 8 . 89. 56. fiecarei componente a structurii i se asociaza un nume.5}}. 90. atunci restul elementelor sunt initializate cu 0 (toti bitii sunt pusi pe 0). se pot utiliza urmatoarele declaratii: typedef double Vector[40]. De exemplu. indica un tablou de 5 intregi si initializeaza elementele acestuia cu valorile precizate. {7.’u’. b[40].’n’. Intr-o structura pot fi incluse date de tipuri diferite. iar in partea de initializare sunt mai putine valori decat elementele tabloului. in locul declaratiei: double a[40]. o Daca in lista sunt mai multe valori decat numarul de elemente al tabloului se produce o eroare la compilare (se afiseaza mesajul de eroare “Too many initializers”). indica o matrice si initializeaza elementele lui. Fiecare pereche de acolade din interior indica lista valorilor de initializare ale elementelor unei linii. // Declara tipul utilizator Vector // Declara variabile ale tipului Vector Structuri Un alt mod de a grupa datele este acela care ia in considerare prezenta unei relatii de “inrudire” intre datele care se grupeaza. Observatii: o Daca dimensiunea tabloului este precizata. char tabCh[] = “Buna”.’a’.Initializarea tablourilor Declararea unui tablou poate fi insotita de initializarea sa. De exemplu. Pentru declararea unei date structurate se foloseste urmatorul format: struct nume_structura { descriere_membru_1.’\0’}. luna si an. De asemenea. unde: • valoare_initiala este lista valorilor de initializare ale elementelor sale. care poate fi considerata ca find o grupa formata din trei date inrudite: zi. In acest caz fiecare element al sirului. Unei astfel de structuri i se asociaza un identificator.

• descriere_membru_i . int an. care poate fi o declaratie de variabila sau o alta declaratie de structura. definit de utilizator.… descriere_membru_n. int an. }. pentru a defini o structura corespunzatoare datelor calendaristice se poate utiliza o declaratie de forma: struct data_calendar { unsigned int luna. Structurile incluse pot fi descrise fie in interiorul structurii de baza fie inaintea acesteia. El nu impune rezervarea de memorie interna. De exemplu. } elev[21]. int nota[7]. Pentru a declara variabile apartinand unui tip structura se poate folosi unul din urmatoarele formate: struct data_calendar data1. Accesul la un membru al unei variabile de tip structura se face dupa urmatorul model: variabila. iar intregul tablou va reprezenta un fisier. iar data1.luna desemneaza luna. structura struct persoana { char nume[25]. in timp ce elev[2].zi indica ziua din variabila structurata data1. De exemplu. data2. De exemplu. sau data_calendar data1. Cand este necesara cunoasterea spatiului ocupat de o variabila de tip structura este indicat sa se foloseasca functia sizeof() pentru a determina acest spatiu.note[1] desemneaza a doua nota a celui de-al treilea elev. } data1. char oras[25] 9 .reprezinta declaratia componentelor ce fac parte din structura. O constructie de forma elev[15]. Memoria ocupata de o variabila de tip structura se determina tinand cont de spatiul necesar stocarii fiecarui membru a structurii si a unor eventuale cerinte de aliniere in memorie.reprezinta numele tipului de data. De exemplu. }. variabile carora insa li se va rezerva spatiul de memorie necesar. unde: • nume_structura .nume desemneaza numele celui de-al 16-elev. data2. O variabila de tip structura poate fi declarata si ca tablou. tablourile de structuri sunt asociate in general cu conceptul de fisier in memorie. deoarece lungimea structurii poate depasi suma dimensiunilor componentelor. dar permite declararea ulterioara a unor variabile apartinand acestui tip. Un membru al unei structuri poate fi el insasi o structura. Numele structurii se comporta ca un tip special de declarator. definirea: struct persoana { char nume[25]. aloca spatiul necesar pentru stocarea informatiilor (numele si cel mult 7 note) pentru 21 de elevi. data1. zi. zi.membru Acest mod de adresare se numeste calificare. Declararea unor variabile de tip structura se poate face concomitent cu declararea tipului de date astfel: struct data_calendar { unsigned int luna. struct domiciliu { char strada[30]. data2. deoarece un element al tabloului va contine un articol (o inregistrare).

} adresa. • Initializarea unui tablou cu date de tip persoana se poate face astfel: Reuniuni Limbajul C (C++) pune la dispozitia programatorilor facilitati de a pastra intr-o zona de memorie date de tipuri diferite. } prof. }. atunci variabilei w i se aloca 4 bytes (32 biti) si in zona respectiva se vor stoca valori numerice intregi reprezentate prin complement fata de 2. De exemplu. De exemplu. Initializarea structurilor Declaratia unei variabile de tip structura poate fi insotita si de specificarea unor valori de initializare. } atunci • Initializarea unei variabile simple de tip persoana se poate realiza astfel: struct persoana pers = {“Alexandru Ion”. daca avem declaratia: long w. {“Vasilescu Vasile”. Formatele precizate in cazul datelor de tip struct sunt valabile si pentru datele de tip union. insa inlocuind cuvantul cheie struct cu union putem “grupa” datele carora li se va aloca aceeasi zona de memorie. 3500}. // numele de membru in structura exterioara char studii[15]. continand cate o valoare pentru fiecare camp (membru) al structurii. // variabila adresa nu mai trebuie declarata aici struct persoana { char nume[25]. 35. De foarte multe ori intr-o aplicatie este de dorit ca in aceeasi zona de memorie sa putem pastra date de tipuri diferite. 3500}. poate fi scris si sub forma: struct domiciliu { char strada[30]. long retributie. Folosind o constructie similara datelor de tip struct. Exemplu: Fie structura: struct persoana { char nume[30]. 45. sa presupunem ca dupa un timp nu mai avem nevoie de variabila w si dorim ca zona de memorie alocata ei sa o utilizam pentru a stoca date apartinand altui tip: int. Lista se include intre acolade.char judet[2]. struct persoana grup[] = {{“Ionescu Ion”. char sau chiar float. domiciliu adresa. Din cele prezentate pana acum a rezultat ca unei date i se aloca o zona de memorie corespunzatoare tipului datei respective si ca in zona respectiva se pot pastra numai date ale acelui tip. Reutilizarea unor zone de memorie conduce la economisirea memoriei. 50. {“Mihai Marin”. } prof. int etate. char studii[15]. 35. char oras[25] char judet[2]. O astfel de grupare se numeste reuniune. 2750} }. 4500}. separate prin virgule. 10 . Valorile de initializarea a unei structuri se precizeaza printr-o lista de valori de initializarea. Tipul introdus prin union este un tip definit de utilizator ca si cel definit de struct.

2. buf. Exemple: const n = 50. variabilei nr i se va aloca o zona de memorie egala cu cea mai mare zona dintre cele necesare pastrarii componentelor (in cazul exemplului anterior zona alocata va fi 8 bytes. la un moment dat al executiei programului. Fie declaratia: union numar { int x. spre deosebire de structuri.Exemple: 1. nume .x. Ele sunt alocate in aceeasi zona de memorie si din acest motiv. atunci nr este o reuniune de tip numar. Fie declaratiile: typedef union { char nume[50]. Constante simbolice Pentru a declara constante cu nume (asa numitele constante simbolice) se foloseste urmatoarea sintaxa: const [tip_data] nume = valoare unde: • • • Nota: tip_data – este o constructie optionala care reprezinta tipul constantei. } nr. constanta este considerata ca fiind de tip int. adica sub forma: nr. daca este absenta.nume[6].reprezinta valoarea constantei. este indicat sa se foloseasca un indicator care sa defineasca tipul de data pastrat in fiecare moment in zona alocata reuniunii.1415. long y. are un alt sens. atunci variabila buf este o reuniune de tip Zona pentru care se vor aloca 50 bytes.ch. in fiecare moment al executiei. Componentele reuniunii vor fi referite folosind notatia cu punct. deoarece programatorul trebuie sa cunoasca. sau valori numerice intregi de tip int sau long la care ne putem referi prin buf.nume[0]. double r.reprezinta numele asociat constantei. nr.cod. deoarece are rolul de a modifica un enunt care. buf.nume. int nrmat. spatiul necesar pastrarii datelor de tip double). In faza de compilare. valoare . In aceasta zona se pot pastra siruri de caractere la care ne vom pute referi sub forma buf. initial. Cuvantul cheie const este numit modificator. Zona buf. ce componenta a reuniunii se gaseste in zona alocata ei. nu pot fi initializate. La utilizarea reuniunilor se pot ivi unele probleme. numai una dintre aceste date este definita (alocata).r si nr. Pentru a evita erorile ce pot sa apara pe timpul utilizarii unei reuniuni. char ch. nr. const float pi = 3.nrmat respectiv buf. ca si in cazul structurilor. const ok = ‘D’. long cod. 11 . Nota: Reuniunile.y. } Zona.

siruri de caractere). deoarece ele sunt alocate pe timpul executiei in memoria dinamica. caracter sau pointer (adresa). la compilare. variabile globale. tipul memoriei folosite. Aceasta declaratie determina ca identificarea efectiva a variabilei sa se faca in faza de linkeditare. Aceasta clasa se poate asocia variabilelor de tip intreg. Nu sunt initializate implicit de catre compilator.corespunde variabilelor comune si se defineste implicit (cand nu este precizata o alta clasa) sau explicit prin cuvantul cheie auto. variabilele se definesc obligatoriu in afara oricarei functii. In limbajul C++ exista urmatoarele clase de memorie: • automatic . • extern . desi ele exista si dupa iesirea din bloc. De regula variabilele din aceasta clasa sunt definite intr-un fisier si folosite in alt fisier. caz in care ele sunt recunoscute din acel loc pana la sfarsitul fisierului sursa. Pentru a declara in mod explicit variabile statice se foloseste cuvantul cheie static. dar initializarea se va face la fiecare intrare in blocul respectiv. o in afara oricarei alte functii.determina ca variabilele sa fie pastrate in registrele calculatorului. Variabilele din aceasta clasa se definesc numai in interiorul unui bloc. • register . Pentru a fi vazute in alte functii.se defineste in raport cu impartirea unui program in mai multe fisiere si functii. Declaratia extern pentru variabile poate sa apara: o in interiorul unui bloc. Utilizarea lor inainte de definire presupune ca ele sa fie in prealabil declarate prin cuvantul cheie extern.Clase de memorie Clasele de memorie sunt asociate variabilelor pentru a evidentia momentul alocarii. Cand sunt definite in afara oricarui bloc. ele nu pot fi accesate. static double t = 5. iar spatiul ocupat de ele este eliberat la iesirea din bloc. cand sunt reunite toate fisierele. Deci. cand este insotita de specificatorul extern. Pot fi initializate explicit la definire. cand nu poarta specificatorul extern si la utilizare. Se face distinctie intre definirea si declararea variabilelor din aceasta clasa. Variabilele automatice sunt recunoscute doar in blocul unde sunt definite. • static . Cuvantul cheie prin care se indica apartenenta la aceasta clasa de memorie este register. Cand sunt definite in interiorul unui bloc. 12 . dar acest lucru se face tot o singura data. variabila externa apare intotdeauna in doua ipostaze: la definire. ca in cazul oricarei variabile.corespunde variabilelor permanente pentru un bloc sau o functie. variabilele statice au caracter global. de exemplu. Initializarea variabilelor statice se poate realiza si in mod explicit. Clasa static este implicita pentru unele categorii de date (tablouri cu initializari. durata de viata si domeniul de vizibilitate. compilabile separat. pastrandu-si continutul intre revenirile succesive in blocul respectiv.6. domeniul de vizibilitate al variabilelor se reduce la acel bloc. Ele sunt alocate in memoria statica si la compilare sunt initializate automat cu 0. caz in care ele sunt recunoscute numai acolo.

marca zecimala apare in aceste 13 . se lasa un spatiu in locul semnului. Ele sunt deschise automat la lansarea in executie a programului si sunt inchise automat la terminarea executiei programului. Caracterele permise in aceasta situatie sunt: csdiu Fara efect 0 Adauga 0 la un argument diferit de zero x or X Adauga 0x (sau 0X) argumentului eEf Rezultatul contine intodeauna marca zecimala. Stream-ul este un concept abstract.este format din una sau mai multe expresii separate prin virgula. completandu-se cu spatii la dreapta.reprezinta un sir de caractere ce defineste textul si formatul datelor de afisat. unde: • format . • Aplica fiecarui argument specificatorul de format continut in argumentul format. • Afiseaza pe ecran data in formatul specificat. + Valorile numerice vor fi precedate intodeauna de semnul + sau minus. # Indica faptul ca argumentul este convertit intr-un format alternativ.precizie] tip unde: Component a indicator Ce controleaza sau specifica Parametru optional. Un specificator de format are urmatoarea forma: % [indicatori] [lungime] [.h. prin care se intelege orice flux de date de la o sursa la o destinatie.Intrari/iesiri standard Atat in C cat si in C++ se foloseste foarte des notiunea de stream. cand datele sunt preluate de la tastatura se considera ca ele sunt citite din fisierul standard stdin. Ca indicatori se pot folosi urmatoarele caractere: Rezultatul este aliniat la stanga. Functia printf executa urmatoarele: • Accepta un sir de argumente. Parametrul format contine atat textul care se va afisa ca atare in pozitiile indicate cat si specificatorii de format ce definesc conversiile datelor din formatul intern in formatul extern. Cele doua fisiere sunt asignate automat oricarui program C. spatiu Daca valoarea nu este negativa. prefixele octale si hexazecimale. Prototipul functiei se gaseste in fisierul header stdio. Exista doua cazuri speciale: cand datele sunt afisate pe monitor se considera ca ele sunt scrise intr-un fisier standard numit stdout. iar valorile negative sunt precedate de semnul minus. chiar daca dupa ea nu urmeaza cifre diferite de 0. Aceste doua fisiere sunt considerate ca fiind fisiere de caractere (fisiere text) si din acest motiv sunt necesare anumite conversii pentru a adapta formatul extern de reprezentare al datelor la formatul intern si invers. Intrari/iesiri C Stream-ul poate fi de intrare (sursa este un fisier oarecare iar destinatia este memoria) sau de iesire (sursa este memoria iar destinatia este un fisier oarecare). lista_argumente]). Functia printf Afisarea pe monitor (scrierea in fisierul stdout) se realizeaza prin utilizarea functiei printf. ale caror rezultate se vor afisa. In mod normal. completandu-se la stanga cu spatii sau zerouri. Sintaxa apelului functiei este: printf(format[. • lista_argumente . semnul numerelor. Conversiile sunt efectuate sub controlul unor specificatori de format. In mod prestabilit alinierea se face la dreapta. Specifica alinierea.

Daca valoarea de iesire are mai putin de n caractere. formatul depinde de modelul de memorie folosit. G. se va completa la stanga cu zerouri. Parametru optional. i. E. . avand E pentru exponent. c. Afiseaza caracterul procent (%) Afiseaza argumentul de intrare ca un pointer. Acelasi ca si e. B. cu E pentru exponent daca este folosit formatul e. • 0n Vor fi afisate n caractere. • 6 pentru tipurile e. dar zerourile de la urma nu sunt eliminate. Acelasi ca si g. • primul caracter diferit de null pentru tipurile s. f nu se afiseaza punctul zecimal.dddd sau e[+/-]ddd Valoare cu semn in format e sau f. u. F) Valoare cu semn in format [-]dddd. X. x. Un singur caracter Afiseaza caracterele pana la intalnirea unui caracter null sau pana la dimensiunea stabilita.precizie tip rezultate numai daca dupa ea urmeaza o cifra. Conversiile ce se realizeaza la afisarea datelor sunt indicate de catre ultima sau ultimile litere ale specificatorului de format (parametrul tip). i. El poate fi specificat prin una din urmatoarele procedee: 1.. Daca valoarea de iesire are mai putin de n caractere. precizia este stabilita la valoarea predefinita. f) Intreg hexazecimal fara semn (cu A. * Indica faptul ca lista de argumente furnizeaza precizia (la fel ca si in cazul paramterului lungime). Precizia prestabilita este urmatoarea: • 1 pentru tipurile d. x. Daca se foloseste asteriscul drept specificator. Valoare cu semn in format [-]d. indirect. o. la stanga in caz contrar). E. Conventiile care se aplica unora dintre specificatorii de format ai functiei printf sunt urmatoarele: • Conversia %e sau %E . b. prin intermediul unui astersic (*). Zerourile de la sfarsit si punctul zecimal sunt afisate daca este necesar.dddd. 2. Parametru optional. • toate cifrele semnificative pentru tipurile g. e. Specifica conversia de tip (vezi tabelul urmator). iar pentru tipurile e. in functie de parametrul tip. direct. prin intermediul unui sir de cifre zecimale: • n Vor fi afisate cel putin n caractere. o. care poate fi XXXX:YYYY sau YYYY (numai offset-ul). in functie de valoarea si precizia data.lungime . f. Literele care se pot utiliza pentru conversie sunt prezentate in tabelul urmator: Caracterul de tip Numerice d sau i o sau u x X f e g E G Caractere c s % Pointeri p Tipul intrarii Intreg Intreg Intreg Intreg Real Real Real Real Real Caractere Pointer sir fara Pointer Formatul iesirii Intreg in zecimal cu semn Intreg octal fara semn Intreg hexazecimal fara semn (cu a. C. u. • nu are efect pentru tipul c. Specifica numarul maxim de caractere pentru valorile numerice intregi si numarul minim de cifre la partea zecimala pentru valorile numerice reale. Valorile acestui argument pot fi: 0 Pentru tipurile d.n Sunt afisate n caractere sau n cifre zecimale. Parametru obligatoriu. d. E. este completata cu spatii (la dreapta daca se foloseste indicatorul “-“. gG Acelasi ca si E. urmatorul argument din apel (care trebuie sa fie un intreg) specifica lungimea minima a campului de iesire.argumentul este convertit pentru a corespunde stilului 14 . ea poate fi trunchiata sau rotunjita. Daca valoarea are mai mult de n caractere. D.

1f \nb=%lf\n”. deorece in parametru format al apelului de functie s-a folosit secventa escape newline (\n). E si F. x). Zerourile de la sfarsit sunt eliminate din rezultat. avand numarul de cifre de dupa punctul zecimal specificat de precizie. e = ’x’.argumentul este afisat in stil e. long double c = 32. este ignorat. afiseaza -12 a 97 61..[-] d. B. b. a). b. • Conversia %x sau %X . b. • printf(“x=%x\n”. in iesire apar literele a. d. care este 97 (codul ASCII al literei b) si ca valoare hexazecimala.87. D. e. afiseaza x=65524. x). Ultimele doua valori au aparut deoarece am solicitat sa se afiseze variabila b ca valoare zecimala (argumentul %d din specificatorul de format).argumentul este convertit la notatia zecimala in stil [-] ddd. Conversia %f .9. Fie declaratiile: float a=67. Fie declaratiile: char a = 12.7d\n”. Pentru conversiile X. • • • printf(“x=%X\n”. a. –12 reprezentat pe un byte are valoarea zecimala egala cu 244. Atunci: • printf(“%d %c %d %x\n”. Nota: Numarul reprezentat in memorie este considerat pozitiv si FFF4(16) convertit in zecimal da 65524 (15 x 163 + 15 x 162 + 15 x 16 + 4). Atunci in urma executarii urmatoarei secvente de instructiuni: printf(“a=%+5.7. d.ddd. afiseaza x=fff4. Argumentul este afisat in stil e sau f (cu anumite limitari) atunci cand caracterul de conversie este g. 3.e[+/-]ddd Observatii: o In fata punctulului zecimal exista intotdeauna o cifra.. unsigned char c = 12. x). x). afiseaza x=FFF4.. b = ‘a’. Pentru a-l reprezenta pe –12 in hexazecimal se efectueaza diferenta dintre 216=1000(16) si 12=C(16) si se obtine astfel FFF4. printf(“x=%u\n”. b).7d\n”. iar litera x are codul zecimal ASCII egal cu 120. Argumentul este afisat in stil E atunci cand G este caracterul de conversie. • Conversia %g sau %G . afiseaza 12 244 x 120. afiseaza x=-0000012 (argumentul lungime. printf(“c=%2Lf\n”. d = -12. e si f. C. deoarece data este de tip intreg) si trece la linia urmatoare. Atunci: • printf(“x = %d\n”. fara a trece la linie noua. unde numarul de cifre de dupa punctul zecimal este egal cu argumentul precizie. in iesire apar literele A. iar punctul zecimal apare numai daca acesta este necesar. 15 ..pentru conversiile x. Nota: Numarul x este negativ si in memorie el este reprezentat in cod complementar pe 2 bytes.b).c). c. Fie declaratia: int x = -12. Stilul e este utilizat numai daca exponentul rezultat din conversie este fie mai mare decat precizie fie mai mic ca –4. double b=-98. 2. • printf(“%d %d %c %d”. • printf(“x=%10. Exemple: 1. • printf(“x=%3.ddd. long int d = 100000. e). x). daca s-a transmis o precizie diferita de zero. c. o Exponentul contine intotdeauna cel putin doua cifre. a. E sau f. afiseaza: x = -12 si trece la linia urmatoare. afiseaza x=bb-0000012 (prin b s-a notat spatiul) si trece la linia urmatoare. o Numarul cifrelor de dupa punctul zecimal este egal cu argumentul precizie din specificatorul de format.. 3.

se va afisa textul “ un sir”. Functia scanf Se foloseste pentru a citi din fisierul stdin. • lista_argumente .este formata dintr-unul sau mai multe argumente separate prin virgule. 5. • Formateaza fiecare camp in concordanta cu specificatorul de format corespunzator. Prototipul functie se gaseste in fisierul header stdio. • Transfera data convertita in zona de memorie a carei adresa a fost transmisa in lista_argumente. transmis ca argument. caracter cu caracter.8 b=-98.900 c=32. • Toate caracterele pana la primul care nu poate fi convertit folosind specificatorul de format curent (cum ar fi o cifra 8 sau 9 in formatul octal). o succesiune de campuri de intrare1. Un specificator de format are urmatoarea forma simplificata: % [*] [lungime] [F|N] tip unde: Component a * 1 Ce este/ce executa (Optional) Suprima atribuirea urmatorului camp de intrare. 16 . ea returneaza numarul de caractere scrise. Atunci cand functia printf este apelata ca functie. ramanand afisat textul “ un sit”. urmatorul program: #include <stdio.70 d=100000 4. Fie instructiunea printf(“\a un sir\bt”). Functiile din C++ pot fi apelate atat folosind sintaxa apelului de procedura (cum au fost apelate in exemplele anterioare) cat si utilizand sintaxa apelului de functie. Adresa zonei de memorie se specifica. iar dupa executie se afiseaza al doilea rand (4). unde n este lungimea specificata a campului. Fiecare argument din lista reprezinta adresa zonei de memorie unde se va transfera data citita. se va sterge caracterul ‘r’ (secventa escape \b) si se va afisa in locul lui caracterul ‘t’.a). Sintaxa apelului de functie este urmatoarea: scanf(format.reprezinta un sir de caractere ce contine text si specificatorii de format (asemanatori celor pentru functia printf). transmis prin argumentul format. De exemplu. printf(“ } %d”. care reprezinta numarul de caractere afisat.h. se va afisa: a=+67. printr-o constructie de forma: &nume_zona_memorie Functia scanf executa urmatoarele operatii: • Scaneaza. va afisa: 673 4 prima linie este afisata de apelul printf(“ %d. unde: • format .printf(“ d=%ld\n”.h> void main() { int a = 673 printf(“\n%d. lista_argumente). Daca in Intr-o functie scanf. de regula. • Toate cele n caractere. La executie se va emite un sunet scurt (secventa escape \a).d).a)). prin camp de intrare se intelege: • Toate caracterele pana la primul “caracter alb” (fara a-l include).

17 . Nota: Informatiile din tabelul anterior se bazeaza pe presupunerea ca in specificatorul de format nu este inclus decat caracterul tipului de conversie. E f g. %5c). deoarece nu au reprezentare grafica. Pentru a sari un “caracter alb” si a se citi urmatorul caracter diferit de “caracterul alb”. octal sau hexazecimal Intreg zecimal. 2 “caracter alb” (whitespace) – caracterele spatiu (‘ ‘). octal sau hexazecimal Intreg zecimal fara semn Intreg zecimal fara semn Intreg hexazecimal Intreg hexazecimal Sir caractere Caracter Tipul argumentului Pointer la un intreg (int *arg) Pointer la un intreg lung (long *arg) Pointer la un real (float *arg) Pointer la un real (float *arg) Pointer la un real (float *arg) Pointer la un intreg (int *arg) Pointer la un intreg lung (long *arg) Pointer la un intreg (int *arg) Pointer la un intreg lung (long *arg) Pointer la un intreg fara semn (unsigned int *arg) Pointer la un intreg lung fara semn (unsigned long *arg) Pointer la un intreg (int *arg) Pointer la un intreg (int *arg) Pointer la un tablou de caractere (char arg[]) Pointer la un caracter (char *arg) daca impreuna cu caracterul de tip este data si o lungime de camp (cum ar fi. tab-ul orizontal (\t). Tabloul contine W elemente. Conventiile care se aplica unora dintre specificatorii de format prezentati in tabelul anterior sunt: • Conversia unui singur caracter (%c): aceasta specifica citirea caracterului ce urmeaza. se foloseste %1s. %p converteste p YYYY:ZZZZ sau ZZZZ dimensiunea implicita a adresei la modelul memoriei. • Conversia tabloului de caractere (%[W]c): adresa argumentului este un pointer catre un tablou de caractere (char arg[W]). G o O i l u U x X Caracter s c W % specificatorul de format dupa caracterul procent (%) urmeaza un asterisc (*). (Obligatoriu) Caracterul tipului de conversie (vezi tabelul urmator). (Optional) Specifica numarul maxim de caractere de citit. linie noua(\n) si retur de car (\r) sunt considerate “caractere albe”. Pointer la un tablou de W caractere (char arg[W]) Nu se executa nici o conversie. tab-ul vertical (\v). F = pointer indepartat. Intrarea asteptata Intreg zecimal Intreg zecimal Real Real Real Intreg octal Intreg octal Intreg zecimal. (Optional) Modifica modul de interpretare implicita a adresei argumentului: N = pointer apropiat. pot fi citite mai putine caractere daca functia scanf intalneste un “caracter alb”2 sau neconvertibil. Intr-un specificator de format pentru conversie se pot folosi urmatoarele caractere: Caracterul % Pointer (adresa) Format hexazecimal Pointer la un obiect (far* sau near*). se stocheaza caracterul %. urmatorul camp de intrare este scanat dar nu este atribuit urmatorului argument de adresa. Data de intrare suprimata este presupusa a fi de tipul specificat de caracterul tip ce urmeaza dupa asterisc. inclusiv un “caracter alb”.lungime F|N tip Caracteru l de tip Numeric d D e.

%g si %G): Numerele reale din campul de intrare trebuie sa fie conform urmatorului format generic: [+/-] ddddddddd [. %c.  Caracterele de pe oricare parte a liniutei de unire trebuie sa fie capete ale domeniului si nu parti ale unui alt asemenea domeniu. Parantezele drepte incadreaza un set de caractere care definesc sirul de cautare. Oprirea si trecerea la urmatorul camp de intrare O functie scanf poate opri scanarea unui camp de intrare particular inainte de intalnirea caracterului final normal (spatiul alb) sau ea se poate termina in intregime. %[0-9A-Za-z] “Captureaza” toate cifrele si toate literele. %O. Conversia realului (%e. folosind o constructie de forma [prmul-ultimul]. %[^. intreg sau intreg lung.. unde constructia [element] indica faptul ca elementul este optional.]. In plus. -INF..] dddd [E|e] [+/-] ddd. • S-a intalnit un caracter care nu poate fi convertit sub formatul curent (de exemplu. %X.. %D. Adresa argumentului este un pointer catre un tablou de caractere (char arg[]). %E. Marimea tabloului trebuie sa fie de cel putin (n+1) bytes. a sirului s. c si d Setul de caractere poate fi specificat si sub forma unui domeniu de litere sau numerale. c si d %[^abcd] Cauta in campul de intrare orice caracter diferit de a. Conversia setului de cautare (%[. caz in care campul curent este scanat dar nu este stocat.si scrierea cu majuscule este obligatorie). 18 . Campul de intrare este un sir nedelimitat de “caractere albe”. Exemple: %[abcd] Cauta in campul de intrare unul din caracterele a. %o. Daca primul caracter din parantezele drepte este simbolul ^. stocheaza campul de intrare curent si trece la urmatorul camp de intrare daca se intalneste una din urmatoarele situatii: • In specificatorul de format apare un caracter pentru suprimarea atribuirii (*) dupa caracterul %. setul de cautare este inversat pentru a include toate caracterele ASCII exceptand pe cele specificate intre parantezele drepte. • S-au citit numarul de caractere specificat prin argumentul lungime. unde n este lungimea. %x. %[A-FT-Z] “Captureaza” literele mari dintre A si F si dintre T si Z. +NAN si -NAN sunt recunoscute ca numere reale (prezenta semnului + sau . Conversia tipurilor fara semn (%d. Functia scanf citeste campul de intrare corespunzator pana la intalnirea primului caracter care nu apare in setul de cautare (sau in inversul setului de cautare).]): setul de caractere dintre parantezele drepte pot fi inlocuite cu tipul s de caractere. %f. iar ddd reprezinta cifre (zecimale. in caractere.• • • • Conversia unui sir (%s): Adresa argumentului este un pointer catre un tablou de caractere (char arg[]). Exemple: Pentru a “captura” toate cifrele zecimal. Functia scanf opreste scanarea. o litera A cand formatul este zecimal). octale sau hexazecimale).  Liniuta de unire nu trebuie sa fie primul semn sau ultimul in setul de cautare. %i. %I. La stabilirea setului de cautare folosind domenii de caractere se va avea in vedere urmatoarele reguli:  Caracterul din fata liniutei de unire (-) trebuie sa fie lexical mai mic decat cel de dupa semn. intreg fara semn sau intreg lung fara semn poate fi utilizat in orice conversie unde este permis un pointer catre un caracter. b. +INF. puteti defini setul de cautare fie sub forma %[0123456789] fie sub forma %[0-9] Pentru a “captura” caracterele alfanumerice puteti folosi una din urmatoarele formate: %[A-Z] “Captureaza” toate literele mari. b. %n): un pointer catre un caracter fara semn.. Un terminator nul este adaugat automat la sir si stocat ca ultim element in tablou. Un spatiu sau un caracter newline termina campul de intrare.

double b.  scanf(“%f 1 %lf. &a). se tasteaza 12. printf(“%c %d\n”.7 4.  scanf(“%1d %*1d %1d. a. Atunci:  scanf(“%d. &b). atunci a retine 2. Spatiile din campul de intrare sunt sarite. variabila a retine doar 4. Cifra 5 este citita dar nu este memorata. Observatie: Cand functia scanf opreste scanarea campului de intrare curent pentru unul dintre motivele prezentate mai sus. b. unde prin b s-a notat prezenta unui spatiu. b va retine 2. %b). 3. &b).&a. Exemple: 1. Daca se tasteaza 1. Fie declaratiile int a. urmatorul caracter este presupus necitit si el poate fi un caracter al urmatorului camp de intrare sau primul caracter dintr-o noua operatie de citire. Fie declaratiile: char a. Presupunand ca se introduc caracterele obbbbbj. a retine 1.2 si b retine –1. Astfel daca se tasteaza 357. &a). iar daca se tasteaza 1. iar daca se introduce 21.3. iar a doua oara codul caracterului introdus. deci daca se tasteaza 4562. Daca se introduc valorile 2 si 3. iar b va retine caracterul ‘ ‘ (spatiu). Daca. &b).7 si c va retine 4.0 si b retine 3. &b).3. 19 . Functia printf afiseaza de doua ori variabila a. 2. Atunci:  Secventa: scanf(“%c”.7. atunci se citeste numai a. Atunci:  scanf(“%f %lf %LF”. Fie decalaratiile float a.2. citeste un intreg si-l atribuie variabilei a. deoarece cele doua formate de citire sunt separate prin spatiu (caracter alb) si la citire se sar toate spatiile intalnite. Dupa executarea citirii variabila a va retine caracterul ‘o’.5 2. intrucat primul caracter asteptat este 1 si intrarea contine 2.8.  scanf(“1%c”. &a). si 0 48 daca s-a introdus 0.5. Cele doua formate de citire nu sunt separate printr-un spatiu. &a.2 7. • S-a terminta sirul de formatare (argumentul format).  scanf(“%c%c”. &c).&a). &a).  scanf(“%f%lf”. &a. va afisa: a 97 daca s-a introdus caracterul a. &a. Daca se tasteaza 2.2 1 -1. a retine 1.&a. Terminarea citirii Functia scanf se va termina in urmatoarele conditii: • Urmatorul caracter din campul de intrare este in conflict cu caractererul corespunzator diferit de “spatiul alb” din sirul de formatare.  scanf(%3f”. long double c. citeste prima cifra a numarului tastat si o atibuie valraibilei a. celelalte caractere din campul de intrare sunt ignorate. Daca se tasteaza valorile 1.  scanf(“%1d”. atunci a nu se citeste. atunci a va retine cifra 2 (caracterul 1 este cel asteptat si este sarit). iar variabila b va memora 7. • Urmatorul caracter din campul de intrare este EOF (sfarsit de fisier). &a.7. &b.  scanf(“%c %c”. variabila a va contine 3. prima data caracterul. a). atunci a va retine caracterul ‘o’.25. si deci primul caracter spatiu din campul de intrare este retinut.• In campul de intrare s-a intalnit un caracter care nu apare in setul de cautare (sau apare in setul de cautare inversat). a va retine 2. iar variabila b va retine caracterul ‘j’. b. citeste prima si a treia cifra a numarului introdus si le atribuie variabilelor a si respectiv b. Sa consideram ca se tasteaza caracterele obbbbbj.0.

pozitia cursorului nu se modifica. Citirea propriu-zisa a caracterului are loc dupa apasarea tastei Enter. cursorul se pozitioneaza in coltul din stanga sus (in prima coloana a primei linii – punctul de coordonate 1.h. Functia se apeleaza folosind sintaxa: getchar(). linie). iar functia wherey returneaza o valoare intreaga din intervalul 1 la 25.h si are rolul de sterge caracterele ce se gasesc pe linia curenta (linia in care se afla cursorul) incepand din pozitia cursorului pana la sfarsitul liniei. Sintaxa apelului functiei este: gotoxy(coloana. Functia are prototipul in fisierul stdio. Functia putchar Functia putchar Are rolul de a scrie un caracter in fisierul stdout. dupa convertirea acestuia intr-un intreg fara semn.h.h.h si returneaza o valoare de tip intreg care reprezinta caracterul scris.h si se foloseste pentru a sterge ecranul. in timp ce functia getche citeste caracterul cu ecou. Daca coordonatele sunt incorecte (in afara ferestrei). Functia clrscr Functia clrscr are prototipul in fisierul header conio. Functia getchar returneaza caracterul citit. Functia clreol Functia clreol are prototipul in fisierul header conio. Functiile wherex si wherey Functiile wherex si wherey au prototipul in fisierul header stdio. Deosebirea dintre ele contsa in faptul ca functia getch citeste caracterul fara ecou (caracterul tastat nu apare pe ecran). Sintaxa apelului functiei este: clreol(). Sintaxa de apelare a functiei este: putchar(variabila). Sintaxa de apel a functiilor este: getch() getche() 20 . functia nu realizeaza nimic. Functia nu returneaza nici o valoare. Functia nu returneaza nici o valoare. Dupa stergere. Caracterul este citit imediat ce a fost tastat (nu se asteapta apasarea tastei Enter).Alte functii de intrare/iesire Functia getchar Functia getchar() are rolul de a citi un caracter din fisierul stdin si are prototipul in fisierul stdio. Functiile au rolul de a citi un caracter introdus de la tastatura. Dupa stergere. Functia nu returneaza nici o valoare. Functia wherex returneaza o valoare intreaga din intervalul 1 la 80 reprezintand coordonata x (coloana) a pozitiei curente a cursorului (din fereastra curenta de text). Sintaxa de apel a functiilor este urmatoarea: wherex() wherey() Functiile getch si getche Prototipurile functiilor getch si getche se gasesc in fisierul header conio. 1 la 43 sau 1 la 50 (in functie de parametrii stabiliti ai ecranului) care reprezinta coordonata y (linia) a pozitiei curente a cursorului. Sintaxa apelului functiei este: clrscr(). iar al doilea indica linia).1). Functia gotoxy Functia gotoxy are prototipul in fisierul header conio.h si are permite pozitionarea cursorului in punctul de coordonate specificat prin argumente (primul argument reprezinta coloana.

cout << “S-a introdus: “ << sir << endl. &x). pentru efectuarea operatiilor de intrare/iesire. Caracterele citite trebuie sa corespunda tipului de data care formeaza operandul din dreapta operatorului de extragere. } Intrari standard Pentru operatiile de intrare se poate utiliza operatorul >>. cout << "Ati introdus: " << x << endl. operatorul << este operatorul de deplasare la stanga. cin >> sir.h> #include <iostream. In mod implicit.h> void main() { int x. cout << “Introducet un cauvant: “. In limbajul C. scanf("%d". printf("S-a introdus: %d \n". scanf(“%C”. In caz contrar. // Are acelasi efect ca si urmatoarea instructiune din C printf(“x = %d c% \n“. Aceste reguli raman valabile si pentru operatorul de inserare. Exemplu: #include <iostream. Operatorii de deplasare au prioritatea imediat mai mica decat operatorii binari aditivi (+ si -) si imediat mai mare decat operatorii relationali. se intra in starea de eroare. cout << "Introduceti un intreg ". sir). numit si operator de extragere. cout << ”x = ” << x << ‘ ‘ << sir << ‘\n’. In afara acestor functii. char sir[10]. Acest operator a fost supraincarcat pentru toate tipurile predefinite de date. x.h> #include <stdio. la fel ca si limbajul C. Deoarece la supraincarcarea operatorului de inserare se returneaza o referinta la obiectul curent. operatorul de extragere realizeaza avansul peste caracterele albe pana la primul caracter deferit de un caracter alb. printf(“S-a introdus sirul %c\n”. cin >> x. x). Operatorii de deplasare se asociaza de la dreapta spre stanga.Intrari/iesiri C++ Limbajul C++. char sir[] = ”Testare cout”. sir). printf("Introduceti un intreg: "). sir). In mod normal. asa cum am vazut mai inainte. nu are instructiuni specifice operatiilor de intrare/iesire. } 21 .h> void main() { int x = 5. printf(“Tastati un cuvant: “). astfel de operatii se realizeaza cu ajutorul unui set de functii din biblioteca standard a sistemului. Iesirea standard Iesirile standard se pot realiza folosind operatorul <<. Aceste functii pot fi utilizate in acelasi mod si in programele scrise in C++. operatorii de inserare se pot aplica inlantuiti. biblioteca standard a limbajului C++ ofera posibilitatea de a folosi stream-urile standard cin (console input) si cout (console output).h. numit si operator de inserare. Prototipurile acestor stream-uri se gasesc in fisierul header iostream. Exemplu: #include <stdio. Operatorul >> este de fapt operatorul de deplasare la dreapta care a fost supraincarcat pentru operatii de intrare.

Utilizarea mediului Visual C++ Pentru a utiliza mediul de programare Microsoft Visual C++ la scrierea si testare programelor C (C++) este necesar sa creati un spatiu de lucru si un nou proiect. Pentru a crea o aplicatie care se ruleaza in linie de comanda (similar aplicatiilor DOS) se selecteaza optiunea Win32 Console Application. Se executa clic pe butonul OK. 6. Operatiile ce trebuie executate pentru crearea si testare unui nou proiect sunt urmatoarele: 1. optiunea An empty project si apoi se executa clic pe Finish. Stabilirea dosarului in care se va salva proiectului se poate realiza si prin utilizarea butonului cu puncte de suspensie din dreapta casetei de text Location. In pagina Projects a casetei de dialog New se selecteaza tipul de proiect ce se va realiza. Executarea unui clic pe acest buton determina afisarea casetei de dialog Change Directory in care folosind caseta combinata Drives se stabileste unitatea de disc pe care se gaseste dosarul si apoi cu caseta combinata Directory name se selecteaza dosarul necesar. Dupa executarea operatiilor prezentate pana acum caseta de dialog New va arata ca in figura urmatoare. Ecranul de deschidere al aplicatiei arata astfel: 2. daca este necesar. In caseta de dialog Win32 Console Application care apare pe ecran se selecteaza. 3. 7. Dupa ce s-a stabilit unitatea de disc si dosarul in care se vor salva fisierele proiectului se executa clic pe butonul OK. 5. In caseta de text Project name se tasteaza numele proiectului. Se lanseaza in executie mediul de dezvoltare Microsoft Visual C++. Se descide meniul File si se selecteaza optiunea New sau se foloseste combinatia de taste Ctrl+N. 22 . In caseta de text Location se tasteaza calea dosarului in care se vor salva fisierele proiectului. Pe ecran se va afisa caseta de dialog New. 4.

care afiseaza informatiile legate de noul proiect. Construirea implica compilarea si linkeditarea lui intr-un executabil. Se deschide meniul File si se selecteaza optiunea New sau se foloseste combinatia de taste Ctrl+N. Ecranul mediului Microsoft Visual C++ va arata astfel: Dupa crearea unui proiect se poate trece la adaugarea codului sursa necesar proiectului. Operatiile de construire a proiectului sunt urmatoarele: 1. se repeta algoritmul prezentat. Pe ecran se va afisa caseta de dialog New avand activata pagina Files. Se deschide meniul Build si se selecteaza optiunea Build <nume proiect> sau se apasa tasta functionala F7 sau se executa clic pe butonul Build Build MiniBar.8. se executa clic pe butonul OK. 2. Dupa introducerea codului se salveaza fisierul (deschizand meniul File si selectand optiunea Save sau folosind combinatia de taste Ctrl+S). Se selecteaza optiunea corespunzatoare tipului de fisier ce se va crea. In caseta de dialog New Project Information. Daca proiectul necesita si alte fisiere (sursa sau alte tipuri). 4. Pentru a crea un fisier sursa se alege optiunea C++ Source File. In fereastra de editare se tasteaza continutul fisierului sursa. din bara instrumentele 23 . se poate incerca construirea proiectului. se tasteaza numele fisierului in caseta de text File name si apoi se executa clic pe butonul OK. Dupa adaugarea tuturor fisierelor necesare proiectului. 3. Pentru a crea fisierul ce va contine codul sursa se procedeaza astfel: 1.

Aceste operatii se vor repeta pana cand numai exista erori.2. Dupa ce au fost eliminate toate erorile de compilare si de linkeditare se poate trece la testarea proiectului. mesajele de eroare ale compilatorului apar in fereastra inferioara. 24 . 3. Se corecteaza eroare si se reconstruieste proiectul. Executati dublu-clic pe mesajul de eroare si linia ce contine eroarea va fi indicata in fereastra codului. Pentru a lansa in executie proiectul se deschide meniul Build si se selecteaza optiunea Execute <nume proiect> sau se foloseste combinatia de taste Ctrl+F5 sau se executa clic pe butonul Execute program . Daca in proiect sunt erori.

15. Unar 3. 11. 9.crt . 6 7. 5. In tabelul urmator sunt prezentati toti operatorii limbajului C++. a operandului) (aloca memoria dinamica) (dealoca memoria dinamica) Dereferentiere Dereferentiere Multiplicare Impartire Restul impartirii intregi Adunare Scadere Deplasare stanga Deplasare dreapta Mai mic ca Mai mic ca sau egal cu Mai mare ca Mai mare ca sau egal cu Egal cu Nu este egal cu (diferit de) SI logic pe bit SAU exclusiv pe bit SAU logic pe bit SI logic SAU logic (a ? x : y inseamna "daca a atunci x. Categorie Operato () [] -> :: . 10. 12. Accesarea membrilor Multiplicativ Aditiv Deplasare Relational Egalitate Conditional Atribuire 25 .Operatorii si instructiunile limbajului C++ Operatorii limbajului C++ Limbajul C++ are un set puternic de operatori. 14. 4. 1. in bytes. ! ~ + ++ -& * sizeof new delete . altfel y") Atribuire simpla Atribuirea produsului Atribuirea catului impartirii Atribuirea restului impartiri intregi Atribuirea sumei Atribuirea diferentei Atribuire SI logic pe biti Prioritate maxima 2. 8. impartiti in 16 categorii (categoria 1 are cea mai mare prioritate). 13. Nr.* ->* * / % + << >> < <= > >= == != & ^ | && || ?: = *= /= %= += -= &= Ce este (sau ce face) Apel de functie Indici de tablou Selector indirect de componenta C++ Accesul/rezolutia domeniului C++ Selector direct de componenta C++ Negare logica (NOT) Complementul fata de 1 Plus unar (nu are nici un efect) Minus unar (schimbare de semn) Preincrementare sau postincrementare Predecrementare sau postdecrementare Adresa Indirectare (returneaza dimensiunea. in ordinea prioritatii acestora.

expresia e2 poate fi evaluata inaintea expresiei e1. se va recurge la utilizarea unor variabile auxiliare. De exemplu.  Daca cel putin unul dintre operanzi este unul din tipurile reale. Observatii: • Daca operanzii legati printr-un operator aritmetic sunt de tip diferit. • -. Sugestie: In toate situatiile in care ordinea de evaluare a operanzilor este importanta. • Operatorul % actioneaza numai asupra operanzilor de tip intreg. Operatori aritmetici Operatiile aritmetice obisnuite – adunare. • Operatorul / (impartire) actioneaza in mod diferit in functie de tipul operanzilor:  Daca ambii sunt de tip intreg. • Pentru a schimba ordinea de executie a operatiilor (prioritatea) se pot utiliza parantezele rotunde. daca cei doi operanzi sunt expresiile e1 si e2. aceasta nu inseamna ca cei doi operanzi sunt neaparat evaluati si ei in aceasta ordine. Acest lucru poate duce la efecte nedorite daca e1 are efecte colaterale pe care se conta in evaluarea expresiei e2. toti ceilalti operatori se asociaza de la stanga spre dreapta. care reprezinta restul impartirii a doi intregi. Nota: Atribuire SAU exclusiv pe biti Atribuire SAU logic pe biti Atribuirea deplasarii stanga Atribuirea deplasarii dreapta Virgula Evaluare Operatorii unari (categoria 2). Desi regula de asociativitate pentru operatorii aritmetici binari de aceeasi prioritate este “de la stanga spre dreapta”. Operatori de incrementare/decrementare Sunt operatori unari si au rolul de a incrementa (aduna 1) sau decrementa (scade 1) continutul unei variabile. Acesti operatori pot fi prefixati (aplicati in fata operandului) sau postfixati (aplicati dupa operand). Astfel. care poate retine rezultatul. rezultatul impartirii este de tip intreg si are semnificatia de impartire intreaga. conditionali (categoria 14) si de atribuire (categoria15) se asociaza de la dreapta la stanga. atunci inainte de a se executa inmultirea operandul intreg este convertit la real. Operatorii de incrementare/decrementare din C++ sunt: • ++ pentru incrementare. Exemple: 26 . In continuare se vor detalia o parte dintre acesti operatori. inmultire si impartire – sunt codificate in limbajul C++.(doua semne -) pentru decrementare. ca de altfel in orice alt limbaj de programare. Un alt operator aritmetic in C++ este %. scadere. prin operatorii: + adunare . rezultatul este real (se efectueaza impartirea obisnuita). ^= |= <<= >>= .scadere * inmultire / impartire dintre care primii doi pot sa apara si ca operatori unari. In functie de pozitia operatorului fata de operand. incrementarea/decrementarea se executa inainte ca valoarea retinuta de variabila sa intre in calcul (pentru operatorii prefixati) sau dupa ce valoarea retinuta de variabila intra in calcul (pentru operatorii postfixati).16. daca un operand intreg este inmultit cu un operand real. inainte de a se executa operatia se converteste unul dintre operanzi catre tipul celuilalt.

Rezultatul evaluarii este 18 deoarece operatia se executa astfel: se incrementeaza variabila a (a retine 3).• • Fie a o variabila de tip intreg (int) care are valoarea 5. stabilit conform tabelului urmator. Atentie: Una dintre greselile frecvente in programarea C++. ale caror valori sunt comparate. valoarea adevarat se codifica prin 1. In cazul particular al comparatiilor. daca in loc de a == b se scrie a = b. Operatorii relationali si logici Este aproape imposibil ca in cazul rezolvarii unei probleme sa nu fie necesara testarea uneia sau mai multor conditii. se evalueaza expresia (3 ori 6 fac 18) si apoi se incrementeaza variabila b (retine 7). Operatorii logici admisi de limbajul C++ sunt: • ! negare logica. Rezultatul operatiilor logice este tot de tipul intreg. in cazul unei expresii logice formata din trei 27 . in anumite situatii. • || SAU logic (disjunctie). De exemplu. rezultatul va fi intotdeauna adevarat. datorita efectelor colaterale pe care le poate provoca. atunci expresia + +a*b++ produce valoarea 18. Operatorii relationali sunt impartiti in doua clase de precedenta (prioritate): • Operatori relationali propriu-zisi:  < mai mic  <= mai mic sau egal  > mai mare  >= mai mare sau egal • Operatori de egalitate:  == egal cu  != diferit de In principal rezultatul unei comparatii este o valoare de tip logic – adevarat sau fals. consta in folosirea operatorului de atribuire (=) in locul celui de egalitate (==). expresiile relationale reprezentand deci una dintre posibilitati. Astfel: • conditia “nul” poate fi codificata prin x == 0 sau !x. iar valoarea variabilei a va fi alterata. dar incrementarea lor se va produce in momente diferite. y si z diferiti de 0” poate fi codificata x != 0 && y != 0 && z != 0 sau x && y && z. la efecte colaterale nedorite. x == 0 != 0 y == 0 != 0 == 0 != 0 x && y 0 0 0 1 x || y 0 1 1 1 !x 1 0 Anumite conditii pot fi exprimate in diferite variante. Astfel. In limbajul C++ nu exista tipul de data logic si de aceea o valoare intreaga 0 este interpretata ca fals. • && SI logic (conjunctie). cu sau fara utilizarea operatorilor relationali. In urma evaluarii expresiei 2+a++ se obtine valoarea 7. iar dupa evaluare a va retine valoarea 6 (operator de incrementare postfixat). operanzii asupra carora se aplica operatorii logici pot fi orice expresie cu rezultat intreg. care nu este intodeauna usor observabila. Datorita conventiei referitoare la codificarea valorilor logice prin valori intregi. Aceasta greseala poate avea consecinte neplacute. iar variabilele a si b retin valorile 3 si respectiv 7. ceea ce poate conduce. Daca a si b sunt variabile de tip int care retin valorile 2 si respectiv 6. in timp ce orice valoare diferita de 0 este interpretata ca adevarat. Acestea reflecta relatiile dintre doua entitati. • conditia “x. Observatie: Evaluarea expresiilor logice in programele C++ este intrerupta in momentul in care valoarea rezultatului devine certa.

Pentru a realiza aceasta prelucrare sunt necesare doua informatii: adresa zonei de memorie afectate si valoarea de memorat. Cele mai multe limbaje de programare pun accentul exclusiv pe efectul colateral al atribuirii. dar care furnizeaza si un rezultat ce poate fi folosit in operatiile ulterioare. suma va retine valoarea 25. • -= din valoarea operandului din stanga se scade valoarea expresiei din dreapta. asa cum arata si denumirea lor.expresii relationale succesive legate prin operatorul && (conjunctie). spre deosebire de alte limbaje de programare. dupa executarea acestei instructiuni. In afara operatorului de atribuire “clasic” (semnul egal ‘=’). Spre deosebire de acestea. exista mai multi operatori de atribuire. Daca suma are valoarea 10 si element valoarea 15. Operatori de atribuire In limbajul C++. rezultatul expresiei logice va fi cert fals si ca urmare celelalte expresii nu mai sunt evaluate. daca rezultatul evaluarii primei expresii relationale este fals. • /= in variabila din stanga se pastreaza catul impartirii rezultatului expresiei din dreapta la valoarea initiala a variabilei din stanga. De exemplu. sub forma: variabila1 operator= variabila2 De exemplu. aceasta fiind tratata ca instructiune de baza. cunoscand dimensiunea laturii unui cub (notata cu. numite suma si element. in limbajul C++ au fost introdusi si operatori de atribuire compusi. dCub). atunci operatia de atribuire: suma = suma + element. Operatorii la nivel de bit (care admit numai operanzi de tip intreg) sunt urmatorii: • ~ negare • & SI logic 28 . Spre deosebire de operatiile discutate pana acum. • %= in variabila din stanga se pastreaza restul impartirii intregi a rezultatului expresiei din dreapta la valoarea initiala a variabilei din stanga. • *= valoarea operandului din stanga este inmultita cu cea a expresiei din dreapta si rezultatul se memoreaza in operandul din stanga. asupra fiecarui bit din reprezentarea operanzilor. volum = dCub * aria. Rolul acestora este de a permite descrierea mai compacta a atribuirilor de forma: variabila1 = variabila1 operator variabila2 utilizand numai informatiile strict necesare. Semnificatia atribuirii este aceeasi in toate limbajele de programare: prelucrare al carui efect consta in modificarea valorii unei entitati. prin depunerea unei noi valori in zona de memorie alocata entitatii respective. poate fi scrisa sub forma prescurtata astfel: suma += element. al carui efect este identic cu cel ar urmatoarei secvente de instructiuni (modalitate specifica celorlalte limbaje de programare): aria = dCub * dCub. Operatorii compusi din limbajul C++ sunt urmatorii: • += in operandul din stanga se cumuleaza valoarea expresiei din dreapta. fiind declarate doua variabile. tratandu-l independent de valorile celorlalti biti. aria unei fete si volumul cubului (notate cu aria si volum) pot fi calculate intr-un program C++ folosind o instructiune de forma: volum = dCub * (aria = dCub * dCub). operatiile la nivel de bit se aplica. limbajul C++ trateaza atribuirea ca operatie cu efect colateral. Operatori logici pe biti Toate valorile prelucrate pe parcursul executiei unui program sunt reprezentate in calculator prin succesiuni de biti (cifrele binare 0 si 1).

expresia a < b ? (t = b. Operatorul conditional Operatorul conditional este un operator ternar (cu trei operanzi). b si c. iar cel de-al doilea operand indica numarul de biti cu care se face deplasarea. se codifica prin expresii. exceptand cazul producerii depasirii. Deci numai primul operand este prelucrat la nivel de bit. In cazul deplasarii spre dreapta cu un bit.• ^ SAU EXCUSIV • | SAU • << deplasare stanga • >> deplasare dreapta.) Operatorul virgula (numit si operator de secventiere) se foloseste atunci cand sintaxa limbajului impune prezenta unei singure expresii. in functie de indeplinirea sau nu a unei conditii. cei doi operanzi au roluri diferite: primul operand este cel al carui biti sunt deplasati. Cei trei operanzi. a > b ? ‘>’: a < b ? ‘<’ : ‘=’. daca variabila a este de tip intreg si are valoare 15 (in binar 0000000000001111). b = a. reprezentand conditia testata si cele doua alternative de obtinerea rezultatului. bitul cel mai putin semnificativ se pierde. deci similar impartirii intregi la 2. Expresiile separate prin operatorul virgula sunt evaluate de la stanga spre dreapta. bitul cel mai semnificativ se pierde. De exemplu. rezultatul final fiind cel al ultimei expresii. El se foloseste in cazurile in care exista doua variante de obtinere a unui rezultat. in caz contrar rezultatul final este date de varianta2. b). Cauzele care determina efectuarea conversiilor de tip sunt urmatoarele: 29 . Rezulta ca. Rezultatul aplicarii primilor 4 operatori din lista de mai sus este prezentat in tabelul urmator: b1 0 0 1 1 b2 0 1 0 1 ~a 1 1 0 0 b1 & b2 0 0 0 1 b1 ^ b2 0 1 1 0 b1 | b2 0 1 1 1 In cazul operatorilor de deplasare. iar cel mai putin semnificativ se completeaza cu 0. De exemplu. iar bitul cel mai semnificativ se completeaza cu 0. iar pentru a afisa relatia dintre valorile a si b se poate folosi instructiunea: print(“d %c %d\n”. dar prelucrarea necesara presupune evaluarea a doua sau mai multe expresii. La deplasarea spre stanga cu un bit. a = t) : a are dublu efect: valorile variabilelor a si b sunt ordonate descrescator si ca rezultat se obtine cea mai mare dintre cele doua valori. Sintaxa expresiei conditionale este urmatoarea: conditie ? varianta1 : varianta2 Rezultatul unei asemenea expresii se stabileste astfel: daca conditie este diferita de 0 (adevarat) atunci rezultatul final este dat de varianta1. Astfel. se poate folosi instructiunea: max = a > b ? (a > c ? a : c) : (b > c ? b : c). deplasarea la stanga cu n biti este echivalenta cu inmultirea cu 2n. Operatorul virgula (. Operatorul de conversie explicita Un aspect foarte important in ceea ce priveste evaluarea expresiilor se refera la conversiile de tip impuse de structura unei expresii. dintre care se va alege una singura. pentru a obtine valoarea maxima dintre cele ale intregilor a. atunci dupa deplasare spre stanga cu 2 pozitii variabila a va memora valoarea 60 (in binar 0000000000111100). a .

). Exista putine limbaje de programare care dispun de o instructiune expresie. apelari de functii etc. } Instructiunile limbajului C++ Instructiunea expresie Se numeste expresie o succesiune de operatori si operanzi legati intre ei. O consecinta importanta a acestei posibilitati o constituie faptul ca o functie poate fi utilizata ca o procedura (de exemplu. Ca operanzi intr-o expresie se pot utiliza: constante. spre deosebire de ceilalti operatori. Conversia poate fi realizata de compilator (conversie implicita) sau poate fi impusa de programator (conversie explicita).h> void main() { printf(“ char short int long float double\n%3d%6d%6d%6d%6d%6d\n”. in functie de implementarea C++ utilizata. dupa reguli specifice limbajului.Din ratiuni de reducere a variantelor de implementare a operatiilor. • Daca operanzii unei atribuiri nu sunt de acelasi tip. operanzii de tipuri intregi mai “slabe” decat int sunt convertiti la tipul int sau unsigned int. 30 . • Marea majoritate a operatorilor necesita operanzi de acelasi tip. operatorul sizeof prelucreaza tipuri (predefinite sau derivate). sizeof este un operator cu efect la compilare. sizeof(double)).5. Ea se evalueaza. sizeof(int). Urmatorul program poate fi utilizat pentru a afla particularitatile de reprezentare. ca in urmatoarele exemple: n = a > b ? a: b. denumit si cast. care prelucreaza valori. unde expresia este evaluata si are efect colateral. Instructiunea expresie are urmatoarea forma generala: expresie. Conversia explicita se indica folosind operatorul conversiei explicite de tip. b=6. chiar daca este reprezentat de o expresie. sizeof(char). exprimat in bytes. care este de fapt o expresie.9. Aceasta inseamna ca operandul asupra caruia se aplica sizeof nu este evaluat. variabile si apeluri de functii. in scopul efectuarii unor operatii (calcule. atunci (float) a/b va avea ca rezultat 0. sizeof(long). pentru cateva dintre tipurile predefinite: #include <stdio. • Fie declaratiile: int a=3. Limbajul C++ permite scrierea unei instructiuni de forma: 8+7. m++. atunci valoarea atribuita trebuie convertita la tipul variabilei careia i se atribuie. O conversie explicita de tip se indica folosind urmatoarea sintaxa: (declaratie_tip) expresie • Exemple: • Fie declaratia: float x=-1. sub forma sizeof expresie Deoarece tipul operatorului este determinat inca din etapa de compilare. Operatorul sizeof Rezultatul aplicarii acestui operator unar asupra operandului său este un intreg care specifica spatiul de memorie. necesar stocarii oricarei valori de acelasi tip cu cea a operandului. sub forma sizeof(declaratie_tip) sau asupra tipului unei expresii. chiar daca rezultatul ei nu se foloseste in nici un fel. Deci. deci este reprezentata de regula de o atribuire sau un apel de functie. atunci (int) x va avea ca rezultat –1. sizeof(float). atribuiri. sizeof(short). Operatorul sizeof poate fi aplicat direct asupra unui tip. utilizarea functiilor de I/E scanf si printf). iar cei de tip float la tipul double.

31 . tinand cont ca anul poate fi o valoare intre 1900 si 2200. se trece la prima instructiune ce urmeaza dupa if. numita si instructiune decizionala. De exemplu. in caz contrar nu se executa nimic. In orice limbaj de programare. dar pentru rezolvarea problemei nu este necesara o prelucrare. insa prelucrarile necesare impune prezenta mai multor instructiuni. [else instructiune2. caz in care ele nu sunt terminate cu caracterul punct si virgula (. Instructiunea vida In eleborarea unui program C++ se pot intalni si situatii in care sintaxa impune aparitia unei instructiuni. care are forma: . • Instructiunea de selectie multipla. alegere exprimata literar sub forma: daca este indeplinita conditia impusa. o asemenea structura se codifica folosind o instructiune decizionala. Rezolvarea acestor situatii se realizeaza incadrand respectiva secventa de instructiuni intre acolade. In astfel de cazuri se foloseste instructiunea vida. Din format. se executa instructiune1. dar ea poate fi si o instructiune compusa. rezulta ca instructiunea if admite doar o instructiune pentru oricare dintre cele doua alternative. printf(“x=%d”. va afisa valoarea variabilei x multiplicata cu 2. x * 2). Instructiunea compusa (bloc) Pe parcursul elaborarii programelor intervin numeroase situatii in care sintaxa limbajului impune prezenta unei singure instructiuni. • Daca valoarea produsa este 0 si exista clauza else. Structura generala a unui bloc este: { declaratii_si_definitii instructiuni } Instructiuni de selectie In majoritatea limbajelor de programare exista instructiuni care permit alegerea unei alternative din doua sau mai multe posibile in functie de anumite conditii. nu exista problema reala in care sa nu apara necesitatea de a opta pentru o alternativa de prelucrare dintre doua posibile.Observatie: Expresiile pot fi utilizate si independent. (caracterul punct si virgula). In cazul general. • Daca valoarea produsa este diferita de 0.).] Principiul de executie este urmatorul: • Se evalueaza expresia. • Dupa executarea instructiunii instructiune1 sau instructiune2 . Exemplu: Sa se scrie un program care valideaza data calendaristica introdusa sub forma ll/zz/aaaa (luna/zi/an). atunci executa prima alternativa. Instructiunea de selectie simpla Practic. se executa instructiune2. In limbajul C++ exista doua instructiuni care permit acest lucru: • Instructiunea de selectie simpla. In limbajul C++ instructiunea decizionala are urmatorul format general (parantezele drepte indica faptul ca respectiva constructie poate fi omisa): if (expresie) instructiune1. obtinandu-se astfel o structura denumita instructiune compusa sau bloc. altfel executa a doua alternativa. un bloc poate sa contina pe langa instructiuni si definitii de variabile locale blocului si alte declaratii.

30. in caz contrar va furniza 0. …. instructiuni_1. ok = 0. an. break. // data calendaristica incorecta } if(ok) { cout << "Data calendaristica introdusa este corecta:\n". break. int nrZile[] = {0. cin >> luna. 31}. break. ok = 0. 31.#include <iostream. [default: instructiuni. // data calendaristica incorecta } if(luna = 2) nrZile[2] += 1. instructiuni . ok = 1. // data calendaristica incorecta } if(luna < 1 || luna > 12) { cout << "Luna = “ << luna << “ este eronata\n". } } Observatii: Pentru a adauga o zi la numarul de zile ale lunii februarie daca anul este bisect se foloseste expresia: (luna == 2 && (an % 4 == 0 && an % 100 != 0 || an % 400 == 0))) care va furniza o valoare egala cu 1 daca luna este egala cu 2 si anul este bisect. cazN . Instructiunea de selectie multipla In structiunea switch permite realizarea unei structuri de selectie multipla (o generalizare a structurii alternative). caz1. cout << “Ziua: “. Sintaxa instructiunii switch este urmatoarea switch(expresie) { case caz1: instructiuni_1.h> int zi. 30. 2200]". cout << luna << “/” << ziua << “/” << an << endl. numite si alternative. cout << “Luna: “. 31. cin >> zi. ok = 0. void main() { cout << "Se introduce data calendaristica (ll/zz/aaaa):\n". instructiuni_n. cout << “Anul: “. instructiuni_2.] } unde: • • • expresie – reprezinta expresia de selectare a alternativei ce se va executa.sunt succesiuni de instructiuni. caz2. cin >> an. 32 . 31. 30. 30. 28. 31. if(zi < 1 || zi > nrZile[luna] + (luna == 2 && (an % 4 == 0 && an % 100 != 0 || an % 400 == 0) ) { cout << "Ziua = “ << ziua << “ este eronata\n"). case caz2: instructiuni_2. if(an < 1900 || an > 2200) { cout << "Anul nu este in intervalul [1900. … case cazN: instructiuni_N. Anul este bisect daca este divizibil prin 4 si nu este divizibil prin 100 sau este divizibil prin 400.sunt constante. 31. …. luna. 31.

putand exista cel mult 15 niveluri de instructiuni swtich incluse una in alta. • In acelasi swtich nu pot exista doua sau mai multe constante caz identice. } } Observatii: • Instructiunea switch difera de if prin aceea ca switch testeaza numai egalitatea. #include <iostream. cazn. • Intr-o instructiune switch sunt permise maximum 257 de clauze case. • Instructiunile asociate cu fiecare case nu trebuie incluse intre acolade. …. Observatie: Instructiunea break de la sfarsitul alternativelor nu este obligatorie. • Ca expresia selectoare a instructiunii switch se poate utiliza numai o expresie care furnizeaza un rezultat de tip int sau char. caz2. Daca dintr-o alternativa lipseste instructiunea break. se executa instructiunea corespunzatoare clauzei default. Rezultatul expresiei se compara pe rand cu valorile constantelor caz1. break. case 4: cout << "Joi\n".Principiu de executie: P1. case 3: cout << "Miercuri\n". se executa succesiunea de instructiuni (alternativa) corespunzatoare acelei constante si apoi se trece la prima instructiune ce urmeaza dupa switch. • Instructiunile asociate unei clauze case pot lipsi. Exemplu: Se citeste o cifra din intervalul [1. caz2. o Daca valoarea rzeultatului expresiei expresie nu corespunde nici uneia dintre constantele caz1. break. P2. …. case 7: cout << "Duminica\n". Se evalueaza expresie. 7].h> void main() { int i. break. case 5: cout << "Vineri\n". deci ele nu formeaza un bloc de instructiuni. } while ((i < 1) || (i > 7)). break. case 2: cout << "Marti\n". …. caz2. atunci dupa executia succesiunii de instructiuni din compunerea alternativei respective se va trece la executia succesiunii de instructiuni din alternativa urmatoare a aceleeasi instructiuni switch. si apoi se trece la prima instructiune ce urmeaza dupa switch. break. ceea ce permite ca pentru doua sau mai multe constante caz sa se execute aceleasi instructiuni fara a mai fi nevoie sa fie repetate. in timp ce la if expresia de conditie poate fi de orice tip. case 6: cout << "Sambata\n". cazN: o Daca valoarea rezultatului expresiei expresie corespunde cu una dintre constantele caz1. do { cout << “Introduceti o cifra intre 1 si 7: "). • Includerea unei instructiuni swtich in interiorul altei instructiuni swtich este permisa. daca exista. break. Sa se afiseze denumirea zilei din saptamana corespunzatoare cifrei citite. cazn. 33 . switch (i) { case 1: cout << "Luni\n". cin >> i.

Intr-o instructiune expresie evaluarea se face de la stanga la dreapta. in corpul ciclului este necesara prezenta unei instructiuni care sa duca la modificarea conditiei testate (expresie) astfel incat rezultatul furnizat sa devina fals. Principiul de executie este urmatorul: P1. Exemplu: Se citeste un numar natural. Toate aceste instructiuni pot controla o singura instructiune. ciclarea se termina si se trece la instructiunea ce urmeaza dupa while. Terminarea fortata se realizeaza plasand in corpul ciclului o instructiune break. care insa poate fi si instructiune compusa. Din aceasta forma a instructiunii while se poate constata ca instructiunea subordonata ei poate fi si o instructiune vida. Observatie: Instructiunea while trebuie sa contina o instructiune care sa asigure terminarea ciclarii (normal sau fortat). n /= 10). #include <iostream. ninv += 10. iar rezultatul ei este dat de ultima expresie din instructiune.Instructiuni de ciclare In orice limbaj de programare exista instructiuni care permit executarea in mod repetat a unei instructiuni sau grup de instructiuni. In limbajul C (C++) exista trei instructiuni de ciclare: while. Daca valoarea furnizata de expresie este diferita de 0 (adevarat). 34 .while. atunci in instructiunea expresie ce formeaza conditia de ciclare trebuie sa existe o expresie care sa realizeaza terminarea ciclarii. in caz contrar (valoarea furnizata este 0 – fals). } Avand in vedere posibilitatile de lucru cu expresii. for si do. n = n / 10. P2. se executa instructiunea subordonata si se revine la pasul P1. cout << “ Introduceti un intreg: ”). Pentru terminarea normala. cin >> n. Daca corpul ciclului este o instructiune vida. ninv = 0. Aceste instructiuni se numesc instructiuni de ciclare sau instructiuni repetitive. instructiunea while din acest program poate fi scrisa si sub forma: while(ninv*=10. iar expresie poate fi si o instructiune expresie. while (n) { ninv = ninv * 10 + n % 10..h> void main() { int n. Se evalueaza expresie. Instructiunea while Instructiunea while permite codificarea unei structuri repetitive conditionata anterior (numita si ciclu cu test initial) si are urmatorul format general: while (expresie) instructiune. } cout << “Numarul inversat este: “ << ninv.. Sa se afiseze in ordine inversa cifrele sale.

Exemple: 1. } Nota: Fisierul antet <math. Sa se intocmeasca un program care sa calculeze factorialul unui numar natural n introdus de la tastatura. #include <iostream. este corecta si declara variabila de control a ciclului j si o initializeaza cu 1. • expr_pas reprezinta o expresie care furnizeaza valoarea de modificare a variabilei de control a ciclului dupa fiecare executie a corpului ciclului. cout << "----------------\n". radian <= 2. P2. rezulta ca structura repetitiva cu numar cunoscut de pasi este de fapt tot o structura conditionat anterior. cout << "|alfa | sinus |\n". Formeaza corpul ciclului. se executa corpul ciclului si apoi se trece la pasul P3. in caz contrar (valoarea data de expresie este 0 – fals) se termina executia instructiunii for. • expr_test reprezinta expresia care verifica daca instructiunea subordonata (corpul ciclului) se va executa sau nu. • Dupa fiecare a executiei corpului ciclului variabila de control poate fi majorata sau micsorata cu o valoare intreaga sau reala. Se evalueaza expresia expr_pas si se reia executia de la pasul P2. for(int i=1. Daca rezultatul evaluarii este o valoare diferita de 0 (adevarat). Observatii: • Din analiza modului de executie. #include <iostream.h> int n. unde: • expr_init este o expresie care furnizeaza valoarea de initializare a variabilei de control a ciclului (numita si contor).1) cout << "| “ << radian << “ | “ << sin(radian) << “|\n”. void main() { cout << "Introduceti un intreg: ".h> void main() { cout << "----------------\n".1). Daca rezulatul evaluarii expresiei expr_test este 0 (fals). cout << "|--------------|\n". se termina executarea instructiunii for si se trece la instructiunea ce urmeaza dupa instructiunea for. i <= n. cin >> n. Se evalueaza expr_test. Sa se scrie un program care sa afiseaze sub forma tabelara valorile sinusului pentru unghiurile cuprinse intre 0 si 1 radian (cu un pas de incrementare de 0. Principiul de executie este urmatorul: P1. P3.ale limbajului C++. expr_test. • instructiune este o instructiune simpla sau compusa.h> #include <math. radian += 0. long fact = 1. De exemplu. care se va executa in mod repetat de un numar fix de ori. Daca rezulatul evaluarii acestei expresii este diferit de 0 (adevarat). Se evalueaza expr_init. Trebuie remarcat faptul ca in aceasta expresie (cu rol special) este posibil chiar si declararea variabilei si a valorii ei initiale. i++) fact *= i.h> contine prototipurile functiilor matematice.Instructiunea for Instructiunea for permite codificarea unei structuri repetitive cu contor (numita si ciclu cu numar cunoscut de pasi) si are urmatorul format general: for(expr_init. se executa corpul ciclului. expr_pas) instructiune. expresia int j = 1. cout << "n!= " << fact. 35 . } 2. for (float radian = 0.

Se executa instructiunea subordonata (corpul ciclului). y. } while (1).while permite codificarea structuriilor repetitive conditionate posterior (numite si cicluri cu test final) si are urmatorul format: do { instructiune } while (expresie). do sau while) se poate utiliza instructiunea break. } while (n < 5 || n > 25). } Instructiunea break Pentru a forta iesirea dintr-o instructiune de ciclare (for. Daca valoarea rezultata este diferita de 0 (adevarat). cin >> n.. cin >> i..while Instructiunea do. n. Daca corpul ciclului este o instructiune vida. if (!(x % 2)) cout << "Numarul " << x << "din pozitia " << i << " este par\n". Exemplu: Se se scrie un program care verifica daca numarul introdus de la tastatura se incadreaza intre doua limite stabilite. if (!x) break. Se evalueaza expresie. i = 1. i += 1.h> void main() { int x = 1. do { cout << "Introductei u intreg (0 pentru terminare)= ".Instructiunea do. Principiul de executie este urmatorul: P1.. Introducerea se termina la tastarea valorii 0 (nu face parte din sir). P2. Pentru numerele pare se va afisa valoarea lor si numarul de ordine. Exemplu: De la tastatura se introduc mai multe numere intregi. atunci in instructiunea expresie ce formeaza conditia de ciclare trebuie sa existe o expresie care sa realizeaza terminarea ciclarii. Instructiunea continue 36 . se reia excutia cu pasul P1: in caz contrar (rezlultatul este 0 – fals) executia instructiunii do se termina. int x.. lim2 = 25. #include <iostream. void main() { void main() { do { cout << "Introduceti un intreg [" << lim1 << ". care are urmatorul format general: break. Terminarea fortata se realizeaza plasand in corpul ciclului o instructiune break. in corpul ciclului este necesara prezenta unei instructiuni care sa duca la modificarea conditiei testate (expresie) astfel incat rezultatul furnizat sa devina fals.. Pentru terminarea normala.h> int lim1 = 5. Observatie: Instructiunea do. #include <iostream. " << lim2 << "]: ". } Observatie: In cazul mai multor instructiuni repetitive imbricate (incluse una in alta) efectul instructiunii break se refera doar la instructiunea repetitiva care o subordoneaza direct.while trebuie sa contina o instructiune care sa asigure terminarea ciclarii (normal sau fortat).

Insumarea se produce numai daca numarul introdus este corect. #include <stdio. Formatul general acestei instructiuni este urmatorul: continue.while si while) sau saltul la expresia de incrementare (in cazul instructiunii for). De exemplu. Daca utilizatorul spune ca numarul este incorect. total = 0. urmatorul program insumeaza numerele introduse de la tastatura pana cand utilizatorul tasteaza o valoare nula. char ch. se reia ciclul fara a se insuma numarul eronat.Instructiunea continue se poate utiliza in instructiunile repetitive pentru a efectua saltul la testul de continuare (in cazul instructiunilor do. scanf("%d". void main() { do { printf("Introduceti un intreg (0 pentru oprire): "). if(ch == 'n' || ch == 'N') continue. total += n. printf("\n"). ch = getche()..h> #include <conio.h> int n. &n).. printf("Numarul %d este corect (D/N)? ". O buna utilizare a instructiunii continue este reluarea executiei unui ciclu atunci cand s-a produs o eroare care poate fi inlaturata de utilizator. n). fara a se mai executa instructiunile din corpul ciclului de dupa aceasta. total). } while (n). } 37 . printf("Suma numerelor introduse este %d\n".

Pentru inceput o vom numi subprogram. Pentru a realiza aceasta generalizare se va considera ca atat baza cat si exponentul sunt variabile. procedura etc.Programarea procedurala Definirea si apelarea functiilor Inca de la primele limbaje de programare de nivel inalt s-a utilizat programarea procedurala. iar partea inclusa intre acolade. De exemplu.. atunci functia nu are parametrii formali. Daca functia nu returneaza o valoare. In acest caz. impreuna cu acoladele. iar acestea la randul lor realizeaza o abstractizare prin parametri. In toate limbajele de programare subprogramele sunt considerate a fi de doua categorii: • Subprograme care definesc o valoare de revenire. Limbajul C (C++) permite utilizarea numai a suprogramelor de tip functie. Secventa de instructiuni organizata in acest fel are diferite denumiri in limbajele de programare: subprogram. functiile limbajului C (C++) pot fi folosite atat independent (apelate ca procedurile in celelalte limbaje) cat si intr-o expresie ca operand (utilizand-se astfel valoarea returnata). Acest salt este un salt cu revenire la instructiunea ce urmeaza dupa cea care a comandat saltul. care semnifica lipsa unei valori returnate la revenire. dar ea poate fi apelata ori de cate ori este necesar in respectivul program. care in anumite limbaje de programare se numesc subrutine sau proceduri. Valoarea de revenire se mai numeste si valoare de intoarcere sau valoarea returnata a functiei. Definitia unei functii are urmatorul format: tip nume(lista_parametri_formali) { [declaratii] instructiuni } Primul rand din formatul de mai sus reprezinta antetul functiei. iar valorile lor se vor precizeaza la fiecare apel al subprogramului respectiv. Definirea functiilor Intr-un program functia are o singura definitie. float etc. char. In acest caz antetul functiei se reduce la: 38 . Valorile transmise la apel parametrilor formali se numesc parametri efectivi (parametri actuali sau reali). atunci tip desemneaza tipul rezultatului prin intermediul unui cuvant cheie (cum ar fi: int. Daca functia returneaza o valoare. formeaza corpul functiei. subrutina.) sau al unui nume de tip definit de utilizator. Uneori subprogramul trebuie sa execute aceleasi operatii de prelucrare. pentru a evalua expresia: 4**10 – 3**20 putem crea un subprogram de ridicare la putere. Aceasta a aparut si s-a dezvoltat din necesitatea de a folosi intr-un program. subprogramul trebuie realizat la modul general. care pot returna sau nu o valoare la revenirea in programul apelant. dar cu date diferite. aceeasi secventa de instructiuni de mai multe ori. si se executa un salt la aceasta zona ori de cate ori este necesar. Pentru a evita scrierea repetata a acestor instructiuni. In plus. atunci ca tip se va folosi cuvantul cheie void. ele sunt organizate ca o parte distincta a programului. denumite de obicei functii. care sa fie general. facandu-se abstractie de datele respective. O functie poate avea mai multi parametri sau nici unul. Variabilele folosite pentru implementarea unui subprogram general si a caror valori sunt concretizate la fiecare apel se numesc parametri formali (parametri fictivi sau argumente). Programarea procedurala are la baza utilizarea subprogramelor. • Subprograme care nu definesc o valoare de revenire. facandu-se abstractie de valorile concrete ale bazei si exponentului. Daca lista_ parametri_formali este vida.

Pentru a le apela este necesar ca inaintea primei utilizari. o forma prescurtata. in codul sursa trebuie sa apara urmatoarea linie: #include <stdio. al caror prototipuri se gasesc in fisierul stdio.h. dar dupa acesta (vezi exemplul de la pagina 41). Deci prototipul unei functii poate fi de forma: tip nume(lista_parametri_formali). Aceste functii se numesc functii standard de biblioteca. fiind urmat insa de caracterul punct si virgula (. Prototipul unei functii contine informatii similare cu cele din antetul ei: • tipul valorii returnate. pentru a executa intr-un program operatii de intrare/iesire se pot apela functiile printf si scanf. int). 39 . adica in antetul functiei este prezenta lista cu parametrii formali. in locul parantezelor unghiulare se folosesc ghilimelele (“). urmat de caracterul punct (. • tipurile parametrilor formali. care are urmatorul format: #include <specificator_fisier> unde: • specificator_fisier este format din numele fisierului cu prototipurile functiilor standard necesare. sau tip nume(lista_tipuri_parametri_formali). adica compilat si se adauga la fiecare program in faza de editare. • numele functiei.) si de extensia h. Prototipurile functiilor standard se includ in program inaintea apelurilor lor folosind constructia #include. daca intr-un program este definita o functie a carui antet este urmatorul: long double putere(long double x. Constructia nume din antet reprezinta numele functiei si trebuie sa respecte conventiile de denumire ale identificatorilor specifice limbajului C (C++). Prototipul unei functii este obligatoriu atunci cand functia este definita in acelasi fisier cu apelantul. Aceasta zona poate lipsi. Deci antetul de mai sus poate fi scris si sub forma: tip nume(void) In cazul in care functia are parametrii. sau long double putere(long. Ele sunt stocate intr-un fisier in format obiect. Zona instructiuni contine instructiunile care descriu operatiile de prelucrare pentru care a fost realizata functia si formeaza corpul functiei. in care in lista_tipuri_parametri_formali sunt precizate doar tipurile parametrilor formali. Prototipurile lor sunt pastrate in diferite fisiere de extensie . ea trebuie apelata. int n). De exemplu.h> Observatie: Pentru ca in program sa se includa un fisier sursa.h (numite si fisiere header).) sau poate avea un format prescurtat. Zona declaratii din corpul functiei se poate folosi pentru definirea variabilelor care se utilizeaza numai in functia respectiva. int n) prototipul functie poate fi: long double putere(long double x. Limbajul C (C++) se livreaza cu o serie de functii ce au o utilizare frecventa in programe. Apelul de functie trebuie sa fie precedat de definitia sau de prototipul ei. Prototipul functiei poate avea acelasi format ca si antetul functiei.tip nume() Lipsa parametrilor formali poate fi mentionata explicit folosind cuvantul cheie void. De exemplu. declaratiile parametrilor sunt separate prin virgule. adica identic cu antetul functiei. Apelarea functiilor Pentru a putea utiliza o functie intr-un program.

Cand functia este apelata ca operand al unei expresii. unde: • nume – este numele functiei cre se apeleaza. Daca functia este apelata printr-o instructiune de apel. se recomanda utilizarea unui apel cu conversie explicita a lui n la tipul double: f((double) n). In cazul in care tipul unui parametru efectiv difera de tipul parametrului formal care-i corespunde. • La intalnirea instructiunii return. se realizeaza printr-o instructiune de apel. pentru a forta terminarea executiei functiei si revenirea in programul apelant. fie contine una sau mai multe expresii separate prin virgula.cpp” indica includerea fisierului fis1. Valoarea furnizata de expresie este chiar valoarea returnata de functie. cand functia a fost declarata fara parametri formali. return expresie. Apelul unei functii care nu returneaza o valoare.cpp aflat in directorul curent.cpp din subdirectorul Programe al directorului BC31 din radacina discului C. rezulta ca intre lista parametrilor efectivi ai apelului de functie si lista parametrilor formali ai definitiei functiei trebuie sa existe o concordanta din punct de vedere al ordinii si al tipului parametrilor.cpp” specifica includerea fisierului Citire. De exemplu. adica utilizand asa numitele expresii cast. La apelul unei functii. valoarea codului ASCII al caracterului citit de la tastatura nu se foloseste. • lista_parametri_efectivi – este fie vida. Instructiunea return poate sa apara oriunde in corpul functiei si are urmatoarele formate: return. valoarea expresiei se converteste automat spre tipul din antet. Cel de-al doilea format se foloseste in corpul unei functii care returneaza o valoarea la revenirea din ea. Din cele prezentate pana in prezent. atunci in locul apelului: f(n). daca functia f are un parametru formal de tip double si n este o variabila de tip int. Daca tipul acestei expresii difera de tipul care precede numele din antetul functiei. fie ca operand al unei expresii. poate fi apelata fie printr-o instructiune de apel. Revenirea dintr-o functie se poate realiza in unul din urmatoarele moduri: • Dupa executia ultimei instructiuni din corpul functiei (s-a ajuns la acolada inchisa care termina corpul functiei). folosind operatorul (tip). in timp ce in cazul apelului din urmatoarea linie getch(). urmatoarea instructiune de atribuire c = getch(). astfel incat utilizatorul sa poata citi rezultatele afisate pe ecran. care are urmatorul format: nume(lista_parametri_efectivi). 40 . Primul format se foloseste numai in corpul unei functii care nu returneaza o valoare. In limbajul C++ se foloseste o regula mai complexa pentru apelul functiilor si de aceea se recomanda utilizarea conversiei explicite. in limbajul C se converteste automat valoarea parametrului efectiv spre tipul parametrului formal respectiv. apoi executia continua cu prima instructiune din corpul functiei apelate. valorile parametrilor efectivi se atribuie parametrilor formali corespunzatori. valoarea returnata de ea se utilizeaza la evaluarea expresiei respective. De cele mai multe ori aceasta concordanta se extinde si la numarul parametrilor. De exemplu. Aceasta forma de apel a functiei getch() se foloseste pentru a bloca executia programului pana la actionarea unei taste oarecare. atribuie variabilei c valoarea codului ASCII al caracterului citit de la tastatura. inainte de a se reveni din functie. valoarea returnata de ea se pierde. linia #include “fis1. iar linia #include “C:\BC31\Programe\Citire. O functie care returneaza o valoare.De exemplu.

numita factorial. else { rezult = factorial(m). for (i = 2. // se verifica daca n este in intervalul [0. else { rezult = factorial(m).0. for (i = 2. int i. atunci inainte de primul apel trebuie sa apara prototipul functie. cout << "Introduceti un intreg (0 la 170)= ".0. cout << m <<"! = " << rezult << "\n". if ((n < 0) || (n > 170)) return -1.h> double factorial(int). asa cum se arata in exemplu urmator: #include <iostream.cpp” void main() { int m. // Proptotipul functiei factorial void main() { int m. f = 1. if ((m < 0) || (m > 170)) cout << "Numarul introdus este < 0 sau > 170\n". cout << m <<"! = " << rezult << "\n". return f.h> #include “C:\BC31\Programe\fact. atunci in programul apelant trebuie inclusa o directiva #include. i <= n. Daca functia este plasata la sfarsitul programului. 170] if (n < 0 || n > 170) return –1. } In cazul in care functia este editata intr-un fisier separat. care sa specifice numele fisierului ce contine functia si locul unde se gaseste acest fisier.0. care primeste ca parametru un intreg din intervalul [0. } } 41 . f = 1. double rezult.0. double rezult. cin >> m. return f } Functia factorial poate fi editata impreuna cu programul apelant la sfarsitul sau la inceputul acestuia. cout << "Introduceti un intreg (0 la 170)= ". int i. } } double factorial (int n) { double f. i <= n. ca in urmatorul exemplu: #include <iostream.De exemplu. i++) // Calculeaza n! f *= i. i++) f *= i. sa consideram urmatoarea functie. if ((m < 0) || (m > 170)) cout << "Numarul introdus este < 0 sau > 170\n". numit fact. 170] si returneaza factorialul valorii receptionate. cin >> m. double factorial(int n) { double f.cpp din subdosarul Programe al dosarului BC31 din radacina discului C.

spre deosebire de limbajul C. . functia apelata poate modifica valorile parametrilor efectivi primiti.. } in care *x si *y sunt doua variabile de tip pointer catre tipul de date int. exista posibilitatea ca in locul valorilor parametrilor efectivi se se transmita adresele acestor valori.b. ppermuta(&a. functia apelata nu modifica parametri efectivi receptionati de la functia apelanta. In limbajul C++. &b). apelul se face prin valoare. *y = aux. parametrilor formali de tip pointer x si y li se atribuie adresele parametrilor efectivi a si b. y = aux. int y) { int aux: aux = x. apelul functiilor s-a realizat prin valoare. b. deoarece asa cum este definita functia. • In cazul apelului prin referinta. Adresele acestor zone se poate transmite prin intermediul pointerilor si deci trebuie sa fie definita astfel: void ppermuta(int *x. Pentru ca interschimbarea valorilor sa afecteze si parametri efectivi este necesar sa se transmita adresele zonelor de memorie in care se gasesc valorile respective. Acest mod de transmitere se numeste apel prin referinta. atunci apelul: permuta(a. Intre cele doua moduri de apelare exista o mare diferenta si anume: • In cazul apelului prin valoare. } care asigura interschimbarea valorilor parametrilor formali x si y.. &b). x = y. b). se poate defini functia de permutare ca o functie cu parametri formali de tip referinta. printre care si limbajul C++. 42 . astfel: void rpermuta(int& x. int *y) { int aux: aux = *x. la apelul unei functii. . Datorita faptului ca la apelare se transfera valori. functia ppermuta se va apela astfel: int a. nu va afecta parametri efectivi. In unele limbaje de programare. *x = *y. Functia ppermuta nu interschimba valorile pointerilor x si y ci valorile intregilor spre care refera cei doi pointeri. ceea ce inseamna ca x si y acceseza aceleasi zone de memorie ca si a si b. int& y) { int aux: aux = x. fiecarui parametru formal i se atribuie valoarea parametrului efectiv care-i corespunde. acest mod de transmitere se numeste apel prin valoare... Considerand declaratia: int a.Transferul parametrilor In limbajul C. In acest caz. In exemplele prezentate anterior. functia apelata primeste doar o copie a acestor valori si deci nu are acces la zonele de memorie alocate parametrilor efectivi. deoarece ea primeste adresele zonelor de memorie alocate parametrilor efectivi si nu valoarea acestora. Prin apelul ppermuta(&a. x = y. adica chiar valorile variabilelor a si b. Pentru a putea intelege diferenta dintre apelul prin valoare si cel prin referinta vom considera urmatoarea functie: void permuta(int x. Adica.

. } while(1). do { cout << "Numarul de componente [2.. // Numarul de componente ale vectorului void CitVect(DVector). b. #include <iostream. void SortAsc(DVector). Acest lucru se produce datorita faptului ca numele de tablou reprezinta adresa zonei in care sunt stocate componentele sale. for(i=0. In acest caz x si y sunt sinonime cu variabilele a si b. 100]: ". } void CitVect(DVector vec) { // Citeste dimensiunea si componentele vectorului int i. i++) cout << vector[i] << " ". void main() { int i. // Sortare descendenta cout << "Sirul sortat este:\n". cout << "Sirul initial este:\n". b). i++) cout << vector[i] << " ".. cout << "\n".} y = aux. i<nr. functia rpermuta se va apela astfel: int a. if(sort >= 'a' && sort <= 'z') sort -=32. // Declara DVector ca tip utilizator DVector vector. Comparand functiile permuta si rpermuta se poate observa ca ele se apeleaza la fel iar sigura diferenta dintre ele consta in modul de declarare al parametrilor formali. i<nr. atunci transmiterea prin valoare se comporta identic cu transmiterea prin referinta. cout << "\n". In acest situatie. for(i=0.h> typedef double DVector[100]. Din aceasta cauza permutarea valorilor referite de x si y inseamna permutarea valorilor referite de a si b. In cazul functiei permuta parametri formali sunt date de tip int. • SortAsc – ordoneaza crescator elementele vectorului. // Sortare ascendenta else SortDesc(vector). Aceasta inseamna ca x si y vor accesa aceleasi date din memorie ca si a si b.… rpermuta(a. } while(1). void SortDesc(DVector). Exemplu urmator foloseste trei functii: • CitVect . • SortDesc – ordoneaza descrescator elementele tabloului. CitVect(vector). i< nr. deci functia va putea sa modifice oricare dintre componentele tabloului. cin >> vec[i]. i++) { cout << "vector[" << i + 1 << "]= ". Daca transmitem un parametru care este un tablou. cin >> sort. // Citeste dimensiunea si componentele vectorului do { cout << "Sortarea va fi ascendenta sau descendenta (A/D)? ". char sort. cin >> nr. // Declara o variabila a tipului utilizator int nr. . 43 . .. if(sort == 'A') SortAsc(vector).citeste numarul de componente ale vectorului si valorile componentelor. iar in cazul functiei rpermuta acestia sunt referinte la date de tip int. for(i=0. if(sort == 'A' || sort == 'D') break. if(nr >=2 && nr <=100) break.

In aceasta situatie prototipurile functiilor ar fi trebuit sa fie urmatoarele: void CitVect(double vec[]).  In general. i++) if (vec[i] < vec[i+1]) { flag = 1. 44 . } } Se poate constata ca in program a fost definit tipul utilizator DVector. vec[i] = vec[j]. Daca nu declaram tipul utilizator DVector. i < nr -1. i++) for(j=i+1. ordinii si tipului cu parametrii formali. void SortAsc(double vec[]).  Functia poate returna cel mult o valoare. ca fiind un tablou unidimensional de 100 elemente de tip double. for(i=0. Pentru a obtine mai multe rezultate la revenirea dintr-o functie. j++) if (vec[i] > vec[j]) { aux = vec[i]. care va fi transmisa functiei prin valoare. el trebuie sa fie inclus intr-o structura. while (flag) { flag = 0. } } } void SortAsc(DVector vec) { // Sortarea ascendenta a vectorului int i. double aux. Am ales aceasta solutie pentru a simplifica scrierea prototipurilor si antetelor functiilor. atunci ar fi trebuit sa se foloseasca o declaratie de forma: double vector[100]. parametrii efectivi trebuie sa corespunda din punct de vedere al numarului. se vor folosi fie tablourile sau structurile pentru a grupa rezultatele de iesire. iar antetele de functie ar fi fost urmatoarele: void CitVect(double vec[]) void SortAsc(double vec[]) void SortDesc(double vec[]) Concluzii:  Tablourile sunt transmise prin referinta in mod prestabilit. j<nr. aux = vec[i]. vec[i] = vec[i + 1]. void SortDesc(double vec[]). i<nr-1. flag = 1. vec[i + 1] = aux. for (i = 0. pentru a declara un tablou cu 100 elemente de tip double. fie variabile globale. fie transferul prin referinta.} } void SortDesc(DVector vec) { // Sortarea descendenta a vectorului int i. j. double aux. pentru ca un tablou sa se transmita prin valoare. vec[j] = aux.

Defineste constante si declaratii pentru signal si raise. Declara prototipurile functiilor matematice. Declara functiile de sortare si cautare.h float. conversii.h assert. Defineste parametrii utilizati in functiile care folosesc fisiere partajate.h). Contine structurile si declaratiile pentru functiile spawn si exec.h iostream. lucru cu zone de memorie. alocare dinamica de memorie.h locale.h fstream.h dos. Defineste un tip folosit de longjmp si setjmp.h errno. declara tutinele de I/O la nivel de stream.h.h math.h stdio.) Functii de manipularea memoriei.h search. manipularea de siruri.h io. Defineste diferite constante si declaratiile necesare pentru apeluri specifice DOS si 8086. Defineste structuri.h memory.h setjmp. Nume fisier header alloc. calcule matematice. exista un numar foarte mare de functii de biblioteca.h fcntl.h ctype. defineste macrocomanda HUGE_VAL si declara structura utilizata de matherr.h dir. Accesul la operatorul new si newhandler. Declara functiile matematice complexe din C++. Contine macrocomenzi pentru declaratii de clase generice. Declara functiile care asigura informatiile specifice tarii si limbii. stdprn si stderr. Declara clasa bcd din C++ si suprascrie operatorii bcd3 si functiile matematice bcd. Defineste cateva tipuri de date si macrocomenzi generale. Contine informatii folosite de catre macrocomenzile de clasificare caractere si de conversie caractere.h signal. grafica etc.numar zecimal codificat binar 45 . Declara clasele stream din C++ ce suporta fisierele de intrare si iesire. Defineste macrocomenzile folosite pentru citirea listei de argumente din functiile declarate pentru a accepta un numar variabil de argumente. Declara diferite functii folosite la apelarea rutinelor IBM-PC ROM BIOS. Defineste tipurile si macrocomenzile necesare Standard I/O Package definit in Kernighan si Ritchie si extins sub sistemul UNIX System V.h Destinatie Declara functiile de manipulare a memoriei dinamice(alocare. Defineste constantele simbolice folosite la conectarea cu rutina de biblioteca deschisa.h new. Defineste stream-urile standard de I/O prestabilite: stdin. Contine parametrii pentru rutinele in virgula mobila.h direct. macrocomenzi si functii pentru lucru cu directoare si cai.h bcd. Declara manipulatorii de stream-uri de I/O din C++ si contine macrocomenzile pentru crearea manipulatorilor parametrizati.0).h process. Declara diferite functii folosite la apelarea rutinelor I/O ale consolei DOS.h bios.h dirent. Contine structurile si declaratiile pentru rutinele de intrare/iesire de nivel inferior. Declara functii si structuri pentru operatii cu directoare POSIX.h stdarg. 3 bcd . macrocomenzi si functii pentru lucru cu directoare si cai. Prototipurile acestor functii se gasesc in fisiere de tip header (extensie . In tabelul urmator sunt prezentate pe scurt fisierele de tip header din C++.Clase de functii In limbajul C++. Declara functiile de manipulare a memoriei (multe dintre ele sunt definite si in string.h limits.). Defineste macrocomanda assert de depanare. care sunt grupate dupa domeniul prelucrarilor efectuate in mai multe clase de functii astfel: prelucrarea fisierelor. Contine structuri.h complex.h generic. Contine parametrii mediului. spre deosebire de alte limbaje. Declara rutinele de baza ale stream-urilor (I/O) din C++ (version 2.h conio. informatiile despre limitarile din timpul compilarii. stdout.h share.. dealocare etc.h mem.h stddef. Defineste constantele mnemonice pentru codurile de eroare.h iomanip.

Declara functiile utime si structura utimbuf. fie direct fie prin intermediul altor functii. …. si anume determinarea sumei elementelor unui vector. } La utilizarea functiilor recursive se va acorda o atentie deosebita controlului finitudinii algoritmului si numarului de apeluri. utilizand tablouri pentru memorarea rezultatelor obtinute in fiecare iteratie si variabile temporare de lucru.stdiostr. Limbajul de programare C++. Un exemplu sugestiv in acest sens il constituie functia care realizeaza afisarea unui numar a. Pentru exemplificarea recursivitatii vom considera o problema simpla. Functia de conversie a unui numar din baza 10 intr-o baza oarecare mai mica decat 10 este: void afis(int a. Declara tipul time_t folosit de functiile de timp. O parte din limbajele de programare asigura implementarea algoritmilor recursivi cu ajutorul structurilor repetitive.h string. Declara cateva rutine de manipulare siruri de caractere.h time. Defineste constante importante. deoarece apar restrictii datorate dimensiunii stivei. din baza 10 intr-o baza b ∈[2 . 10]. Defineste constantele simbolice folosite pentru deschiderea si crearea fisierelor. Se foloseste stdarg. n –1). Pentru vectorul A = (a0.h varargs. an). Declara cateva rutine folosite in mod normal: rutine de conversie. Defineste macrocomenzile de stil vechi pentru procesarea listelor de argumente variabile.h values. functia este impartita in doua: o portiune se executa in mod repetat cu incarcarea in stiva a contextului si a adresei de revenire si o portiune a carei executie repetata incepe numai dupa ce s-a atins nivelul maxim de profunzime a recursivitatii (impus prin conditia de terminare).h utime. Definitii pentru parametrul mode al functiilor de blocare. 46 . permite descrierea algoritmilor recursivi cu ajutorul functiilor care se apeleaza pe ele insele. rutine de cautare/sortare si alte operatii. int n) { return n ? v[n-1] + sum(v.0) pentru a fi folosite cu structurile FILE pentru stdio.h sys\types.h sys\stat.h Declara clasele stream C++ (version 2. ca de altfel si alte limbaje de programare. Pentru afisare. daca n = 0 Sn = Sn-1 + an.h sys\locking. n = 0) si modalitatea prin care se modifica valoarea parametrilor efectivi la apeluri succesive (in exemplul de mai sus. inclusiv cele dependente de masina pentru compatibilitatea cu UNIX System V. Defineste o structura completata de rutinele de conversie ale timpului si un tip folosit de catre alte rutine de timp. Declara functia ftime si structura timeb ce este returnata de ftime. n – 1) : 0. trebuie sa se obtina resturile impartirilor succesive la baza in ordinea inversa si deci afisarea cifrei se va face dupa apelul recursiv. Declara clasele stream C++ pentru folosirea cu tablourile de byte din memorie.h Functii recursive Una din caracteristicile de baza ale algoritmilor este recursivitatea. a1. unsigned b) { if (a < b) cout >> a.h strstrea. Pentru aceasta in componenta functiei recursive distingem intodeauna conditia de terminare a procesului recursiv (in exemplul anterior. Practic prin apelul recursiv. daca n>0 Functia care va realiza aceasta operatie are urmatoarea forma: double sum(double v[]. Recursivitatea defineste modalitatea de a determina valoarea unei variabile pe baza valorilor ei anterioare si eventual a altor valori cunoscute. suma elementelor se poate descrie recursiv astfel: 0. de asemenea asigura prototipurile pentru aceste rutine.h stdlib.h sys\timeb.

Pointerii se folosesc pentru a face referiri la date prin adresa lor. Deci conditia pentru controlul recursivitatii se poate baza pe modificarea argumentelor de apel. localizator. Pentru a intelege pointerii. Astfel. care furnizeaza valoarea din zona de memorie a carei 4 In limba romana. in timp ce variabilele statice si externe exista intr-un singur exemplar pentru toate apelurile. daca este necesara cunoasterea acestor adrese. aceste numere de ordine se numesc adrese. astfel. long sVar = -65535. } Rezultatele afisate pe ecran vor arata ca cele din urmatoarea figura: Nota: Daca rulati acest program pe calculatorul dumneavoastra. in timp ce variabilele statice reflecta efectul cumulat al tuturor modificarilor anterioare. Pointeri Una dintre cele mai puternice facilitati disponibile pentru un programator C++ este posibilitatea de a manipula direct memoria calculatorului prin intermediul unor variabile care pot contine adrese. In constructia *p. reper. variabilele automatice vor exista in cate un set pentru fiecare apel recursiv (stiva se va incarca cu aceste variabile). pentru pointer. 47 . nu este necesar ca programatorul sa cunoasca adresa fizica de memorie in care este stocata valoarea unei variabile. } } Implicatiile claselor de memorare asociate variabilelor locale din functie sunt importante. incepand cu 0. b). cout << "Variabila shortVar= " << shortVar << " are adresa: " << &shortVar << endl. este posibil sa obtineti alte adrese. In mod normal. cout << "Variabila longVar= " << longVar << " are adresa: " << &longVar << endl. caracterul * se considera ca fiind un operator unar (numit si operator de indirectare sau de dereferentiere). cum ar fi: referinta. Memoria interna a unui calculator este impartita in locatii de memorie numerotate. cout >> a % b. deoarece compilatorul manipuleaza aceste detalii. daca p este o variabila de tip pointer care are ca valoare adresa lui x. unsigned long longVar=65535. Modificarile dintr-un apel nu se regasesc la revenire in cazul variabilelor automatice. pe variabile statice al caror continut se pastreaza intre doua apeluri succesive sau pe variabile globale. se mai folosesc si alte denumiri. se poate utiliza operatorul de adresa (&). cout << "Variabila sVar= " << sVar << " are adresa: " << &sVar << endl. asa cum este ilustrat in urmatorul program: #include <iostream. trebuie reamintite cateva lucruri despre memoria calculatorului.h> void main() { unsigned short shortVar=5. Pointerul4 este o variabila care are ca valoare o adresa. numite pointeri. Fiecare variabila a programului este stocata in memorie incepand de la o anumita adresa. Totusi.else { afis(a / b. indicator de adresa etc. atunci: *p reprezinta chiar valoarea lui x.

int *pPointer = &Variabila. putem considera ca: tip * dintr-o declaratie de pointeri reprezinta tip dintr-o declaratie obisnuita. in mod indirect. p = &q. De exemplu: int x. In urma acestei declarari. // lui p i se atribuie adresa lui q *p = 275. Pentru a atribui o adresa unei variabile de tip pointer se poate folosi operatorul unar & (numit si operator adresa sau de referentiere).. // in zona referita de p. va contine adresa variabilei x de tip int. Astfel. vom folosi atribuirea de forma: p = &x. p = &x. vom spune ca p refera (pointeaza) spre x. deci in q. cout << “Valoarea lui q este ” << q. Este permisa folosirea operatorului * in partea stanga a unei operatii de asignare pentru a atrubui unei variabile o noua valoare folosind un pointer. q = 235. Acest tip se spune ca este tipul pointer spre tip. Sa consideram urmatoarea secventa de cod: int Variabila = 5.h> void main() { int *p. daca dorim sa declaram un pointer p pentru a pastra adresa unor variabile de tip intreg vom folosi urmatoarea declaratie: int *p. urmatorul program atribuie variabilei q o noua valoare. int *p. utilizand un pointer: #include <iostream. atunci vom spune ca p refera spre tip. Declararea pointerilor Pentru a declara un pointer se foloseste sintaxa urmatoare: tip *nume. In urma executiei acestei secvente de instructiuni. sa aiba adresa lui x). aceasta trebuie atribuita explicit. } Notiunea de pointer joaca un rol important. deoarece permite calcule cu adrese. De asemenea. care se interpreteaza astfel: identificatorul nume refera zona de memorie alocata datelor de tipul tip si va putea contine adresa acestei zone. calcule specifice limbajelor de asamblare. Cand valoarea unei variabile este referita printr-un pointer. de tip pointer spre tipul int.. Din acest motiv constructia tip * se spune ca reprezinta un tip nou. daca p are ca valoare adresa de inceput a unei zone de memorie care contine o data de tipul tip. Valoarea de la adresa pastrata de pPointer este 5. variabila p. De exemplu. De exemplu. Facand comparatie cu declararea unei variabile obisnuite: tip nume. se va stoca 275 cout << “Noua valoarea a lui q este ” << q. Variabila este declarata ca fiind de tip intreg si initializata cu valoarea 5. pPointer este declarat ca fiind un pointer spre un intreg si este initializat cu adresa variabilei Variabila. variabila p nu contine nici o adresa. Daca p contine adresa zonei de memorie alocata variabilei x. procesul este numit indirectare. tipul pointer. Tipul int indica faptul ca p va contine adrese de memorie in care se pastreaza date de tip int. 48 .adresa este continuta in p. Este foarte important sa facem distinctie intre un pointer. daca vrem ca p sa refere spre x (adica. . adresa pe care o pastreaza pointerul si valoarea de la adresa retinuta de pointer.

adica in intervalul de adrese ale unui segment (0 la 65535). In aceasta situatie. char etc. p = &y. deoarece nu este definit tipul datei spre care refera p. deoarece valoarea lui p trebuie convertita spre tipul int * si de aceea expresia cast are forma: (int *)p Reamintim ca. .. iar acesti pointeri se numesc pointeri far. Tipul pointerului se poate cere in mod explicit la declararea unei variabile pointer. este incorecta.. p = &c. pentru conversia explicita a unui tip se foloseste urmatoarea forma generala: (tip) operand Un pointer poate referi o variabila care se gaseste in acelasi segment cu el. pentru a preciza tipul de date spre care refera pointerul la un moment dat. Exemplu: int x. precum si pe sistemele care folosesc numai adrese pe 32 biti (noile variante de Windows) este nevoie de 4 bytes pentru adresa. Cand se folosesc pointeri de tip void. float.. permitand construirea de programe cu un inalt grad de generalitate si abstractizare. p = &x.Exista situatii cand dorim ca un pointer sa fie folosit cu mai multe tipuri de date. specifica lucrului cu tablouri. caz in care adresa variabilei va contine doar deplasamentul (offset-ul) fata de inceputul segmentului. Pointerii stau la baza adresarii indirecte.. . fie declaratiile: void *p. atribuirea *p = 10. void *p. De exemplu. sunt necesare conversi explicite prin expresii de tip cast. Ei se folosesc nu numai ca o alternativa de adresare. float y. Forma corecta a acestei atribuiri este: *(int *)p = 10.. prin folosirea cuvintelor cheie near sau far. . lui p i se pot atribui adrese de memorie care pot contine date de tipuri diferite: int. Pentru pointerii care refera variabile ce se gasesc intr-un segment diferit de cel al pointerului. Deci formatul complet al declaratiei unei variabile pointer este urmatorul: tip [near | far]*nume. int x. Intr-un pointer near se poate reprezenta o adresa de maximum 64 KB.. char c. 49 . deoarece nu putem specifica un tip se va folosi cuvantul cheie void: void *nume... . In acest caz pointerul ocupa 2 bytes si se numeste pointer near. • adresarea functiilor. adresa de inceput a segmentului fiind implicita. ci si pentru a realiza unele sarcini specifice cum ar fi: • adresarea indexata. • alocarea si gestiunea memoriei dinamice. Datorita faptului ca p a fost declarat folosind cuvantul void.

Sugestie: In general. si instructiunea: p = &vec[i}. De exemplu. Deci o atribuire de forma: y = vec[n]. Acest lucru mareste viteza de lucru. adunarea si scaderea unui intreg. reiese ca rezultatul acestei expresii este chiar adresa elementului vec[n] si ca urmare *(vec + n) va furniza chiar valoarea elementului vec[n]. dar modul de executie este diferit. cum ar fi: incrementarea si decrementarea. diferenta etc. Atunci vec este un pointer constant5 si ca atare o expresie de forma vec + n este corecta.Operatii cu pointeri Datorita faptului ca pointerii sunt un tip de date asupra lor se pot aplica diferite operatii. deoarece operatiile de inmultire care intervin la evaluarea variabilelor cu indici sunt inlocuite cu adunari. In continuare sunt prezentate pe scurt opratiile cu pointeri. 50 . variabilele cu indici pot fi inlocuite prin expresii cu pointeri. pentru i putand lua valori intre 0 si 9. unde prin r s-a notat numarul de bytes necesari pastrarii in memorie a datei de tip t. compararea. double *p. este echivalenta cu y = *(vec + n). valoarea incrementului (decrementului) este egala cu numarul de bytes necesari pentru a pastra data de tipul referit de pointer. deci p va avea adresa lui vec[i-1]. int i. la fel ca si in cazul variabilelor. In cazul pointerilor. atunci expresiile: ++p si p++ majoreaza valoarea lui p cu 8 (pentru stocarea unui numar de tip double sunt necesari 8 bytes). 5 Numele unui tablou este un pointer constant si contine adresa primului element al tabloui (elementul de indice 0). atunci se pot utiliza expresiile: p + n si p – n Rezultatul acestor operatii consta in marirea (micsorarea) valorii lui p cu produsul r * n. deci p va avea adresa lui vec[i+1].se pot aplica si asupra operanzilor de tip pointer. expresiile: p-- si --p micsoreaza valoarea lui p cu 8. Fie vec un vector de tipul t. sa consideram urmatoarele declaratii: double vec[10]. Incrementarea si decrementarea pointerilor Operatorii ++ si -. In mod analog. Din cele afirmate mai sus. Adunarea si scaderea unui intreg la/dintr-un pointer Daca p este un pointer spre tipul t si n un intreg.

Diferenta a doi pointeri Doi pointeri care refera spre elementele aceluiasi tablou pot fi scazuti. folosind operatorii relationali (<. In acest caz. i++. memoreaza valoarea citita in zona referita de p. ci chiar valoarea 0. p >= q. daca p refera spre elementul vec[i] al tabloului vec (deci contine adresa elementului i) si q refera spre elementul vec[j] al aceluiasi tablou (deci contine adresa elementului j). De exemplu. } return i. Operatorii de egalitate pot fi utilizati si pentru a compara pointerii cu o constanta speciala NULL. !=). cin >> temp if (temp) break. >=) si de egalitate (==. dupa care valoarea lui p este incrementata. In mod analog se evalueaza si celelalte expresii relationale. Fie p un pointer catre elementul vec[i] si q un pointer spre elementul vec[i + n] al aceluiasi tablou. <=. i). deci in acest moment ciclul while trebuie sa se termine. p == q si p != q sunt corecte. char temp[255]. daca nu se forteaza terminarea ciclului (se introduc mai putin de n valori). diferenta q – p are valoare n. De regula aceste operatii se executa asupra pointerilor care refera elementele unui tablou. >. p devine egal cu q. Dimensiunea acestei zone este de 8 * n bytes (o valoare de tip double ocupa 8 bytes) si deci in pointerul q se va stoca adresa ultimei zone de memorie desitinata pastrarii valorilor citite.h astfel: #define NULL 0 si ea reprezinta asa numitul pointer null. p <= q. int i = 0. adresa de inceput a zonei in care se vor stoca numerele citite. } Observatii:  Parametrul p are ca valoare la apel. *p++ = d. daca i < j si fals (0) in caz contrar. // adresa de sfarsit a zonei de memorie while (p < q) { cout << “Valoarea elementului= “. double *p) { double d. 51 . le pastreaza in zona de memorie a carei adresa de inceput este indicata de valoarea parametrului formal p al functiei si va returna numarul de valori citite. expresia: p < q are valoarea adevarat (1). astfel ca la urmatoarea iteratie p va referi zona in care se va stoca elementul urmator. care este definita in fisierul stdio. atunci expresiile: p < q. if (gets(temp) == NULL) return i.  Instructiunea *p++ = d. Exemplu: a) Sa se scrie o functie care citeste cel mult n elemente de tip double diferite de 0. Introducerea unei valori 0 va forta terminarea operatiilor de citire a valorilor int pnrdblcit(int n. double *q = p + n.  Dupa citirea a n valori. p > q. Astfel. adica pointerul este definit dar nu contine o adresa. Sugestie: In limbajul C++ se recomanda sa nu se foloseasca constanta NULL.Compararea a doi pointeri Doi pointeri care refera acelasi tip de date pot fi comparati.

5 este o constanta de tip double. prin caracterele care o compun.14159 se foloseste de mai multe ori in program.. In limbajul C++ constantele pot fi definite si prin utilizarea modificatorului const in declaratii. Aceste constante se deosebesc de cele declarate prin constructia #define prin faptul ca ele nu sunt prelucrate la preprocesare. preprocesarea (faza care se executa in mod automat inaintea compilarii) substituie nume cu succesiune_caractere peste tot in textul sursa care urmeaza constructiei #define respective.. const tip nume = valoare.Modificatorul const O constanta este definita. // se substituie cu int vector[50 + 5] Constantele simbolice se folosesc frecvent in locul constantelor obisnuite datorita urmatoarelor avantaje: • Permite atribuirea unor nume sugestive constantelor. Aceasta are fromatul: #undef nume Succesiunea de caractere dintr-o constructie #define poate contine constante simbolice care au fost definite in prealabil prin alte constructii #define. Daca o constanta se foloseste de mai multe ori intr-un program si ulterior este necesara modificarea valoarii ei (de exemplu trebuie schimbata precizia constantei). Folosind aceasta constructie. de regula. Permite realizarea unor prescurtari. Formatele posibile ale unei declaratii cu modificatorul const sunt: • tip const nume = valoare. Constructia #define are urmatorul format: #define nume succesiune_caractere unde: • nume reprezinta identificatorul constantei simbolice. decat sa o inlocuim peste tot in program. • Permite inlocuirea simpla a unei constante printr-o alta. atunci este mai simplu sa folosim numele PI atribuit ei.. b) #define lim_sup 50 . De exemplu. atunci este mai simplu sa schimbam o singura data valoarea in constructia #define. exceptand cazul cand nume apare intr-un comentariu sau sir de caractere... Cu ajutorul constructiei #define se pot defini constante simbolice. De exemplu . int vector[lim_sup + 5]. O constructie #define autorizeaza substitutia pe care o defineste din punctul in care a fost scrisa si pana la sfarsitul fisierului sau pana la intalnirea unei constructii #undef care o anuleaza. De exemplu.. “a” este o constanta de tip sir de caractere. . Exemple: a) #define A 23 #define B A * 5 . este mult mai sugestiv sa folosim constanta PI definita prin: #define PI 3. • succesiune_caractere reprezinta valoarea constantei simbolice. daca valoarea 3. 52 .14159 decat valoarea ei. // se substitue cu x = 5 + 23 * 5 . ‘a’ este o constanta de tip char. Caracterele unei constante pot indica atat tipul cat si valoarea acesteia.. 1234 este o constanta de tip int. 1.. numele lor exista la compilare. tip const *nume = valoare. x = 5 + B.

p = (char *)s. 1. constanta respectiva poate fi modificata folosind un pointer diferit de s: char *p. // pointerul p refera acum aceeasi zona ca si s Aceste atribuiri sunt corecte.i este o constanta intreaga si are valoarea 10. De obicei. 3. 4. . Pentru a proteja datele din zona referita de nume este indicat sa se declare parametrul formal ca un pointer catre o zona constanta. *(s+1) = ‘b’. const tip *const nume = valoare. deoarece p nu mai este un pointer catre o zona constanta. *(p+1)=’b’. Exemple: a) int const i = 10. Declaratia de forma: (6) const nume = valoare. const tip *nume. In acest caz valoarea pointerului nume se poate schimba. Toate acestea se numesc declaratii de constante. se foloseste pentru a declara un parametru formal. este identica cu declaratia (1) daca tip nu este un tip pointer. const nume = valoare. Daca tip este un tip pointer. atunci ea este identica cu declaratia (2). Acest lucru da posibilitatea functiei f sa modifice data din zona de memorie spre care refera nume folosind o atribuire de forma: *nume = valoare. deoarece s refera o zona in care se pastreaza o data constanta.14159265. in locul formatului (2) pentru a declara un pointer spre o zona constanta. O declaratie de forma: (1) tip const nume = valoare. unde t este un pointer spre tipul char este corecta. De exemplu. sunt eronate. Declaratia de forma: (5) const tip *nume. Fie functia f de antet: tip f(tip *nume) La apelul functiei f.declara numele pi ca fiind o constanta de tip double si care are valoarea 3. atunci atribuirea: s = t. 2. defineste nume ca un pointer spre o zona constanta. 53 . este asemanatoare cu o declaratie de variabila avand o valoare de initializare: (1’) tip nume = valoare. In schimb atribuirile: *s = ‘a’. fie declaratia: char const *s = “Sir de caractere”. Diferenta dintre cele doua declaratii consta in aceea ca valoarea lui nume atribuita prin declaratia (1) nu poate fi schimbata folosind o expresie de atribuire de forma: nume = expresie. . Declaratia de forma: (3) const tip nume = valoare. se utilizeaza formatul (4). deci antetul functiei f trebuie sa fie de forma: tip f(const tip *nume) 5.14159265. parametrului formal nume i se atribuie ca valoare o adresa. b) double const pi = 3. Totusi. tip *const nume = valoare. adica declaratia (3) este de forma: (4) const tip *nume = valoare. O declaratie de forma: (2) tip const *nume = valoare.const tip *nume = valoare.

. lui pi i se atribuie ca valoare adresa lui x. const char *sir = “test”. Prin utilizarea pointerilor.. Din acest motiv.. apelul prin valoare se poate transforma in apel prin referinta. . Astfel. atunci valoarea 100 se atribuie varaibilei x. In cazul functiei g parametrul formal pi este un pointer spre date de tip int. si respectiv void g(int *pi). Exercitii: Sa se scrie o functie care dintr-o data calendaristica definita prin numarul zilei din an si anul respectiv. *pi = 100. a carei adresa s-a atribuit la apel lui pi. nu este permisa. daca x este o variabila simpla. Declaratia de forma: (7) tip *const nume = valoare. cu deosebirea ca nu se mai specifica tipul constantei. char *s. int *luna) { /* zz – ziua din an an – anul. .este similara cu declaratia (1). g(&x). 7. . Atunci. atribuirile: *psir = ‘A’. // Se transfera valoarea lui x // Se transfera adresa lui x In acest caz cele doua functii vor avea antete diferite si anume: void h(int x). int *zi. h(x). parametrilor formali li se atribuie valorile parametrilor efectivi ce le corespund. functia g are posibilitatea de a modifica valoarea lui x. La apel.. in timp ce: psir = s. int an. daca in corpul functiei g se foloseste instructiunea: . In schimb. zi – pointer a carei valoare este adresa zonei in care se pastreaza ziua 54 . deoarece psir este un pointer constant. defineste un pointer constant spre o data care nu este constanta. sunt corecte. determina luna si ziua din luna respectiva. fie declaratiile: char *const psir = “abc”. In acest caz tipul constantei se stabileste in functie de valoarea atribuita acesteia. adresa lui x: int x.. este corecta. De exemplu. Declaratia de forma: (8) const tip *const nume = valoare.. apelul prin valoare devine de fapt un apel prin referinta si deci parametrului formal corespunzator lui i se atribuie ca valoare adresa primului element al tabloului. *(psir + 2)= ‘C’. atunci la un apel putem transfera in locul valorii lui x. Daca un parametru efectiv este un nume de tablou. *(psir + 1)= ‘B’. . De exemplu.. Aceasta inseamna ca la apelarea unei functii.. Apel prin referinta folosind parametrii de tip pointer In limbajul C apelul functiilor este prin valoare. void luna_ziua(int zz. o atribuire de forma: sir = s.. defineste un pointer constant catre o data constanta.. 6.

Notiunea de expresie lvalue apartine autorilor limbajului C. Ulterior s-a introdus si notiunea de expresie rvalue. corespund formatului (1) al expresiei de atribuire. 55 . void *p. lucru care s-a prezentat mai devreme in acest material. Fie: p = &n.14159 realizeaza acelasi lucru ca si expresia x = 3.14159 Expresiile utilizabile in partea stanga a unei expresii de atribuire se numesc expresii lvalue.*/ // Se determina daca anul este sau nu bisect int bisect = an % 4 == 0 && an % 100 != 0 || an % 400 == 0. luna . In mod analog. *zi = zz.14159. o variabila cu indice (permite accesul la un element de tablou) sau defineste un element de structura. atunci atribuirea: *(double *)p = 3. x = 3. // pointer catre un tip nedefinit de date In aceste conditii expresiile de atribuire de forma: n = 123. int i. care este o expresie ce se poate utilza in partea dreapta a unei expresii de atribuire. // Valoarea lui i (luna determinata) este stocata in // zona a carei adresa se transmite prin parametrul // efectiv corespunzator parametrului formal *luna } determinata de functie. Din cele prezentate rezulta ca o expresie lvalue poate fi: • un nume de variabila simpla. double x. zz > nrzile[i] + (i == 2 && bisect). Definitia expresiei de atribuire poate fi completata in momentul de fata prin adaugarea unui nou format: (2) *ep = expresie unde: ep este o expresie care defineste un pointer nenul si care nu este un pointer catre o zona constanta.pointer a carei valoare este adresa zonei in care se pastreaza ziua determinata de functie. // Valoarea lui zz este pastrata in zona a carei adresa // se transmite prin parametrul efectiv corespunzator // parametrului formal *zi *luna = i. daca p = &x. Litera l provine dela cuvantul englezesc left. Expresia (int *)p defineste un pointer spre o zona care nu este constanta si deci atribuirea de mai sus corespunde formatului (2) al expresiilor de atribuire. i++) zz -= (nrzile[i] + (i == 2 && bisect)). extern int nrzile[]. Sa consideram urmatoarele instructiuni de declarare: int n. Expresie lvalue Pana in prezent s-au folosit expresii de atribuire de forma: (1) variabila = expresie unde: variabila – se considera ca fiind o variabila simpla. // Tablou definit in apelant cu zilele lunilor for (i = 1. dar nu si in partea stanga. atunci *(int *)p = 123 realizeaza acelasi lucru ca si n = 123.

2. se poate utiliza functia coreleft: unsigned coreleft(void). vector=new double[m]. delete [3] a. double (*a)[5]=new double [3][5]. }. char c. Alocarea dinamica a memoriei În limbajul C++ alocarea dinamica a memoriei si eliberarea ei se pot realiza cu operatorii new si delete. Operatorul delete elibereaza zona de memorie spre care pointeaza argumentul sau. // delete vector va dezaloca numai prima componenta Sa se aloce dinamic memorie pentru o matrice cu 3 linii si 5 coloane de tip double. p=new double(-7. initializând-o cu valoarea -7. delete [nr_elem] tipdata_pointer. pint=new int. double *p. adaptata programarii orientate obiect. Pentru a putea afla memoria RAM disponibila la un moment dat. Exercitii: Sa se aloce dinamic memorie pentru o data de tip întreg: int *pint. Operatorul new este un operator unar care returneaza un pointer la zona de memorie alocata dinamic. tipdata_pointer = new tipdata(val_initializare). constructie ce permite accesul sau modificarea valorii unui element de structura. // prelucrari cu *pint delete pint. delete [m] vector. Sa se aloce dinamic memorie pentru o structura cu doua campuri: unul intreg iar celelat de tip caracter: struct articol { int nr. constructie de forma *ep (unde ep este o expresie care defineste un pointer nenul spre o data care nu este constanta.2). În situatia în care nu exista suficienta memorie si alocarea nu reuseste. // prelucrari cu a de exemplu a[2][2]==4. articol *pa.• • • variabila cu indici. Folosirea acestor operatori reprezinta o metoda superioara. Sa se aloce dinamic memorie pentru un vector de m elemente reale. double *vector. operatorul new returneaza pointerul NULL. // prelucrari cu *p delete p. dubla precizie. tipdata_pointer = new tipdata[nr_elem]. Sa se aloce dinamic memorie pentru o data reala.9 etc. • tipdata_pointer este o variabila pointer catre tipul tipdata. Sintaxa: tipdata_pointer = new tipdata. unde: • tipdata reprezinta tipul datei (predefinit sau obiect) pentru care se aloca dinamic memorie. delete tipdata_pointer. // prelucrari cu vector de exemplu vector[3]=4 etc. 56 .

Ultima componenta va avea informatia de legatura egala cu NULL. cout<<endl<<"nr=". pointer. Componentele listei se vor aloca dinamic. trebuie sa contina si o informatie de legatura cu componenta cu care se leaga logic in succesiune. Alocarea dinamica a componentelor structurii impune un mecanism prin care noua componenta aparuta este legata intr-o succesiune logica de corpul structurii deja format pana atunci. In functie de tipul inlantuirii realizate intre componente. }.pa=new articol. Memoria folosita pentru alocarea dinamica se numeste heap. nod *next. Se poate defini un tip de date structurat numit nod. Avantajele alocarii dinamice fata de alocarea acelorasi structuri de date in mod static (in segmentul de date) sau volatil (in segmentul de stiva) sunt: • memorie suplimentara pentru programe. Fie acesta cap. pentru o lista de numere reale. Structuri dinamice de date Structurile dinamice de date sunt date structurate ale caror componente se aloca in mod dinamic. inregistrare) si informatia de legatura (adresa la care e memorata urmatoarea componenta). cout<<endl<<"caracterul ". Liste liniare simplu inlantuite O componenta a unei liste simplu inlantuite se declara ca o data structurata de tip inregistrare. delete pa. iar mecanismul se mai numeste si alocare inlantuita dupa adrese. In heap. tablou. cout<<endl<<pa->nr<<" "<<pa->c<<" tot la adresa "<<pa<<endl. formata din doua campuri: informatia propriu-zisa (care poate fi de orice tip: numeric. caracter. ale carui campuri le vom numi info si next. Rezulta ca fiecare componenta. putem defini urmatoarea structura: struct nod { float info. pe langa informatia propriu-zisa pe care o detine. structura respectiva va avea zone alocate componentelor sale in locurile gasite disponibile. De exemplu.cin>>pa->c. Valoarea acestei variabile se va atribui dupa fiecare generare de variabila dinamica si astfel ea pastreaza intotdeauna adresa ultimei 57 . Aceasta informatie de legatura va fi adresa componentei spre care se realizeaza succesiunea logica.cin>>pa->nr. avand semnificatia ca dupa ea nu mai urmeaza nimic (retine adresa „nici o adresa”). • posibilitatea de a utiliza doar cantitatea de memorie necesara. ele pot fi:  liste simplu inlantuite (liniare si circulare)  liste dublu inlantuite (liniare si circulare) • structuri arborescente (de exemplu reteaua ierarhica a angajatilor dintr-o firma) • structuri retea (de exemplu o retea de orase care schimba materiale. care nu intotdeauna se succed in ordinea in care este realizata inlantuirea logica. exista urmatoarele tipuri de organizari: • structuri liniare (de exemplu o lista care prelucreaza elevii inscrisi la un examen). combustibili etc). Pentru a putea accesa primul element din lista este necesara o variabila statica sau locala.

nod *next. }. si astfel din aproape in aproape putem ajunge la ultimul element al listei (de fapt primul element generat).h> #include<conio. // se afiseza campul informatie utila c = c->next. la urmatorul nod 58 . c = p. // campul adresa urmatoare a ultimului nod este 0 } void afis() { // Functia parcurge si afiseaza nodurile nod *c. nod *p.*u . nod *cap.h> struct nod { int info.zone alocate. // se stabileste noul nod c ca fiind ultimul } u->next = 0. // acceseaza primul respective ultimul nod int n. u->next = c. Cu ajutorul ei putem accesa prima componenta. cin >> p->info. // la creare primul si ultimul nod vor fi identici } else { // altfel se adauga un nou nod la sfarsit c = new nod. // numarul de noduri void cre_ad() { // functia de creare si adaugare a unui nou nod nod *c. // se aloca un nou nod cout << "informatia utila :" . cout << "valoare primului nod ". u = p. care contine o legatura catre cea de-a doua etc. // se porneste de la primul nod din lista while(c) { // cat timp c retine o adresa nenula cout << c->info< < " ". Referirea la informatia primului nod al listei se va face astfel: cap->info Referirea la adresa la care e memorat al doilea nod al listei se va face astfel: cap->next Referirea la informatia celui de-al nod al listei se va face astfel: cap->next->info Operatiile care se pot face utilizand liste simplu inlantuite sunt: • crearea listei • adaugarea unui element la inceputul listei • adaugarea unui element la sfarsitul listei • adaugarea unui element in interiorul listei • stergerea elementului de la inceputul listei • stergerea elementului de la sfarsitul listei • stergerea unui element din interiorul listei • traversarea listei. // se avanseaza la urmatoarea adresa. // se adauga dupa ultimul nod u = c. cin >> c->info. cu prelucrarea elementelor acesteia Exemplu de lista liniare simplu inlantuite Urmatorul program ofera o modalitate de creare si afisare a unei liste liniare simplu inlantuite care prelucreaza numere intregi: #include<iostream. if(!p) { // daca lista este vida (p==0) se aloca primul nod p = new nod.

Evident s-ar putea realiza si inserare pe o pozitie data astfel incat toate elemantele de la acea pozitie incepand se vor deplasa mai la "dreapta". cout << "n= ". se creaza un nou nod (nodul a) apoi se genereaza legaturile: mai intai de la a la c->next apoi de la c la a.i<=n. Exista posibilitatea ca ultimul nod sa retina valoarea dupa care se realizeaza inserarea caz in care se modifica ultimul. c a c a c a c->next c->next c->next In cazul in care inserarea se face "inainte" se avanseaza cu un nod intermediar c in lista pana la un nod situat inaintea celui cautat .} cout << endl. } void main() { int b. for(int i=1.i++) cre_ad(). In cazul in care inserarea se face "dupa" se avanseaza cu un nod intermediar c in lista pana la nodul dupa care se va face inserarea. afis(). getch(). Exista posibilitatea ca primul nod sa retina valoarea inainte de care se realizeaza inserarea caz in care se modifica primul. cin >> n. cout<<endl. c a c a c a c->next c->next c->next 59 . } Inserarea si stergerea unui element din lista Inserarea unui element nou in lista se poate face inainte sau dupa un nod care retine o valoare data (continut util). se creaza un nou nod (nodul a) apoi se genereaza legaturile: mai intai de la a la c->next apoi de la c la a.

primul // si prin urmare adresa urmatoare lui este 0 ultim = prim. ultim->next = c. cout << "valoarea de adaugat in lista ". // Functiile de creare si adaugare se pot comprima intr-un singura care poate // testa daca exista un prim nod si in functie de rezultatul testului va // realiza alocarea primului nod sau se va adauga un nou nod la sfarsit void creare_adaugare() { if(prim == NULL) { prim = new Nod. // Nod *a retine adresa nodului ce se va insera in lista // cu *c se face avansarea in lista pana la nodul ce contine valoarea dupa // care se face inserarea. se genereaza noile legaturi intre precedentul si urmatorul nodului de sters. se elibereaza spatiul de memorie. while(c != 0) { // cat timp mai sunt noduri in lista cout << c->info <<" ". cin >> c->info.. c = new Nod. Nod *prim.*a. evident se porneste de la primul. avand un singur element acesta va fi si // primul si ultimul } else { Nod *c. dupa ultimul din lista ultim = c. ultim->next = 0. }. //.. // se avanseza in lista trecand la urmatoarea adresa } cout << endl.. // evident. } // Inserarea dupa o valoare val transmisa din main() void inserare_dupa(int val) { Nod *c. c = c->next.h> #include<fstream. cout << "introduceti valoarea retinuta in primul nod:".si dupa ultimul nu e nimic. // se "agata" noul nod c.In cazul in care se realizeaza stergerea se avanseaza cu un nod intermediar c in lista pana la un nod situat inaintea celui cautat (nodul de sters). // la crearea listei va exista un singur nod. deci nici o adresa } } void listare() { // Afisarea datelor din lista Nod *c. Nod *next. prim->next = 0. c = prim. *ultim. cin >> prim->info. c c->next c->next->next c c->next c->next->next c c->next->next Iata o solutie pentru implementarea functiilor anterioare: #include<conio.h> struct Nod { int info. 60 .. // evident noul nod e ultimul.

Se va genera o noua // legatura intre c si a->next c = prim. i<=n. cin >> a->info. listare(). // se elibereaza memoria } } void main() { int i. *a. // urmeaza a fi sters a = c->next. // primul va deveni urmatorul element delete a. while(c->info != val c = c->next. // a se sterge. // exista si posibilitatea ca valoarea se face inserarea sa fie retinuta de ultimul nod 61 . delete a. c->next = a->next. } // Stergerea nodului al carui continut a fost transmis ca parametru void stergere(int val) { Nod *c.} // Inserarea inainte de o valoare val transmisa din main() void inserare_inainte(int val) { Nod *c. *a. cin >> a->info. c->next = a. if(a==ultim) ultim = c. // Nod *a retine adresa nodului ce se va insera in lista // cu *c se face avansarea in lista pana la nodul ce contine valoarea // inainte care se face inserarea. //se elibereaza memoria } else { while(c->next->info!=val &&c) // se pozitioneaza pe elementul ce c = c->next. c = prim. if(c == ultim) ultim // dupa care && c) inserat ". cin >> n. // pentru ca exista si posibilitatea ca valoarea inainte de care se face // inserarea sa fie retinuta de primul nod se va face un test si in caz // afirmativ se va stabili un nou prim element if(prim->info == val) { c = new Nod. for(i=1. } else { while(c->next->info!=val &&c) //c se pozitioneaza inainte de nodul cautat c = c->next. a->next = c->next. cout << "Numarul de noduri din lista: ". prim = c. c->next = prim. c->next = a. c = prim. val_info. = a. // se retine in a prim = prim->next. evident se porneste de la primul. if(prim->info==val) { //daca primul nod retine val se sterge primul a = prim. cout << "valoarea de inserat ". cout << "valoare de inserat ". i++) creare_adaugare(). a = new Nod. cin >> c->info. a = new Nod. cout << "valoarea de a->next = c->next. n. c este precedentul sau.

ultim->next = c. creare_adaugare(). } c = c->next. while(c != 0) { // cat timp mai sunt noduri in lista 62 . ord = 0. prim->next = 0. }. void creare() { prim=new Nod. // la crearea listei va exista un singur nod.. Ordonarea se poate face prin aceleasi metode.. cin>>val_info. Nod *next. inserare_inainte(val_info). listare().h> struct Nod { int info. Nod *prim. //. avand un singur element acesta va fi // si primul si ultimul } void adaugare() { Nod *c. cout << "Valoarea retinuta in primul nod:".h> #include<conio. inserare_dupa(val_info). } void listare() { Nod *c. cout << "valoarea de adaugat in lista ". ord = 1. // se "agata" noul nod c.. cout<<"valoarea inainte de care se face inserarea ". deci nici o adresa } void ordonare() { Nod *c. In exemplul urmator este prezentata ordonarea prin metoda Bubble Sort: #include<iostream. listare(). c = new Nod. // evident noul nod e ultimul. stergere(val_info).} cout<<"dupa ce valoare din lista se realizeaza inserarea ". *ultim. cin>>val_info. while(c->next) { if(c->info > c->next->info) { aux = c->info. do { c = prim. cin >> prim->info. ultim->next = 0. c->next->info = aux. // evident. c->info = c->next->info. cout<<endl<<"valoarea ce urmeaza a fi stearsa: ".int ord. // primul si prin urmare adresa urmatoare lui este 0 ultim = prim. Ordonarea unei liste Principiul acestei operatii este acelasi ca si la vectori.} } while(ord == 0). listare(). cin>>val_info.. dupa ultimul din lista ultim = c. listare().aux. c = prim.si dupa ultimul nu e nimic. cin >> c->info.

h> struct nod { int info. // se avanseza in lista trecand la urmatoarea adresa } cout<<endl. i. c->next = a->next. cout << "cate elemente va avea lista? ". c->info = x.*a. u->next = 0. *u. nod *p. u = c. nod* next. } else if(x > u->info) { // inserare dupa ultimul u->next = c. } Este preferabil ca in unele situatii sa se genereze de la inceput o lista ordonata (crescator de exemplu) si astfel in functia de creare a listei vor exista urmatoarele situatii: • inserarea se face inainte de primul nod (informatia mai mica decat p->info) • inserarea se face dupa ultimul nod (informatia mai mare decat u->info) • inserarea se face in interior Iata o solutie pentru functia de creare ordonata a listei: #include<iostream. } void main() { int n. listare().h> #include<conio. nr. cin >> n. p->info = x. }. i++) adaugare(). c = new nod. cout << endl <<"elementele listei dupa ordonare. val. creare(). if(!p) { // test lista vida p = new nod. listare(). getch(). i<=n.cout << c->info << " ". 63 . c = c->next. p = c. } else { // inserare in interior a = p. } else if(x <= p->info) { // inserare inainte de primul c->next = p. ordonare(). while(x > a->next->info) a = a->next. u->next = 0. cout << "Elementele listei sunt: ". void cre_ord(int x) { // creare lista ordonata nod *c. u = p." << endl. for(i=2.

} Structura de date de tip coada Aceasta structura de date este un caz particular de lista care functioneaza pe principiul FIFO (first in – first out. } } void afisare() { nod *c. c = p. cre_ord(y).20.} a->next = c. c = c->next. Prin urmare principalele prelucrari care se refera la aceasta structura de date vor fi: • creare coada • listare coada (parcurgere) • adaugare la sfarsir • stergere primului element • prelucrare primului element Specific acestei structuri de date este faptul ca adaugarea se va face intotdeauna dupa ultimul element in timp ce prelucrarea (sau stergerea) se va face la celalat capat. cout << endl << "continutul listei " << endl. retinut de varful cozii. Functia pune(). cin >> y. Fie o structura de tip coada care retine 4 numere intregi: 10. prin apelul functiei scoate se va obtine: 10 20 20 30 30 40 40 50 50 64 .10. prin apelul functiei pune( ) se va obtine: 10 10 20 20 30 30 40 40 50 50 In urma eliminarii primului element. for(int i=1. primul intrat este primul servit).30. } afisare().40 10 20 30 40 In urma adaugarii valorii 50 . elimina elementul din varful cozii. i++) { cout << "informatia ". getch(). while(c) { cout << c->info << " ". i<=n. cin >> n. } } void main() { int n. Pentru a prelucra o structura de tip coada vor fi necesari doi pointeri: unul il vom numi varful cozii (primul nod creat) in timp ce la capatul opus ne vom referi la ultimul element. creaza structura de tip coada cand aceasta este vida sau adauga un nou element la sfarsit in caz contrar. cout << "nr de noduri ". Functia scoate(). y.

pune(varf. cin >> a. } else { c = new nod.*ultim = 0. c->info = x. delete c. int x) { nod *c. sf->next = 0. // varful si ultimul nod al cozii cout<<"Numarul initial de noduri ". ultim.h> struct nod{ int info. afisare(varf). cout << endl << cate adaugari ?". cin>>n. c = c->next. Structura de tip coada prelucreaza numere intregi. } } void afisare(nod *v) { nod *c. void pune(nod* &v. nra. i<=nra. v->info = x. if(!v) { v = new nod. } } void main() { int n. a). nod *varf = 0. int nre. i++) { cout << "valoarea de adaugat in coada ". 65 . } } void scoate(nod* &v) { nod* c. while(c) { cout << c->info << " ". nod *next. nod* &sf. }. v = v->next. for(i=1.In continuare se prezinta o solutie pentru implementarea functiilor anterioare.h> #include<conio. v->next = 0. sf = c. #include<iostream. cin >> nra. i<=n. c = v. pune(varf. i++) { cout << "Valoarea de adaugat ". if(!v) cout << "Coada este vida si nu mai ai ce elimina!!!". cin >> a. a). } cout << endl. sf->next = c. sf = v. for(int i=1. else } c = v.a. ultim.

cout << "coada are " <<n << " elemente" << endl. Functia push(). 40 La primul pas se creaza stiva caz in care primul element creat va fi varful stivei: vf Apoi se adauga prin operatia push() un nou element. afisare(varf). Se observa ca in main se realizeaza prelucrarea (modificarea) informatiei primului nod. elimina elementul din varful stivei. ultimul intrat este primul servit. Functia pop(). Fie o stiva de numere intregi pentru care elementele sunt adaugate in ordinea: 10. operatie numita push()) • stergere element din varful stivei (operatie numita pop()) • prelucrarea varfului stivei Specific acestei structuri de date este faptul ca prelucrarile se fac intotdeauna la elementul de la acelasi capat. getch().} } cout << endl << "Dupa adaugare" << endl. Structura de date de tip stiva Aceasta structura de date este un caz particular de lista care functioneaza pe principiul LIFO (last in – first out. cout << "coada are " << n << " elemente" << endl. afisare(varf).i<=nre. cin >> nre. cout << endl << "Cate eliminari ?". n -= nre. 30. element pe care il vom numi varf. cout << endl << "Dupa eliminare" << endl. 20: vf 20 20 10 vf In continuare se adauga 30 care va deveni noul varf: 30 30 10 10 vf 20 20 10 10 66 . 20. afisare(varf). // se prelucreza varful cozii: de exemplu se poate dubla continutul varf->info = 2 * varf->info. cout << endl << "Dupa dublarea valorii varfului " << endl. Prin urmare principalele prelucrari care se refera la aceasta structura de date vor fi: • creare stiva • listare stiva (parcurgere in ordine inversa creerii) • adaugare la sfarsit ( peste varful stivei. for(i=1. n += nra. puteti sa va ganditi la o stiva de materiale pentru care un nou material se va adauga intotdeauna deasupra si se va extrage tot de deasupra).i++) scoate(varf). creaza stiva cand aceasta este vida sau adauga un nou element in caz contrar.

30: pop() vf 40 rezulta: 30 20 20 10 10 30 vf Pentru a prelucra aceasta structura de date va fi suficient un singur pointer.h> struct nod { int info. int x) { nod *c. 10. } } void afisare(nod *v) { nod *c. c->back = v. v->info = x. pentru varf. nod *back. dupa care noul varf va deveni precedentul sau. 30. c->info = x. } else { c = new nod. if(!v) { v = new nod. v->back = 0. }. c = v. 40.h> #include<conio. Iata o solutie de implementare: // Prelucrari stiva : (LIFO) #include<iostream. Pentru stergere. while(c) { 67 . prin operatia pop( ) se va sterge elementul din varful stivei. 20.La sfarsit se adauga 40: 40 40 30 30 20 20 10 10 vf Afisarea se va face in ordine inversa generarii si prin urmare se va afisa: 40. void push(nod* &v. v = c. nod *varf.

// se prelucreza varful stivei: de exemplu se poate dubla continutul varf->info = 2 * varf->info. 68 . cout << "numarul initial de noduri ". getch(). afisare(varf). v = v->back. cin >> n. c = c->back. afisare(varf). i++) pop(varf). i++) { cout << "valoarea de adaugat in stiva ". cout <<endl << "cate adaugari ?". a. cout << "stiva are " << n << " elemente" << endl. nra. a).cout << c->info << " ". cin >> a. cin >> nra. cout << "stiva are " << n << " elemente" << endl. if(!v) cout << "stiva este vida si nu mai ai ce elimina!!!". push(varf. } Liste liniare dublu inlantuite O componenta a unei liste dublu inlantuite se declara ca o data structurata de tip inregistrare. i++) { cout << "valoarea de adaugat ". Ultima componenta va avea informatia de legatura corespunzatoare urmatoarei adrese NULL (sau 0). afisare(varf). cin >> nre. delete c. tablou. for(i=1. i<=nra. cout << endl << "dupa eliminare" << endl. for(int i=1. formata din trei campuri: informatia propriu-zisa (care poate fi de orice tip: numeric. i<=nre. cout << endl << "dupa dublarea valorii varfului " << endl. int nre. push(varf. caracter. else { c = v. a). } } void main() { int n. i<=n. inregistrare) si informatiile de legatura (adresa la care e memorata urmatoarea componenta si adresa la care e memorata precedenta componenta). } cout << endl. n -=nre. pointer. } cout << endl << "dupa adaugare" << endl.La fel si in cazul primei componente pentru campul adresa precedenta. cout << endl << "cate eliminari ?". afisare(varf). cin >> a. for(i=1. } } void pop(nod* &v) { nod* c. n +=nra. cu semnificatia ca dupa ea nu mai urmeaza nimic (retine adresa „nici o adresa” a urmatoarei componente).

pentru o lista de numere intregi. c->back = ultim. cin >> c->info.h> #include<conio. if(!prim) { prim = c. prim->next = 0. Operatiile care se pot face utilizand liste dublu inlantuite sunt: • crearea listei • adaugarea unui element la inceputul listei • adaugarea unui element la sfarsitul listei • adaugarea unui element in interiorul listei • stergerea elementului de la inceputul listei • stergerea elementului de la sfarsitul listei • stergerea unui element din interiorul listei • traversarea listei. *back.0 adr prec informatia utila adr urm 0 Conform celor enuntate anterior memorarea si prelucrarea acestor structuri de date este similara cu cea a listelor liniare simplu inlantuite. Componentele listei se vor aloca dinamic. c = new Nod. void creare_lista() { Nod *c. putem defini: struct nod { int info. }. cout << "Informatia ". } else { ultim->next = c. Nod *prim. ultim->next = 0. *ultim. *back. ba chiar unele prelucrari (cum ar fi stergerea si inserarea inainte se simplifica avand intr-un mod facil acces la componenta anterioara). prim->back = 0. nod *next. Se poate defini un tip de date structurat numit nod. 69 . ale carui campuri le numim info pentru informatia utila.h> struct Nod { int info. Nod *next. Prelucrarea listei se va face tot prin intermediul a doi pointeri care acceseaza primul si respectiv ultimul element al listei. }. ultim = c. cu prelucrarea elementelor acesteia. c = prim. next pentru adresa urmatoare si back pentru adresa precedenta. ultim = prim. } } void listare_stanga_dreapta() { Nod *c. intr-un „sens” sau celalalt Iata o solutie de implementare a functiilor de creare ( si adaugare) si de parcurgere stangadreapta si dreapta-stanga: #include<iostream. De exemplu.

while(c) { cout << c->info << " "; c = c->next; } } void listare_dreapta_stanga() { Nod *c; c = ultim; while(c) { cout << c->info << " "; c = c->back; } } void main() { int n, i; cout << "cate elemente va avea lista? "; cin >> n; for(i=1; i<=n; i++) creare_lista(); cout << endl << "Elementele listei de la stanga la dreapta sunt:" << endl; listare_stanga_dreapta(); cout << endl << "Elementele listei de la dreapta la stanga sunt:" << endl; listare_dreapta_stanga(); getch(); }

Liste circulare
In unele aplicatii este necesar sa se prelucreze structuri de date inlantuite simplu sau dublu circulare. Acestea se obtin din liste liniare printr-o singura operatie daca lista este simpla sau prin doua operatii daca lista este dubla: Fie urmatoarea lista dubla definita astfel:
struct nod { int info; nod *next, *back; }; nod*prim,*ultim;

prim 0 Lista devine circulara prin operatiile:
ultim->next = prim; prim->back = ultim; // urmatorul ultimului devine primul // precedentul primului devine ultimul

ultim 0

Prin urmare nici un nod nu va mai contine pentru campurile *next sau *back valoarea 0 (ultimul si primul in cazul listelor liniare) ceea ce va determina modificarea functiei de parcurgere in scopul prelucrarilor . prim

ultim

70

Iata o solutie de generare a unei liste duble circulare de intregi pornind de la o lista liniara dublu inlantuita si o modalitate de parcurgere a listei circulare astfel obtinute:
#include<iostream.h> #include<conio.h> struct Nod { int info; Nod *next, *back; }; Nod *prim, *ultim; int n; void creare_lista() { Nod *c; c = new Nod; cout << "Informatia: "; cin >> c->info; if(!prim) { prim = c; prim->next = 0; prim->back = 0; ultim = prim; } else { ultim->next = c; c->back = ultim; ultim = c; ultim->next = 0; } } void listare_stanga_dreapta() { Nod *c; c = prim; while(c) { cout << c->info << " "; c = c->next; } } void creaza_lista_circulara() { ultim->next = prim; prim->back = ultim; } void afiseaza_lista_circulara(Nod *c) { // Parcurge lista pornind de la o adresa for(int i=1; i<=n; i++) { cout << c->info << " "; c= c->next; } } void main() { int i; cout << "cate elemente va avea lista? "; cin >> n; for(i=1; i<=n; i++) creare_lista(); cout << endl << "Elementele listei de la stanga la dreapta sunt:" << endl; listare_stanga_dreapta(); creaza_lista_circulara(); cout << endl << "afiseaza lista circulara incepand de la primul:" << endl; afiseaza_lista_circulara(prim); cout << endl << "afiseaza lista circulara incepand cu al doilea:" << endl;

71

}

afiseaza_lista_circulara(prim->next); cout << endl << "afiseaza lista circulara incepand cu ultimul:" << endl; afiseaza_lista_circulara(ultim); getch();

Prelucrarea sirurilor de caractere
Declararea variabilelor sir de caractere
In limbajul C++ nu exista un tip fundamental pentru sirurile de caractere (string), asa cum exista in alte limbaje de programare (Visual Basic, Turbo Pascal etc.), ele se declara ca tablouri de tip char. De exemplu, declaratia:
char a[10], b[3];

defineste doua tablouri unidimensionale (vectori) a si b de tip char avand 10 si respectiv 3 componente. Ca la orice vector, numele are semnificatia de adresa nemodificabila catre prima componenta (cea de indice 0). La declarare se poate face si initializare, respectand regulile de de initializare ale tablourilor. De exemplu, urmatoarea declaratie:
char a[3] = {‘a’, ‘b’, ‘c’]; defineste un vector de tip char avand trei componente si le initializeaza astfel: a[0]=‘a’, a[1]=‘b’ si a[2]=‘c’.

Mai simplu, vectorul a se poate declara si asa:
char a[] = {‘a’, ‘b’, ‘c’];

compilatorul determinand numarul de componente dupa valorile de initializare. Totusi, folosirea acestui procedeu pentru declararea sirurilor de caractere implica o munca chinuitoare. Reamintim ca: sirul de caractere este o succesiune de caractere cuprinse intre doua caractere ghilimele (“). De exemplu, “Acesta este un sir de caractere”, “Borland C++” etc.
Observatie: Orice sir de caractere se termina prin caracterul NULL (‘\0’). Acesta este pus automat de compilator si ocupa ultimul byte din zona alocata memorarii sirului. Deci sirul “Borland C++” va ocupa 12 bytes. La declarare, unui vector cu componente de baza de tip char i se poate atribui un sir de

caractere. De exemplu, urmatoarea declaratie:
char sir[12] = “Borland C++”; (1) defineste un tablou de tip char cu 12 componente pe care le initializeaza astfel: sir[0]=’B’, sir[1]=’o’, …, sir[10]=’+’, sir[11]=’\0’.

Declaratia anterioara (1) poate fi scrisa si sub forma:
char sir[] = “Borland C++”;

caz in care compilatorul va determina numarul de bytes necesari pentru memorarea sirului, adaugand unul pentru caracterul NULL.
Observatii: • Daca la declarare, numarul de componente este indicat explicit, iar sirul de initializare are un numar de caractere mai mic decat numarul specificat de componente, componentele ramase neinitializate vor fi initializate cu caracterul NULL. • Daca sirul are un numar de caractere (fara caracterul NULL) mai mare decat numarul de componente indicat, se semnaleaza eroare de sintaxa. • Daca sirul are un numar de caractere (fara caracterul NULL) egal cu numarul de componente specificat, caracterul NULL nu se memoreaza.

Initializarea unei zone de memorie cu un sir de caracter se poate realiza si prin utilizarea pointerilor la tipul char. De exemplu, declaratia anterioara se poate realiza si in maniera:
char *psir = “Borland C++”; (2)

Ca urmare a acestei declaratii, in faza de compilare se construieste sirul “Borland C++” intr-o zona disponibila de memorie, a carei adresa este stocata in pointerul psir. In acest caz, 72

i < 7. programul urmator prezinta modul de initializare a unui tablou bidimensional cu componente de tip char si posibilitatile de adresare. cout << "Varianta I\n". i++) { for (j = 0. care reflecta aritmetica de pointeri pentru siruri. cout << "Varianta II\n". #include <stdio. j++) cout << zile[i][j]. for (i = 0. In varianta (2) se rezerva 2 sau 4 bytes pentru pointerul psir. cout << "Varianta II\n". "duminica"}. (j < 10) && (zile[i][j] != '\0'). precum si prima litera a fiecarei zile. j++) cout << *(pzile[i] + j). In timp ce in varianta (1). La rulare se afiseaza zilele saptamanii. "sambata". } for (i = 0. cout << "\n". // Adresarea cu doi indici for (i = 0. "joi". i++) cout << *pzile[i] << "\n". cout << "\n". "duminica"}. "marti". "vineri". *(pzile[i] + j) != '\0'.adresarea caracterului ‘r’ din a doua pozitie a sirului se poate face prin constructia: *(psir+1). i < 7. de la initializare). i < 7. int i. } 73 . // Adresarea cu un singur indice for (i = 0. i++) cout << zile[i] << "\n".h> void main() { char zile[7][10] = {"luni".i < 7. Reprezentarea sirurilor ca tablouri de caractere si modul de interpretare a tablourilor in C asigura ca intr-un tablou cu doua dimensiuni. #include <iostream. in doua variante. i < 7. int i. for (i = 0. adresarea cu un indice sa individualizeze un anumit sir din multimea sirurilor. iar adresarea cu doi indici sa localizeze un caracter. cout << "Varianta I\n". i < 7. } for (i = 0. "miercuri".h> void main() { char *pzile[7] = {"luni". odata alocata memoria pentru vectorul sir. "joi". De exemplu. "miercuri". precum si posibilitatea de a modifica adrese. } Aceeasi probleme rezolvata folosind pointeri evidentiaza si mai pregnant tratarea ca entitate distincta a fiecarui sir. i++) cout << pzile[i]) << "\n". j. utilizand unul sau doi indici. adresa acestuia nu poate fi modificata. j. a carei adresa va fi continuta intr-un element al vectorului de pointeri pzile. "marti". "vineri". Intre declaratiile (1) si (2) exista deosebiri esentiale in ceea ce priveste dimensiunea memoriei ocupate. i++) { for (j = 0. in varianta (2) pointerul psir poate fi refolosit utlerior pentru a referi alte adrese de variabile de tip caracter (cu riscul pierderii adresei constantei sir. "sambata". i++) // Afisarea primei litere a fiecarei zile cout << zile[i][0].

fara a se astepta prezenta unui terminator (Enter). Daca se doreste simpla lor afisare. alte coduri de control default: // // // // Coduri de control al imprimarii 74 .h si respectiv putch() .h> #include <ctype. Un astfel de filtru este realizat in programul urmator: #include <stdio. break. urmatoarea secventa de program asigura citirea si scrierea unui caracter: int c.) executa diferite functii la imprimare.prototipul in stdio. Esc. Echivalentele lor pentru iesire sunt functiile putchar() . la citirea unui caracter acesta este preluat imediat din zona tampon a tastaturii. FF. Pe langa aceste functii...h> #include <conio. caracterele spatiu. Backspace etc.h. datorita faptului ca functiile getchar si getch sunt declarate ca fiind de tip int. pe fisierul satndard de iesire (stdout). while((c = getchar()) != EOF) putchar(c).h.Functii de intrare pentru siruri de caractere Pentru realizarea operatiilor de I/E. pana la intalnirea sfarsitului de fisier: #include <stdio.h> void main() { int c. S-a definit c ca fiind de tip int. Cr. De exemplu. break. c = getchar(). neputand fi citit intr-o singura variabila un text format din doua sau mai multe cuvinte. break. case '\r': printf("\\r"). prin descriptorii %c si %s in functiile scanf si printf.h> void main() { int c. ofera o modalitate comoda de citire si afisare a caracterelor si sirurilor de caractere. while ((c = getch()) != 0x1a) switch (c) { case '\n': printf("\\n"). prototipul functiei se gaseste in fisierul stdio. } Caracterele speciale (Tab. break. limbajul C. Prototipul functiei getch() se gaseste in fisierul conio. Aceste functii au fost prezentate detaliat intr-o lectie anterioara si utilizate in toate exemplele folosite. urmatorul program copiaza din fisierul standard de intrare (stdin). in plus. caracter cu caracter. necesare testarii unor conditii. case '\t': printf("\\t"). Varianta getch() realizeaza acelasi lucru fara a returna si ecoul caracterului citit. ca siruri de caractere aferente secevntelor escape.. De exemplu. // . Un inconvenient major al functiei scanf il constituie faptul ca.h. case '\v': printf("\\v").are prototipul in conio. EOF (Ctrl+Z) etc. Din aceste motive in continuare ne vom referi la alte functii ce se pot folosi pentru realizarea operatiilor de I/E. simbolurile asocaite lor trebuie filtrate si transmise in conformitate ci conventiile limbajului C. pentru a putea prelua in afara caracterelor obisnuite si pe cele care transmit doua coduri la apasare (de exemplu tastele sageata) sau coduri asociate functiilor de fisier: New Line. limbajul C pune la dispozitie si functii de I/E pentru un caracter si respectiv pentru siruri de caractere. fara a fi executate. Tab si NewLine actioneaza implicit ca separatori si deci prin aparitia lor in sirul de intrare forteaza sfarsitul sirului. Functii pentru citirea si afisarea caracterelor Functia getchar() asigura preluarea intr-o variabila de tip int sau char a unui caracter de la terminal. putchar(c).

a la f) Fiecare macrocomanda este un predicat care returneaza o valoare diferita de zero pentru adevarat si o valoare egala cu 0 pentru fals. Principalele macrocomenzi de clasificare sunt: Macrocomand a isalnum(c) isalpha(c) isascii(c) isdigit(c) isgraph(c) islower(c) isprint(c) isspace(c) isupper(c) isxdigit(c) Descriere Verifica daca c este o litera (A la Z sau a la z) sau o cifra (0 la 9) Verifica daca c este este o litera (A la Z sau a la z) Verifica daca byte-ul de ordin inferior al lui c este in intervalul 0 la 127(0x00-0x7F) Verifica daca c este o cifra (0 la 9) Verifica daca c este un caracter imprimabil.} } if (isprint(c)) putchar(c). fara ecou si fara a astepta terminator de introducere. din care primul este ^. cu expectia faptului ca spatiul este exclus Verifica daca c este o litera mica (a la z) Verifica daca c este un caracter imprimabil (0x20 la 0x7E) Verifica daca c este un spatiu. linie noua (new line). c + 'A' -1). astfel: • caractere imprimabile – pe care le afiseaza ca atare. • alte coduri – pe care le afiseaza sub forma secventelor escape in octal. In programul prezentat anterior. // ^X coduri CTRL+X else printf("\\%3o".0x09 la 0x0D. afisare in octal Programul asigura afisarea corespunzatoare a tuturor codurilor ASCII citite. In program s-a apelat o macrocomanda de clasificare. c). // coduri imprimabile else if (c >= 1 && c <= 26) printf("^%c". 75 . • caractere de control – pe care le afiseaza ca doua caractere. tab vertical salt de pagina (formfeed) . tab. isprint. prin folosirea unei structuri de selectie multipla se impart codurile ASCII in patru clase. • secvente escape – pe care le afiseaza conform conventiilor din limbajul C.h). A la F. Limbajul C++ pune la dispozitie mai multe macrocomenzi de clasificare (care se gasesc in fisierul antet ctype. la fel ca isprint. // restul. 0x20 Verifica daca c este o litera mare (A la Z) Verifica daca c este o cifra hexazecimala (0 la 9. pentru a verifica apartenenta caracterului introdus (daca este sau nu tiparibil). retur de car (carriage return). pentru a verifica apartenenta la o clasa a unui caracter.

n = strlen(tab). Majoritatea acestor functii au prototipul in fisierul string.. Lui n i se atribuie valoarea 18. int n. Cele mai frecvente prelucrari asupra sirurilor de caractere sunt urmatoarele: • Determinarea lungimii sirurilor de carctere. Exemple: char *const p = “Borland International”. • Compararea sirurilor de caractere. 76 . n = strlen(“Acesta este un sir”). char tab[] = “Acesta este un sir”. Lui n i se atribuie valoarea 21. • Dupa ultimul caracter al sirului se pastreaza caracterul NULL (‘\0’). int n.Functii pentru prelucrarea sirurilor de caractere Biblioteca standard a limbajului contine o serie de functii care permit operatii cu siruri de caractere. • Numele tabloului este considerat un pointer constant spre sirul respectiv. Functia pentru determinarea lungimii unui sir de caractere se numeste strlen si apelarea ei se realizeaza folosind urmatorul format de apel: strlen(sir) unde: sir reprezinta numele tabloului ce contine sirul a carui lungime se va determina. • Copierea sirurilor de caractere. care este de fapt o valoare numerica. n = strlen(p). • Pentru a opera cu un sir de caractere se poate utiliza numele tabloului in care sunt pastrate caracterele sau pointeri variabili spre sirul respectiv de caractere. . Lungimea unui sir de caractere Lungimea unui sir de caractere se defineste prin numarul de caractere proprii care intra in compunerea sirului respectiv. unsigned n. care reprezinta numarul de caractere proprii din compunerea sirului referit de p. Prezenta acestui caracter este necesara. Functiile standard prin care se realizeaza aceste operatii au fiecare un nume care incepe cu prefixul str (prescurtare de la string).. Caracterul NULL este un caracter impropriu si el nu este luat in considerare la determinarea lungimii unui sir de caractere. deoarece el indica terminarea sirului si la determinarea lungimii unui sir se numara caracterele sirului pana la intalnirea caracterului NULL. Lui n i se atribuie aceeasi valoare ca si in exemplul anterior. Fiecare caracter se pastreaza pe cate un byte prin codul sau ASCII. vom reaminti cateva dintre caracteristicile sirurilor de caractere: • Un sir de caractere se pastreaza intr-o zona de memorie organizata ca tablou unidimensional de tip char. Inainte de a prezenta principalele functii de prelucrare a sirurilor de caractere.h. • Concatenarea sirurilor de caractere.

. • sursa este adresa sirului ce se va aduaga. adica chiar valoarea lui destinatie. • sursa reprezinta adresa zonei de memorie in care se gasesc caracterele de copiat. sursa) unde: • destinatie reprezinta adresa zonei de memorie in care se vor copia caracterele. // dupa copiere. care se apeleaza respectand urmatoarea sintaxa: strcpy(destinatie. Functia strcpy copiaza sirul de caractere spre care refera sursa in zona de memorie a carei adresa de inceput este valoarea lui destinatie. 3. Functia copiaza atat caracterele proprii sirului. La revenire. char *p = “Sir de caractere”. numar_caractere) unde: • destinatie reprezinta adresa zonei de memorie in care se vor copia caracterele. strcpy(sir. Formatul de apelare a functiei este: strcat(destinatie. q = strcopy(sir. sursa) unde: • destinatie este adresa zonei sirului de caractere la sfarsitul caruia se va adauga sirul concatenat. Atat sursa cat si destinatie pot fi nume de tablouri de tip char sau pointeri constanti catre zonele de memorie ce contin/vor contine caractere.Copierea unui sir de caractere Pentru a copia un sir de caractere din zona de memorie in care se afla intr-o alta zona de memorie se foloseste functia strcpy. “Copiere sir de caractere”). atunci toate caracterele sirului respectiv se transfera in zona referita de destinatie. strcpy(sir. tab). 2. Concatenarea sirurilor de caractere Pentru concatenarea (unirea) a doua siruri limbajul dispune de doua functii: strcat si strncat. Apelarea functiei strcpy se realizeaza utilizand urmatorul format: strcpy(destinatie.. functia returneaza adresa de inceput a zonei in care s-a transferat sirul. Functia strcat Concatenarea a doua siruri de caractere (adaugarea celui de-al doilea sir la sfarsitul primului sir) se realizeaza cu ajutorul functiei strcat. p). Daca numar_caractere este mai mare decat lungimea sirului referit de sursa. lui q i se atribuie adresa // zonei in care s-a copiat sirul de caractere. • sursa reprezinta adresa zonei de memorie in care se gasesc caracterele de copiat. cat si caracterul NULL de la sfarsitul sirului respectiv. char *q. char sir[sizeof tab]. Exemple: 1. 77 . char sir[100]. char tab[] = “Borland International”. // Are acelasi numar de elemente ca si tab . char sir[100]. Pentru a copia cel mult n caractere se foloseste functia strncpy. • numar_caractere reprezinta numarul de caractere ce se vor copia. sursa.

sir1 contine: Limbajul E este mai bun decat limbajul C++ Compararea sirurilor de caractere Sirurile de caractere se compara folosind codurile ASCII ale caracterelor din compunerea lor. 2. cel mult nr_car ale sirului referit de sursa. Forma de apelare a functiei strncat este: strncat(destinatie. i –1. char sursa[] = “este C dezvoltat”. nr_car) unde: • destinatie este adresa zonei sirului de caractere la sfarsitul caruia se vor adauga cele nr_car din sirul referit de sursa. cate un sir de caractere. stricmp si strincmp. in zona de memorie ce urmeaza imediat dupa ultimul caracter propriu al sirului referit de destinatie.Aceasta functie copiaza sirul de caractere referit de sursa. Functia strcat returneaza valoarea pointerului destinatie. care este un superset a lui C”. …. atunci se concateneaza intregul sir. Functia strncat Functia strncat adauga la sfarsitul primului sir prmele n caractere din cel de-al doilea sir. char sir2[] = “limbajul C++. strcat(dest. fiecare. daca exista un indice i astfel incat: s1[i] < s2[i] si s1[j] = s2[j] pentru j = 1. strncat(sir1. Exemple: char dest[] = “Limbajul C++“. Functia strncat concateneaza la sfarsitul sirului referit de destinatie. 12). sir2. 2. ‘ ‘). strncmp. …. sursa. • Sirul s1 este mai mare decat s2. Se presupune ca zona referita de pointerul destinatie este suficienta pentru a pastra caracterele proprii celor doua siruri care se concateneaza. • sursa este adresa sirului in care se gasesc caracterele ce se vor concatena cu sirul referit de destinatie. Daca nr_car este mai mare decat lungimea sirului referit de sursa. Exemplu: char sir1[]=”Limbajul E este mai bun decat “. Rezultatul compararii celor doua siruri se determina dupa urmatoarele reguli: • Cele doua siruri sunt egale daca au lungimi egale si s1[i] = s2[i] pentru toate valorile posibile ale lui i. i –1. altfel numai primele nr_car caractere. Fie s1 si s2 doua tabouri unidimensionale de tip caracter utilizate pentru a pastra. plus caracterul NULL. • nr_car este numarul de caractere din sirul referit de sursa ce se vor concatena cu sirul referit de destinatie. sursa). // Adauga la sfarsitul sirului dest un spatiu strcat(dest. Compararea sirurilor de caractere se realizeaza folosind urmatoarele functii: strcmp. 78 . // Adauga textul din sursa dupa caracterul // spatiu din sirul dest. daca exista un indice i astfel incat: s1[i] > s2[i] si s1[j] = s2[j] pentru j = 1. • Sirul s1 este mai mic decat s2. Dupa revenirea din functie. care termina sirul rezultat in urma concatenarii.

Daca minimul dintre lungimile celor doua siruri este mai mic decat n. Functia strncmp Sintaxa de apelare a functiei strncmp este: strncmp(sir1. Valorile returnate de functia strncmp sunt acelasi ca si in cazul functie strcmp. • zero daca sirul referit de pointerul sir1 este mai mic decat sirul referit de pointerul sir2. sir2) unde: sir1 si sir2 reprezinta pointeri catre zonele in care se gasesc caracterele celor doua siruri ce se vor compara. Valorile returnate de functia strincmp este acelasi ca si in cazul functie strcmp. Daca minimul dintre lungimile celor doua siruri este mai mic decat n. Valorile returnate de functia stricmp sunt aceleasi ca si in cazul functiei strcmp.h> #include <string. cu deosebirea ca la compararea sirurilor nu se face deosebire intre literele mari si mic. Functia strincmp Functia strincmp este similara functiei strncmp. Sa se scrie un program care citeste o succesiune de cuvinte si-l afiseaza pe cel mai lung dintre ele. Formatul de apelare este: strincmp(sir1. • n reprezinta numarul maxim de caractere din ambele siruri ce se vor lua in considerare. atunci se va utiliza funtia stricmp. Formatul de apelare este: stricmp(sir1.h> #define MAX 100 void main() { int max = 0. sir2) unde: sir1 si sir2 reprezinta pointeri catre zonele in care se gasesc caracterele celor doua siruri ce se vor compara.Functia strcmp Functia strcmp compara doua siruri de caractere si poate fi apelata folosind urmatoarea sintaxa: strcmp(sir1. n) unde: • sir1 si sir2 reprezinta pointeri catre zonele in care se gasesc caracterele celor doua siruri ce se vor compara. Functia strcmp returneaza: • o valoare negativa daca sirul referit de pointerul sir1 este mai mic decat sirul referit de pointerul sir2. • o valoare pozitiva daca sirul referit de pointerul sir1 este mai mare decat sirul referit de pointerul sir2. • n reprezinta numarul maxim de caractere din ambele siruri ce se vor lua in considerare. sir2. functia strncmp realizeaza aceeasi comparatie ca si functia strcmp. n) unde: • sir1 si sir2 reprezinta pointeri catre zonele in care se gasesc caracterele celor doua siruri ce se vor compara. functia strncmp realizeaza aceeasi comparatie ca si functia strcmp. i. Exercitii: a. 79 . #include <stdio. sir2. Functia stricmp Daca la compararea a doua siruri de caractere nu dorim sa se faca distinctie intre literele mari si mici.

strcpy(tabel[j]. nr. for (i = 0. cin >> nr. cuvant) != EOF) if (max <(i = strlen(cuvant))) { max = i. care pastreaza lungimea maxima curenta. strcpy(tabel[i]. • Cuvantul citit se pastreaza in tabloul cuvant. i < nr. Citirea se face utilizand spefificatorul de format %100s. j < nr . char cuv_max[MAX + 1]. } if (max) printf("%s\n". b. aux[MAXC]. tabel[j]). #include <iostream. aux). j. • Daca lungimea cuvantului curent citit este mai mare decat lungimea maxima.char cuvant[MAX + 1]. int i. cuv_max). cout << "Numarul de cuvinte de ordonat: ". atunci lui max i se atribuie aceasta valoare. strcpy(cuv_max. tabel[j]) > 0) { // Permutare cuvinte neordonate strcpy(aux. care permite citirea a cel mult 100 caractere diferite de cele albe. • Dupa citire se determina lungimea sirului si se compara cu valoarea variabilei max.h> #define MAXS 30 // Numarul maxim de cuvinte #define MAXC 25 // Numarul maxim de caractere dintr-un cuvant void main() { char tabel[MAXS][MAXC]. i < nr .2. i++) cin >> tabel[i]. } cout << "Sirurile ordonate alfabetic sunt:\n".1. } Observatii: • Se presupune ca un cuvant are cel mult 100 caractere. iar cuvantul citi este copiat in cuv_max. i++) // Se ordoneaza cuvintele for (j = i + 1. Sa se scrie un program care citeste o succesiune de cuvinte.h> #include <string. for (i = 0. i < nr. cuvant). tabel[i]). while (scanf("%100s". cout << "Se introduc sirurile de ordonat:\n". for (i = 0. sunt separate prin spatii si pentru terminare se tasteaza sfarsitul de fisier (Ctrl+Z). } 80 . i++) cout << tabel[i] << "\n". j++) if (strcmp(tabel[i]. le sorteaza in ordine crescatoare si apoi le afiseaza.

Aceste functii asigura o portabilitate buna a programelor. insa datorita faptului ca acest cuvant poate avea mai multe intelesuri. • Inchiderea unui fisier. Un alt concept care trebuie inteles corect este pozitia curenta. 81 . este locul din fisier in care se va produce urmatoarea operatie de intrare/iesire. In limbajul C++. Exista doua tipuri de stream-uri: text si binar. Stream-ul binar poate fi utilizat cu orice tip de data. iar corespondenta intre ceea ce este transmis catre stream si ceea ce contine fisierul este totala. In mod analog. Cele mai utilizate suporturi pentru fisiere sunt cele magnetice.Prelucrarea fisierelor Datorita faptului ca limbajul C++ nu dispune de instructiuni de intrare/iesire. • Crearea unui fisier. un stream7 este o interfata logica intre calculator si unul dintre diferitele sale periferice. un stream este o interfata logica pentru un fisier. specialistii au optat pentru pastrarea termenului din engleza. numita si locatie curenta. • Stergerea unui fisier. Datele introduse de la tastatura se considera ca formeaza un fisier de intrare. • Adaugarea de inregistrari intr-un fisier. el este convertit intr-o secventa carriage return/line feed. In cazul fisierelor de intrare de la tastatura. este posibil sa apara translatarea unor caractere. pentru programator. ecran. Inainte de a incepe prezentarea functiilor de intrare/iesire. In acest caz. • Citirea (consultarea) inregistrarilor unui fisier. Majoritatea operatiilor de I/E se realizeaza in ipoteza ca datele sunt organizate in fisiere6. sfarsitul de fisier se genereaza prin secventa Ctrl+Z. Din acest motiv. De exemplu. stream-ul. Ele se numesc si suporturi reutilizabile. Prelucrarea fisierelor implica un numar de operatii specifice acestora. Si in acest caz inregistrarea este formata din caracterele unui rand. el se poate referi la un fisier disc. el nu produce translatari de caractere. Un stream este asociat unui fisier prin executarea unei operatii de deschidere (open) si este disociat de un fisier prin executarea unei operatii de inchidere (close). deoarece zona utilizata pentru pastrarea inregistrarilor unui fisier poate fi ulterior refolosita pentru pastrarea inregistrarilor altui fisier. inregistrarea este formata din datele tastate pe un rand. • Deschiderea fisierului. Cand se foloseste un stream text. toate perifericele apar ca fiind la fel. 6 7 Fisierul este o colectie ordonata de elemente. aceste operatii se realizeaza prin intermediul unor functii din biblioteca standard a limbajului. stream-urile sunt aceleasi. Un stream text este utilizat pentru a manipula caractere ASCII. deci caracterul de rand nou (newline) este terminator de inregistrare. Pozitia curenta. tastatura etc. Dupa cum defineste limbajul C++ termenul de fisier. datele ce se afiseaza pe ecran sau se tiparesc la imprimanta formeaza un fisier de iesire. Avantajul acestei aproximari este ca. Stream – tradus in limba romana ar insemna flux. Un fisier are o inregistrare care marcheaza sfarsitul de fisier. este necesar sa intelegem un concept foarte important al limbajului. • Pozitionarea intr-un fisier. Cu toate ca fisierele difera ca forme si proprietati. fiind implementate intr-o forma compatibila pe diferite sisteme de operare. • Actualizarea unui fisier. care sunt pastrate pe diferite suporturi externe de informatii. este posibil sa nu existe o corespondenta totala intre ceea ce este trimis catre stream si ceea ce contine fisierul. In forma sa cea mai comuna. numite inregistrari. cand este intalnit caracterul newline.

• Daca se deschide un fisier existent in modul scriere (“w”). Aceasta functie returneaza un pointer spre tipul FILE (tipul fisier) sau pointerul NULL in caz de eroare. a caror prototipuri se gasesc in fisierul stdio. Nivelul superior de prelucrare a fisierelor in C La acest nivel operatiile de prelucrare a fisierelor se executa prin utilizarea unor functii specializate de gestiune a fisierelor.h. Deschiderea unui fisier Pentru a deschide un fisier si a-l asocia unui stream. • Folosind functia fopen puteti deschide un fisier inexistent in modul scriere (“w”) sau adaugare (“a”). pozitia curenta. • Caracterele “t” sau “b” pot fi inserate intre litera si semnul + din sirul modului de prelucrare. Acest sir de caractere poate fi:  “r” – deschidere pentru citire (read) a unui fisier existent. In continuare ne vom ocupa numai de nivelul superior de prelucrare a fisierelor. Observatii: • Pentru a specifica faptul ca un fisier este deschis sau creat in modul text se adauga caracterul “t” la sirul ce indica modul de prelucrare (de exemplu. modul de acces etc. w+t etc.  “a” – deschidere in adaugare (append). cand se face apel direct la sistemul de operare. mod) unde: • cale reprezinta un sir de caractere care specifica numele si eventual calea fisierului (specificator de fisier) ce se va deschide . Valoarea returnata de functia fopen trebuie sa fie atribuita unei variabilei pointer catre tipul FILE (pe care o vom numi in continuare variabila fisier). se presupune ca fisierul este deschis in modul text. 82 . a+b etc.h. atunci el se va crea din nou si vechiul sau continut se va pierde. • Pentru a specifica modul binar. trebuie sa folosim functia fopen. atunci el va fi rescris. Tipul FILE este definit in fisierul antet stdio. • La nivel superior.).  “a+” – deschidere pentru adaugarea de noi inregistrari la sfarsit de fisier. • mod este un sir de caractere care defineste modul de prelucrare al fisierului dupa deschidere. Functia fopen returneaza de fapt un pointer catre structura asociata cu fisierul prin procesul de deschidere. rt. functia fopen returneaza un pointer catre fisier. In general.).  “w” – deschidere pentru scriere (write) a unui fisier. • Daca in sirul modului de prelucrare nu se specifica “t” sau “b”. se adauga caracterul “b” la sirul ce indica modul de prelucrare (de exemplu. aceasta structura identifica fisierul. De asemenea. rt+ este echvalent cu r+t. tip definit in fisierul stdio.  “w+” – deschiderea unui nou fisier pentru citire/scriere (read/write) . Daca fisierul nu exista. Daca operatia de deschidere a fost efectuata cu succes. wb. fiecarui fisier i se asociaza o structura de tip FILE. cum ar fi: dimensiunea fisierului. cand se folosesc proceduri specializate de prelucrare ce utilizeaza structuri speciale de tip FILE. daca fisierul deja exista.Tratarea fisierelor se poate face la doua niveluri: • La nivel inferior. atunci el este creat.  “r+” – deschidere pentru citire/scriere (read/write) a unui fisier existent. Sintaxa apelului functiei este: fopen(cale. De exemplu. care se va folosi in continuare in toate apelurile functiilor de prelucrare a respectivului fisier.h si este de fapt o structura care contine diferite informatii despre fisier.

FILE *pf = fopen(“C:\\BC31\\Programe\\TEST. In acelasi mod se poate proceda si pentru fisierele binare.. se va afisa mesajul “Fisierul specificat nu exista” si in continuare se vor executa operatiile corespunzatoare. numit TEST. cea mai importanta fiind pierderea datelor atunci cand se deschide un fisier existent folosind modul “w”. // pointer catre tipul FILE // citirea se va face de la tastatura b. Adica este foarte important sa se verifice daca functia fopen a returnat un pointer valid sau nu. . pentru scriere. Daca fisierul nu exista.  stdprn – pentru a tipari la imprimanta paralela. “r”)) == NULL) { printf(“Fisierul specificat nu exista.”). c. Inchiderea unui fisier se realizeaza apeland functia fclose. Functia fopen este apelata pentru a deschide un fisier text.• • Deschiderea unui fisier in modul adaugare (“a”). Pentru a utiliza aceste fisiere se vor folosi urmatorii pointeri spre tipul FILE:  stdin – pentru a citi de la intrarea standard.  stderr – pentru afisarea erorilor la iesirea standard. Inchiderea fisierelor Dupa terminarea prelucrarii unui fisier.  stdaux – pentru iesirea la comunicatia seriala. Exemple: a. o foarte mare atentie trebuie acordata testarii existentei fisierului.txt”. Fiind un fisier de intrare standard. Urmatoarea linie declara un pointer catre tipul FILE si-l initializeaza cu valoarea furnizata de apelul functiei fopen. Pentru a verifica valoarea returnata de functia fopen se poate folosi urmatoarea secventa de instructiuni: FILE *fp. Daca fisierul exista. Fisierul se afla in subdirectorul Programe din directorul BC31 al radacinii discului C. Liniile urmatoare declara un pointer catre tipul FILE si ii atribuie valoarea stdin. Urmatoarea linie declara un pointer catre tipul FILE si-l initializeaza cu valoarea furnizata de apelul functiei fopen..TXT. pentru citire.  stdout – pentru a afisa pe ecranul de la iesirea standard. Functia returneaza urmatoarele valori: 83 . deoarece ele se deschid automat la lansarea in executie a programului. . pf=stdin. acesta trebuie inchis. Neverificarea valorii returnate de functia fopen poate avea urmari grave. FILE *pf. In sirul care precizeaza calea au fost incluse doua caractere backslash (\) in loc de unul singur. In aplicatiile de prelucrare fisiere. deoarece caracterul backslash se foloseste si pentru a include secventa escape intrun sir de caractere. Prin intermediul functiilor din aceasta clasa de prelucrare fisiere se pot trata si fisierele standard. Apelul functie fclose are urmatoarea sintaxa: fclose(varaiabila_fisier) unde: variabila_fisier – este un pointer catre tipul FILE a carei valoare a fost returnata de functia fopen la deschiderea fisierului. } Aceasta secventa incearca sa deschida un fisier text cu numele “fisier.. if((fp = fopen(“fisier. Aceste fisiere nu trebuie deschise. “wt”).TXT.TXT. “rt”). FILE *pfr = fopen(“TEST. Fisierul se afla in directorul curent. atunci el va fi deschis si datele lui pot fi prelucrate. Functia fopen este apelata pentru a deschide un fisier text. permite scrierea de noi inregistrari la sfarsitul unui fisier existent. nu este necesarea deschiderea sau inchiderea lui.txt” existent in directorul curent.TXT. numit TEST..

sau EOF. Desi in prototipul functiei. char ch.h> char sir[80] = "Testarea scrierii si citirii caracter cu caracter".h> #include <stdlib. } 84 . char numeFis[] = "c:\\Bc31\\Programe\\Fisiere\\Note. void main() { // Deschiderea fisierului pentru scriere if ((fp = fopen(numeFis. functia poate fi apelata cu o variabila de tip char aceasta fiind de fapt utilizarea cea mai comuna. Functia fputc scrie byte-ul continut de caracter in fisierul asociat cu variabila_fisier ca un unsigned char. fp) == EOF) { printf("Eroare la scriere in fisier. daca operatia s-a inceiat cu succes. exit(1). Functia fputc returneaza caracterul scris. caracter este definit ca un int. while (*p) { if (fputc(*p. care este o valoare intreaga. *p. } // Scrierea fisierului p = sir. Motivul pentru care byte-ul citit este returnat ca un intreg este acela ca. exit(1). Functia fgetc Sintaxa apelului functie este: fgetc(varaiabila_fisier) unde: variabila_fisier – este un pointer catre tipul FILE a carei valoare a fost returnata de functia fopen la deschiderea fisierului din care se citeste. stdprn sau stdaux. variabila_fisier poate fi unul din pointerii: stdin sau stdaux.txt". stderr. fgetc returneaza EOF. • variabila_fisier – este un pointer catre tipul FILE a carei valoare a fost returnata de functia fopen la deschiderea fisierului in care se scrie. "w")) == NULL) { printf("Fisierul nu poate fi deschis. varaiabila_fisier) unde: • caracter – este codul ASCII al caracterului care se scrie in fisier. • fgetc – pentru citire. FILE *fp. -1 – daca pe timpul inchiderii s-a produs o eroare.\n")."). fgetc returneaza EOF si atunci cand este atins sfarsitul de fisier. Functia returneaza valoarea lui caracter sau –1 in caz de eroare. Functia putc Sintaxa apelului functiei este: fputc(caracter. Exemplu: #include <stdio. daca apare o eroare. In particular. variabila_fisier poate fi unul din pointerii: stdout. Functia fgetc citeste byte-ul din pozitia curenta ca un unsigned char si-l returneaza ca un intreg. De asemenea.• • 0 – daca inchiderea fisierului a decurs normal. Prelucrarea pe caractere a unui fisier Pentru a prelucra un fisier caracter cu caracter se folosesc functiile: • fputc – pentru scriere. in cazul aparitiei unei erori. In particular.

in caz contrar. "\\"). strcat(specFile.. ea trebuie apelata dupa fiecare operatie asupra fisierului. "copie. putchar(ch).h> #include <string. Aceasta inseamna ca este posibil ca un byte sa aiba aceeasi valoare (cand este evaluat ca un int) ca si EOF. } fclose(fp). Intrebarea se pune. la citirea/scrierea unui caracter se testeaza daca functiile fgetc/fgetc returneaza EOF.\Dn):\n\t\t"). in caz contrar. } // Citirea fisierului for( . cale[65]. care au urmatorul format de apelare: feof(variabila_fisier) ferror(variabila_fisier) unde: variabila_fisier este pointerul catre tipul FILE asociat fisierului la deschiderea acestuia cu functia fopen. numele si extensia fisierului de copiat printf("Calea fisierului de copiat (C:\D1\D2. ea returneaza 0 (fals).txt"). } fclose(fp).h> #include <stdlib. *dest. strcat(specCopie. . gets(cale). strcat(specFile.p++. "r")) == NULL) { printf("Nu se poate deschide fisierul. daca se opereaza asupra unui fisier binar. ea returneaza 0 (fals). strcpy(specCopie. #include <stdio. void main() { // Se introduce calea.\n"). Deci. specFile). ) { if ((ch = fgetc(fp)) == EOF) break. Functia feof returneaza o valoare diferita de 0 (adevarat) daca s-a ajuns la sfarsitul fisierului asociat cu variabila_fisier. cum putem afla care dintre cele doua evenimente s-a produs? Mai mult. specFile[80]. ch.. } Functiile feof si ferror Dupa cum am vazut in exemplu anterior. printf("Numele si extensia fisierului: "). nume). Pentru a face o verificare totala a erorilor. Aceasta functie se poate folosi atat la fisiere text cat si la fisiere binare. cale). Exemplu: Urmatorul program realizeaza o copie a unui fisier text al carui cale si nume se introduce de la tastatura. 85 . specCopie[80]. exit(1). // Deschiderea fisierului pentru citire if((fp = fopen(numeFis. strcat(specFile. orice valoare este valida. adica daca s-a ajuns la sfarsitul fisierului sau daca s-a produs o eroare. fflush(stdin). char nume[15]. Functia ferror returneaza o valoare diferita de 0 (adevarat) daca in fisierul asociat cu variabila_fisier a aparut o eroare. ferror se refera la erorile aparute relativ la ultima operatie de acces la fisier. fflush(stdin).h> FILE *sursa. gets(nume). in ce mod putem afla daca a fost returnata o data valida sau s-a ajuns la sfarsitul fisierului? Solutia acestei probleme o constituie utilizarea functiilor feof si ferror.

Functia fgets citeste caracterele din fisier in sirul a carei adresa este indicata de pointerul sir. La un apel se citesc cel mult numar – 1 caractere.care se scriu. dest). } Intrari/iesiri fara format Citirea si scrierea inregistrarilor care sunt siruri de caractere se poate realiza apeland functiile fgets si fputs: • fgets – citeste un sir de caractere dintr-un fisier. fclose(dest). variabila_fisier) unde: • sir – este un pointer catre zona in care se vor stoca caracterele citite sau unde se gasesc caracterele. Functiile se pot apela folosind urmatoarele sintaxe: fgets(sir. "w")) == NULL) { printf("Nu se poate deschide fisierul destinatie. Functia fgets returneaza o valoare pozitiva daca operatia de scriere a decurs normal si EOF daca se produce o eroare la scriere. Functia fgets adauga un caracter null la sfarsitului sirului. fie cand s-a ajuns la sfarsitul fisierului. in byte. specCopie). exit(1).\n").. 86 . in sensul ca retine caracterul newline."). fie cand s-a citit un caracter newline. care dintre ele se produce primul. fclose(sursa). Citirea se termina fie cand s-au citit numar – 1 caractere. Caracterul null de terminare a sirului nu este scris in fisier. • variabila_fisier – este un pointer catre tipul FILE a carei valoare a fost returnata de functia fopen la deschiderea fisierului din care se citeste sau in care se scrie. numar. } if(!feof(sursa)) fputc(ch. } } printf("Fisierul %s\ns-a copiat in %s". } // Copierea fisierului sursa in destinatie while(!feof(sursa)) { ch = fgetc(sursa). exit(1).\n"). specFile. variabila_fisier) fputs(sir. Functia fputs copiaza sirul de caractere sir terminat prin caracterul null in fisierul asociat cu variabila_fisier. Functia returneaza pointerul sir daca operatia s-a terminat normal sau un pointer NULL daca s-a produs o eroare.// Se verifica existenta fisierului sursa if((sursa = fopen(specFile. • fputs – scrie un sir de caractere intr-un fisier. if(ferror(dest)) { printf("Eroare la scriere. exit(1). in sensul ca nu adauga automat in fisier secventa carriage return/line feed. exit(1). Functia fgets lucreaza diferit fata de functia inrudita gets. • numar – este dimensiunea. Deci functia fputs lucreaza diferit fata de functia inrudita puts. } // Se deschide fisierul destinatie if((dest = fopen(specCopie. if(ferror(sursa)) { printf("Eroare de citire/n"). a zonei in care se citesc caracterele din fisier. "r")) == NULL) { printf("Fisierul indicat nu exista.

do { gets(linie). #include <stdio. // Se scriu in fisier caracterele tastate. FILE *fis = fopen("C:\\BC31\\Programe\\Notite. if ((i = scanf("%d".txt". "rt"). atunci caracterele ramase necitite din zona tampon se pierd. Folosind functia fgets sa se afiseze pe ecran continutul fisierului creat anterior. fis). #include <stdio. FILE *fis = fopen("C:\\BC31\\Programe\\Notite. } Golirea zonei tampon a fisierului In foarte multe cazuri este necesara golirea (vidarea) zonei tampon (buffer) a unui fisier. fputs("\n". } b. Sintaxa apelului functiei este: fflush(variabila_fisier) unde: variabila_fisier – este pointerul catre tipul FILE a carei valoare a fost returnata de functia fopen la deschiderea fisierului a carei zona tampon se goleste. if (i > 0) fputs("\n". // Se testeaza daca nu s-a transmis un rand gol if (strlen(linie)==0) break. deoarece dupa apelul functiei zona tampon devine goala. • Daca fisierul este deschis in citire. Observatii: • Daca fisierul este deschis in scriere. Transmiterea unei linii goale indica terminarea scrierii in fisier.Exemple: a) Folosind functia fputs sa se scrie intr-un fisier liniile tastate. do { printf("Valoarea lui n este: "). 100. fis). "wt").h> void main() { int i. fis).h> void main() { char linie[100]. fis). Pentru a executa aceasta operatie se apeleaza functia fflush.txt". Exemplu: Sa se scrie un program care sa nu permita introducerea unui sir de caractere in locul unei valori numerice. &n)) == 1) break. linie).h> #include <stdlib. // Scrie articolul citit } fclose(fis). #include <string.h> void main() { char linie[100]. n. fputs(linie. fclose(fis). while (!feof(fis)) { fgets(linie. int i = 0. // Trecerea la o noua inregistrare else i = 1. Functia returneaza 0 daca operatia s-a terminat normal si EOF daca s-a produs o eroare. Fiecare linie va constitui un articol in fisier. 87 . // Citeste un articol din fisier printf("%s". atunci continutul zonei tampon se scrie in fisierul respectiv. // Trecerea la o noua inregistrare } while (EOF). printf("Nu s-a tastat un intreg\n").

h. a unui fisier text. } // // // // Se determina pozitia curenta in fisier Pozitionare pe sfarsitul de fisier Determinarea lungimii fisierului Pozitionarea pe inceputul de fisier 88 . functia returneaza un cod de eroare. Observatii: • Daca fisierul este binar.h. pz. lungime = ftell(pf). 1 sau 2) indicate in tabelul urmator: Constanta SEEK_SET SEEK_CUR SEEK_END Valoare 0 1 2 Pozitia in fisier Inceputul fisierului Pozitia curenta Sfarsitul fisierului Functia returneaza 0 la o pozitionare corecta si o valoare diferita de zero in caz de eroare. deplasament-ul trebuie sa fie 0 sau valoarea returnata de functia ftell. dintre origine (pozitia pointerului de fisier) si noua pozitie. pz = ftell(pf). Functia returneaza –1 in caz de eroare. Functia ftell Functia returneaza un intreg lung reprezentand pozitia curenta a pointerului de fisier. } Pozitionarea intr-un fisier Pentru deplasarea capului de citire/scriere al discului in vederea prelucrarii inregistrarilor (operatie numita si pozitionarea pointerului de fisier) si pentru a determina pozitia curenta a pointerului de fisier. deplasament. 0L. Prototipul functiei se gaseste in fisierul stdio. return lungime. SEEK_SET). • deplasament – diferenta. fflush(stdin). Forma de apelare a functiei este: ftell(variabila_fisier) unde: variabila_fisier – este pointerul catre tipul FILE a carei valoare a fost returnata de functia fopen la deschiderea fisierului in care se determina pozitia curenta a pointerului de fisier. in limbajul C++ exista mai multe functii.if (i==EOF) // S-a tastat sfarsitul de fisier (Ctrl + Z) exit(1). printf("Numarul introdus este: %d\n". fseek(pf. Forma de apelarea a functiei este: fseek(variabila_fisier. • Valoarea returnata de functie poate fi utilizat intr-un apel ulterior al funtiei fseek. origine) unde: • variabila_fisier – este pointerul catre tipul FILE a carei valoare a fost returnata de functia fopen la deschiderea fisierului in care se face repozitionarea. Exemplu: Sa se intocmeasca o functie care sa determine lungimea. dintre care in continuare sunt prezentate doar doua: fseek si ftell. fseek(pf. long FileSize(FILE *pf) { long pz. in bytes. deplasamentul este specificat in bytes fata de inceputul fisierului. • origine – una din cele trei localizari SEEK_xxx ale pointerului de fisier (0. Pentru fisierele text. Prototipul functiei se gaseste in fisierul stdio. n). In cazul cand este aplicata asupra unui fisier nedeschis. SEEK_END). lungime. // Se elimina caracterele necitite din zona tampon a // fisierului de intrare standard } while (1). Functia fseek Asigura repozitionarea pointerului de fisier. in bytes.

printf("\tMediile elevului:\n"). Sintaxa apelului functiei este: fwrite(variabila. lungime. "Chimie". Prototipul functiei se gaseste in fisierul stdio. Daca valoarea returnata de functie este 0. in bytes. "Matematica". i < 5. a unui articol. Exemple: a. for (int i = 0. "Istorie"}.h. do { printf("Numele si prenumele elevului: "). intr-o zona speciala. Sintaxa apelului functiei este: fread(variabila. nr_articole – numarul de articole dintr-o inregistrare fizica. lungime. "wb"). Fisierele binare pot fi prelucrate folosind functiile fread si fwrite. "Fizica". variabila_fisier) unde: • • • • variabila – este un pointer catre zona tampon ce va contine inregistrarea fizica citita. a unui articol. Functia fwrite Functia fwrite asigura scrierea articolelor in fisier.Prelucrarea fisierelor binare In fisierele binare (bytes nu sunt considerati ca fiind coduri de caractere). Daca operatia de citire decurge normal. variabila_fisier – este pointerul catre tipul FILE a carei valoare a fost returnata de functia fopen la deschiderea fisierului. } elev. fflush(stdin). float med. float medie[5]. In mod analog. numita zona tampon (buffer). gets(elev. Daca operatia de scriere s-a terminat normal. Prototipul functiei se gaseste in fisierul stdio. predefinit sau definit de utilizator. i++) { 89 . se transfera un numar de articole care se presupun ca au o lungime fixa. Functia fread Functia fread asigura citirea datelor dintr-un fisier binar. In caz de eroare. Sa se creeze un fisier care sa contina numele si prenumele elevului si mediile la cinci materii. nr_articole. char materie[][11]={"Romana".dat". functia fread returneaza numarul de articole (nu numarul de bytes) citite. variabila_fisier – este pointerul catre tipul FILE a carei valoare a fost returnata de functia fopen la deschiderea fisierului. Articolul este o data de un tip oarecare. functia fwrite returneaza numarul de articole (nu numarul de bytes) scrise. la scriere se transfera din zona tampon un numar de articole de lungime fixa. functia fwrite returneaza –1.nume). variabila_fisier) unde: • • • • variabila – pointer catre zona tampon ce contine inregistrarea fizica ce va fi scrisa. inregistrarea fizica reprezinta o colectie de articole. nu a fost citit nic un articol sau a fost atins sfarsitul de fisier (se pot folosi functiile feof si ferror pentru a afla care dintre aceste evenimente s-a produs). nr_articole. lungime – dimensiunea. in bytes. nr_articole – numarul de articole din inregistrarea fizica. #include <stdio. void main() { char ok.h.h> struct catalog { // Structura articolului char nume[30]. lungime – dimensiunea. La citire. FILE *pf = fopen("C:\\BC31\\Programe\\Catalog.

poz. } Nivelul superior de prelucrare a fisierelor in C++ Intrare/Iesire (prescurtat IO de la Input/Output) este procesul de transfer a datelor de pe un periferic al calculatorului pe altul sau dintr-o componenta a calculatorului pe alta. #include <stdio. intrari/iesiri memorie si intrari/iesiri retea. Sa se realizeze un program care sa citeasca fisierul creat anterior si sa afiseze numele si prenumele elevului si media acestuia. 1. pf). } fclose(pf). } while (ok == 'D' || ok == 'd'). SEEK_SET). } printf("%10s: ". long lung.2f\n". elev. Exista trei categorii de intrari/iesiri: intrari/iesiri standard. IO in C++ sunt realizate prin intermediul claselor stream. elev. fclose(pf). // Numar articole din fisier // Se citesc articolele si se afiseaza elevii si mediile lor for (int i = 0. &med). materie[i]). pf). cout. Intrarile/iesirile standard sunt utilizate frecvent si limbajul C++ furnizeaza stream-urile cin. // Repozitionare pe primul articol nrArt = lung / sizeof(elev). fflush(stdin). 0L. i++) med += elev. care au urmatoarea ierarhie de mostenire: 90 . b. printf("%6. cerr si clog. } elev. i++) { med = 0. scanf("%c". // Se determina numarul de articole din fisier poz = ftell(pf). nrArt.h> struct catalog { // Structura articolului char nume[30].dat".medie[i] = med. FILE *pf = fopen("C:\\BC31\\Programe\\Catalog. // Scrierea articolului printf("\n"). sizeof(elev). poz. scanf("%f". // Lungimea fisierului fseek(pf. void main() { float med.medie[i]/5. sizeof(elev). i < 5.} fwrite(&elev. for (int i = 0. fflush(stdin). // Determinarea pozitiei curente fseek(pf. float medie[5]. printf("Mai sunt date de introdus (D/N)? "). SEEK_END). 1. // Pozitionare pe sfaristul fisierului lung = ftell(pf). fread(&elev. &ok).nume). "rb"). med). fflush(stdin). printf("%30s ". i < nrArt.

txt"). suporta doua tipuri de fisiere: • fisiere text • fisiere binare Principala diferenta dintre fisierele text si fisierele binare consta in faptul ca in fisierele text se executa diferite translatari ale caracterelor.ios::app|ios::binary). ios::binary).txt"). ca si limbajul C.dat".txt". ios::app | ios::binary). sau sau ifstream in .close(). Pentru adaugare (adaugare date la sfarsitul unui fisier existent) ofstream out("fis. ofstream out.dat". ifstream in .Putem folosi clasele ifstream. In tabelele urmatoare sunt prezentate pasii si operatiile ce pot (sau trebuie) fi executate pentru a utiliza fisierele in C++: 1.ios::binary). Pentru citirea datelor ifstream in ("fis. C++ deschide fisierele in modul text. Citirea/scrierea datelor din/in fisiere 4.txt").dat". ofstream out("fis. sau sau ofstream out. Functii pentru citirea din fisier get().open("fis. sau sau ofstream out. ios::app).close(). ofstream out ("fisier.open("fis. in. de exemplu. out. ofstream si fstream pentru a efectua intrari/iesiri de fisiere ( (cin este un obiect al clasei istream si cout este un obiect al clasei ostream). Inchiderea fisierelor (dupa terminarea prelucrarii lor) obiect ofstream "out" out. << (operator inserare) << (operator inserare) write() La fel ca mai sus 3. "\r+\f" (returul de car) este convertit in "\n" (linie noua). out.txt".open("fisier. Data caracter un cuvant mai multe cuvinte obiecte date binare Operatie obiect Ifstream "in" in. Fisiere text si binare Limbajul C++. 2.open("fis. In mod implicit. ofstream out. hard-discul) in memoria principala si invers. out.dat".open("fis. ifstream in ("fis. in timp ce in fisierul binar nu se fac asemenea translatari.txt"). Crearea sau deschiderea unui fisier Fisiere text Fisiere binare Pentru scrierea datelor ofstream out ("fis. ios::binary). Schematic fluxul de date poate fi reprezentat astfel: Memoria RAM Utilizatori Hard disc Sagetile indica fluxul de date.dat".dat". out.open("fis. IO de fisier reprezinta transferul datelor dintr-o memorie externa (de exemplu. read() La fel ca mai sus Functie Descriere Functii pentru scrierea in fisier put(). Functii ce pot fi utilizate pentru a executa sarcini speciale 91 . >> (operator extragere) getline().ios::app). ios::binary). in.

se inchide fisierul. Deschide fisierul si seteaza pointerul la sfarsitul fisierului. fara a muta pointerul get pe urmatorul caracter. gcount() Returneaza numarul de bytes cititi din fisier. Deschide fisierul si truncheaza intregul sau continut.txt si le afiseaza. // codul pentru scrierea caracterelor in fisier rasp='d'. In cazul fisierelor binare accesul aleator este realizat cu ajutorul acestor functii. returneaza adevarat daca fisierul este deschis si flas in caz contrar. 92 . dar si in cazul intalnirii unei erori de format.put(c). Returneaza adevarat in aceleasi cazuri ca si bad(). ignore() peek() seekg() seekp() tellg() tellp() Ignora n bytes din fisier (pointerul get este pozitionat dupa n caractere) Verifica urmatorul caracter disponibil. Verifica daca un fisier este sau nu deschis.Verificarea sfarsitului de fisier Verificarea daca operatia a esuat Verificarea daca operatia a esuat Verificarea pentru fisier deschis Numarul de bytes deja cititi Ignorarea caracterelor pe timpul citiri fisierului Verificarea urmatorului caracter Acces aleator (numai pentru fisiere binare) eof() bad() fail() Se foloseste pentru a verifica daca s-a intalnit sfarsitul de fisier pe timpul citirii fisierului. Deschide fisierul in modul binar. cin >> c. Indicatori speciali Tabelul urmator prezinta indicatorii speciali ce pot fi utilizati pentru gestionarea fisierelor (acesti indicatori sunt folositi pe timpul deschiderii fisierelor). out. cin >> rasp.put(char variabila) unde variabila este identificatorul zonei de memorie in care se va citi sau de unde se va scrie fisierul. iar obiect este numele stream-ului de intrare sau de iesire.get(char variabila) obiect. Ele furnizeaza sau seteaza pozitia pointerilor get sau put pe o anumita locatie. Exemplu: Programul urmator scrie pe rand in fisierul test. is_open(). #include<iostream> #include<fstream> using namespace std.txt caracterele (tastate de utilizator) atata timp cat raspunsul la intrebarea “Continui?” este d (de la da). Ultima parte a programului citeste toate caracterele din fisierul test. Indicator ios::app ios::ate ios::binary ios::in ios::out ios::trunc Descriere Deschide fisierul in modul append (adaugare). void main() { char c.txt"). Returneaza adevarat daca o operatie de scriere sau citire a esuat.is_open()) { // Ciclarea va continua pana se tasteaza un caracter diferit de d while (rasp =='d') { cout <<endl << "Continui? ". rasp. Deschide fisieul pentru scriere. if(rasp =='d') { cout << endl << "Introduceti un caracter: ". Citirea si scrierea caracterelor in fisierele text Pentru citirea si scrierea unui caracter in fisierele text se pot folosi functiile get (pentru citire) si put (pentru scriere) al caror format este: obiect. ofstream out ("test. Daca raspunsul este diferit de d. // Deschiderea unui stream de iesire if(out. In continuare prin intermediul unor exemple se va prezenta modul de lucru cu fisierele in C++. Deschide fisierul pentru citire.

93 . } Fisiere binare Copierea unui fisier Programul urmator copiaza un fisier binar in alt fisier binar.txt"). #include<iostream> #include<fstream> using namespace std. buf este tabloul de caractere unde se gasesc datele ce vor fi scrise in fisier.get()). if(!in.eof()) cout << c. out. care au urmatoarele prototipuri: obiect. Citirea si scrierea unui obiect Pentru a citi/scrie blocuri de date se folosesc functiile read() si write(). Daca se intalneste sfarsitul de fisier inainte de citirea a “num” caractere.} } } out. Pentru ambele functii.close(). } } // Inchiderea ambelor fisiere in.put(in. } } in. streamsize num). obiect este numele stream-ului de intrare sau de iesire.jpg". buf este tabloul de caractere unde va fi memorat blocul citit. Dupa deschiderea celor doua fisiere se va copia continutul primului fisier in celalalt fisier. streamsize num). cout << endl. obiect. num este o valoare numerica care defineste cantitatea de date (numarul de caractere) ce se citesc/scriu. } Nota: Fisierul detectiv.ios::binary). unul pentru citire si celalalt pentru scriere.close(). // Deschiderea unui stream de intrare if(in.get().is_open()) { while(!in.is_open()) { while(!in. Cele doua fisiere sunt deschise in modul binar. Pentru functia read(). Aceasta functie returneaza numarul de caractere citite la ultima operatie de intrare neformatata. cout << endl.close().jpg".jpg (sau un alt fisier) trebuie sa existe in calea specificata (in acelasi dosar cu programul executabil). // Stream-ul de intrare ofstream out("copiedetectiv.eof()) { c = in.close().write(const char *buf. // Stream-ul de iesire if(in.is_open() && out. void main() { // Deschiderea stream-ului de intrare si de iesire in modul binar ifstream in("detectiv.read(char *buf. atunci pentru a sti cate caractere au fost citite se poate apela functia gcount().ios::binary). // codul pentru citirea intregului fisier ifstream in("test. Pentru functia write().eof()) { out.

is_open()) { // Ciclarea va continua pana cand se raspunde cu ceva diferit de d while( rasp == 'd') { cout << endl << "Continui? ". } void main() { char rasp='d'. cin >> rasp. // Se deschide stream-ul de iesire if(out.dat".read((char*) &stud. cin >> stud. cout << "Codul studentului:".eof()) { cout << stud. } } } in. if(rasp == 'd') { IntroducereDate().cod << endl. out. ios::app).nume << "\t\t" << stud.close().write((char*) &stud. sizeof(stud)). struct Student { // Definirea structurii inregistrarilor char nume[20]. int cod.nume. sizeof(stud)). cin >> stud.Exemplu: Programul va scrie si citi un obiect (obiectul Student) in si dintr-un fisier binar: #include<iostream> #include<fstream> using namespace std.dat este deschis in modul append ofstream out("student. } stud.eof()) { in. ifstream in("student. // Se deschide stream-ul de intrare if(in. // Fisierul student.is_open()) { cout << "Datele studentilor sunt:" << endl.cod.dat"). } } } out.close(). while(!in. } 94 . if(!in. void IntroducereDate() { cout << "Numele studentului: ".

).. Astfel. iar definitia unuia este acceptata doar in prezenta definitiei celuilalt. caractere etc. O clasa poate fi definita ca o abstractizare a trasaturilor esentiale ale unei colectii de obiecte inrudite intre ele. nu numere reale sau intregi.Programarea orientata spre obiecte Programarea orientata spre obiecte realizeaza contopirea celor doua entitati ale prelucrarii informatiilor: datele aplicatiei si codul necesar tratarii lor. Noile tipuri de date (complex si matrice) se numesc clase. obiecte. o. Din punct de vedere al limbajului C++. pentru a desemna una si aceeasi notiune. n. Notiunile de clasa si obiect impreuna cu relatiile existente intre clase. indivizi sau instante ale unei 95 . un programator poate crea tipul complex sau matrice. Pentru a ajunge la acest deziderat. Tipurile predefinite existente in limbajul C++ se numesc tipuri fundamentale. c. tablouri) • Notiunea de clasa derivata este strans legata de clasificarea tipurilor. poate deveni o realitate. referinte. se numesc tipuri utilizator sau clase utilizator. existenta unui obiect este strans legata de clasa careia ii apartine. In aceste conditii. m. c. o. in procesul dezvoltarii unei aplicatii. Acestea se comporta la fel ca tipurile fundamentale (intregi. de foarte multe ori termenii se folosesc alternativ. De aceea obiectele mai sunt denumite si reprezentanti. Programarea care are la baza un model orientat pe obiecte poarta numele de programare orientata pe obiecte (OOP – Object Oriented Programming) sau programare obiectuala. declarationala. in functie de ceea ce are nevoie. variabilele a. iar tipurile definite de utilizator. b. limabjul de programare pune la dispoziti facilitati deosebite pentru definirea unor tipuri de date proprii si a unor operatori destinati manipularii lor. astfel: • Prin tip derivat se intelege orice tip obtinut din tipurile fundamentale sau utilizator prin constructii specifice limbajului (pointeri. b. Conceptele de baza ale programarii orientate pe obiecte Modelul orientat pe obiecte este un model computational ale carui principale abstractii sunt clasele si obiectele. iar operatorii redefiniti + si * se numesc metode ale claselor respective. cat si metodele (functiile) cu ajutorul carora obiectele in cauza pot fi manipulate. Clase si obiecte Conceptele de clasa si obiect sunt inseparabile. b. clasa este o entitate statica. Un obiect poate fi definit ca o materializare (concretizare) a tipologiei descrise de o anumita clasa. n. Intre clasa si tip se face distinctie doar in cazul expresiilor tip derivat si clasa derivata. o secventa de genul: complex matrice . numere reale. chiar daca cele sase variabile reprezinta numere complexe si matrici. Din aceasta scurta prezentare rezulta ca o clasa contine atat structurile de date necesare descrierii unui obiect. o poarta numele de obiecte (sau instantieri) ale claselor complex si matrice.. care sa opereze cu numere complexe sau cu matrici. scadere etc. Desi notiunile de clasa si tip nu se confunda (tipul implica doar consideratii comportamentale). care descrie o tipologie in termenii definirii structurii si comportamentului pentru toate obiectele care o determina. Mai mult. c = a + m = n * a. m.) si operatorii standard (adunare. si clase si obiecte reprezinta conceptele fundamentale sau de baza ale programarii orientate pe obiecte.

clase. Din definitie, rezulta ca obiectul este o entitate dinamica care poate fi creata, utilizata si distrusa. Modelul algoritmic al unui program descrie executarea unor operatii conform diagramei de flux, in timp ce in modelul orientat pe obiecte, programul descrie un sistem de obiecte care interactioneaza. Modul de interactiune al obiectelor este determinat de relatia obiect-obiect existenta intre obiecte si de relatia clasa-clasa existenta intre clasele de care apartin obiectele.

Relatia obiect-obiect
Existenta unui obiect poate fi descrisa prin intermediul a doua caracteristici: • caracteristica statica – descrie starea interna a unui obiect in termenii unor trasaturi numite atribute sau date membru. Starea interna determina posibilitatile de acumulare a informatiei pe care le detine un sistem de obiecte; insa ea nu confera obiectelor proprietatea de identitate sau individualitate. Deci, un obiect nu poate fi descris doar de starea lui interna, existand posibilitatea ca la un moment dat sa fie mai multe obiecte cu aceeasi stare interna (numite obiecte echivalente). • caracteristica dinamica – descrie evolutia in timp a starii interne a unui obiect in termenii interactiunii acestuia cu alte obiecte. Interactiunea obiectelor se realizeaza prin schimbul de informatii. Schimbul de informatii este numit simbolic mesaj si este modul de baza de comunicare a obiectelor din cadrul unui sistem. In urma primirii unui mesaj, un obiect se manifesta prin modificarea particulara a starii sale interne si transmiterea de mesaje altor obiecte. Schimbul de mesaje determina tipurile de relatii dintre obiecte si deci si modul de operare asupra obiectelor. Deci exista: • obiecte de tip server, asupra carora se opereaza de catre alte obiecte in vederea satisfacerii anumitor cereri; • obiecte de tip client, care sunt beneficiarii serviciilor formulate catre obiectele de tip server; • obiecte de tip agent, care sunt obiecte mixte, atat de tip server cat si de tip client.

Relatia obiect-clasa
Este de fapt relatia obisnuita dintre o variabila si tipul variabilei.
Definitie: Procesul de definirea a unei clase pornind de la o multime de obiecte se numeste clasificare sau tipizare si este specific fazei de proiectare a unui program. Procesul invers se numeste instantiere si este specific fazei de implementare.

Prin procesul de instantiere a unei clase rezulta o relatie de tip INSTANTA_A (INSTANCE_OF) intre instantele clasei si clasa, relatie care poate fi reprezentata grafic sub forma unui arbore, asa cum se arata in figura urmatoare: oClasa
INSTANTA_A Obiect1 INSTANTA_A Obiect2

Figura 1. Reprezentarea grafica a relatiei obiect-clasa.

96

Relatia clasa-clasa
Relatiile dintre tipuri este o consecinta a modului de definire a unor noi tipuri pornind de la cele existente. Un tip abstractizeaza un segment din lumea inconjuratoare. Modul si nivelul de abstractizare a fiecarui tip depind de cerintele formulate asupra lui pe timpul procesului de modelare. Pentru definirea unui nou tip, exista doua procedee si anume: • compunere (agregare) - obiectele instantiate ale noului tip vor exista ca imbinari ale obiectelor corespunzatoare (numite si obiecte inglobate) tipurilor agregate. Cu toate ca starea interna a noilor obiecte este o cumulare a starilor interne ale obiectelor componente, comportarea acestora este descrisa de reguli specificate in mod explicit la definirea tipului. Simbolic, agregarea a doua tipuri poate fi reprezentata cu ajutorul unei scheme ca cea din fig. 2, in care noul tip Calculator, este rezultatul compunerii tipurilor Hard si Soft. Calculator Hard Soft
Figura 2. Reprezentarea simbolica a agregarii tipurilor Hard si Soft pentru obtinerea noului tip Calculator.

Prin procesul de agregare a doua sau mai multe tipuri rezulta o relatie de tip PARTE_DIN (PART_OF) intre tipurile agregate si noul tip. Operatia de agregare afecteaza numai asupra caracteristicilor statice ale tipurilor. specializare – prin procesul de specializare a tipurilor se realizeaza dotarea unor tipuri existente cu calitati suplimentare. Este posibila specializarea unui tip din unul sau mai multe tipuri. Noul tip va mosteni trasaturile tipurilor pe care le specializeaza, la care poate contribui in mod optional cu propriile sale caracteristici. Operatia de specializare afecteaza atat asupra caracteristicilor statice cat si asupra caracteristicilor dinamice ale tipurilor angrenate in proces. Operatiile de derivare a claselor si instantierea claselor template sunt cazuri particulare de specializare. Prin procesul de derivare rezulta relatii de tip ESTE_O sau ESTE_UN (IS_A) intre tipul rezultat si tipurile antrenate in proces. Relatiile clasa-clasa obtinuta prin derivarea unei clase se poate reprezenta schematic ca in fig. 3, care se interprezteaza astfel: tipul CalculatorPersonal ESTE_UN AparatElectronic si ESTE_UN BunComercial
AparatElectronic BunComercial

ESTE_UN CalculatorPersonal

ESTE_UN

Figura 3. Relatia clasa-clasa obtinuta prin derivarea unei clase.

generalizare – definirea noilor tipuri se realizeaza pornind de la trasaturile comune ale unui set de tipuri. Generalizarea se rasfrange atat asupra structurii cat si asupra comportamentului tipurilor antrenate in proces. Operatiile de factorizare a claselor (opusul derivarii) si definirea claselor template sunt cazuri particulare ale generalizarii. In urma generalizarii rezulta o relatie de tip UN_FEL_DE (KIND_OF) intre tipul rezultat si tipurile angrenate in proces, relatie care poate fi reprezentata schematic ca in figura 97

urmatoare, in care tipul AparatElectronic este UN_FEL_DE Televizor si UN_FEL_DE CalculatoPersonal.
AparatElectronic UN_FEL_DE UN_FEL_DE

Televizor

CalculatorPersonal

Figura 4. Relatia clasa-clasa obtinuta prin generalizarea claselor.

Abstractizare
Abstractizarea reprezinta procesul de ignorare intentionata a detaliilor nesemnificative si retinerea proprietatilor definitorii ale unei entitati. Abstractizarea imbraca urmatoarele aspecte: • abstractia procedurala – reprezinta ignorarea detaliilor de desfasurare ale proceselor. Programul scris intr-un limbaj care permite abstractia procedurala este format dintr-un set de rutine ce se apeleaza in functie de fluxul evenimentelor. Utilizarea unei rutine nu presupune cunoasterea detaliilor legate de implementarea acesteia. Ca urmare, utilizatorul va gandi in termenii unei operatii logice de program si nu in termenii unor instructiuni sau comenzi de limbaj. • abstractia datelor – permite ignorarea datelor legate de reprezentarea unui tip de date. De exemplu, utilizarea in Pascal a tipului de data set nu implica cunostinte legate de reprezentarea interna a datelor sau de modul in care compilatorul efectueaza operatiile primitive cu acest tip de data. • clasele – cele doua abstractii prezentate reprezinta cheia modelului algoritmic de programare, sintetizat de Dijkstra astfel: “Algoritmi + structuri de date = programe”. In programarea orientata pe obiecte cele doua abstractii se regasesc intr-una singura, clasa. Deci, clasa combina pentru o entitate de nivel inalt atat abstractia procedurala cat si abstractia datelor. La utilizarea obiectelor este suficienta interpretarea obiectului ca entitate; utilizatorul nu trebuie sa cunoasca detaliile legate de datele interne ale clasei si de metodele acesteia. Cea mai puternica forma de abstractizare in procesul de definire a unor noi tipuri este operatia de agregare.

Incapsulare
Incapsularea reprezinta procesul de separare a informatiilor de manipulare ale unei entitati de informatiile de implementare. Incapsularea implica abstractizarea. Clasele pot fi impartite in doua sectiuni distincte: interfata si implementarea. Interactiunea obiectelor se realizeaza cu ajutorul interfetei; implementarea nu este cunoscuta de utilizatorii externi, ea fiind destinata exclusiv uzului intern al obiectelor. Incapsularea este o forma de abstractizare care prin eliminarea accesului ocazional, intamplator, la sectiunea de implementare a claselor promoveaza o vedere exterioara a obiectelor care este determinata de sectiunea de interfata. Obiectivele urmarite prin incapsulare sunt: • “contopirea” datelor cu codul in cadrul claselor; • facilizarea detectarii erorilor (intotdeauna cauza unei erori se afla in “interiorul” unei singure clase); • modularizarea problemei de rezolvat (fiecare clasa va rezolva o anumita problema).

98

Mostenire Mostenirea este o facilitate noua introdusa de limbajele orientate pe obiecte si are la baza doua din operatiile de definire a unor noi tipuri: specializarea si generalizarea. ierarhia de aparteneta a unei clase trebuie specificata in mod unic. Clasele derivate vor fi vederi externe diferite ale aceleaiasi entitati. Prin mostenirea codului se urmareste implementarea functionalitatii mai multor clase intruna singura care devine astfel clasa de baza. sau graficul aciclic directionat in cazul mostenirii multiple. O clasa este superclasa (supertip) sau clasa de baza pentru toate clasele care o specializeaza si subclasa (subtip) sau clasa derivata pentru toate clasele pe care le specializeaza. Ierarhiile care promoveaza acest gen de mostenire pot fi identificate prin prezenta masiva a codului in clasele de baza si simplitatea relativa a codului claselor derivate. iar modificarile la care este supusa o clasa de baza se rasfrang si asupra claselor derivate. rezolvand o mostenire multipla. in cazul mostenirii simple. O ierarhie este o clasificare diferentiala a tipurilor. Identificarea unei clase de baza pentru un set de clase este tot o forma de abstractizare. In functie de pozitia ocupata in ierarhie. intr-un fel. calitatea de generalitate a unui tip crescand pe masura urcarii in ierarhie. Ierarhiile care promoveaza acest gen de mostenire pot fi recunoscute prin concentrarea codului in clasele derivate si caracterul generic al codului claselor dr baza. Intr-adevar. Clasele de baza vor fi modele abstracte ale claselor derivate si vor defini interfata. generalizarea implica concentrarea atentiei asupra trasaturilor comune si ignorarea particularitatilor caracteristice fiecarei clase in parte. In figura anterioara au fost evidentiate doar relatiile care pot fi deduse din ierarhiile initiale (cu toate ca si tipul Televizor ESTE_UN BunComercial) iar relatiile de tipul UN_FEL_DE au fost transformate in relatii de tipul ESTE_UN. exemplele prezentate la specializare si generalizare pot fi unite in cadrul aceleasi ierarhii. Astfel. In procesul de proiectare a ierarhiilor de clase se incearca factorizarea caracteristicilor comune – fie ele statice sau dinamice – ale claselor de pe un anumit nivel de ierarhizare prin implementarea lor pe nivelele superioare. situatia opusa mostenirii codului. iar clasele derivate vor oferi versiuni de operare ale claselor de baza prin dezvoltarea interfetei. Mostenirea interfetei este. Cel mai intuitiv mod de reprezentare a unei ierarhii de tipuri este arborele multicai. Reprezentarea unei mosteniri multiple. Trasaturile unei clase derivate se exprima in termenii diferentelor fata de trasaturile claselor de baza. 99 . • extensibilitatea si adaptabilitatea – sunt caracteristici care fac posibila dezvoltarea si intretinerea unei taxonomii de clase. un tip poate fi generalizarea sau specializarea altor tipuri. Clasificarea diferentiala a tipurilor determina si principalele caracteristici ale unei ierarhii de clase si anume: • reutilizarea codului – poate fi atinsa prin mostenirea interfetei sau mostenirea codului. De aici rezulta ca o clasa nu are sens in afara ierarhiei de care apartine. Ea confera limbajului capacitatea de clasificare a tipurilor prin organizarea acestora in taxonomii sau ierarhii de tipuri. In rest avem o mostenire multipla. AparatElectronic ESTE_UN ESTE_UN BunComercial ESTE_UN Televizor CalculatorPersonal Figura 5. Cele doua cazuri de mostenire nu se exclud reciproc. Daca in cadrul unei ierarhii de clase specializarea se aplica exclusiv unui singur tip spunem ca avem o mostenire simpla. Oricand este posibila expandarea ierarhiei prin alipirea de noi clase. Desi contravine modului natural de clasificare a tipurilor.

sau cu ajutorul unei structuri. float _im._im). evedentiind astfel legatura intrinseca dintre cele doua elemente. Se poate remarca aparitia cuvantului cheie public necesar asigurarii accesului la membrii clasei CComplex: _re si _im. Teoretic. z. %f)\n”. obiectele unei clase D pot fi utilizate in orice situatie care necesita prezenta unor obiecte de tipul B. Trecerea de la structura Complex la clasa CComplex se face foarte usor prin utilizarea cuvantului cheie class in locul cuvantului cheie struct astfel: // clasa “CComplex” este echivalenta cu structura “Complex” class CComplex { public: float _re. } In cazul clasei CComplex functia Tipareste trebuie sa fie declarata chiar in interiorul clasei. partea reala si partea imaginara: struct Complex { float _re. complex2. unde B este o superclasa a clasei D. Im. pentru a afisa numarul complex z sub forma (parte_reala. z. care poate avea urmatoarea forma: void Tipareste(Complex z) { printf(“(%f. Limbajul C++ suporta urmatoarele forme de polimorfism: • polimorfismul parametric – apare in cazul aplicarii unei functii pe argumente de tipuri diferite. declarate in mod indpendent: float Re. void Tipareste() { 100 ._re. Declararea unei variabile de tipul CComplex. }. Este cea mai slaba forma de polimorfism si este mostenita din limbajul C (de fapt este intalnita in majoritatea limbajelor algoritmice) • polimorfismul ad-hoc – este forma de polimorfism care se bazeaza pe supraincarcarea functiilor si mascarea metodelor in cadrul unei ierarhii de clase. presupunand ca intr-o aplicatie sunt necesare foarte multe operatii cu numere complexe. • polimorfismul de mostenire – apare ca o consecinta a clasificarii claselor in ierarhii si presupune manipularea obiectelor de un anumit tip in situatii care cer tipuri diferite de tipul obiectelor. pentru a evidentia astfel obiectul operatiei de tiparire a numerelor complexe: class CComplex { public: float _re. Declararea claselor Pentru a exemplifica modul de declarare a unei clase vom introduce clasa numerelor complexe. float _im. Folosind structura Complex. aceeasi functie suporta implementari diferite si metodele unei clase de baza pot fi redefinite prin mascare sau pot fi supraincarcate in clasele derivate. se face similar variabilelor de tipul Complex. parte_imaginara) se poate folosi o functie numita Tipareste. adica prin declaratii de instantiere: CComplex complex1. Astfel.Polimorfism Prin definitie. // instantierea clasei “CComplex” Sa presupunem ca este necesara tiparirea unor numere complexe. adica a unui obiect. polimorfismul este capacitatea unei entitati de a imbraca mai multe forme. float _im. }. Un numar complex poate fi reprezentat cu ajutorul a doua numere reale.

Functia Tipareste este o functie membru sau o metoda. In declaratia unei clase pot fi specificate toate cele trei forme. caz in care in cadrul clasei se va include prototipul functiei. doar unele sau nici una. // Apelarea functiei Tipareste Functia Tipareste si variabilele _re si _im sunt membrii clasei CComplex. _im). Operatorul de rezolutie Pentru a nu incarca excesiv declaratia clasei. } Controlul accesului la membrii clasei Incapsularea nu inseamna numai combinarea datelor cu functiile in cadrul unei singure entitati. _im). iar variabilele _re si _im sunt date membru sau atribute ale clasei CComplex. Sugestie: Functiile membru (metodele) scrise in cadrul clasei sunt privite de catre compilator ca functii inline. printf(“(%f. In cazul functiilor inline. float _im. o metoda poate fi definita si in afara clasei. daca vrem ca functia Tipareste sa fie definita in afara clasei. sintaxa declararii unei clase poate arata astfel: class <nume_clasa> { public: // sectiunea publica protected: // sectiunea protejata private: // sectiunea privata proteced: // continuarea sectiunii protejate . void Tipareste(). Functia Tipareste nu mai necesita prezenta argumentului de tipul CComplex – acesta fiind determinat de obiectul care necesita tiparirea – iar sintaxa de apel a functiei este: CComplex complex1. _re. De aici rezulta ca o functie va fi descrisa in cadrul clasei numai daca are codul foarte scurt si nu contine instructiuni de ciclare. 101 .} }. _re. compilatorul inlocuieste fiecare apel cu codul functiei.Tipareste(). De exemplu. ea implica si un anumit grad de limitare a accesului la membrii clasei.. proteced si private. Tinand cont de specificatorii de acces. iar metodele descriu comportarea obiectelor de tipul respectiv in momentul satsfacerii unei cereri de executie. }. // Instantierea clasei complex1. iar repetarea unora in cadrul aceleasi clase este permisa. %f)\n”.. Atributele descriu starea interna a obiectului. In C++ exista trei forme de specificare a accesului la membrii unei clase prin mentionarea cuvintelor cheie public. atunci declaratia clasei CComplex ar trebui sa arate astfel: class CComplex { public: float _re. Ordinea in care sunt indicate aceste cuvinte cheie nu are importanta. %f)\n”. clasa trebuie sa contine numai prototipul functiei. In caz contrar. numite si specificatori de acces. // Prototipul functiei Tipareste Pentru a specifica apartenenta functiei Tipareste la clasa CComplex in antetul de definire a functiei se va folosi operatorul de rezolutie (::) precedat de numele clasei: void CComplex::Tipareste() { // Definirea functiei in afara clasei printf(“(%f.

Im = -1.Yipareste(). %f)\n”._re. membrul este inaccesibil Din cele prezentate rezulta ca. float _im.6 Afiseaza (15. in sectiunea privata se definesc datele interne si metodele folosite in comun de catre unele metode din sectiunea publica. clasa CComplex. // // // // // Instantiere Partea reala ia valoarea 15 Partea imaginara ia valoarea –1. z. _im). sectiunea publica a unei clase reprezinta interfata cu exteriorul.6. } private: // Sectiune privata float _re. deoarece permite accesul la datele membru _re si _im. float x = z._re. care asigura o diferentiere a modului de tratare a untilizatorilor unei clase in functie de natura acestora. interzicand accesul extern. Limbajul C++ ofera un mecanism mai rafinat de control al accesului la membri unei clase prin introducerea cuvantului cheie protected. %f)\n”. care totusi tin de “bucataria” clasei. _re. _re. class CComplex { public: // Sectiune publica void Tipareste() { printf(“(%f. daca vrem ca ulterior sa declaram o subclasa. accesul utilizatorilor la membrii clasei se face astfel: CComplex z. Nota In limbajul C++ limitarea dreptului de acces la membri se face la nivelul clasei si nu al obiectului. ca in unele dintre celelalte limbaje orientate pe obiecte. Daca utilizatorul nu este o subclasa a clasei. Sectiunea privata a unei clase este destinata uzului intern al clasei. z._re = 10. atunci nu va avea acces la sectiunea protejata pe care o va vedea ca pe o sectiune privata. De exemplu. z. iar sectiunea privata corespunde implementarii.Yipareste()._im = -7. Forma in care a fost definita clasa CComplex nu este cea mai potrivita. float x = z. } // Metode de modificare a datelor membru 102 . In aceste conditii. _im). este indicat ca sectiunea privata s-o declaram ca sectiune protejata. In cazul clasei CComplex.Re = 15. -1. z. Sectiunea publica a unei clase este destinata folosirii neingradite. 100). permite accesul unui utilizator la oricare din membrii sai: CComplex z. In aceste conditii declararea clasei CComplex ar trebui sa aiba urmatoarea forma: class CComplex { public: // Sectiune publica void Tipareste() { printf(“(%f. Daca utilizatorul este o subclasa a clasei respective. in forma care a fost definita (vezi pag. z. }. atunci ea va avea acces la sectiunea protejata pe care o va vedea ca pe o sectiune publica. De obicei. indiferent de natura potentialului utilizator. } // Metode de modificare a datelor membru void Re(float re) { _re = re. z. Vom modifica definirea clasei trecand datele membru in sectiunea privata si vom defini in sectiunea publica doua noi metode pentru manipularea lor.6) Genereaza o eroare.6. } void Im(float im) { _im = im.}.

7. Indicatorul this Pentru fiecare instanta a clasei CComplex (definita anterior) compilatorul va rezerva o zona de memorie pentru stocarea atributelor _re si _im. orice tentativa de atribuire a unei valori acestuia va determina aparitia unei erori. this este de fapt un cuvant cheie al limbajului C++ si poate fi referit in cadrul definitiei unei clase ca adresa obiectului transmisa suplimentar la apelul unei metode: void CComplex::TiparesteThis() { printf(“Adresa obiectului: %p”. this va putea fi folosit numai in operatii de citire. modificarea obiectului z va avea ca efect actualizarea referintei sale. Rezulta ca. // referinta la obiectul “z” Orice operatie efectuata asupra referintei rZ va afecta si entitatea referita. iar apelul: compl. Adresa instantelor clasei CComplex este un pointer de tipul CComplex* const si poarta numele this. invoca metoda Tipareste cu parametrul this = &z. } Ca pointer de tipul CComplex* const. this). toata clasa este considerata ca fiind sectiune privata. } protected: // Sectiune protejata float _re. cu exceptia unor situatii speciale (referinta la referinta sau pointer la referinta).7) invoca metoda Re cu parametrii this = &compl si 3. De asemenea. iar specificarea instantei de apel se face prin transmiterea suplimentara a indicatorului this intr-un mod transparent pentru utilizator. O entitate poate avea la un moment dat mai multe referinte. In momentul accesarii unei date membru. Metodele clasei CComplex au un statut special. De asemenea. referinta este un alias. adica un alt nume (pseudonim) pentru una si aceeasi entitate. void Re(float re) { _re = re. float _im. cunoscuta din declaratia clasei. iar variabilele de tip referinta se vor declara astfel: CComplex z. compilatorul se va folosi de adresa obiectului. 103 . cunoscuta in urma procesului de alocare a memoriei si de pozitia relativa a datei membru fata de inceputul zonei de memorie alocata obiectului. atunci apelul: compl. Tipurile referinta pot fi utilizate in orice constructie a limbajului care accepta tipuri fundamentale sau tipuri utilizator. in cazul de fata obiectul z.Tipareste(). CComplex& rZ = z. } void Im(float im) { _im = im.Re(3. Nota: In absenta oricarui specificator de acces. De exemplu. avand declaratia: CComplex z. Pentru clasa CComplex tipul referinta se va nota CComplex&. Exista o singura copie a fiecarei metode definita in cadrul clasei CComplex.}. rZ. Tipuri referinta Tipurile referinta sunt tipuri derivate introduse ca o alternativa la utilizarea tipurilor indicator.

} In programe. la declararea functiei se folosesc argumente cu valori prestabilite (implicite). nefiind necesare deferentieri pentru a accesa entitatea referita. 15). z. Restul operatiilor actioneaza asupra entitatii referite. CComplex& rZ1.ReIm(7. _im = 15 // _re = 0. Lipsa parametrilor actuali din apel va determina atribuirea valorilor prestabilite parametrilor formali: CComplex z. // “b !=0” deoarece adresele sunt egale Deoarece referinta este un alias. z.5. metoda ReIm poate fi apelata cu un singur argument. Din cele prezentate rezulta ca. De exemplu. ea se poate folosi si ca initializare. este o eroare. dar de regula nu in ambele locuri. // rI = i = i + 1 rI = j. rI++. antetul unei functii de forma: void CComplex::ReIm(float re = 0. declaratie inconsistenta // declaratie cu initializare Deci singura operatie ce actioneaza direct asupra referintei este initializarea referintei. Argumentele implicite se folosesc. Lipsa entitatii referite din cadrul declaratiei unei referinte determina aparitia unei erori: CComplex z.5). atunci cand vrem sa extindem prelucrarile oferite de o functie fara a fi a modifica codul sursa care apela vechea versiune a functiei. deorece argumentul prestabilit nu este ultimul in lista parametrilor formali. // _re = 7. // eroare. 104 .ReIm(0. float im). In asmenea situatii. float im = 0) ( _re = re. CComplex& rZ = z.variabilele de tip referinta pot fi folosite in orice situatie care necesita folosirea entitatii referite. atunci specificarea argumentelor implicite se poate face fie la declarare fie la definire (cazul exemplului prezentat). dar daca ele exista atunci trebuie sa fie plasati ultimele in lista parametrilor formali. inclusiv atribuirea unei variabile de tipul entitatii referite: int i = 10. int& rI = i. Chiar si adresa variabilei referinta este aceeasi cu a entitatii referite: CComplex z. // rI = j = 20 Cea mai buna utilizare a tipurilor referinta este folosirea acestora la transmiterea prin referinta a parametrilor de apel al functiilor. j = 20.5. _im = 0 Daca functia in cauza este o metoda si este definita in afara clasei. metoda ReIm prezentata in continuare este o functie cu doua argumente avand valori prestabilite: void CComplex::ReIm(float re = 0. _im = 0 // _re = 0. cu doua argumente sau cu nici un argument. Nu este obligatoriu ca toate argumentele functiei sa aiba valori prestabilite.5. de regula. _im = im. CComplex& rZ2 = z.ReIm(). int b = &z == &rZ. z. Argumente cu valori prestabilite Exista foarte multe situatii cand este necesar ca unei functii sa i se transmita un numar de parametrii actuali mai mic decat numarul parametrilor formali.

. ca parametru al unei functii sau ca entitate returnata de o functie. // eroare. Orice alt tip de manipulare a unui obiect.Construirea si distrugerea obiectelor Scop si vizibilitate Dupa cum se cunoaste. // corect int j = VAL2. poate fi folosit in operatii de atribuire. protected: typedef int WORD.. prototip de functie si fisier). Rezulta deci.. facand abstractie de ceilalti identificatori. .. // nume inaccesibil int k = CClasa::ANONIM1. 105 . iar prin vizibilitatea unui identificator se intelege zona de program in care identificatorul poate fi accesat in prezenta celorlalti identificatori. limbajul C++ introduce o noua categorie de scop. // Scop clasa.. private: enum {ANONIM1.. obiectele pot fi entitati globale. }.. VAL3}. este impus de metodele clasei a carei instanta este. Toate numele introduse in cadrul unei declaratii de clasa apartin scopului clasei respective. } Un membru al unei clase poate fi referit in cadrul scopului clasei chiar si inainte de punctul de declarare. prin scopul unui identificator se intelege zona din program in care identificatorul ar putea fi accesat. // nume inaccesibil In exemplul prezentat. inclusiv inhibarea operatiilor enumerate mai sus.. La utilizarea unui nume in afara clasei care l-a introdus se respecta restrictiile impuse de specificatorul de acces sub incidenta careia a fost definit numele in cadrul clasei si regulile de rezolutie a scopului: class CClasa { public: enum TIP_VAL {VAL1. VAL2. }. iar numele unui obiect respecta aceleasi reguli de scop si vizibilitate ca si numele variabilelor de tip fundamental. Un obiect. si anume clasa. } void Post(). ca scopul unui identificator include si vizibilitatea identificatorului respectiv. automatice sau alocate si eliberate in mod dinamic... ANONIM2}. Metoda Post(). void CClasa::Metoda() { int i =3. int i = CClasa::VAL1. Pe langa categoriile de scop din limbajul C (bloc. numele ANONIM1 si ANONIM2 nu vor putea fi utilizate nici macar in posibilele subclase ale clasei CClasa datorita restrictiilor de acces la membru existente. la fel ca orice variabila de tip fundamental. .. void Metoda() { . // Referire la scopul global . proprietate valabila de altfel doar pentru scopul de tip clasa: class CClasa { .. scop nespecificat WORD w1. La fel ca in cazul variabilelor de tip fundamental. pentru referirea entitatii globale din cadrul clasei se va folosi operatorul de rezolutie urmat de numele entitatii: int i =8 // Scop global . int j = ::i. In cazul unui nume atasat la doua entitati diferite dintre care una are scop global iar cealalta are scop clasa. functie.

De exemplu.5. va determina aparitia mesajului de eroare “Cannot convert ‘int’ to ‘CComplex”.. Deoarece in cazul clasei CComplex nu s-a definit nici un constructor. } . compilatorul va genera un constructor (numit constructor generat implicit) care in mod prestabilit nu executa nici o operatie.7). }. constructorul definit in continuare in clasa CComplex initializeaza cu 0 ambele date membru: class CComplex { public: CComplex() { // Functie constructor _re = _im = 0. 3. Constructorul este o functie fara tip avand acelasi nume ca si clasa in care este definita si nu returneaza nici o valoare. CComplex(float re.. atunci orice incercare de initializare la declararea unui obiect. De exemplu.Construirea obiectelor Sa consideram urmatoarea declaratie de instantiere a unui numar complex: CComplex z. CComplex z(1.7). // Initializeaza partea imaginara cu 1. float im) { // Primul constructor _re = re. } }. CComplex z(1. float _im. In momentul intalnirii declaratiei compilatoarele C++ vor genera codul necesar alocarii spatiului de memorie si pentru apelarea unei functii de construire a obiectului numita constructor. } . }. %f)\n". _im = im. Constructorul care va fi folosit in momentul instantierii va depinde de parametrii indicati. float im) { // Functie constructor _re = re. de exemplu. .7 Intr-o clasa se pot defini mai multe functii constructor.5. } CComplex(float im) { // Al doilea constructor _re = 0.5 // si partea imaginara cu 3. Daca se doreste ca in momentul instantierii clasei CComplex sa se poata specifica valori pentru initializarea datelor membru... _re. Rolul functiei constructor este efectuarea unor operatii de initializare si achizitionarea de resurse de catre instantele clasei. } void Tipareste() { // Metoda pentru afisarea valorilor printf("(%f. clasa CComplex prezentata in continuare are doi constructori: class CComplex { public: float _re. _im = im. -2. atunci functia constructor trebuie definita cu parametri: class CComplex { public: CComplex(float re. _im). 106 . Daca la definirea unei clase nu am declarat explicit un constructor. _im = im.. dar ele trebuie sa difere prin numarul si tipul parametrilor formali.

-4. de forma: CComplex() { } // Constructor care nu excuta nici o operatie • De regula.5. atunci la instantierea unui obiect al clasei respective este obligatoriu sa se specifice valorile de initializare.Primul constructor permite programatorului sa specifice valorile de initializare pentru ambele date membru ale clasei si va fi putea fi folosit la instantierea obiectelor sub forma: CComplex z(1. Definirea clasei CString. // Destructor int retlung(). In cazul in care vrem sa instantiem obiecte carora sa nu le specificam valoarea de initializare. atunci la eliberarea memoriei se poate folosi operatorul delete. Daca pentru a crea obiectul unei clase se foloseste operatorul new pentru a aloca memorie dinamica. in timp ce al doilea constructor permite programatorului sa specifice valoarea de initializare doar pentru componenta imaginara.h. In cazul clasei CComplex. Un destructor este o functie care are acelasi nume ca si clasa de care apartine. dar este precedat de caracterul tilda (~). Observatii: • Daca o clasa contine una sau mai multe functii constructor care asigura initializarea datelor membru. numita lung.5). numite destructori. Deoarece aveti nevoie de lungimea sirului. deoarece acestia ar trebui sa aiba putine instructiuni si sa nu contina instructiuni repetitive.cpp. este necesar ca in clasa sa se defineasca si o functie constructor fara parametri. decideti ca ea sa fie stocata ca o variabila membra. true}. // Defineste un tip boolean class CString { char *pSir. Distrugerea obiectelor Exista foarte multe situatii cand la crearea unor obiecte este necesara alocarea unor spatii de memorie.h este urmatoarea: // Interfata clasei CString enum Boolean {false. Destructorii nu au argumente. deoarece la crearea unui obiect al clasei respective nu se aloca memorie folosind operatorul new. terminate cu caracterul null. componenta reala fiind initializata direct in constructor si va fi putea fi folosit la instantierea obiectelor sub forma: CComplex z1(7. // Constructor pentru initializarea obiectului // cu pointerul spre sirul de caractere stocat in heap CString(int nrcar = 80). care dupa terminarea folosirii obiectelor respective trebuie sa fie eliberate pentru a le putea refolosi. sa zicem. // Returneaza lungimea sirului 107 . ceea ce doriti este un pointer (numit pSir) la un tablou de caractere (avand. // Constructor care rezerva in heap spatiul // necesar unui sir de cel mult 80 caractere CString(const CString&). salvata in fisierul ClassSir. Sa presupunem ca doriti sa creati un tip de date cu numele CString. int lung. folosita pana in prezent pentru exemplificare. constructorii se pot defini in interiorul clasei. // Constructor de copiere ~CString(). public: CString(char *s). nu este necesara o functie de distrugere. • Un constructor nu poate avea un parametru formal de tipul clasei din care fac parte. cel mult 80 de caractere) si posibilitatea de a stoca in acest tablou siruri in stil C.55). dar sa utilizeze o abordare orientata pe obiecte. folosind operatorul new sau orice alta functie de alocare dinamica a memoriei admisa de limabjul C++. iar definirea functiilor membru intr-un fisier cu extensia . Destructorii sunt necesari numai daca la crearea obiectelor s-a alocat spatiu in memoria heap. Este indicat ca definirea clasei sa fie salvata intr-un fisier cu extensia . care sa dispuna de proprietatile sirurilor terminate cu caracterul null din C. Altfel spus. Pentru a elibera memoria alocata de constructor la crearea obiectelor se folosesc functiile de distrugere asociate clasei respective.

#ifndef _STDIO_h #include <stdio. // lungimea sireului pSir = new char[lung + 1]. strcpy(pSir. se truncheaza sirul si se returneaza // false. } int CString::citeste() { // Metoda . 108 . lung).h sunt sau nu incluse. pSir = new char[lung + 1]. *pSir = '\0'. // aloca spatiu in heap strcpy(pSir. printf("\n"). }.citeste un sir de la intrarea standard si-l stocheaza // in zona heap alocata obiectului curent. // Afiseaza sirul de caractere int citeste().pSir).void afiseaza(). In caz ca aceste // fisiere nu sunt incluse.cpp: // Definirea functiilor membru ale clasei CString . Definirea functiilor membru (metodele) ale clasei CString au fost salvate in fisierul ClassSir. altfel returneaza true. // Transfera sirul referit de s in zona // rezervata obiectului curent.h si stdio.la trunchierea sirului citit // 1 . Daca zona nu este // suficienta. char t[255].afiseaza sirul referit de pSir printf(pSir).lung. } inline void CString::afiseaza() { // Metoda . // transfera sirul in heap } CString::CString(int dim) { // Constructor . // Citeste un sir de caractere Boolean atribsir(CString *s). pSir = new char[lung + 1]. s. } inline CString::~CString() { // Destructor .la terminare normala. // Variabila temporara pentru citire if(gets(t) == 0) return 0.h" CString::CString(char *s) { // Constructor .Initializeaza obiectul cu pointerul spre copia // in heap a sirului referit de s lung = strlen(s). t. atunci ele se vor include. Se verifica daca // fisierele antet string.h> #define _STRING_H #endif #include "ClassSir.Rezerva spatiu in heap pentru siruri de lungime dim lung = dim. // S-a intalnit sfarsitul de fisier strncpy(pSir. } inline int CString::retlung() { // Metoda . // Stocheaza sirul nul } CString::CString(const CString& s) { // Constructor de copiere lung = s. s). Returneaza: // 0 .la sfarsit de fisier // -1 .h> #define _STDIO_H #endif #ifndef _STRING_H #include <string.Elibereaza zona heap a lui pSir delete pSir.returneaza lungimea sirului return lung.

Operandul operatorului new in forma lui cea mai simpla. Destructorul este definit astfel: inline CString::~CString() { // Destructor . este numele unui tip (predefinit sau definit de utilizator). in caz contrar se returneaza true. return true.cpp contine implementarea clasei CString.} Boolean CString::atribsir(CString *s) { // Transfera sirul referit de s in zona rezervata sirului obiectului // curent. va stoca intregul 100 in zona respectiva. urmatoarele instructiuni: int *pInt. Acesta este un operator unar si are aceeasi prioritate ca si ceilalti operatori unari. atunci trebuie sa folosim urmatorul format: new tip[exp_int] unde: exp_int este o expresie de tip intreg. alocarea dinamica a zonelor de memorie in zona heap se poate realiza folosind functii de biblioteca. iar expresie este o expresie a carei valoare initializeaza zona de memorie alocata. Prin aceasta constructie se rezerva. stocheaza in aceasta zona valoarea 100 si atribuie lui pInt adresa de inceput a zonei respective.h contine de fapt interfata clasei CString. pInt = new int. Expresia: *pInt = 100. 109 . else return 1. asigura alocarea in memoria heap a unei zone de memorie in care se pot pastra date de tip int si stocheaza adresa de inceput a acestei zone in pointerul pInt. new tip(expresie) Zonele de memorie alocate cu operatorul new pot fi initializate utilizand o expresie de forma: unde: tip este numele unui tip (fundamental sau definit de utilizator). aloca spatiu in heap pentru un intreg. In clasa CString este necesara prezenta unui destructor deoarece se aloca spatiu in memoria heap la crearea unui obiect al clasei. Limbajul C++ permite alocari in zona heap si prin intermediul operatorului new. s->pSir. } *(pSir + lung) = '\0'. if(strlen(s->pSir) > lung) return false. iar fisierul ClassSir. lung). instructiunea pInt = new int(100). Opeartorul new are ca valoare adresa de inceput a zonei de memorie alocata in heap sau zero (pointerul null) in cazul in care nu poate face alocarea. // Plaseaza terminatorul de sir // Sirul citit a fost trunchiat Fisierul ClassSir. } Operatorul new In limbajul C. folosind functia malloc astfel: pInt = (int *)malloc(sizeof(int)).Elibereaza zona heap ocupata de pSir delete pSir. if(strlen(t) > lung) return -1. o zona de memorie de exp_int * sizeof(tip) bytes. in heap. strncpy(pSir. De exemplu. daca nu este suficient spatiu. se truncheaza sirul referit // de s si se returneaza false. spatiu care trebuie eliberat la distrugerea obiectului. Daca vrem sa folosim operatorul new pentru a aloca zona de memorie in heap pentru tablouri. Aceeasi alocare se obtine in limbajul C. De exemplu. cum ar fi malloc si alloc.

De fapt. Din acest motiv. la apelul unei functii obisnuite nu se substituie apelul functiei prin corpul ei. atunci zona de memorie heap alocata cu ajutorul lui new se elibereaza folosind constructia: delete p. indiferent de tipul acestora. Obiectele puse pe stiva sunt construite conform sarcinilor trasate de un constructor special numit constructor de copiere (in engleza. strcpy(pSir. La distrugerea celui de-al doilea obiect s-ar fi incercat eliberarea unei resurse inexistente situatie care. situatie care nu poate fi acceptata. 110 . initializarea argumentelor de tipul CString s-ar fi realizat prin copierea datei membru pSir. Pentru a elibera memoria heap alocata tablourilor cu ajutorul lui new. } In absenta constructorului de copiere. lasand celalalt obiect intr-o stare inconsistenta. Exemple de functii inline se pot vedea in codul clasei CString prezentat anterior. ar fi util ca apelul functiilor sa se realizeze la fel ca apelul de macrocomenzi. in momentul distrugerii unuia dintre cele doua obiecte prin apelul destructorului calsei. unde: exp_int defineste numarul elementelor tabloului (prezenta lui nu este obligatorie). unde CClasa este numele clasei care-l defineste. Limbajul C++ vede operatia de transmitere a argumentelor ca o operatie de construire de obiecte. Constructorul de copiere La apelul oricarei functii compilatorul genereaza codul care va asigura copierea argumentelor transmise functiei in stiva programului. ar fi existat doua obiecte care exploateaza una si aceeasi resursa. si p = new tip. implica diferite operatii suplimentare. are ca efect blocarea sistemului. adica prin expandare (inlocuirea apelului prin corpul functiei). numit si apel cu revenire. compilatorul va genera un constructor de copiere implicit (default copy constructor). Acest lucru este posibil daca antetul functiei este precedat de cuvantul cheie inline. Chiar mai grav.lung. ci se realizeaza un salt la zona de memorie in care se pastreaza corpul functiei respective. se foloseste o constructie de forma: delete[exp_int] p. operatiile implicate de apel si revenire pot fi mai costisitoare decat cele implicate de functia insasi. in astfel de situatii. Dupa terminarea executiei functiei se revine in punctul imediat urmator apelului. a obiectului de apel si nu a entitatii referite de acesta.pSir). s. Un apel de aceasta forma.Operatorul delete O zona de memorie alocata folosind operatorul new se elibereaza prin operatorul delete. O astfel de functie se numeste functie inline. Functia inline Dupa cum se cunoaste. Aceasta inseamna ca datele membru ale unui obiect sunt tratate tot ca niste obiecte. pSir = new char[lung + 1]. Atunci cand functia este foarte simpla. Constructorul de copiere este un constructor obisnuit cu un singur argument de tipul const CClasa&. In limbajul C copierea consta in multiplicarea configuratiei de biti (operatie numita si copiere la nivel de biti. resursa partajata ar fi fost eliberata. Daca nu este definit nici un constructor de copiere. are 2-3 instructiuni. de obicei. care va copia obiectele la nivel de membru (memberwise copy). Daca p este un pointer spre tip: tip *p. copy constructor). Pentru clasa CString definitia constructorului de copiere este urmatoarea: CString::CString(const CString& s) { lung = s. in engleza bitwise copy) a datelor transmise.

• afisare siruri de caractere. sir4. printf("sir4:\n"). } 111 .Tot constructorul de copiere este folosit si la copierea valorii returnate de o functie sau la initializarea unor obiecte prin intermediul unor instante de acelasi tip. De fapt. Prezenta tipului referinta ca tip de apel al constructorului de copiere este necesara pentru a evita situatia de recursivitate infinita creata de existenta unui constructor de forma: CString::CString(CString s). // Instantiere prin copiere // Afisarea obiectelor create mai sus printf("sir1:\n").afiseaza(). printf("sir3:\n"). incercarea de a defini un astfel de constructor de copiere va fi semnalata ca o eroare la compilare. sir3.cpp" void main() { // Instantieri de obiecte de tip CString CString sir1("C++ este o extensie a limbajului C standard").afiseaza(). dar argumentele suplimentare trebuie sa fie cu valoare implicita. // Citirea de siruri de la intrarea standard printf("Introduceti mai multe siruri de la tastatura\n").").\n\ . permitand astfel copierea automata a obiectelor la apelul functiilor si la revenirea din functii. sir1.citeste()) sir3. Acest amanunt este util la declararea obiectelor cand se urmareste o initializare selectiva in functie de anumiti indicatori. #ifndef _STDIO_H #include <stdio.prin abstractizarea datelor. Exemplu de utilizare a clasei CString: Sa se scrie un program care realizeaza urmatoarele operatii asupra obiectelor de tip CString: • initializare. sir3. CString sir2("C++ suporta stilul de programare:\n\ . if(sir3. // Instantiere la sir nul CString sir4 = sir1. printf("sir2:\n"). printf("Pentru terminare folositi Ctrl + Z\n").afiseaza(). CString sir3.afiseaza().atribsir(&sir2) == false) printf("Trunchiere la atrbuire\n"). • atribuire de obiecte de tip CString. // Atribuiri de siruri if(sir3.afiseaza().h> #define _STDIO_H #endif #ifndef _STDLIB_H #include <stdlib. Un constructor poate promova mai multe argumente.afiseaza().afiseaza(). • copiere de obiecte de tip CString.orientata spre obiecte. while(sir3. • citire de siruri de la intrarea standard.h> #define _STDLIB_H #endif #include "ClassSir.atribsir(&sir1) == false) printf("Trunchiere la atrbuire\n"). sir2. sir3.

ele pot fi clasificate in urmatoarele grupe: • Obiecte globale – sunt create in segmentul de date al aplicatiei si au ca scop fisierul sau programul. • Obiecte temporare – prin obiect temporar se intelege orice obiect care nu are asociat un nume. dinamice si temporare In functie de modul de declarare a obiectelor. o functie obisnuita poate fi 112 . In acest fel.. Distrugerea obiectelor globale are loc la terminarea programului dupa iesirea din functia main. in ordinea inversa a construirii lor la inceputul programului. De exemplu: void Funct() { // . Diferenta dintre acesti operatori si functiile standard de gestiune a memoriei consta in apelarea constructorilor si a destructorilor in cazul operatorilor si ignorarea lor in cazul functiilor standard. In mod obisnuit se spune ca datele protejate ale unui tip abstract sunt incapsulate in clasa care defineste tipul respectiv.Tipareste().Obiecte globale. in functie de specifcatiile de linkeditare.. } Functii friend Dupa cum am vazut una dintre proprietatile de baza a tipurilor abstracte este protectia datelor membru ale tipului respectiv.. inainte de intrarea in functia main. // obiectul temporar nu mai exista // .. atunci ea poate fi apelata numai prin intermediul unei functii membru a clasei respective. Obiectele globale sunt construite la lansarea programului. • Obiecte dinamice – sunt create in memoria heap. // instantierea unui obiect temporar CString(“Programare orientata pe obiecte”).nume_functie_membru(…) sau pointer_nume_clasa->nume_functie_membru() In cazul functiilor obisnuite nu sunt admise astfel de apeluri. desi exista functii descrise in C++ care pot fi folosite pentru a prelucra instantieri ale unei clase. in anumite situatii este considerat ca fiind rigid. Construirea si distrugerea obiectelor se realizeaza prin apelarea constructorilor si respectiv a destructorilor definiti pentru clasele respective. Mai mult. Protectia datelor se realizeaza prin faptul ca ele pot fi accesate numai de catre functiile membru ale clasei respective. Tipurile abstracte se definesc cu ajutorul claselor. iar pentru gestionarea lor se folosesc operatorii new si delete. • Obiecte automatice – sunt create in stiva aplicatiei ai au ca scop functia sau blocul de cod in care sunt definite. Elementele protejate constituie asa numita implementare a tipului abstract. Construirea si distrugerea obiectelor automatice are loc in functie de fluxul programului si implica apelul constructorilor si destructorilor definiti in clasa respectiva. cu toate ca asigura o buna protectie a elementelor membru protejate ale unei clase (elemente protejate prin private sau protected). ele nu se pot utiliza simplu deoarece nu sunt functii membru ale clasei respective si deci nu au acces la datele membru. Din acest motiv. care este numit obiectul curent al apelului. automatice. toate datele prelucrate de functie fie sunt transferate prin parametrii fie sunt globale. orice referire ulterioara in afara expresiei este imposibila. iar constructia lui se realizeaza fie prin apelul explicit al constructorilor clasei fie in mod automat de catre compilator. daca o functie membru este protejata. Acest mod de lucru. o functie membru se poate apela prin unul din urmatoarele formate: nume_obiect. Astfel. o functie membru se apeleaza totdeauna in dependenta cu un obiect. De asemenea. Existenta unui obiect temporar este limitata la expresia care determina instantierea.

folosita la prelucrarea obiectelor unei clase numai daca ea se modifica in asa fel incat sa devina functie membru. Totusi, pentru ca anumite functii care nu sunt functii membru sa poata accesa elementele protejate ale unui clase s-a facut un compromis prin introducerea functiilor friend (prieten). Ele trebuie precizate ca atare in definitia clasei, prin prezenta numai a prototipurilor lor in definitia clasei si sunt precedate de cuvantul cheie friend. De exemplu, sa consideram urmatoroarea definitie a clasei CComplex:
class CComplex { float re; float im; public: CComplex(float x=0, float y=0) { // Constructor re = x; im = y; } // Functie friend pentru calculul modulului unui numar complex friend float modul(CComplex *z); // Functie membru pentru afisarea unui obiect complex void Tipareste(); }; // Definirea functiei membru si si a functiei prieten // Afisarea unui numar complex void CComplex::Tipareste() { printf("(%f, %f)", re, im); } // Determinarea modulului unui numar complex float modul(CComplex *z) { return sqrt(z->re * z->re + z->im * z->im); }

Dupa cum se poate constata, functia modul, prieten al clasei CComplex, are aceeasi definitie ca o orice functie din C si ea va putea fi apelata ca o functie obisnuita. De exemplu, sa consideram urmatoarele declaratii:
CComplex y(5, 5); float f;

atunci executarea instructiunilor:
f = modul(&y); printf(“%f”, f);

va afisa:
7.071068

Datorita faptului ca functiile friend nu dispun de pointerul implicit this, o functie friend are nevoie de un parametru in plus fata de o functie membru care va avea acelasi efect. Functia friend poate fi o functie obisnuita (ca in cazul functiei modul de ma sus) sau o functie membru a unei alte clase.

Supraincarcarea si redefinirea operatorilor
Limbajul C++ mosteneste integral setul de operatori ai limbajului C la care se adauga operatorul de rezolutie (::), operatorii de acces la un membru (.* si ->*) si operatorii de gestiune a memoriei dinamice (new si delete). O facilitate puternica a limbajului C++ o reprezinta posibilitatea supraincarcarii partiale a setului de operatori pentru un tip anume, astfel incat aplicarea operatorilor redefiniti asupra instantelor sau a tipului respectiv sa rezulte in actiuni definite de programator ca fiind specifice tipului respectiv. Limbajul C++ vede operatorii ca functii obisnuite, iar definirea unui operator este foarte asemanatoare definirii unei functii. 113

Intre supraincarcarea unui operator si redefinirea operatorului respectiv pentru un tip anume exista unele deosebiri: • Supraincarcarea unui operator pentru un tip implica posibilitatea folosirii operatorului respectiv in conjunctie cu tipul respectiv sau cu instante ale acestuia si invers; posibilitatea utilizarii operatorului in conjunctie cu un tip sau o instanta a acestuia implica existenta supraincarcarii operatorului pentru tipul respectiv. • Redefinirea unui operator pentru un tip este o supraincarcare si implica posibilitatea existentei unei versiuni definite in mod explicit de catre programator a operatorului pentru tipul in cauza. O parte dintre operatorii limbajului C++, si anume: . .* :: ?: sizeof si operatorii de preprocesare # si ##, nu pot fi redefiniti. Ceilalti operatori pot fi redefiniti pentru orice tip utilizator, iar aceste redefinitii pot fi la randul lor supraincarcate de catre alte definitii ale aceluiasi operator, in cadrul aceleasi clase. La definirea unei clase exista automat un subset de operatori gata supraincarcati de compilator pentru tipul respectiv, cum ar fi: ., ->, &, ::, .*, sizeof, =, new si delete. Acesti operatori pot fi folositi in conjunctie cu orice tip sau obiect fara a fi necesare redefiniri din partea programatorului. De asemenea tipurilor fundamentale ale limbajului C++ au gata supraincarcati majoritatea operatorilor existenti. Daca este posibila redefinirea unui operator gata supraincarcat pentru un tip utilizator si exista o definitie data de programator, atunci vechea acceptiune a operatorului, acordata implicit de compilator, va fi inlocuita de noua definitie. La redefinirea unui operator trebuie sa se tina cont de urmatoarele restrictii: • Nu este permisa extinderea setului de operatori ai limbajului C++ prin adaugarea de noi simboluri (caractere sau grupuri de caractere). • La redefinirea unui operator nu se schimba precedenta operatorului. • La redefinirea unui operator asociativitatea operatorului ramane neschimbata. • Nu este permisa redefinirea operatorilor pentru tipurile fundamentale ale limbajului C++. Redefinirea operatorilor pentru un tip poate fi realizata, in functie de operator, cu ajutorul metodelor, a functiilor globale sau a metodelor statice, iar in unele cazuri pot exista mai multe versiuni diferite ale unui operator pentru acelasi tip ca functii de tipuri diferite. Toti operatorii redefiniti pentru o clasa, cu exceptia operatorului de atribuire (=), sunt mosteniti de clasele derivate. Modul de redefinire a unui operator este asemanator unei definitii de functii obisnuite, cu deosebirea ca in locul numelui functiei se foloseste cuvantul cheie operator urmat de simbolul operatorului dorit. Deci sintaxa redefinirii unui operator este:
tip_rezultat operator simbol_operator(lista_argumente) { corp_functie }

Pentru a ilustra modul de redefinire a unui operator vom supraincarca operatorul + pentru clasa CComplex:
class CComplex { float re; float im; public: CComplex(float x=0, float y=0) { // Constructor re = x; im = y; } // Functie membru pentru adunarea obiectelor de tip complex CComplex addComplex(CComplex& z); // Functie prieten pentru scaderea obiectelor de tip complex friend CComplex subComplex(CComplex& z1, CComplex& z2); // Supraincarcarea operatorului plus (+) CComplex operator+(CComplex& z); // Functie membru pentru afisarea unui obiect complex

114

void Tipareste(); }; // Definirea functiilor membru si prieten CComplex CComplex::addComplex(CComplex& z) { // Returneaza suma dintre obiectul complex curent si cel referit de z CComplex tmp; // Obiect local de tip complex tmp.re = re + z.re; tmp.im = im + z.im; return tmp; } CComplex CComplex::operator+(CComplex& z) { // Supraincarca operatorul + pentru obiectele de tip CComplex // Returneaza un obiect complex care reprezinta suma dintre // obiectul complex curent si cel referit de z CComplex tmp; tmp.re = re + z.re; tmp.im = im + z.im; return tmp; } CComplex subComplex(CComplex& z1, CComplex& z2) { // Returneaza diferenta dintre obiectele referite de z1 si z2 CComplex tmp; // Obiect local de tip complex tmp.re = z1.re - z2.re; tmp.im = z1.im - z2.im; return tmp; } void CComplex::Tipareste() { // Afisarea unui numar complex printf("(%f, %f)", re, im); }

Presupunand existenta urmatoarei declaratii:
CComplex a(10,20), b(15,25), c;

atunci instructiunile:
c = a.addComplex(b); c.Tipareste();

vor afisa:
(25.0000 45.0000)

instructiunile:
c = subComplex(a, b); c.Tipareste();

vor afisa:
(-5.0000 –5.0000)

iar instructiunile:
c = a + b; c.Tipareste();

va afisa:
(25.0000 45.0000)

115

116 . ESEC}. exista doua tipuri de mostenire: mostenire simpla obtinuta prin specializarea unui singur tip si mostenire multipla obtinuta prin specializarea a cel putin doua tipuri. int numar_partitii. int capacitate. class disk { protected: stare_prot_scriere ind_prot. iar cea de a doua clasa derivata. stare_op ProtejeazaScriere(). stare_op CitestePista(int drive. stare_op ScriePista((int drive. nr_sect. void *buffer). nr_sect. class hard1 { stare_prot_scriere ind_prot. putem defini o alta clasa D care sa preia toate caracteristicile primeia. int numar_sectoare. Analizand definitiile celor doua clase se poate constata ca o parte din datele si functiilor membru ale clasei disk se regasesc si in clasa hard1. int numar_sectoare. stare_op CitestePista(int drive. stare_op ScriePista((int drive. mai eficace si mai usor de inteles. Antetul folosit la definirea clasei hard2: class hard2:public disk are rolul de a indica compilatorului urmatoarele: • clasa hard2 va derivata din clasa disk. int numar_sectoare. void *buffer).Mostenirea In functie de numarul tipurilor specializate la definirea unui tip. enum stare_prot_scriere {PROTEJAT. Este evident ca procedeul folosit pentru definirea clasei hard2 este mult mai elegant. public: stare_op Formatare(). int capacitate. stare_op ProtejatScriere(). proprii doar acesteia din urma. Prima clasa se va numi clasa de baza. int sector_start. }. void *buffer). Pentru a folosi avantajele mostenirii proprietatilor clasei disk se poate defini o clasa hard2 astfel: class hard2:public disk { int numar_partitii. Clase derivate Pentru a intelege mai usor mecanismul mostenirii vom incepe prin a prezenta un exemplu. public: stare_op ParcareDisc(). }. int sector_start. Sa presupunem ca avem definite doua clase disk si hard1 astfel: enum stare_op {REUSIT. int sector_start. public: stare_op ParcareDisc(). stare_op Formatare(). int sector_start. void *buffer). }. In cateva cuvinte mecanismul de mostenire se poate exprima astfel: avand o clasa oarecare B. la care sa pot adauga altele noi. NEPROTEJAT}. int numar_sectoare.

In acest fel. ea putand fi dezvoltata adaugand clase noi. ele putand fi refolosite exact in maniera in care au fost definite. Daca la definirea clasei derivate se foloseste modificatorul de protectie private. Ca modificatori de protectie se pot folosi modificatorii public si private. fiecare clasa de baza. avanatjul ca nu trebuie rescrise nici macar functiile membru ale clasei de baza. Clasele de baza din lista_clase_baza se separa prin caracterul virgula (. in lista_clase_baza. Clasa cl1 este o clasa derivata a clasei cl. unde lista_clase_baza indica clasele de baza pentru clasa derivata care se defineste. Din acest motiv. o clasa derivata se defineste astfel: class nume_clasa_derivata: lista_clase_baza { // elemente specifice clasei derivate }. in plus. In acest caz. 117 . modificatorul de protectie este private la definirea unei clase derivate. numele clasei precedat eventual de un modificator de protectie. Clasa cl1 este o clasa derivata a clasei cl. modificatorul public in loc de private. Aceasta • lista contine pentru. Clasele cl si cls sunt clase de baza a clasei cl1. Clasa cl1 este o clasa derivata a claselor cl si cls. In schimb. insa daca este necesar ele pot fi redefinite pentru a asigura o cu totul alta functionalitate. • toti membrii de tip protected ai clasei disk vor putea fi utilizati ca fiind de tip protected in cadrul clasei hard2. o ierarhie de clase nu este o ierarhie finala. Apare. Tabelul prezentat in continuare indica accesul in clasa derivata a elementelor mostenite in functie de protectia fiecarui element mostenit si de modificatorul de protectie utilizat in lista_clase_baza. In mod prestabilit. Accesul in clasa de baza private protected public private protected public Modificatorul de protectie din lista_clase_baza private private private public public public Accesul in clasa derivata a elementului mostenit inaccesibil privat privat inaccesibil protejat public Din acest tabel rezulta ca o clasa derivata nu are acces la elementele clasei de baza care au protectia private. Este evident ca eficacitatea nu inseamna doar faptul ca in declaratia clasei derivate nu mai apar informatiile mostenite (ele fiind automat luate in considerare de catre compilator). care deriva din clasele terminale. se scrie lista claselor de baza pentru clasa respectiva folosind ca separator caracterul doua puncte (:). clasa derivata are acces la elementele clasei de baza care au protectia public sau protected. Acest lucru va putea fi posibil numai daca la definirea claselor derivate se foloseste. public cls{…}. modificatorul private se va folosi in lista_clase_baza numai daca respectiva clasa derivata nu va constitui o clasa de baza pentru o alta derivare. In acest scop dupa numele clasei derivate. class cl1:private cl{…}. Exemple: class cl1:cl{…}. atunci elementele protejate prin protected sau public devin protejate prin protectie private. De regula.toti membrii de tip public ai clasei disk vor fi mosteniti (deci vor putea fi folositi) ca public de catre clasa hard2. Dupa cum s-a putu constata din exemplul prezentat. relatia de derivare intre doua clase se exprima la definirea clasei derivate. Clasa cl este singura clasa de baza a clasei cl1. Clasa cl este singura clasa de baza a clasei cl1. class cl1:cl. elementele protejate prin protected si public se mostenesc in clasa derivata prin aceeasi protectie.).

Definitia celor doua clase ar putea arata astfel: enum Boolean {false. Relatia dintre constructorii si destructorii clasei derivate si ai claselor de baza Constructorii si destructorii sunt functii membru care nu se mostenesc. o parte din valori se folosesc pentru initializarea datelor membru specifice ale clasei derivate. Constructorii vor fi apelati intotdeauna in ordinea in care sunt scrise clasele in lista_clase_baza. o lista de expresii care definesc valorile initiale pentru datele membru ale clasei Cls_i.. destructorii sunt apelati in ordine inversa. n din antetul constructorului Cls contine. Cls_2.. Cls(.. La distrugerea unui obiect al clasei derivate.. adica pentru initializrea tuturor datelor membru ale unui obiect de tip Cls. true}. Lista care defineste apelurile constructorilor claselor de baza nu este prezenta in prototipurile constructorilor claselor derivate ci numai in antetele acestora... class B { protected: 118 . in paranteza. ..). Cls_n(.). Expresia Cls_i(. • Redeclararea datelor membru si supraincarcarea functiilor membru ale claselor de baza in clasa derivata.) in antetul constructorului clasei Cls este arbitrara... Transferul valorilor datelor membru ale claselor de baza se defineste prin intermediul antetului constructorului clasei derivate..Cls_2(. // protoptipul constructorului . Expresia Cls_i(…) pentru i=1. …. .).. Constructorul clasei derivate contine parametrii pentru toate valorile ce se folosesc la initializarea obiectelor.. Cls_n: class Cls:public Cls_1.. Primii sunt apelati constructorii clasei de baza urmati de constructorul clasei derivate. Primul va fi apelat destructorul clasei derivate si apoi cei a calselor de baza in ordine inversa fata de modul in care au fost apelati la instantierea obiectului respectiv. iar restul pentru initializarea datelor membru ale claselor de baza. Daca o clasa Cls_i nu are constructor.) este un apel explicit al unui constructor al clasei Cls_i. • Conversiile obiectelor claselor derivate si ai claselor de baza. Constructorul Cls are un antet de forma: Cls::Cls(.. Ordinea de apelare a constructorilor claselor de baza este ordinea in care apar clasele respective in lista_clase_baza din definitia clasei derivate. precum si ai pointerilor catre astfel de obiecte..) in antetul constructorului Cls. Ordinea in care se scriu expresiile Cls_i(. Fie o clasa Cls derivata din clasele de baza Cls_1. 2.):Cls_1(. public Cls_n { .. expresia respectiva nu va fi prezenta in antetul lui Cls daca datele membru ale clasei Cls_i se initializeaza cu valori implicite sau clasa Cls_i nu are date membru... In cazul unui obiect al unei clase derivate. La instantierea unui obiect al clasei derivate se apeleaza atat constructorii clasei derivate cat si cei ai claselor de baza. }... La instantierea unui obiect se transmit valori tuturor parametrilor constructorilor pentru initializarea datelor membru. …...Relatia dintre clasa derivata si clasele ei de baza ridica cateva probleme si anume: • Relatia dintre constructorii si destructorii clasei derivate si ai claselor de baza.) Constructorul Cls are o lista de parametrii completa... atunci pentru clasa respectiva nu va fi prezenta o expresie de forma Cls_i(.. public Cls_2. O conditie esentiala pentru a se putea apela constructorii claselor de baza prin intermediul clasei derivate este ca acestia nu trebuie sa aiba protectie de tip private. De asemenea.. Exemple: Sa presupunem ca avem o clasa A derivata din clasa B si ambele au nevoie de un constructor.

a. if(0 <= p && p <= lx && 0 <= q && q <= ly) ecran = true.ecran=false Datele abs si ord pot fi initializate chiar in antetul constructorului. } Nota: O clasa derivata trebuie sa aiba cel putin un constructor in cazul in care cel putin una din calsele de baza au un constructor care nu este implicit sau nu are toti parametrii impliciti.int ly=357):B(p.. y..q).x=0. a. Copierea bit cu bit nu este suficienta si atunci este necesara definirea unor constructori de copiere. copiaza datele membri ale obiectului sursa (care se copiaza) in datele membru corespunzatoare ale obiectului destinatie (care se creaza). atunci cand sunt aplicati.ord=357.abs=758. b. In lipsa constructorilor de copiere. else ecran = false. y = yy. } . // Constructor Pentru a instantia obiecte ale clasei A se pot folosi instructiuni de forma: A a.int lx=758.y=0. ord.}.ecran=true // b.abs(lx). double x. Acest mod de copiere a fost denumita copiere bit cu bit. La definirea constructorilor de copiere a clasei derivate trebuie sa se aiba in vedere doua reguli principale: • Apelul constructorului de copiere al clasei derivate nu conduce automat la apelul constructorilor de copiere al claselor de baza. double yy = 0) { x = xx. int ly=357):B(p. // a. a. a. A b(100. Fie A o clasa derivata din B o clasa de baza a ei.. public: A(double p=0. int abs. } . compilatorul genereaza in mod automat constrtuctori de copiere impliciti care.abs=758.ord(ly) { // Constructorul clasei A if(0 <= p && p <= lx && 0 <= q && q <= ly) ecran = true. else ecran = false. b. public: B(double xx = 0. Tot ceea ce s-a prezentat despre relatiile dintre constructorii clasei derivate si ai claselor de baza a avut in vedere constructorii obisnuiti. adica constructorii care nu sunt pentru copiere.x=100. b.q) { // Constructorul clasei A abs = lx. asa cum se arata in continuare: A(double p=0. Sa presupunem ca A are un constructor de copiere de antet: A(const A& a) 119 .y=400.ord=357. }. ord = ly..400).b. double q=0. Constructorii de copiere sunt necesari mai ales cand clasele au ca date membru pointeri spre zone alocate dinamic in memoria heap. int lx=758.double q=0. class A:public B { protected: Boolean ecran.

Conversia dintr-un tip abstract intr-un alt tip abstract se poate realiza prin ambele metode. pb = &b. Conceptul de mostenire impune reguli de conversie si pentru pointeri si referinte la obiecte ale claselor derivate si de baza. unde B este o clasa de baza a clasei A. atunci constructorul de copiere al clasei derivate A trebuie sa contina apelul explicit al unui constructor al clasei B. Conversiile de acest gen sunt utile cand se creeaza si prelucreaza obiecte de tip colectie. Ambele metode se pot aplica implicit cat si explicit. In schimb atribuirile: pa = &b.• In acest caz. sunt eronate.. 120 . Compilatorul C++ permite atribuiri de felul celor de mai jos: pa = &a. In mod analog. In continuare ne vom referi la conversiile care se pot realiza tinand seama de conceptul de derivare. B *pb. conversiile dintr-un tip predefinit intr-un tip abstract se realizeaza cu ajutorul constructorilor. colectia fiind un set de obiecte de diferite tipuri abstracte derivate dintr-un tip abstract de baza. deoarece obiectele clasei A sunt obiecte specializate ale clasei B. Daca clasa de baza B are constructori dar nici unul nu este implicit sau cu toti parametri impliciti. Apelul constructorului de copiere al clasei A conduce automat la apelul constructorului implicit al clasei B (cel generat de compilator sau cel definit de programator daca exista un astfel de constructor) sau. iar conversia inversa cu ajutorul supraincarcarii operatorului cast. A a. clasa de baza B. Aceasta. unde a este un obiect al clasei A in prealabil declarat.. un pointer sau o referinta la un obiect al unei clase derivate se poate atribui la un pointer sau o referinta la un obiect al unei clase de baza a clasei derivate respective. O atribuirea de forma: pb = &a. este corecta. La o instantiere de forma: A b=a. Ele pot fi acceptate de compiltor daca se utilizeaza expresii cast care sa asigure conversia explicita de pointeri: pa = (A *)&b. Atribuirile de acest fel se realizeaza pe baza supraincarcarii implicite a operatorului adresa (& unar). care permite ca acesta sa se aplice la o data sau obiecte de acelasi tip. Deci constructorul de copiere al clasei A va avea antetul: A(const A& a):B(. atribuirea: pb = pa. Fie A o clasa pentru care B este o clasa de baza. In general. Un obiect care este o instantiere a clasei A se converteste in mod implicit intr-un obiect al clasei B. este acceptata de compilator la fel ca si atribuirea obiectului a la obiectul b: b = a. Fie declaratiile: A *pa. se apeleaza constructorul de copiere al clasei A cu antetul indicat mai sus. in caz contrar. Atribuirile de acest gen se realizeaza prin conversii implicite corespunzatoare. fie ca nu are nici un constructor definit de programator. fie ca are un constructor definit de programator care nu are parametrii sau are numai parametri impliciti. la apelul constructorului clasei B care are toti parametrii impliciti. Conversia inversa nu se poate realiza fara a fi definita printr-un constructor sau prin supraincarcarea operatorului cast.) Conversiile obiectelor claselor derivate si ai claselor de baza In principiu. B b. pa = (A *)pb. pb = pb.

double by=0):B(bx.Redefinirea datelor membru ale unei clase de baza intr-o clasa derivata O data membru a unei clase de baza se poate redefini ca data membru a unei clase derivate. double bx=0. Majoritatea functiilor membru si friend are ca efect faptul ca supraincarcarea operatorilor pentru clasa de baza este valabila si pentru o clasa derivata a clasei de baza respective. class A:public B { protected: double x. // y este data membru a clasei derivate } void afis() const. x este data membru a clasei A.f(.. Astfel. y = yy.. daca B este o clasa de baza pentru clasa A si f este o functie membru a clasei B.. double dy=0. } . }. functia afis poate fi definita astfel: void A::afis() const { printf(“x_baza= %g\ty_baza= %g\n”. atunci f este si o functie membru a clasei A..by) { x = dx.. Totusi. se foloseste operatorul de rezolutie. x. B b: atunci apelurile b.. B::x. . cat si cu obiecte ale clasei A. La supraincarcarea pentru clasa derivata se poate pastra nu numai numele 121 . // x este data membru a clasei derivate y = dy. B::y). In astfel de situatii. double yy=0) { x = xx. Astfel daca exista declaratiile: A a. Functia membri afis se apeleaza pentru obiecte ale clasei derivate.f(. functiile friend ale clasei de baza sunt functii friend si pentru clasa derivata. }. Ea afiseaza datele membru ale clasei derivate.. Tinand cont de aceste lucruri. Astfel: B::x este data membru a clasei B. double y. } Supraincarcare functiilor membru ale unei clase de baza intr-o clasa derivata Functiile membru ale claselor de baza sunt mostenite de clasele derivate.. De exemplu: class B { protected: double x. public: B(double xx=0. nu intotdeauna o functie mostenita de la o clasa corespunde necesitatilor prelucrarilor obiectelor clasei derivate. public: A(double dx=0. In mod analog. printf(“x_derivat= %g\ty_derivat= &g\n”. double y. functia respectiva poate fi supraincarcata pentru clasa derivata.) si a. precum si datele membru mostenite de la clasa B. Deci functia f poate fi utilizata atat cu obiecte ale clasei B. Pentru a face distinctie intre datele membru ale clasei derivate si cele ale clasei de baza. y).) sunt apeluri corecte ale functiei f.

}.) // se apeleaza functia membru f a clasei de baza B pentru // obiectul curent care este instantiere a clasei A derivata din B.by) { xx = dx.. O problema care apare in acest caz este aceea cand in corpul functiei membru f.. class A { protected: double xx.. . y = yy.. x. public: B(double xx=0.. double bx=0... } void afis() const { printf(“bx = %g\tby= %g\n”. y). Functiile supraincarcate in acest fel se selecteaza nu numai dupa numarul si tipul parametrilor ci si dupa obiectul pentru care sunt apelate. la apelul: b. este supraincarcata pentru clasa derivata A.. }. supraincarcata pentru clasa A. Functia afis aplicata la o instantiere a clasei B va afisa valorile lui x si y. yy = dy.functiei.... Exemplu: class B { protected: double x.) atunci acesta se realizeaza pentru obiectul curent si conduce la un apel recursiv al functiei f.) { . reluand exemplul de mai sus.. }.. void A::afis() const { B::afis()...) { . double dy=0. Cu alte cuvinte..) Deci corpul functiei s se va defini astfel: A::f(. ci chiar si numarul si tipul parametrilor.... }. Pentru a apela functia membru f a clasei de baza B pentru obiectul curent care este o instantiere a clasei derivate A.. // Afiseaza x si y 122 . } void afis() const.. double y.f(. Functia afis aplicata la o instantiere a clasei A va afisa atat valorile lui x si y ale obiectului curent cat si valorile lui xx si yy ale aceluiasi obiect. . a clasei de baza B. folosim operatorul de rezolutie: B::f(. } .. // aici este necesar apelarea functiei membru f a clasei B . Daca in corpul functiei f utilizam un apel obisnuit: f(. iar la apelul: a.) se apeleaza functia membru f a clasei de baza B... public: A(double dx=0.f(.) se apeleaza functia membru f a clasei A daca f este dupraincarcata pentru clasa A si functia membru f a clasei de baza B daca f nu este supraincarcata pentru clasa A. double by=0):B(bx. B::f(. Functia membru afis. De aceea la definirea functiei afis pentru clasa A vom apela functia membru a clasei B. se doreste apelul functiei membru f a clasei B: A::f(. double yy.. double yy=0) { x = xx..

.yy=2 atunci instructiunea: b. yy). care mentionat intotdeauna ca ultim argument al unei functii va valida apelurile de functie cu un numar oarecare de argumente.afis().} printf(“dx= %g\tdy= %g\n”. // se apeleaza functia membru afis a clasei B va afisa: bx= 1 by= 2 // se apeleaza functia membru afis a clasei A iar instructiunea: a. In continuare se va prezenta doar notiunile generale despre polimorfism.xx=1. pentru a suporta functiile polimorfe parametric s-a introdus un nou tip de argument. 4). xx.afis(). fiind interpretata cu int printf(void). folosind constanta simbolica _cplusplus. Specificarea cuvantului void in cadrul listei parametrilor formali ai unei functii. de tipuri oarecare. // b. ad-hoc si de mostenire. a.. a. va determina validarea doar a apelurilor neparametrizate pentru functia respectiva.). Ordinea de incarcare a parametrilor actuali in stiva este inversa ordinii de mentionare a acestora in cadrul formulei de apel (primul argument pus in stiva va fi primul in lista). A doua forma a semnaturii este mai sigura deoarece impune tipul primului argument ca fiind const char*. adica functia in cauza (apel de functie tip Pascal8). Polimorfismul parametric este posibil in limbajele C si C++ datorita modului de incarcare a stivei cu parametrii de apel al functiilor: stiva este incarcata si descarcata de catre entitatea apelanta (apel de functie tip C) nu de catre entitatea apelata. Exemple de apelare a functiei afis: Considerand urmatoarele declaratii: B b(1. 8 Asemanare cu numele limbajului Pascal este absolut intamplatoare. Semnatura functiei printf in limbajul C++ are forma int printf(. 3. b. a. O semnatura de forma int printf() are cu totul alt inteles in limbajul C++ fata de limbajul C.. A a(1.. Pentru a oferi declaratii valide de functii cu un numar oarecare de argumente.) – ellipsis. In limbajul C++. va afisa: bx= 3 dx= 1 by= 4 dy= 2 Poliformismul In limbajul C++ polimorfismul se manifesta sub trei forme: parametric. indiferent de natura compilatorului folosit (C sa C++).y=2 // a. 2). Ca atare... se va compila conditionat. in sensul posibilitatii apelului functiei cu un numar oarecare de argumente de tipuri diferite. 2. argumentul orice tip (. 123 .x=3. O functie care nu specifica nici un argument de apel – nici chiar cuvantul cheie void – in cadrul semnaturii sale este exclusa de catre compilatoarele C in procesul de verificare a tipurilor si va putea fi apelata cu un numar arbitrar de parametri. Un exemplu clasic de polimorfism parametric este dat de functiile de iesire formatata a informatieie: printf. Fiecare dintre aceste functii este o entitate polimorfa parametric.y=4..x=1. semnatura functiei printf in limbajul C poate avea forma int printf(). Semnatura functiei printf in limbajul C++ are un numar oarecare de argumente.) sau int printf(const char*. fprintf si sprintf.

You're Reading a Free Preview

Download
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->