Sunteți pe pagina 1din 13

5.

Tipuri de date structurate


Au fost prezentate n capitolul 3 i s-au utilizat n exemplele prezentate tipurile de date predefinite (sau standard). Pe lng aceste tipuri limbajul C, ca i alte limbaje de nivel nalt, ofer i alte mecanisme pentru reprezentarea valorilor unor obiecte, de alt natur dect cele direct reprezentabile cu ajutorul tipurilor (predefinite) standard. Principalele tipuri de date predefinite, simple , sunt tipurile : ntreg, real i caracter. Pentru reprezentarea valorilor logice s-a recurs la codificarea ntreag a acestora conform conveniei : fals - codificat prin valoarea ntreag 0 i adevrat codificat printr-o valoare ntreag diferit de 0. O alt codificare ntreag a valorilor unui tip , o reprezint tipul enumerare . Pe lng aceste tipuri simple, limbajul C permite definirea unor tipuri structurate (derivate), pe baza celor fundamentale, cum ar fi: tablouri, iruri de caractere, structuri, uniuni. De asemenea mai exist alte dou tipuri : funcia i pointerul .

5.1 Tablouri
Un tablou este o structur omogen, format dintr-un numr finit de componente, ordonate, de acelai tip, denumit tip de baz al tabloului; elementele tabloului sunt memorate ntr-o zon continu i compact de memorie, adic elementele sunt memorate succesiv. Componentele tabloului sunt acelai tip de baz ce poate fi un tip simplu (ntreg, real, caracter) sau un tip structurat (derivat ). Sintaxa de baz pentru declararea unui tablou este : tip_de_baz nume_tablou [dim] ={val_iniial , val_iniial , } ; unde dim este o expresei constant i reprezint numrul de elemente; De obicei dimensiunea se specific printr-un identificator (constant simbolic), introdus cu ajutorul directivei #define (a preprocesorului). Aceast modalitate uureaz munca programatorului n cazul n care se modific dimensiunea tabloului, ntruct se face o singur modificare n program, i anume a directivei #define. Numele utilizate pentru a identifica elementele vectorului sunt denumite indici. Valorile indicilor trebuie s fie valori ntregi i valorile lor ncep de la 0. Deci, dac un tablou a fost declarat de dimensiune n, elementele sale vor fi referite prin indici n intervalul 0 n-1. Valoarea iniial, val_iniial, este o expresie constant de acelai tip cu tipul se baz i reprezint valoarea iniial asociat elementului respectiv din tablou; valorile iniiale sunt asociate n ordine elementelor tabloului. Pot fi specificate mai puine valori, n aceast list, dect numrul de elemente ale tabloului, dar nu mai multe . Dac sunt specificate mai puine valori, vor fi iniializate cu aceste valori, n ordine, attea elemente cte valori exist n list, ncepnd cu primul element. Elementele rmase vor fi iniializate cu 0 . Dimensiunea unui tablou poate fi omis n urmtoarele situaii : - se declar un tablou extern i atunci pentru acesta s-a rezervat spaiu n alt fiier surs ; - se declar un tablou ca parametru formal al unei funcii i n acest caz declaraia este echivalent cu cea a unui pointer ; - declaraia conine o list de iniializare i n aceast situaie dimensiunea tabloului este determinat, implicit, de numrul de expresii constante din lista de iniializare ; 5

