Sunteți pe pagina 1din 35

Programarea calculatoarelor

Limbajul C

CURS 8

Alocare dinamic

Clase de alocare a memoriei


Cnd, cum i unde se aloc memorie pentru o variabil. Orice variabil are o clas de memorare care rezult fie din declaraia ei, fie implicit din locul unde este definit variabila. Zona de memorie utilizat de un program C cuprinde 4 subzone:
1. 2. 3. 4.

Zona text: codul programului Zona de date: variabilele globale Zona stiv: date temporare (variabilele locale) Zona heap: memoria dinamic

Programarea calculatoarelor

Moduri de alocare a memoriei


Static: variabile implementate n zona de date Auto: variabile implementate n stiv Dinamic: variabile implementate n heap
Memoria se aloc dinamic (la execuie) n zona heap ataat programului, dar numai la cererea explicit a programatorului, prin apelarea unor funcii de bibliotec (malloc, calloc, realloc). Memoria este eliberat numai la cerere, prin apelarea funciei free

Register: variabile implementate ntr-un registru de memorie


Programarea calculatoarelor

Variabile locale / variabile globale


Durata de via vs. domeniu de vizibilitate Variabile globale Alocare Durata de via Iniializare Static; la compilare Cea a ntregului program Cu zero
Programarea calculatoarelor

Variabile locale Auto; la execuie bloc Cea a blocului n care e declarat Nu se face automat

Clase de alocare a memoriei: Auto


int doi() { int x = 2; return x; } void main() { int a; { int b = 5; a = b*doi(); } printf(a = %d\n, a); }
Programarea calculatoarelor

Clase de alocare a memoriei: Auto


Coninut stiv: x 2 b 5 a 10 Variabilele locale unui bloc (unei funcii) i argumentele formale sunt implicit din clasa auto; Durata local:
memoria este alocat automat, la activarea unei funcii, n zona stiv alocat unui program i este eliberat automat la terminarea funciei.

Programarea calculatoarelor

Clase de alocare a memoriei: Static


Memoria este alocat la compilare n segmentul de date din cadrul programuluii nu se mai poate modifica n cursul execuiei Variabilele globale sunt implicit statice Pot fi declarate static i variabile locale, definite n cadrul funciilor. O variabil sau o funcie declarat static are durata de via egal cu cea a programului static face ca o variabil global sau o funcie s fie privat(proprie) unitii unde a fost definit: ea devine inaccesibil altei uniti, chiar prin folosirea lui extern.
Programarea calculatoarelor

Clase de alocare a memoriei: Static


