Până acum, dimensiunea datelor era fixată la scrierea programului. Alocarea dinamică permite stabilirea ei în timpul execuției, cerând memorie exact atunci când este nevoie și eliberând-o când nu mai este utilă.
1Obiectivele lucrării
- Distingerea alocării statice, automate și dinamice
- Utilizarea funcțiilor
malloc,calloc,reallocșifree - Verificarea obligatorie a reușitei alocării
- Identificarea și prevenirea pierderilor de memorie
- Folosirea operatorilor
newșideletedin C++
2De ce este necesară alocarea dinamică
La declararea unui tablou, dimensiunea trebuie să fie o constantă cunoscută la compilare. Aceasta duce la două probleme opuse:
| Situație | Consecință |
|---|---|
| Se declară un tablou mult mai mare decât e nevoie | memorie ocupată inutil |
| Numărul real de elemente depășește dimensiunea declarată | trebuie modificat codul și recompilat |
SoluțiaSe stabilește dimensiunea în momentul execuției, când numărul de elemente
este cunoscut, se alocă exact atât cât trebuie, iar la final memoria se eliberează pentru a putea fi
refolosită în alt scop.
3Segmentele de memorie
Memoria unui program este împărțită în zone specializate, adresate prin regiștri de segment (CS, DS, ES, SS):
| Zonă | Ce conține | Când se alocă | Când se eliberează |
|---|---|---|---|
| Segment de cod (CS) | instrucțiunile executabile | la încărcarea programului | la terminare |
| Segment de date (DS) | variabile globale și statice | la încărcare | la terminare |
| Stivă (SS) | variabile locale, parametri, adrese de revenire | la intrarea în bloc | automat, la ieșire |
| Heap | date alocate dinamic | la apelul malloc / new | manual, prin free / delete |
4Funcțiile de alocare din C
| Funcție | Prototip | Efect |
|---|---|---|
malloc | void* malloc(size_t n) | alocă n octeți, neinițializați |
calloc | void* calloc(size_t nr, size_t dim) | alocă nr×dim octeți, puși pe zero |
realloc | void* realloc(void* p, size_t n) | redimensionează o zonă deja alocată |
free | void free(void* p) | eliberează zona indicată |
tiparul corect de utilizare
int *v = (int *)malloc(n * sizeof(int)); // 1. alocare
if (v == NULL) { // 2. VERIFICARE obligatorie
printf("Memorie insuficienta!\n");
return 1;
}
/* ... utilizare ... */ // 3. folosire
free(v); // 4. eliberare
v = NULL; // 5. evita folosirea accidentala
Verificarea nu este opționalăDacă sistemul nu poate satisface cererea,
malloc returnează NULL. Folosirea rezultatului fără verificare duce la
dereferențierea unui pointer nul și oprirea brutală a programului.realloc - atenție la pointerul returnatFuncția poate muta blocul la altă adresă.
Nu scrieți niciodată
v = realloc(v, ...) direct: dacă alocarea eșuează, se returnează NULL,
iar adresa veche se pierde definitiv. Folosiți o variabilă temporară.5Simulator: pointer către zona alocată
Selectați ținta pentru a vedea diferența dintre un pointer valid, unul care indică spre altă zonă și unul pus pe NULL după eliberare.
De ce se pune pointerul pe NULL după freeDupă eliberare, pointerul păstrează
adresa veche, dar zona nu mai este a noastră. Un
free repetat pe aceeași adresă produce
coruperea structurilor interne ale alocatorului. Atribuirea v = NULL transformă o eroare
subtilă într-una imediat vizibilă.6Operatorii new și delete din C++
| Operație | C | C++ |
|---|---|---|
| Alocare pentru o valoare | p = (int*)malloc(sizeof(int)); | p = new int; |
| Alocare cu inițializare | alocare + atribuire separată | p = new int(42); |
| Alocare pentru un tablou | v = (int*)malloc(n*sizeof(int)); | v = new int[n]; |
| Eliberare valoare | free(p); | delete p; |
| Eliberare tablou | free(v); | delete[] v; |
Nu amestecați cele două mecanismeMemoria obținută cu
malloc se
eliberează cu free; cea obținută cu new, cu delete.
Pentru tablouri se folosește obligatoriu delete[] - altfel destructorii elementelor
nu se apelează.Avantajele lui newNu necesită conversie explicită de tip, calculează singur
dimensiunea, apelează constructorii pentru obiecte și, la eșec, generează o excepție în loc să returneze
NULL în tăcere.
7Erori clasice
| Eroare | Cauză | Efect |
|---|---|---|
| Memory leak | lipsa free | consum crescător de memorie; programele lungi ajung să epuizeze sistemul |
| Double free | eliberare de două ori | corupere internă a alocatorului, oprire imprevizibilă |
| Use after free | acces după eliberare | date aleatoare sau blocare; vulnerabilitate de securitate |
| Buffer overflow | scriere peste dimensiunea alocată | suprascrierea structurilor vecine din heap |
| Alocare neverificată | lipsa testului == NULL | dereferențierea unui pointer nul |
Regula de aurFiecare
malloc trebuie să aibă exact un
free corespunzător, iar fiecare new exact un delete.
Scrieți-le pereche, chiar în momentul alocării, ca să nu le uitați.8Cod sursă
vector_dinamic.c - dimensiune stabilită la execuție
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int n;
printf("Cate elemente? ");
if (scanf("%d", &n) != 1 || n <= 0) {
printf("Valoare invalida\n");
return 1;
}
int *v = (int *)malloc(n * sizeof(int)); // alocare exacta
if (v == NULL) { // verificare obligatorie
printf("Alocare esuata!\n");
return 1;
}
for (int i = 0; i < n; i++) {
printf("v[%d] = ", i);
scanf("%d", &v[i]);
}
long suma = 0;
for (int i = 0; i < n; i++) suma += v[i];
printf("\nSuma = %ld, media = %.3f\n", suma, (double)suma / n);
free(v); // eliberare
v = NULL; // protectie impotriva folosirii accidentale
return 0;
}
redimensionare.c - realloc folosit corect
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int capacitate = 4, nr = 0;
int *v = (int *)malloc(capacitate * sizeof(int));
if (!v) return 1;
printf("Introduceti numere (0 = stop):\n");
int x;
while (scanf("%d", &x) == 1 && x != 0) {
if (nr == capacitate) { // s-a umplut: dublam capacitatea
capacitate *= 2;
int *temp = (int *)realloc(v, capacitate * sizeof(int));
if (temp == NULL) { // NU atribuim direct lui v!
printf("Redimensionare esuata\n");
free(v);
return 1;
}
v = temp;
printf(" [capacitate marita la %d]\n", capacitate);
}
v[nr++] = x;
}
printf("\nAti introdus %d valori: ", nr);
for (int i = 0; i < nr; i++) printf("%d ", v[i]);
printf("\n");
free(v);
return 0;
}
matrice_dinamica.c - tablou bidimensional alocat dinamic
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int linii, coloane;
printf("Linii si coloane: ");
scanf("%d %d", &linii, &coloane);
// 1. tablou de pointeri catre linii
int **m = (int **)malloc(linii * sizeof(int *));
if (!m) return 1;
// 2. cate o linie pentru fiecare pointer
for (int i = 0; i < linii; i++) {
m[i] = (int *)malloc(coloane * sizeof(int));
if (!m[i]) { // eliberam ce am alocat deja
for (int k = 0; k < i; k++) free(m[k]);
free(m);
return 1;
}
}
for (int i = 0; i < linii; i++)
for (int j = 0; j < coloane; j++)
m[i][j] = (i + 1) * (j + 1); // tabla inmultirii
for (int i = 0; i < linii; i++) {
for (int j = 0; j < coloane; j++) printf("%5d", m[i][j]);
printf("\n");
}
// eliberare in ordine INVERSA alocarii
for (int i = 0; i < linii; i++) free(m[i]);
free(m);
return 0;
}
new_delete.cpp - varianta C++
#include <iostream>
using namespace std;
int main()
{
int n;
cout << "Cate elemente? ";
cin >> n;
int *v = new int[n]; // fara conversie, fara sizeof
for (int i = 0; i < n; i++) v[i] = (i + 1) * (i + 1);
for (int i = 0; i < n; i++) cout << v[i] << " ";
cout << endl;
delete[] v; // OBLIGATORIU cu paranteze patrate
// alocare cu initializare
int *p = new int(42);
cout << "*p = " << *p << endl;
delete p; // fara paranteze, pentru o singura valoare
return 0;
}
9Atelier de cod
În modul Pas cu pas, panoul din dreapta are o secțiune heap: acolo apar blocurile alocate cu malloc și se vede exact când devin „eliberate”.
De încercatȘtergeți linia
free(v); și rulați din nou: programul funcționează la fel. Tocmai de aceea scurgerile de memorie sunt periculoase - nu se văd. Acum mutați free(v); înainte de bucla de afișare și veți primi o eroare clară.10Sarcini de lucru
- Alocați dinamic un vector de dimensiune citită de la tastatură și calculați suma și media.
- Verificați ce returnează
mallocpentru o cerere absurd de mare (de exemplu 10¹⁰ octeți). - Comparați conținutul zonelor obținute cu
mallocși cucalloc, imediat după alocare. - Implementați un vector care își dublează capacitatea prin
realloccând se umple. - Alocați dinamic o matrice și eliberați-o corect, în ordinea inversă alocării.
- Omiteți intenționat
freeîntr-o buclă și observați creșterea memoriei în Task Manager. - Rescrieți unul dintre programe în C++, folosind
newșidelete[].
11Aplicație de aprofundare
ExtindereImplementați o listă simplu înlănțuită de numere întregi, cu alocare
dinamică pentru fiecare nod: adăugare la început și la sfârșit, ștergerea unui element după valoare,
afișarea listei și eliberarea completă a memoriei. Verificați cu un contor global că numărul de apeluri
malloc este egal cu numărul de apeluri free la terminarea programului.