20 0 188KB
Programarea Calculatoarelor - Limbaje Lucrarea 11. Alocarea dinamică
Obiective
O1. Utilizarea metodelor de alocare dinamică în C O2. Utilizarea diferitelor funcții de alocare dinamică
INTRODUCERE Alocarea dinamică permite crearea, exploatarea şi distrugerea unor obiecte în timpul execuției programului, la inițiativa programatorului. Se foloseşte dacă se poate obține o reducere a dimensiunii programului prin reducerea zonei de date incluse în program. Se foloseşte în cazul datelor de dimensiune variabilă sau în cazul unor structuri cu număr variabil de elemente, număr cunoscut abia la execuție. Odată alocată memoria, acea zona poate fi tratată ca fiind un tablou și accesată astfel, pentru că alocarea se face în mod secvențial. Limbajul C nu dispune de un sistem încorporat pentru alocarea şi eliberarea variabilelor dinamice, așa cum are pentru variabile statice şi locale. Biblioteca standard C oferă un set de funcții pentru alocarea şi eliberarea variabilelor dinamice. Funcțiile ce permit alocarea dinamică sunt declarate în fişierele antet: stdlib.h Alocarea dinamică implică folosirea pointerilor. Este necesară folosirea conversiilor de tip şi a operatorului sizeof(). Pointerii obtinuți cu funcțiile malloc() şi calloc() se vor dealoca numai cu funcția free(). Se recomandă testarea pointerilor obținuți înainte de folosire şi înainte de dealocare!!! MALLOC() void* malloc(unsigned nr_oct); • alocă o zonă de memorie de dimensiune nr_oct octeți în memoria heap • returnează un pointer generic, în caz de succes, sau valoarea NULL • dimensiunea maximă ce poate fi alocată depinde de sistemul de operare, Pointerul generic este convertit cu operatorul cast, iar dimensiunea memoriei este calculată cu operatorul sizeof astfel: int *p, n=20; p = (int *) malloc(n * sizeof(int)); Zona de memorie alocată este echivalentă cu un tablou de 20 de întregi, iar elementele lui pot fi referite: • cu operatorul de indexare: p[0], p[i] • sau cu operatorul de indirectare: *p, sau mai general, *(p+i) Se recomandă testarea pointerului obținut înainte de folosire!! © Adriana Stan, Ligia Chiorean, Mircea Vaida
1
Programarea Calculatoarelor - Limbaje
CALLOC() void* calloc(unsigned nr_elem, unsigned dim_elem); • alocă o zonă de nr_elem*dim_elem octeți în memoria heap • inițializează zona de memorie cu 0 • returnează un pointer generic, în caz de succes, sau valoarea NULL FREE() void free(void *); Permite eliberarea zonelor de memorie alocate anterior, prin specificarea unui pointer ce conține adresa de început a zonei ce se dorește a fi eliberată. Dacă se apelează cu un parametru NULL rezultatele sunt imprevizibile. Dacă datele din heap nu mai sunt necesare se recomandă eliberarea zonei aferente După eliberare: • nu poate folosi din nou acea zonă de memorie cu pointerul eliberat • pointerul eliberat rămâne vizibil, dar valoarea lui nu are nici o semnificație • până la sfârșitul blocului acest pointer poate fi refolosit la o altă alocare sau ca simplu pointer către o dată căreia i se asociază Regulă practică: numărul de apeluri către funcții de alocare trebuie să fie egal cu numărul de apeluri către funcții de dealocare REALLOC() void *realloc(void *block, size_t size); Permite realocarea memoriei prin extinderea sau comprimarea unui bloc ce a fost alocat anterior cu una din funcțiile malloc(), calloc() sau chiar realloc() • dacă size == 0 blocul de memorie este eliberat şi se returnează valoarea NULL • dacă block == NULL, funcția realloc( ) lucrează la fel ca şi funcția malloc( ) • funcția returnează un pointer la zona de memorie realocată în caz de succes sau valoarea NULL în caz de eşec sau dacă size == 0 • în urma ajustării dimensiunii blocului de memorie este posibilă mutarea conținutului memoriei în altă zonă, datele fiind păstrate
Funcții pentru manipularea zonelor de memorie Inițializare zonă de memorie #include // sau void *memset(void *dst, int c, size_t n);
© Adriana Stan, Ligia Chiorean, Mircea Vaida
2
Programarea Calculatoarelor - Limbaje
Funcția setează primii n octeți începând cu adresa dst la valoarea dată de caracterul conținut în parametrul c şi returnează adresa şirului pe care lucrează Copierea zonelor de memorie #include // sau void *memcpy(void *dest, const void *src, size_t n); copiază un bloc de memorie de lungime n de la adresa src la adresa dest. Nu se garantează o funcționare corectă în cazul în care blocul sursa şi cel destinație se suprapun. Funcția returnează adresa blocului destinație. #include // sau void *memccpy(void *dest, const void *src, int c, size_t n); copiază n octeți de la sursă la destinație, însă copierea se poate opri în următoarele situații: • la întâlnirea caracterului c (ce este copiat la destinație) • s-au copiat deja n octeți la adresa de destinație Returnează adresa din blocul destinație ce urmează după caracterul c, dacă acesta a fost copiat sau valoarea NULL în caz contrar Mutarea zonelor de memorie #include // sau void *memmove(void *dest, const void *src, size_t n); • se mută un bloc de lungime n de la adresa sursă src la adresa destinație dest • se garantează copierea corectă chiar dacă cele două zone de memorie se suprapun • funcția returnează adresa zonei destinație Compararea zonelor de memorie #include // sau int memcmp(const void *s1, const void *s2, unsigned n); int memicmp(const void *s1, const void *s2, unsigned n); memcmp() compară primii n octeți ai zonelor de memorie s1 şi s2 şi returnează un întreg mai mic decât, egal cu, sau mai mare decât zero dacă s1 este mai mic decât, coincide, respectiv este mai mare decât s2 memicmp() face acelaşi lucru, dar fără a ține cont de litere mari, respectiv litere mici (ignoring case) - DOAR ÎN COMPILATOARE MICROSOFT!! Interschimbarea octeților #include © Adriana Stan, Ligia Chiorean, Mircea Vaida
3
Programarea Calculatoarelor - Limbaje
void swab(char *src, char *dest, int n); preia primii n octeți de la sursă, aplică interschimbarea fiecărei perechi şi depune rezultatul la destinație dacă n este par, interschimbarea se aplică pentru toți octeții, începând cu primul octet dacă n este impar, interschimbarea se aplică numai la primii (n-1) octeți, începând cu primul octet
EXEMPLE 1. Program pentru alocarea unui tablou unidimensional folosind funcţii de bibliotecă.
#define _CRT_SECURE_NO_WARNINGS #include #include int main(void){ int i,n, *tab=NULL; printf("\nIntroduceti dimensiunea tablolului: "); scanf("%d", &n); if((tab = (int *)malloc(n * sizeof(int)))) { printf("\nIntroduceti elementele tablolului: \n"); for(i=0; i