Scrigroup - Documente si articole

Username / Parola inexistente      

Home Documente Upload Resurse Alte limbi doc  
AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml


Siruri si pointeri

c



+ Font mai mare | - Font mai mic



Siruri si pointeri

Un sir (se mai spune si vector) este o secventa de date ce contine articole de acelasi tip, indexate si memorate contiguu. De obicei, sirurile se folosesc pentru reprezentarea unui numar mare de valori omogene (in capitolul urmator vom studia sirurile de caractere). O declaratie obisnuita de sir aloca memorie incepand de la adresa de baza. Numele sirului este un pointer constant la aceasta adresa de baza.
O alta notiune pe care o vom explica este transmiterea sirurilor ca argumente in functii.

Siruri uni-dimensionale


Exemplu:

Presupunem ca vrem sa lucram cu trei intregi:
int a1, a2, a3;
Totusi, daca avem mai multe numere este anevoios sa declaram numerele in acest fel. Solutia consta in utilizarea unui sir (de lungime trei) de intregi.
int a[3];
Elementele acestui sir vor fi accesate astfel: a[0] a[1] a[2]
Deci numarul 3 reprezinta lungimea sirului, elementele sale fiind indexate incepand cu numarul 0. Aceasta este o trasatura a limbajului C.
O declaratie de sir uni-dimensional este un tip urmat de un identificator urmat la randul lui de paranteze patrate ce cuprind o expresie integrala constanta. Valoarea expresiei constante, care trebuie sa fie pozitiva, se numeste lungimea sirului si ea specifica numarul de elemente ale sirului. Pentru memorarea elementelor intr-un sir, compilatorul rezerva un spatiu de memorie corespunzator, pornind de la adresa de baza. Dimensiunea spatiului de memorie este egala cu numarul de elemente ale sirului inmultit cu numarul de octeti necesari memorarii unui element al sirului.

Exemplu:

Vom scrie un mic program care initializeaza un sir, tipareste valorile sale si insumeaza elementele sirului.

#include
#define N 5

void main()

Sa vedem ce se intampla in memorie ?
Nume Tip Valoare Adresa
-------- ----- ------ ------
| a[4] | int | 23 | 3A38:0FFE |
-------- ----- ------ ------
-------- ----- ------ ------
| a[3] | int | 16 | 3A38:0FFC |
-------- ----- ------ ------
-------- ----- ------ ------
| a[2] | int | 11 | 3A38:0FFA |
-------- ----- ------ ------
-------- ----- ------ ----
| a[1] | int | 8 | 3A38:0FF8 |
-------- ----- ------ ----
-------- ----- ------ ----
| a[0] | int | 7 | 3A38:0FF6 |
-------- ----- ------ ----
Deci vectorul (sirul) 'a' se va memora incepand de la adresa 3A38:0FF6. Deci 'a = &a[0]'.
Se recomanda definirea lungimii unui sir ca o constanta simbolica (folosind directiva '#define').

Initializarea sirurilor

Sirurile pot apartine claselor de memorare 'auto', 'extern', 'static' sau 'constant', dar nu pot fi 'register'. Ca si variabilele simple,
sirurile pot fi initializate in timpul declararii lor. Initializarea sirurilor se face folosind acolade si virgule.

Exemplu:

float x[7] = ;
Asta inseamna, echivalent:
x[0] = -1.1;
x[1] = 0.2;
. . . . .
x[6] = 7.7;
Daca lista de valori de initializare este mai mica decat numarul de elemente ale sirului, atunci elementele ramase se initializeaza cu 0. Daca un sir declarat 'extern' sau 'static' nu este initializat, atunci sistemul initializeaza toate elementele cu 0. Vectorii declarati constanti sau automatic (cei impliciti) sunt initializati cu valori 'garbage' (adica cu valorile existente in momentul executiei in memorie la acele adrese). C traditional permite doar initializarea vectorilor declarati 'extern' sau 'static', pe cand ANSI C permite initializarea sirurilor automate si constante.
Daca un sir este declarat fara precizarea lungimii si initializat cu o serie de valori, atunci lungimea sa se considera implicit numarul
de valori initiale.

Exemplu:

Declaratiile
int a[] = ; si int a[4] = ;
sunt echivalente.

Indexul unui sir

Presupunem ca avem declaratia
int i, a[lungime];
Pentru accesarea unui element din sir, vom scrie 'a[i]', sau mai general 'a[expresie]', unde 'expresie' este o expresie integrala. 'i' de mai sus se numeste index al sirului 'a' si poate avea valori intre 0 si 'lungime-1'. Daca indexul depaseste acest domeniu, compilatorul va da eroare in timpul executiei programului.