Tabloul este o structur cu acces direct deoarece timpul de acces la o component nu depinde de valoarea indicelui (adic de poziia componentei n cadru tabloului). Exemple de declaraii : 1) int tab_i [20] ; definete un tablou denumit tab_i, cu 20 de elemente de tip ntreg; acestui tablou i vor fi alocai 20* sizeof (int ) = 20*2 = 40 octei. Elementele sale vor fi referite astfel: tab_i[0] , tab_i[1] , , tab_i[19] . 2) float tab _f [10] ; definete tabloul tab_f cu 10 elemente de tip float; acestui tablou i vor fi alocai: 10*sizeof (float) = 10*4 =40 octei 3) double tab_d [30] ; un tablou de tip double, cu 30 elemente, cruia i vor fi alocai : 30 *sizeof (double) =30*8 =240 octei 4) char tab_c [10] ; un tablou de caractere, cu 10 elemente, ce va ocupa 10*sizeof =10*1 =10 octei 5) int tab_ii [ ] = {1 , 2 , 3 , 4 , 5 }; definete un tablou de numere ntregi , de 5 elemente , iniializate cu valorile tab_ii [0] = 1 , tab_ii [1] = 2 ;, tab_ii [4] = 5 ; declaraia este echivalent cu : int tab [5] = {1 ,2 , 3 , 4 ,5 }; Exemple de programe utiliznd tablouri : 1) Generarea numerelor lui Fibonacci; se vor genera cel mult 100 de numere din acest ir, ce se construiete cu relaia: fib (n) = fib (n-1) + fib (n-2) ; pentru n >= 2 ; cu fib (0) = 0; fib (1) = 1; /* fib.c genereaza cel mult 100 numere Fibonacci */ # include <stdio.h> main ( ) { int fib[100] , i , n ; fib [0] = 0 ; fib [1] = 1 ; /* initializarea primelor doua numere */ printf (Specificati dimensiunea sirului Fibonacci (<= 100) : ) ; scanf (%d , &n ) ; for (i =2 ; i < n ; i++) fib [i] = fib [i-1] +fib [i-2] ; for (i = 0 ; i < n ; i++) printf ( %i / , fib [i] ) ; printf("\n"); } 2) Determinarea numerelor prime ntr-un interval [2 , n] i depunerea lor ntrun tablou. /* prime . c - numere prime in intervalul [2 , n] */ #include <stdio.h> #include <math.h> main ( ) 6

{ int i, n, p, este_prim, prim [1000], index_prim; prim [0] = 2 ; prim [1] = 3 ; printf (Se determina numerele prime in intervalul [2 , n], n = ); scanf (%d , &n) ; index_prim = 1; for (p = 5 ; p <= n ; p = p + 2) { este_prim = 1; for (i = 1 ; este_prim && prim[i] * prim[i] <= p ; i++) este_prim = p % prim [i]; if (este_prim) { index_prim ++; prim [ index_prim ] = p ; } } for (i = 0 ; i <= index_prim ; i++) printf ( %i / , prim [i] ); printf ( \n ) ; } La acest algoritm, pentru un numr, p, se determin dac este prim sau nu prin ncercarea de a gsi un divizor al su printre numerele prime determinate pn la el. 3) Aceeai problem, dar utiliznd alt algoritm: sita lui Eratostene. Algoritmul const din eliminri succesive ale multiplilor numerelor prime, n ordine cresctoare. Se asociaz intervalului un tablou boolean iniializat cu valorile adevrat (diferit de zero) pentru toate componentele; indicii tabloului reprezint numerele pentru care se determin dac sunt prime sau nu (ncepnd, bineneles, de la 2). Numrul prim, pentru care se elimin multipli si este primul indice, mai mare dect, cruia i corespunde n tabloul asociat valoarea adevrat (diferit de 0). Eliminarea const , de fapt, n modificarea valorii asociate indicelui din adevrat n fals. Se contorizeaz numerele prime i cele eliminate. Algoritmul i-a sfrit cnd contorul devine egal cu n-1. /* prime2.c sita lui Eratostene */ # include <stdio.h > #define MAXIM 100 main ( ) { int prim [ MAXIM ], n , index , contor , multiplu ; printf ( Determina numerele prime in intervalul 2-n ,n = ) ; scanf ( %d , &n ) ; for (index = 2, index <n+1 ; ++index ) prim [index] = 1 ; {primele 2 elemente nu sunt folosite } contor = 0 ; index = 1; do { 7

