Documente Academic
Documente Profesional
Documente Cultură
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);
variabila
variabila
variabila
variabila
i
i
a
a
are dimensiunea
este plasata la
are dimensiunea
este plasata la
4
adresa 0xff99f02c
20
adresa 0xff99f018
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:
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;
}
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);
Fazan-Antic-icoana-naftalina-Narcisa-santier-erudit