Exemplu:

#include
#include

void main()

printf('nn');

Acest program citeste de la tastatura sau dintr-un fisier (folosind indirectarea) un sir de caractere si numara in vectorul 'litera' fiecare aparitie (in parte) a literelor.

Relatia dintre vectori si pointeri

Am vazut ca numele unui sir (de exemplu 'a') este o adresa, deci poate fi privit ca valoare a unui pointer. Deci sirurile si pointerii pot fi priviti oarecum la fel in ceea ce priveste modul cum sunt folositi pentru accesarea memoriei. Cu toate acestea, sunt cateva diferente (subtile si importante). Cum numele unui sir este o adresa fixa (particulara), atunci aceasta o putem gandi ca un pointer constant. Cand este declarat un sir, compilatorul trebuie sa aloce o adresa de baza si un spatiu suficient de memorie care trebuie sa contina toate elementele sirului. Adresa de baza a unui sir este locatia initiala din memorie unde sirul este memorat; aceasta coincide cu adresa primului element (de index 0) al sirului.

Exemplu: Presupunem ca avem declaratiile:

#define N 100
int a[N], *p;
Atunci sistemul va rezerva octetii (sa zicem) numerotati 300, 302, , 498 ca fiind adresele elementelor a[0], a[1], , a[99]
Instructiunile
p = a; si p = &a[0];
sunt echivalente si vor asigna lui 'p' valoarea 300 (ca adresa de memorie). Aritmetica pointerilor pune la dispozitie o alternativa pentru indexarea sirurilor.
Instructiunile p = a + 1; si p = &a[1]; sunt echivalente si va asigna lui 'p' valoarea 302 (adresa, bineinteles).

Exemplu: Presupunem ca avem un sir ale carui elemente au deja valori. Pentru a face suma elementelor, putem folosi pointeri.

suma = 0;
for (p = a; p < &a[N]; ++p)
suma += *p;

Pointeri aritmetici si lungimea elementelor

Pointerii aritmetici reprezinta una din trasaturile puternice ale limbajului C. Daca variabila 'p' este pointer catre un tip particular, atunci expresia 'p + 1' reprezinta adresa masina pentru memorarea sau accesarea urmatoarei variabile de acest tip. In mod similar, expresiile
p + i ++p p += i
au sens. Daca 'p' si 'q' sunt pointeri catre elemente de tip vector, atunci 'p - q' intoarce valoarea 'int' si reprezinta numarul de elemente dintre 'p' si 'q'. Chiar daca expresiile pointer si expresiile aritmetice seamana, exista diferente mari intre cele doua tipuri de expresii.

Exemplu:

void main()


Trimiterea sirurilor ca argumente pentru functii

Intr-o definitie de functie, un parametru formal care este declarat ca un sir este de fapt un pointer. Cand este trimis un sir, atunci se
trimite de fapt adresa de baza (evident prin 'call-by-value'). Elementele vectorului nu sunt copiate. Ca o conventie de notatie, compilatorul permite folosirea parantezelor patrate ([,]) in declararea pointerilor ca parametri.

Exemplu: Suma elementelor unui sir de tip vector

int suma(int a[], int n) /* n dimensiunea sirului */

In antetul functiei precedente, declaratia:
int a[]; este echivalenta cu int *a;
Pe de alta parte, declaratiile de mai sus nu sunt echivalente daca se utilizeaza in alta parte:
- prima se refera la creearea unui pointer constant (fara spatiu de memorie);
- a doua va crea o variabila pointer.
Presupunem ca 'v' este declarat ca fiind un sir de 100 de elemente de tip 'int'. Dupa ce am atribuit valori elementelor sale, putem utiliza functia 'suma()' pentru a aduna anumite valori ale lui 'v'.
-------- ----- ------ ----- ----- --------- ----- -------
| Apel | Ce se calculeaza si se returneaza ? |
-------- ----- ------ ----- ----- --------- ----- -------
suma(v, 100) v[0] + v[1] + + v[99]
suma(v, 88) v[0] + v[1] + + v[87]
suma(&v[7], k-7) v[7] + v[8] + + v[k - 1]
suma(v + 7, 2 * k) v[7] + v[8] + + v[2 * k + 6]
-------- ----- ------ ----- ----- --------- ----- -------

Exemplu: Sortare cu bule - 'Bubble sort'