index ++; if (prim [index] ) { printf ( %d / , index ); for (multiplu = 1 ; multiplu <= n / index ;++ multiplu ) if ( prim[ index * multiplu ] ) { prim [index * multiplu ] = 0 ; contor ++; } } } while (contor <n-1) ; } 4) Ordonarea unui ir utiliznd algoritmul Shell (Donald Shell): se accelereaz ordonarea unui ir, prin parcurgerea lui cu un pas variabil, pentru a reduce dezordinea irului . Fiecare etap realizeaz inversiuni (n sensul ordonrii), ntre elementele aflate la o anumit distan (pas). Pasul iniial este egal cu jumtate din numrul de elemente; la fiecare parcurgere cu un anumit pas, se iniializeaz o variabil logic, care e modificat doar dac apar inversiuni, cel puin ntre dou elemente . Dac la sfritul unei etape variabila logic a fost modificat, deci au existat inversiuni, se reia parcurgerea cu acelai pas, pn cnd nu se mai realizeaz inversiuni, deci variabila logic nu mai este modificat. Astfel dup fiecare etap numerele mai mici se apropie de nceputul irului i cele mari de sfritul su. Dup fiecare parcurgere, cnd nu au mai aprut inversiuni se reia parcurgerea cu un pas njumtit. Algoritmul i-a sfrit cu etapa n care pasul, prin njumtiri succesive, devine 1, ceea ce asigur i convergena algoritmului. /* ordonare .c ordonarea unui sir cu metoda Shell */ #include < stdio.h > #define N_MAX 100 main ( ) { double sir [N_MAX], a; int n, i, j , inv ; printf ( numrul de elemente ale irului (: <= 100) : ) ; scanf ( %d , &n ) ; for ( i = 0 ; i < n ; ++i ) { printf ( sir [ % d ] = , i+1 ); scanf ( %d, &sir [ i ] ) ; } pas = n; while (pas >1 ) { pas = pas / 2 ; do { 8

inv = 0 ; /* fals */ for (i = 0 ; i < n pas ; ++i) { j = i+pas ; if ( sir [i] > sir [j] ) /* sau if (sir[i] > sir[i+pas] )*/ { a: = sir [j]; sir [j] = sir [i]; sir[i] = a; inv = 1; } } } while (inv); } for ( i = 0 ; i < n ; ++i ) printf ( inv [ %d ] = %f / , i+1 , sir [i] ) ; }

/*

programul determina maximul dintr-un vector cu n componente si pozitia acestuia #include<stdio.h> #define DIM 100 main() { double vector[DIM],axim; int n, i, pozitie; printf("INTRODUCETI NUMARUL DE ELEMENTE DIN SIR :"); scanf("%d", &n); for(i=0; i<n; i++) { printf("v(%d)=",i+1); scanf("%lf",&vector[i]); } maxim=vector[1]; pozitie=0; for(i=0; i<n; i++) if(maxim<vector[i]) { maxim=vector[i]; pozitie=i; } printf("VALOAREA MAXIMA ESTE v(%d)=%g :",pozitie+1,maxim); }

*/

5.1.1. Tablouri multidimensionale Pot fi definite tablouri nu numai de o singur dimensiune ci cu oricte dimensiuni. Un astfel de tablou multidimensional poate fi considerat un tablou ale crui elemente sunt tablouri. Declaraia este asemntoare cu cea a tabloului unidimensional; n acest caz parantezele ptrate sunt asociate de la stnga la dreapta. De exemplu o matrice cu 4 linii i 5 coloane cu valori de tip real se declar astfel : double MAT [4] [5] ; Referirea la un element al matricei din linia l, coloana c se face sub forma: MAT [l] [ c] deci matricea este memorat pe linii . Pentru tablouri multidimensionale, deci cu mai muli indici, primul indice este cel care variaz cel mai lent, iar ultimul indice variaz cel mai repede. Exemple : 1) nmulirea a dou matrice A i B are ca rezultat matricea C (prodmat.c): A(la , ca) * B(lb , cb) = C(la , cb ) , cu condiia ca = lb. Termenul general al matricei C este: c(i, j ) = a (i, k ) * b( k , j )
k =1 lb