int f(void) { static int nr_apeluri=0; nr_apeluri++; printf("\nfuncia f() este apelata pentru a %d-a oara.", nr_apeluri); // ... return nr_apeluri; } int f(void); int main(void) { int i; for (i=0; i<10; i++) if (cond(i)) f(); return 0; }

Programarea calculatoarelor

Observaie
O variabil static declarat ntr-o funcie:
i pstreaz valoarea ntre apeluri succesive ale funciei spre deosebire de variabilele auto care sunt realocate pe stiv la fiecare apel al funciei i pornesc de fiecare dat cu valoarea primit la iniializarea lor (sau cu o valoare imprevizibil, dac nu sunt iniializate).

Variabilele locale statice se folosesc foarte rar n practica programrii ( funcia de bibliotec strtok este un exemplu de funcie cu o variabil static). Programarea calculatoarelor Consumul de memorie stack (stiva) este

Clase de alocare a memoriei: Register


O variabil declarat register solicit sistemului alocarea ei ntr-un registru main, dac este posibil De obicei se las compilatorului decizia de alocare a registrelor mainii pentru anumite variabile auto din funcii Se utilizeaz pentru variabile foarte solicitate, pentru mrirea vitezei de execuie:
{
register int i; for(i = 0; i < N; ++i){ /* */

} } /* se elibereaza registrul */

Programarea calculatoarelor

Funcii C pentru alocarea dinamic a memoriei

stdlib.h
void *malloc(int numar_de_octeti); Aloc memorie de dimensiunea cerut void *calloc(int nitems, int size); Aloc memorie de dimensiunea cerut i iniializeaz zona alocat cu zerouri

Programarea calculatoarelor

Funcii C pentru alocarea dinamic a memoriei


Au ca rezultat adresa zonei de memorie alocate (de tip void *) Au ca argument dimensiunea zonei de memorie alocate Dac cererea de alocare nu poate fi satisfcut, pentru c nu mai exista un bloc continuu de dimensiunea solicitat, atunci funciile de alocare au rezultat NULL. Funciile de alocare au rezultat void* deoarece funcia nu stie tipul datelor ce vor fi memorate la adresa respectiv. Se folosesc:
Operatorul sizeof pentru a determina numrul de octei necesar unui tip de date (variabile); Operatorul de conversie cast pentru adaptarea adresei primite de la funcie la tipul datelor memorate la adresa respectiv (conversie necesar atribuirii ntre pointeri de tipuri diferite).

Programarea calculatoarelor

Funcii C pentru alocarea dinamic a memoriei


void *realloc(void* adr,int numar_de_octeti); Aloc o zon de dimensiunea specificat ca al doilea argument Copiaz la noua adres datele de la adresa veche (primul argument) Elibereaz memoria de la adresa veche. Atenie! Realocarea repetat de memorie poate conduce la fragmentarea memoriei heap, adic la crearea unor blocuri de memorie libere dar neadiacente i prea mici pentru a mai fi reutilizate ulterior. Se va evita redimensionarea unui vector cu o valoare foarte mic de un numr mare de ori; o strategie de realocare folosit pentru vectori este dublarea capacitii lor anterioare.
Programarea calculatoarelor

Eliberarea memoriei
void free(void* adr); Elibereaz memoria de la adresa adr (dimensiunea ei este memorat la nceputul zonei alocate (de ctre funcia de alocare) Eliberarea memoriei prin "free" este inutil la terminarea unui program, deoarece nainte de ncrcarea i lansarea n execuie a unui nou program se elibereaz automat toat memoria "heap!

Programarea calculatoarelor

Exemple
char *str; str=(char *)malloc(10*sizeof(char)); str=(char *)realloc(str,20*sizeof(char)); free(str); int *p; p=(int *)malloc(50*sizeof(int)); int * a= (int*) calloc (n, sizeof(int) );

Programarea calculatoarelor

Exerciii alocare dinamic


1. Program care aloc spatiu pentru o variabil ntreag dinamic, dup citire i tiprire, spaiul fiind eliberat. Modificai programul astfel nct variabila dinamic s fie de tip double. 2. Program care citete mai multe linii de text de la tastatur, aloc dinamic memorie pentru fiecare linie i pune adresa liniei ntrun vector de pointeri (vector alocat i realocat dinamic).
Programarea calculatoarelor

Rezolvare 1
#include <stdlib.h> #include <stdio.h> int main(){ int *pi; pi=(int *)malloc(sizeof(int)); if(pi==NULL){ puts("*** Memorie insuficienta ***"); return 1; // revenire din main } printf("valoare:"); //citirea variabilei dinamice, de pe heap, de la adresa din pi!!! scanf("%d",pi);

Programarea calculatoarelor

Rezolvare 1
*pi*=2; // dublarea valorii printf("val=%d,pi(adresa pe heap)=%p,adr_pi=%p\n", *pi, pi, &pi); /* sizeof aplicat unor expresii */ printf("%d %d %d\n",sizeof(*pi), sizeof(pi), sizeof(&pi)); free(pi); //eliberare spatiu printf("pi(dupa elib):%p\n",pi); system("pause"); return 0; }

// nemodificat, dar invalid

Programarea calculatoarelor

Rezolvare 2
#include <stdio.h> #include <stdlib.h> #define INCR 4 int main() { int n,n_crt,i ; float x, * v; n=INCR; // dimensiune memorie alocata n_crt=0; // numar curent elemente n vector v=(float *)malloc (n*sizeof(float)); //alocare initiala

Programarea calculatoarelor

Rezolvare 2
while (scanf("%f",&x) !=EOF){ if (n_crt == n) { n= n+ INCR; v=(float *) realloc (v, n*sizeof(float) ); } v[n_crt++]=x; } for (i=0;i<n_crt;i++) printf ("%.2f ",v[i]); free(v); system(pause); return 1; }
Programarea calculatoarelor

//realocare

Observaie
Nu orice vector cu dimensiune constant este un vector static!! Un vector definit ntr-o funcie (alta dect main) nu este static deoarece nu ocup memorie pe toat durata de execuie a programului, dei dimensiunea sa este stabilit la scrierea programului. Un vector definit ntr-o funcie este alocat pe stiv, la activarea funciei, iar memoria ocupat de vector este eliberat automat la terminarea funciei. calculatoarelor Programarea

Matrice alocate dinamic


Folosete ntr-un mod economic memoria i evit alocri acoperitoare, estimative. Permite matrice cu linii de lungimi diferite. Reprezint o soluie bun la problema argumentelor de funcii de tip matrice. O matrice alocat dinamic este de fapt un vector de pointeri ctre fiecare linie din matrice, deci un vector de pointeri la vectori alocai dinamic. O astfel de matrice se poate folosi la fel ca o matrice declarat cu dimensiuni constante.
Programarea calculatoarelor

Matrice alocate dinamic


Dac numrul de linii este cunoscut sau poate fi estimat valoarea lui maxim, atunci vectorul de pointeri are o dimensiune constant: int * a[M]; Dac nu se poate estima numrul de linii din matrice atunci i vectorul de pointeri se aloc dinamic: int** a;
se va aloca mai nti memorie pentru un vector de pointeri (funcie de numrul liniilor) apoi se va aloca memorie pentru fiecare linie (funcie de numrul coloanelor) cu memorarea adreselor liniilor n vectorul de pointeri.

Programarea calculatoarelor

Exemplu
S se scrie funcii de alocare a memoriei i afiare a elementelor unei matrice de ntregi alocat dinamic. #include<stdio.h> #include<stdlib.h> // rezultat adresa matrice sau NULL int ** intmat ( int nl, int nc) { int i; int ** p=(int **) malloc (nl*sizeof (int*)); if ( p != NULL) for (i=0; i<nl ;i++) p[i] =(int*) calloc (nc,sizeof (int)); return p; }

Programarea calculatoarelor

Exemplu
void printmat (int ** a, int nl, int nc) { int i,j; for (i=0;i<nl;i++) { for (j=0;j<nc;j++) printf (%2d, a[i][j] ); printf(\n); } } int main () { int **a, nl, nc, i, j; printf ("nr linii i nr coloane: \n"); scanf ("%d%d", &nl, &nc); a= intmat(nl,nc); for (i=0;i<nl;i++) for (j=0;j<nc;j++) a[i][j]= nc*i+j+1; printmat (a ,nl,nc); return 1; }
Programarea calculatoarelor

Vectori de pointeri la date alocate dinamic Date alocate dinamic ale cror adrese sunt reunite ntr-un vector de pointeri. Situaiile cele mai frecvente sunt:
vectori de pointeri la iruri de caractere alocate dinamic vectori de pointeri la structuri alocate dinamic.

Programarea calculatoarelor

Exemplu
Program pentru citirea unor nume, alocare dinamic a memoriei pentru fiecare ir (n funcie de lungimea irului citit) i pentru memorarea adreselor irurilor ntr-un vector de pointeri. n final se vor afia numele citite, pe baza vectorului de pointeri. S se adauge programului anterior o funcie de ordonare a vectorului de pointeri la iruri, pe baza coninutului fiecrui ir. Programul va afia lista de nume n ordine alfabetic. Apoi vectorul de pointeri se va aloca dinamic, funcie de numrul de iruri.
Programarea calculatoarelor

Exemplu
void printstr ( char * vp[], int n) { //afisare int i; for(i=0;i<n;i++) printf ("%s\n",vp[i]); } int readstr (char * vp[]) { // citire siruri i creare vector de pointeri int n=0; char * p, sir[80]; while ( scanf ("%s", sir) == 1) { vp[n]= (char*) malloc (strlen(sir)+1); strcpy( vp[n],sir); //sau: vp[n]=strdup(sir); ++n; } return n; }
Programarea calculatoarelor

Exemplu
/* ordonare vector de pointeri la iruri prin Bubble Sort (metoda bulelor)*/ void sort ( char * vp[],int n) { int i,j,schimb=1; char * tmp; while(schimb){ schimb=0; for (i=0;i<n-1;i++) if ( strcmp (vp[i],vp[i+1])>0) { tmp=vp[i]; vp[i]=vp[i+1]; vp[i+1]=tmp; schimb=1; } } } Programarea calculatoarelor

Exemplu
int main () { int n; char * vp[1000]; // vector de pointeri, cu dimens. fixa n=readstr(vp); sort ( vp,n); printstr (vp,n); // citire siruri i creare vector // ordonare vector // afiare iruri

system(pause); return 1; }
Programarea calculatoarelor

Observaie
O funcie nu poate avea ca rezultat un vector sub forma: int [] funcie() {} O funcie poate avea ca rezultat un pointer !! int *funcie() {} De obicei, rezultatul pointer este egal cu unul din argumente, eventual modificat n funcie. Exemplu corect: // incrementare pointer p char * incptr ( char * p) { return ++p; } Atenie! Acest pointer nu trebuie s conin adresa unei variabile locale!

Programarea calculatoarelor

Atenie!
O variabil local are o existent temporar, garantat numai pe durata executrii funciei n care este definit (cu excepia variabilelor locale statice) Adresa unei astfel de variabile nu trebuie transmis n afara funciei, pentru a fi folosit ulterior!! Exemplu greit: // vector cu cifrele unui nr intreg de maxim cinci cifre int * cifre (int n) { int k, c[5]; // vector local for (k=4;k>=0;k--) { c[k]=n%10; n=n/10; } return c; // aici este eroarea ! } //warning la compilare i rezultate greite n main!!
Programarea calculatoarelor

funcie cu rezultat pointer


O funcie care trebuie s transmit ca rezultat un vector poate fi scris corect n mai multe feluri: 1. Primete ca argument adresa vectorului (definit i alocat n alt funcie) i depune rezultatele la adresa primit (este soluia recomandat!!) void cifre (int n, int c[ ]) { int k; for (k=4;k>=0;k--) { c[k]=n%10; n=n/10; } } int main(){ int a[10]; . cifre(n,a); . }
Programarea calculatoarelor

funcie cu rezultat pointer


2. Aloc dinamic memoria pentru vector (cu "malloc") aceast alocare (pe heap) se menine i la ieirea din funcie. funcia are ca rezultat adresa vectorului alocat n cadrul funciei. problema este unde i cnd se elibereaz memoria alocat. int * cifre (int n) { Programarea calculatoarelor int k, *c; // vector local

Exemplu: corect sau greit?


// extrage un subsir de lungime n de la adresa s char * substr (char* s, int n) { char aux[1000]; // o variabila locala pentru subsirul rezultat int m; m=strlen(s); // cate caractere mai sunt la adresa s if (n>m) n=m; strncpy(aux,s,n); aux[n]=0; // terminator de (sub)sir return aux; // pointer la o variabila locala ! } // verificare funcie void main () { char s[]=abcdef; puts (substr(s,3)); puts (substr (substr(s,4),2)); } aux= (char*) malloc(strlen(s)+1); char * aux=strdup(s);
Programarea calculatoarelor