You are on page 1of 12

8.

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

8.1

Tipuri de dat simple i tipuri de dat structurate

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

8.2

Tablouri unidimensionale

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

Programarea calculatoarelor i limbaje de programare I

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

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

Programarea calculatoarelor i limbaje de programare I

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

Accesarea componentelor individuale


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

Programarea calculatoarelor i limbaje de programare I

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

Indicii din afara limitelor


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

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

Programarea calculatoarelor i limbaje de programare I

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

Programarea calculatoarelor i limbaje de programare I

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

Tabourile cu valori tip char


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

Programarea calculatoarelor i limbaje de programare I

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

8.3

Transmiterea tablourilor ca parametri de funcii

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

Programarea calculatoarelor i limbaje de programare I

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

Programarea calculatoarelor i limbaje de programare I

cout << << << << <<

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

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

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

Programarea calculatoarelor i limbaje de programare I

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

8.4

Tablouri multidimensionale

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

10

Programarea calculatoarelor i limbaje de programare I

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

Programarea calculatoarelor i limbaje de programare I

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

12

You might also like