cu: 1<= i<= la, 1 <= j <= cb /* Calculeaza produsul a doua matrice, daca se pot inmulti (adica numarul de coloane de la prima este egal cu numarul de linii de la a 2-a) #include <stdio.h> #define DIM 10 void main() { double a[DIM][DIM], b[DIM][DIM], c[DIM][DIM]; double x; int la, ca, lb, cb, i, j, k; printf ("Dimensiunea matricei A (linii,coloane):"); scanf ("%d%d", &la, &ca); printf ("Dimensiunea matricei B (linii,coloane):"); scanf ("%d%d", &lb, &cb); if (ca != lb) printf ("Nu se pot inmulti\n"); else { printf ("Matricea A:"); for ( i = 0 ; i < la ; i++) for ( j = 0; j < ca ;j++) { printf (" A(%d,%d)=",i+1, j+1); scanf ("%lf", &a[i][j]); } printf("Matricea B:"); for ( i = 0 ; i < lb ; i++) for ( j = 0 ; j < cb ; j++) { printf (" B(%d,%d)=", i+1, j+1); scanf ("%lf", &b[i][j]); 10

*/

} for ( i = 0 ; i <la ; i++) for ( j = 0 ; j < cb ; j++) { c[i][j] = 0; for ( k = 0 ; k < ca ; k++) c[i][j] += a[i][k] * b[k][j]; printf (" C(%d,%d)=%lf\n", i+1, j+1, c[i][j]); } } } Tablourile multidimensionale pot fi iniializate ntr-o manier asemntoare cu cele cu o singur dimensiune, cu precizarea c elementele, dup cum am artat, trebuie specificate pe linii . Pentru separarea liniilor ntre ele se utilizeaz acolade. De exemplu : int M [4][5] = { {1, 2, 3, 4, 5 }, { 2, 5, 17, 82, 16 }, { 3, 9, 29, 89, 243 }, { 4, 8, 16, 32, 64 }, }; innd cont de precizarea anterioar matricea poate fi iniializat i astfel : int M [4][5]={1, 2, 3, 4, 5, 2, 5, 17, 82, 16, 3, 9, 27, 89, 24, 4, 8, 16, 32, 64} Dac sunt prezente mai puine valori, restul vor fi iniializate cu 0 ca n exemplul: int M[4][5] = { {1 , 2, 3}, {2 , 4, 8 }, {3 ,6 ,12 }, }; Se vor iniializa primele trei valori din fiecare linie cu valorile specificate, ultimele dou fiind 0. De asemenea ultima linie va fi iniializat cu 0. Utilizarea acoladelor incluse va fora iniializarea dorit. Fr aceste acolade incluse, dac se utilizeaz forma precedent, se vor iniializa primele 9 elemente: deci prima linie (cinci elemente) i patru elemente din linia a 2-a, restul fiind 0. /* determinarea maximului de pe fiecare linie a unei matrice */ #include<stdio.h> #include<conio.h> main() { int a[10][10], M[10]; int n, i, j, *matrice; clrscr(); printf("Introduceti dimensiunea matricei :"); scanf("%d", &n); for (i=0; i<n; i++) for (j=0; j<n; j++) { printf("a(%d,%d)=", i+1, j+1); 11

scanf("%d", &a[i][j]); } matrice = &a[0][0]; for(i=0; i<n; i++) { M[i]=*(matrice+i*10 + i); for(j=0; j<n; j++) if (*(matrice+i*10+j)>M[i]) M[i]=*(matrice+i*10+j); } printf("\n Elementele maxime sunt :\n"); for (i=0;i<n;i++) printf("linia %i = %d\n",i+1,M[i]); }

5.2. iruri de caractere


Un ir de caractere este o serie de unul sau mai multe caractere, de exemplu : Un sir de caractere Ghilimelele nu fac parte din ir, ci ele sunt utilizate pentru a delimita irul de caractere, n mod asemntor cu apostrofurile care marcheaz un caracter ('a'). n C nu exist un tip de date special pentru irurile de caractere (cum este n Pascal) . irurile de caractere sunt memorate n tablouri de tip caracter (char), ntruct un tablou este o secven ordonat de elemente de acelai tip. Pentru a marca sfritul unui ir de caractere, de lungime variabil, se utilizeaz caracterul NULL, adic pe ultima poziie se afl caracterul \0 (nu este codul ASCII al cifrei 0, ci valoarea numeric 0 i este un caracter, cod, netipribil) . Pentru un ir care conine n caractere, compilatorul va rezerva n+1 locaii de memorie: n primele n sunt depuse caracterele din ir, iar ultima conine terminatorul (caracterul null \0). Exemplu de iniializare i tiprire a unui tablou de caractere: # include < stdio.h > main ( ) { char salut [ ] = { S, a , l , u , t , ! } ; int i ; for ( i = 0 ; i < 6 ; ++i ) printf (%c , salut [ i ] ) ; printf ( \ n ) ; /* in continuare este transformat tabloul de caractere in sir */ salut[5] = \0; printf(%s!\n, salut); } Acest exemplu definete i tiprete irul de caractere Salut!. Pentru tiprirea tabloului caracter cu caracter se utilizeaz, n funcia printf( ), specificatorul %c (tiprirea unui caracter). Pentru a simplifica citirea / tiprirea 12

