Sunteți pe pagina 1din 10

Principii de baz

n momentul n care ntr-un program declarm variabile, aceste variabile sunt plasate n memorie
n mod automat de ctre compilator. Despre acest lucru s-a mai discutat la laboratorul despre
pointeri. De data aceasta vom privi lucrurile dintr-o alt perspectiv. Memoria calculatorului, cea
n care rezid variabilele din programe, este compus din dou pri, pe care le vom
numimemorie static respectiv memorie dinamic. n practic lucrurile sunt ceva mai complexe,
dar pentru prezentarea noastr ne putem rezuma la aceast mprire. Vom vedea care este
diferena dintre memoria static i memoria dinamic i cum putem avea acces la fiecare din
cele dou.
Memoria static este cea n care variabilele sunt plasate de compilator. Toate variabilele pe care
le declarm ntr-un program ajung n zona static. Spre exemplu urmtorul program declar o
variabil ntreag i un tablou de 5 ntregi i afieaz adresele de memorie la care sunt plasate n
memorie.

#include <stdio.h>
int main(void)
{
int i, a[5];
printf("variabila
printf("variabila
printf("variabila
printf("variabila
return 0;
}

i
i
a
a

are dimensiunea
este plasata la
are dimensiunea
este plasata la

%d\n",
adresa
%d\n",
adresa

sizeof(i));
%p\n", &i);
sizeof(a));
%p\n", a);

Dac punem n execuie programul, se va afia

variabila
variabila
variabila
variabila

i
i
a
a

are dimensiunea
este plasata la
are dimensiunea
este plasata la

4
adresa 0xff99f02c
20
adresa 0xff99f018

Adresele de memorie afiate pot s difere de la un calculator la altul.


Compilatorul a stabilit dimensiunea i locaia zonelor de memorie ocupate de variabile.
Schematic, harta memoriei pentru acest program se poate reprezenta ca n figura urmtoare.

Problema cu zona de memorie static este c odat ce compilatorul a stabilit locaia i


dimensiunea zonelor de memorie ocupate de variabile, acestea nu mai pot fi modificate.
Limitarea devine vizibil la tablouri i se poate traduce prin: odat ce am declarat un tablou de o
anumit dimensiune, nu mai putem mri sau micora aceast dimensiune. n exemplul nostru de
mai sus, tabloul ava avea ntotdeauna 5 elemente de tip int i nu vom putea niciodat pstra n
el mai mult de 5 elemente. Dac folosim tablouri n zona static de memorie, trebuie s calculm
care va fi numrul maxim de elemente pe care le vom pstra n tablou i s declarm tabloul de
acea dimensiune.
Zona de memorie dinamic elimin aceast limitare, prin faptul c ne permite s mrim sau s
micorm dimensiunea blocurilor de memorie. n schimb apare o alt limitare, i anume c nu
putem declara variabile care s fie plasate n zona de memorie dinamic. Toate variabilele pe
care le declarm n program vor ajune n zona static. Pentru a obine acces la memoria dinamic
trebuie s folosim anumite funcii standard.
Spunem c memoria din zona dinamic se aloc n aa numite blocuri de memorie. Dac dorim
s ni se aloce un bloc de memorie de o anumit dimensiune, solicitm acest lucru apelnd
funcia malloc i trimind ca parametru dimensiunea n octei pe care o dorim. Dac mai este

suficient spaiu liber n zona de memorie dinamic atunci un bloc de memorie de dimensiunea
specificat va fi marcat ca ocupat, iar funcia malloc va returna adresa de nceput a acelui bloc.
Pentru a putea lucra cu blocul alocat, trebuie s reinem adresa lui de nceput ntr-un pointer,
dup care prin intermediul pointer-ului vom folosi blocul de memorie ca i pe un tablou oarecare.
Pointerul n care pstrm adresa returnat de malloc va fi plasat n zona de memorie static.
Programul de mai sus poate fi rescris astfel nct s solicite spaiu din zona dinamic pentru
tablou a.

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
/* De data asta a este un pointer. */
int i, *a;
/* Urmarim dimensiunile si adresele variabilelor.
Atat i cat si a sunt plasate in zona statica. */
printf("variabila i are dimensiunea %d\n", sizeof(i));
printf("variabila i este plasata la adresa %p\n", &i);
printf("variabila a are dimensiunea %d\n", sizeof(a));
printf("variabila a este plasata la adresa %p\n", &a);
/* Cerem un bloc de memorie din zona dinamica.
Solicitam un bloc de 5*4=20 de octeti, iar
adresa o pastram in pointerul a. */
a = (int *) malloc(5 * sizeof(int));
printf("blocul de memoria este plasat la adresa %p\n",
a);
/* Daca malloc a returnat NULL, inseamna ca nu
mai este spatiu in zona de memorie dinamica.
In acest caz incheiem executia. */
if (a == NULL) {
printf("Nu pot aloca memorie.\n");
exit(EXIT_FAILURE);
}
/* Daca am ajuns aici, alocare a reusit. Pot
folosi blocul alocat prin intermediul
pointer-ului a, ca pe un tablou oarecare. */
for (i = 0; i < 5; i++)
a[i] = i;
/* La final eliberez blocul de memorie. */
free(a);
return 0;
}
Harta memoriei va arata de data asta astfel:

Cteva observaii importante:

Blocurile alocate n zona de memorie dinamic nu au nume. Ca urmare nu ne putem referi


la ele din program ca i cum ne-am referi la variabilele pe care le-am declarat. Singurul
nostru mod de acces la un asemenea bloc este prin adresa lui de memorie.

Folosind pointeri, putem pcli sistemul. Dac pstrm adresa de nceput a unui
asemenea bloc ntr-un pointer, putem pe urm folosi numele variabilei pointer pentru a
accesa blocul de memorie alocat dinamic.

Orice bloc de memorie alocat dinamic trebuie eliberat nainte s se ncheie execuia
programului. Un bloc de memorie se elibereaz apelnd funciafree i trimind ca
parametru adresa de nceput a blocului.

Verificare
Cum arat harta memorie n timpul i la finalul execuiei urmtoarelor programe?
Atentie! Unele din programe pot fi scrise intentionat gresit, pentru a evidentia
greseli care se fac in mod tipic de studenti.

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *a, *b;
a = (int *) malloc(7 * sizeof(int));
if (!a) {
printf("Eroare alocare 1.\n");
exit(EXIT_FAILURE);
}
b = (int *) malloc(24 * sizeof(int));
if (!b) {
printf("Eroare alocare 2.\n");
free(a);
exit(EXIT_FAILURE);
}
free(a);
free(b);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *a, *b;
a = (int *) malloc(7 * sizeof(int));
if (!a) {
printf("Eroare alocare 1.\n");
exit(EXIT_FAILURE);
}
a = (int *) malloc(24 * sizeof(int));
if (!a) {
printf("Eroare alocare 2.\n");
exit(EXIT_FAILURE);
}
free(a);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *a, *b;
a = b = (int *) malloc(7 * sizeof(int));
if (!a) {
printf("Eroare alocare 1.\n");
exit(EXIT_FAILURE);
}
free(a);
free(b);
return 0;
}