Algoritmii eficienti de sortare au, de obicei, O(n*log n) operatii. Metoda sortarii cu bule este ineficienta din acest punct de vedere
deoarece are O(n^2) operatii. Totusi, pentru siruri de lungime mica, numarul de operatii este acceptabil. Un cod 'elegant' ar fi:
void interschimba(int *, int *);

void bubble(int a[], int n) /* n este lungimea lui a[] */


Siruri multidimensionale

Limbajul C permite siruri de orice tip, inclusiv siruri de siruri. Putem obtine siruri de dimensiune 2, 3, .

Exemple:

int a[100]; <- sir de dimensiune 1
int b[2][7]; <- sir de dimensiune 2
int c[5][3][2]; <- sir de dimensiune 3
Pornind de la adresa de baza, toate elementele sirului sunt memorate contiguu in memorie. Prin definitie un tablou bidimensional este de fapt un tablou unidimensional ale carei elemente sunt fiecare in parte cite un tablou. Prin urmare, indicii se scriu astfel a[i][j] in loc de a[i, j] ca in majoritatea limbajelor. In plus un tablou bidimensional poate fi tratat in mai multe moduri decat in alte limbaje. Elementele sunt memorate pe linii, ceea ce inseamna ca indicele din dreapta variaza primul in asa fel incit elementele sunt accesate in ordinea memoriei.

Vectori 2-dimensionali

Presupunem ca avem un vector 2-dimensional cu elemente intregi.
int a[3][5];
Incepand cu adresa de baza, compilatorul va aloca spatiu contiguu pentru 15 intregi. Atunci putem gandi acest vector ca o matrice,
astfel:
col1 col2 col3 col4 col5
lin1 a[0][0] a[0][1] a[0][2] a[0][3] a[0][4]
lin2 a[1][0] a[1][1] a[1][2] a[1][3] a[1][4]
lin3 a[2][0] a[2][1] a[2][2] a[2][3] a[2][4]
Pentru a[i][j] avem expresiile, de exemplu, echivalente:
*(a[i] + j)
(*(a + i))[j]
*((*(a + i)) + j)
*(&a[0][0] + 5*i + j)
Putem gandi 'a[i]' ca a 'i'-a coloana a lui 'a' (numarand de la 0), si 'a[i][j]' ca elementul din linia 'i', coloana 'j' a sirului (numarand de la 0). Numele sirului ('a') este tot una cu '&a[0]'; acesta este un pointer catre un sir de 5 intregi. Adresa de baza este '&a[0][0]', si nu 'a'. Ultimul exemplu de mai sus reflecta functia de corespondenta in memorie dintre valoarea pointerului si indicele sirului.
Cand un vector multidimensional este un parametru formal in definitia unei functii, toate dimensiunile, exceptand prima trebuie specificate.

Exemplu:

Presupunem ca sunt date elementele vectorului 'a'. Functia de mai jos se poate folosi pentru suma elementelor unui sir. Atentie ! Trebuie specificat numarul de coloane.
int suma(int a[][5])

In antetul functiei, urmatoarele declaratii sunt echivalente:
int a[][5] int (*a)[5] int a[3][5]
Constanta 3 actioneaza ca o reminiscenta a omului, dar compilatorul nu tine cont de ea.
Nou venitii in C sunt uneori confuzi in legatura cu deosebirea dintre un tablou bidimensional si un tablou de pointeri cum ar fi 'a' din exemplul de mai sus. Fiind date declaratiile
int a[10][10];
int *b[10];
utilizarile lui 'a' si 'b' pot fi similare, in sensul ca a[5][5] si b[5][5] sunt ambele referinte legale ale aceluiasi 'int'.
Avantaje pentru utilizarea vectorilor (dezavantaje pentru pointeri):
- 'a' este un tablou in toata regula: toate cele 100 celule de memorie trebuie alocate, iar pentru gasirea fiecarui element
se face calculul obisnuit al indicelui;
- pentru 'b', oricum prin declararea sa se aloca 10 pointeri; fiecare trebuie facut sa pointeze un tablou de intregi.
Presupunind ca fiecare pointeaza cate 10 elemente din tablou, atunci vom obtine 100 celule de memorie rezervate, plus cele 10
celule pentru pointeri. Astfel tabloul de pointeri utilizeaza sensibil mai mult spatiu si poate cere un procedeu explicit de
initializare.
Avantaje pentru utilizarea pointerilor (dezavantaje pentru vectori):
- accesarea unui element se face indirect prin intermediul unui pointer, in loc sa se faca prin inmultire si adunare;
- liniile tabloului pot fi de lungimi diferite. Aceasta inseamna ca nu orice element al lui b este constrins sa pointeze pe un vector
de 10 elemente, unii pot pointa pe cate 2 elemente, altii pe cate 20 si altii pe niciunul.