irurilor de caractere se poate utiliza specificatorul %s , ca n exemplul urmtor : /* nume .c citeste si tipareste numele utilizatorului */ #include < stdio.h > #include <string.h> # define INTREB Cum te cheama ? # define SALUT Salut ! main ( ) { char nume [50] ; printf (INTREB); scanf (%s, nume); printf (SALUT, %s, nume ); printf (Numele tau are %d litere, dar s-au rezervat %d locatii de memorie\n, strlen (nume), sizeof nume); printf (Propozitia \%s\ , are %d litere , INTREB, strlen (INTREB)) ; printf (si ocupa %d octeti de memorie \n , sizeof INTREB) } La citirea unui ir de caractere, funcia scanf ( ) va pune caracterul NULL (\0), de sfrit de ir de caractere, cnd citete irul; citirea i-a sfrit la ntlnirea primului spaiu alb (blanc, tab sau linie nou). Pentru a citi numele i prenumele sau, n general o fraz, nu numai un singur cuvnt, limbajul C ofer o alt funcie de citire iruri i anume gets (s), care citete pn la sfrit de linie, care nu e memorat n buffer, ci se pune caracterul de sfrit \0; iar pentru tiprire exist puts (s). n acest exemplu se utilizeaz funcia strlen( ), care furnizeaz lungimea unui ir de caractere (numrul efectiv de caractere din ir). Aceast funcie se afl n fiierul header <string.h>. Spre deosebire de aceast funcie, operatorul sizeof furnizeaz numrul de octei rezervai pentru un tip sau o variabil. Dac operatorul are drept operand un tip acesta se pune ntre paranteze: sizeof (int), iar dac este o variabil, ca n exemplul precedent, nu se pune ntre paranteze . Observaie: irul a nu este acelai cu caracterul a. O prim diferen, esenial, este c a este un tip de baz, char, iar a este un tip derivat (structurat), un tablou de tip char. O a doua diferen este c irul a const din dou caractere: caracterul a i caracterul \0. Exemple: /* cnvbazas.c - programul realizeaza conversia unui numar intreg intr-o alta baza */ #include <stdio.h> #define LUNG_MAX 32 main () { long int numar, baza, ind1, ind2; char cifra [ LUNG_MAX ], car_aux; printf ("numarul de convertit = "); scanf ("%ld", &numar); printf ("conversie in baza :"); scanf ("%ld%", &baza); 13

if ( numar < 0 ) { printf ("-"); numar = - numar; } ind1 = 0; do { if ( numar % baza < 10 ) cifra [ ind1 ] = '0' + numar % baza; else cifra [ ind1 ] = 'A' + numar % baza - 10; numar /= baza; ind1 ++; } while ( numar > 0 ); for (ind2 = 0; ind2 <= ind1/2 ; ind2 ++) { car_aux = cifra [ ind2 ]; cifra [ ind2 ] = cifra [ ind1 ind2 1 ]; cifra [ ind1 ind2 1 ] = car_aux; } cifra [ind1] = \0; printf ("%s", cifra); } /* Programul concateaza doua siruri de caractere, utilizand o functie, si determina lungimea sirului obtinut */ #include <stdio.h> void main() { void concat ( char rezultat[], char s1[], char s2[]); int lungime ( char sir[]); char sir1[]={"Test pentru"}; char sir2[]={" concatenare siruri de caractere"}; char s[60]; concat (s, sir1, sir2); printf ("Sirul rezultat este:\n%s\n", s); printf ("Lungimea sirului este: %d\n", lungime(s)); } void concat (char rezultat[], char s1[], char s2[]) { int i, j; for ( i = 0 ; s1[i] != '\0' ; i++ ) rezultat[i] = s1[i]; for ( j = 0 ; s2[j] != '\0' ; j++) rezultat[i+j] = s2[j]; 14

rezultat[i+j] = '\0'; } int lungime (char sir[]) { int contor; for ( contor = 0 ; sir[contor] != '\0' ; contor++); return (contor); } Specificatorul de conversie %s este utilizat pentru a tipri, cu funcia printf(), un ir de caractere ce se termin cu caracterul NULL. Implicit, specificatorul %s presupune c tabloul de caractere se termin cu acest caracter. Dac se utilizeaz %s la citirea unui ir de caractere, cu funcia scanf(), atunci citirea va avea loc pn la ntlnirea spaiului alb (blanc, tab sau linie nou); dac se introduc mai multe caractere dect este dimensiunea tabloului, n care se depun, se pot ntmpla lucruri neprevzute; din nefericire funcia scanf() nu posed un mecanism de determinare a dimensiunii tabloului pentru a suspenda citirea caracterelor n exces; ea citete caractere pn la ntlnirea spaiului alb i le depune n memorie la locaii succesive. Pentru a limita numrul (maxim) de caractere citite se poate utiliza un modificator, reprezentnd numrul maxim de caractere admise, n faa specificatorului %s, astfel: scanf (%60s, sir); care va citi cel mult 60 de caractere, caracterele n exces fiind ignorate. Testarea egalitii a dou iruri Nu se poate testa direct, dac dou iruri sunt egale, printr-o declaraie de forma : if (sir 1== sir 2 ) deoarece operatorul de egalitate poate fi aplicat doar unor variabile de tip simplu (ntregi, reale sau caracter), nu asupra unor tipuri mai sofisticate (cum ar fi tablourile sau structurile). Pentru a determina dac dou iruri de caractere sunt egale, trebuie deci comparate explicit cele dou iruri, caracter cu caracter . Dac se ajunge simultan la sfritul ambelor iruri de caractere, i toate caracterele pn n acel punct sunt identice, atunci cele dou iruri sunt egale, altfel nu sunt . Deoarece ne intereseaz s determinm dac cele dou iruri de caractere sunt egale sau nu, vom poziiona o variabil pe 1 (adevrat) dac sunt identice i pe 0 (fals) dac nu sunt identice: int i = 0 , egale = 0 ; /* presupunem c nu sunt egale */ while ( sir1[i] == sir2[i] && sir1[i] != \0 && sir2[i] != \0 ) ++i ; if ( sir1[i] == \0 && sir2[i] == \0 ) egale =1 ; sau int i = 0 , egale = 1 ; /* presupunem c sunt egale */ while ( egale ) { egale = (sir1[i] == sir2[i] && sir1[i] != \0 && sir2[i] != \0 ); ++i; } if ( egale ) 15

egale = (sir1[i] == \0 && sir2[i] == \0 ); Pentru a declara o constant ir de caractere, mai lung, care nu ncape pe o linie, se va utiliza caracterul de continuare de linie \; altfel compilatorul va genera un mesaj de eroare. n cazul continurii irului de caractere pe o linie nou trebuie ca aceast continuare s se fac de la nceputul liniei urmtoare, altfel spaiile libere (blancurile) de la nceputul liniei pn la continuarea irului vor fi incluse i, deci, memorate n acest ir . Exemplu : char litere [ ] = { abcdefghijklmnoprstuvwxyz \ ABCDEFGHIJKLMNOPRSTUVWXYZ }; Pentru a simplifica o astfel de declaraie, a unor iruri lungi, se poate mpri irul iniial n dou sau mai multe subiruri adiacente astfel : char litere [ ] ={ abcdefghijklmnoprstuvwxyz ABCDEFGHIJKLMNOPRSTUVWXYZ}; invcar.c - inversarea tipului de caractere dintr-un text: de la litera mare la litera mica si invers */ #include <stdio.h> #include <string.h> #include <ctype.h> #define LIMITA 30 #define DIMENS 81 main() { void transforma ( char linie[] ); char text [LIMITA] [DIMENS]; int nr_linie =0 , i; printf ("Introduceti cel mult %d linii pentru transformare\n", LIMITA); printf ("Introducerea se termina cu ENTER, pe o linie noua\n"); while ( nr_linie < LIMITA && gets(text[nr_linie]) != NULL && strcmp ( text[nr_linie], "") !=0 ) nr_linie ++; for ( i = 0 ; i < nr_linie ; i++) { transforma ( text[i] ); puts ( text[i] ); } } void transforma ( char *ptr_car ) { while ( *ptr_car != '\0' ) { if ( isupper ( *ptr_car ) ) *ptr_car = tolower ( *ptr_car ); else if ( islower ( *ptr_car ) ) *ptr_car = toupper ( *ptr_car ); ptr_car++; } } 16 /*

17

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