Exemplu de utilizare
Ca exemplu de utilizare a alocrii dinamice, s rezolvm urmtoarea problem: se citete de la
tastatur un numr N, urmat de N numere reale. CeleN numere reale se vor memora ntr-un
tablou. La final se mai citete un numr real X. S se afieze acele numere din tablou care se afl
n intervalul deschis(X-1,X+1).
Problema se poate rezolva prin urmtorul program. Explicaii suplimentare se gsesc sub form
de comentarii n cod.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(void)
{
float *a, x;
int n, i;
/* Citim N de la tastatura. */
printf("Cate numere? ");
scanf("%d", &n);
/* Dupa ce il stim pe N, alocam din zona
dinamica un bloc de memorie in care sa
putem pastra N float-uri. */
a = (float *) malloc(n * sizeof(float));
/* Daca alocarea de memorie a esuat,
incheiem executia cu mesaj de eroare. */
if (!a) {
printf("Nu pot aloca memorie.\n");
exit(EXIT_FAILURE);
}
/* Daca am ajuns aici inseamna ca alocarea
de memorie a reusit. Citim cele N numere
reale. */
for (i = 0; i < n; i++) {
printf("a[%d]: ", i);
scanf("%f", &a[i]);
}
/* Citim numarul X. */
printf("x: ");
scanf("%f", &x);
/* Afisam numerele din intervalul cerut. */
printf("Numerele din intervalul (%.2f, %.2f) sunt: ",
x - 1, x + 1);
for (i = 0; i < n; i++)
if (fabs(x - a[i]) < 1)
printf("%.2f ", a[i]);
printf("\n");
/* Obligatoriu eliberam blocul de memorie
alocat dinamic. */
free(a);
return 0;

}
Redimensionarea blocurilor alocate dinamic
Din ce am prezentat pn acum, a rezultat unul din avantajele alocrii dinamice de memorie, i
anume c nu trebuie s tim de la compilarea programului ct memorie s rezervm unui
tablou, ci putem stabili acest lucru n timpul execuiei programului i putem solicita memorie doar
n momentul n care tim sigur de ct avem nevoie.
Un alt avantaj este faptul c dup ce am alocat dinamic un bloc de memorie, acesta poate fi
redimensionat dup necesiti. Acest lucru se face prin funciarealloc, care primete ca
parametri adresa de memorie a unui bloc deja alocat i noua dimensiune care o dorim pentru
blocul respectiv. Funcia va redimensiona blocul i va returna noua lui adres de memorie. Este
posibil ca n timpul redimensionrii blocul s fie mutat la o nou locaie de memorie, dar ni se
garanteaz c tot coninutul care se afla n blocul de memorie va fi pstrat. Dac nu mai este
suficient spaie pentru redimensionarea blocului, funcia realloc va returna NULL.
Urmtorul program aloc iniial un tablou de 100 de ntregi, iar pe urm l redimensioneaza la 200
de ntregi.

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *a;
/* Initial alocam memorie pentru 100 de intregi. */
a = (int *) malloc(100 * sizeof(int));
if (!a) {
printf("Nu pot aloca memorie.\n");
exit(EXIT_FAILURE);
}
/* Pe urma redimensionam blocul alocat la 200 de
intregi. */
a = (int *) realloc(a, 200 * sizeof(int));
if (!a) {
printf("Nu pot redimensiona blocul.\n");
exit(EXIT_FAILURE);
}
/* Eliberez blocul la final. */
free(a);
return 0;
}

Strict vorbind, programul de mai sus nu este 100% corect. n cazul n care redimensionarea
blocului eueaz, funcia realloc va returna NULL. Ca urmare pointer-ul a va deveni NULL. Blocul
de memorie alocat iniial a rmas n memorie, iar noi am pierdut orice referin ctre el, deoarece
am suprascris singurul pointer care pstra adresa de nceput a blocului. Ca urmare nu vom mai
putea elibera blocul de memorie. Varianta corect este utilizarea unei variabile auxiliare pentru a
pstra adresa returnat de realloc. Urmtorul program arat modul corect de redimensionare a
unui bloc.

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *a, *aux;
/* Initial alocam memorie pentru 100 de intregi. */
a = (int *) malloc(100 * sizeof(int));
if (!a) {
printf("Nu pot aloca memorie.\n");
exit(EXIT_FAILURE);
}
/* Pe urma redimensionam blocul alocat la 200 de
intregi.
Pastram adresa returnata de realloc intr-un pointer
auxiliar, pentru a nu-l suprascrie pe a. */
aux = (int *) realloc(a, 200 * sizeof(int));
/* Daca redimensionarea a esuat, eliberez blocul
alocat
initial, dupa care inchei programul cu mesaj de
eroare. */
if (!aux) {
printf("Nu pot redimensiona blocul.\n");
free(a);
exit(EXIT_FAILURE);
}
/* Daca alocarea a reusit, copiez adresa din aux in a
si
continui in mod normal executia. */
else {
a = aux;
}
/* Eliberez blocul la final. */
free(a);
return 0;
}
Exemplu de utilizare a redimensionrii de blocuri
Un exemplu de utilizare a redimensionrii de blocuri este urmtorul: se citete de la tastatur un
text pn la apariia lui EOF. Nu se tie de la nceput ce lungime are textul. Scriei un program

care folosete un ir alocat dinamic i redimensionat dup necesiti pentru a pstra textul citit.
Programul trebuie s poat citi texte de orice lungime (mii, sute de mii de caractere), n limita
memoriei dinamice disponibile.
Programul care rezolv problema, cu explicaii sub form de comentarii, este urmtorul:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *a, *aux;
int c, n;
/* Initial textul nostru este sirul vid, adica "". */
a = (char *) malloc(1);
if (!a) {
printf("Nu pot aloca memorie pentru sirul vid.\n");
exit(EXIT_FAILURE);
}
a[0] = 0;
/* Initial lungimea sirului este 0. */
n = 0;
/* Intram intr-o bucla in care citim de la tastatura
cate un caracter, pana la EOF. */
while ((c = getchar()) != EOF) {
/* Dimensiunea curenta a blocului este n+1 (adica
lungimea sirului, plus 1 pentru terminatorul de
sir).
Redimensionam blocul la n+2 pentru a face loc
pentru noul caracter citit. */
aux = (char *) realloc(a, n + 2);
if (!aux) {
free(a);
printf("Nu pot redimensiona blocul.\n");
exit(EXIT_FAILURE);
} else {
a = aux;
}
/* Plasam noul caracter la sfarsitul sirului. */
a[n] = c;
/* Mutam terminatorul de sir cu o pozitie mai la
dreapta
fata de unde era pana acum. */
a[n + 1] = 0;
/* Lungimea sirului creste cu 1. */
n++;
}
/* La final afisam sirul. */
printf("%s", a);
/* Si eliberam memoria alocata dinamic. */
free(a);
return 0;
}

Probleme propuse spre rezolvare


Problema 1 Scriei un program care citete de la tastatur un numr N, apoi citete N numere
ntregi, iar la final afieaz cele N numere n ordine invers. Programul va folosi alocarea
dinamic pentru memorarea celor N numere.
Problema 2 Aceeai problem ca mai sus, cu diferena c nu se tie de la nceput cte numere
vor fi introduse. Programul va ncepe direct cu citirea numerelor, fr a-l citi pe N. Citirea se va
ncheia n momentul n care se introduce numrul 0. Programul va folosi alocarea dinamic astfel
nct spaiul de memorie consumat s fie minim.
Problema 3 Se citesc de la tastatur cuvinte separate prin spaii albe (spaiu, tab sau enter).
Pornind de la cuvintele citite, se construiete un ir de caractere dup urmtoarele reguli:

Primul cuvnt citit se adaug la irul de caractere;

Fiecare din urmtoarele cuvinte citite se adaug la irul de caractere dac ultimele dou
litere ale irului coincid cu primele dou litere ale cuvntului (nu se face distincie ntre
literele mici i cele mari);

Cuvintele adugate la irul de caractere sunt desprite de caracterul -.

S se afieze irul astfel construit.


Spre exemplu pentru intrarea

Fazan Antic Noe icoana Egipt


naftalina nimic Narcisa
trei altceva
santier iarba
Caine Pisica erudit
se afieaz

Fazan-Antic-icoana-naftalina-Narcisa-santier-erudit

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