Vectori 3-dimensionali

Vectorii de dimensiune mai mare decat 3 lucreaza intr-un mod similar. Daca avem declaratia
int a[7][9][2];
atunci compilatorul va aloca spatiu pentru 7*9*2 intregi. Adresa de baza a sirului este '&a[0][0][0]', iar functia de corespondenta in memorie este specificata de
a[i][j][k] care este echivalent cu *(&a[0][0][0] + 9*2*i + 2*j + k)

Initializarea vectorilor

Exista mai multe moduri de a initializa un vector multidimensional.

Exemplu: Urmatoarele declaratii sunt echivalente:

int a[2][3] = ;
int a[2][3] = , };
int a[][3] = , };
Indexarea se face dupa linii. Daca nu sunt suficiente elemente care sa initializeze vectorul, atunci restul elementelor sunt initializate cu 0. Daca prima componenta lipseste, atunci compilatorul extrage lungimea din numarul de perechi de acolade interioare.

Exemplu: Consideram initializarea:

int a[2][2][3] = , },
, }

O initializare echivalenta poate fi data si astfel:
int a[][2][3] = , }, , }};
De obicei, daca un sir declarat 'auto' nu este explicit initializat, atunci elementele sirului vor contine valori 'garbage'.
Sirurile 'static' si 'external' sunt initializate implicit cu 0. Iata un mod simplu de a initializa toate valorile unui vector cu 0:
int a[2][2][3] = ;

Alocarea dinamica a memoriei

C pune la dispozitie pentru alocarea memoriei functiile 'calloc()' si 'malloc()' din biblioteca standard. Aceste functii au prototipul
declarat in . Acest lucru va permite rezervarea memoriei pentru un vector (de exemplu) in care ii aflam dimensiunea abia la rularea in executie (pana acum declararam dimensiunea unui vector cu #define). Un apel de tipul
calloc(n, dimensiune_tip)
va returna un pointer catre un spatiu din memorie necesar pentru memorarea a 'n' obiecte, fiecare pe 'dimensiune_tip' octeti. Daca
sistemul nu poate aloca spatiul cerut, atunci acesta va returna valoarea NULL.
In ANSI C, tipul 'size_t' este dat ca 'typedef' in . De obicei, tipul este 'unsigned'. Definitia tipului este folosita in
prototipurile functiilor 'calloc()' si 'malloc()':
void *calloc(size_t, size_t);
void *malloc(size_t);
Deoarece pointerul returnat de aceste functii are tipul 'void', acesta poate fi asignat altor pointeri fara conversie explicita (cast). Totusi unele sisteme nu accepta aceasta conversie, deci ea trebuie facuta explicit. Octetii rezervati de 'calloc()' sunt automat initializati cu 0, pe cand cei rezervati cu 'malloc()' nu sunt initializati (deci vor avea valori 'garbage'). Numele 'calloc', respectiv 'malloc', provine de la 'contiguous allocation', respectiv 'memory allocation'.

Exemplu: Program care citeste dimensiunea unui sir interactiv

#include
#include

void main()

Prototipul functiei 'free()' se gaseste in si este
void free(void *ptr);
Spatiul alocat de 'calloc()' si 'malloc()' ramane ocupat pana cand este eliberat de catre programator. Acesta nu se elibereaza cand se iese dintr-o functie (in care s-a facut rezervarea de memorie).
In programul de mai sus, instructiunea
a = calloc(n, sizeof(int));
este echivalenta cu
a = malloc(n * sizeof(int));
Singura diferenta este deci initializarea cu 0 in cazul functiei 'calloc()'.

Exemplu: Exemplu de citire interactiva a dimensiunii si a elementelor
------------ unei matrice de intregi
void main()



Exercitii propuse spre implementare

1. Scrieti o functie care insumeaza elementele de rang (index) impar, respectiv par, ale unui vector cu elemente de tip 'double'.
Sugestie: functia poate incepe cam asa
void suma(double a[],
int n, /* n - lungimea sirului a */
double *impar,
double *par)
{
. . . . .
2. Modificati programul de sortare cu bule astfel incat terminarea iteratiilor sa aiba loc cand nu se mai fac interschimbari de
elemente.
3. Calculati valoarea unui determinant asociat unei matrice patratice. In cazul in care determinantul este nenul, calculati
inversa matricei.
4. Calculati inversa unei permutari cu un numar constant de variabile suplimentare.







Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 808
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved