POO Cauta

Saptamana 02 / 14 Programare Orientata Obiect

Recapitulare - pointeri si memorie dinamica

Pointeri si operatii pe pointeri, const si pointeri, vectori si matrice, citire si afisare cu cin si cout, new si delete, memory leaks si dangling pointers.

Obiective
  • Sa folosesti corect operatorii de adresa si de dereferentiere si nullptr
  • Sa explici ce inseamna p + 1 si de ce p[i] este acelasi lucru cu *(p + i)
  • Sa deosebesti pointerul la constanta, pointerul constant si pointerul constant la constanta
  • Sa lucrezi cu vectori si matrice statice si sa le transmiti functiilor
  • Sa aloci si sa eliberezi memorie cu new / delete si new[] / delete[]
  • Sa recunosti un memory leak si un dangling pointer si sa spui cine detine un bloc de memorie

Pointeri: adresa si valoare

Un pointer este o variabila care retine o adresa. Doi operatori fac toata treaba:

int a = 5;
int* p = &a;        // p retine adresa lui a
*p = 8;             // scrie in a
int* q = nullptr;   // nu arata nicaieri, intentionat

Un pointer care inca nu are unde sa arate trebuie sa fie nullptr. Nu il dereferentia niciodata: *q opreste programul.

Operatii pe pointeri

Daca aduni 1 la un pointer, el trece la elementul urmator, nu la octetul urmator. Compilatorul inmulteste singur cu dimensiunea tipului. De aceea p[i] si *(p + i) sunt acelasi lucru.

#include <iostream>
using namespace std;
int main() {
    int v[5] = {10, 20, 30, 40, 50};
    int* p = v;
    p += 2;
    cout << *p << " " << *(p + 1) << " " << p[-1] << endl;
    cout << p - v;
}

Diferenta a doi pointeri in acelasi vector este numarul de elemente dintre ei. Sa compari sau sa scazi pointeri din vectori diferiti nu are sens.

const si pointeri

Citeste declaratia de la dreapta la stanga. const se aplica la ce este imediat in stanga lui (sau in dreapta, daca e primul).

DeclaratieModifici *p?Modifici p?
const int* p (pointer la constanta)NuDa
int* const p (pointer constant)DaNu
const int* const pNuNu
int a = 1, b = 2;
const int* p1 = &a;   p1 = &b;   // OK      *p1 = 3;  // eroare
int* const p2 = &a;   *p2 = 5;   // OK      p2 = &b;  // eroare
const int* const p3 = &a;        // *p3 = 3; si p3 = &b; sunt ambele erori

Vectori si matrice

Numele unui vector static se comporta ca un pointer constant la primul element: v[i] merge, v = alt_vector nu compileaza. Cand transmiti un vector unei functii, se copiaza doar adresa, deci dimensiunea trebuie trimisa separat:

int suma(const int v[], int n);   // la fel ca const int* v

O matrice int m[2][3] este memorata linie cu linie, ca 6 intregi consecutivi.

#include <iostream>
using namespace std;
int main() {
    int m[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int s = 0;
    for (int i = 0; i < 2; i++) s += m[i][2];
    cout << s << " " << m[1][0];
}

cin, cout si new[]

Cand dimensiunea se afla abia la executie, aloca pe heap cu new[] si elibereaza cu delete[]. Ruleaza, apoi incearca alte date de intrare.

#include <iostream>
using namespace std;
int main() {
  int n;
  cin >> n;
  int* v = new int[n];
  for (int i = 0; i < n; i++) cin >> v[i];
  int s = 0;
  for (int i = 0; i < n; i++) s += v[i];
  cout << "suma: " << s << endl;   // asteptat pentru 4 7 9 10 6: suma: 32
  delete[] v;
  v = nullptr;
}
Intrare (stdin)

Deschide in Godbolt

new / delete si new[] / delete[]

Fiecare alocare are exact o eliberare pereche:

AlocareEliberare
new int(7)delete p;
new int[n]delete[] p;
malloc(...)free(p);

Daca le amesteci (delete pe un bloc new[], free pe un bloc new), ai comportament nedefinit. La fel daca eliberezi acelasi bloc de doua ori.

STIVA
v: ●
HEAP
[7][9][10]

int* v = new int[3]{7, 9, 10}; - v este pe stiva, cei trei intregi sunt pe heap.

Pasul 1/3

Memory leaks

Un leak este un bloc de pe heap a carui adresa nu o mai ai. Nimeni nu il mai poate elibera.

int* p = new int[10];
p = new int[10];      // primul bloc s-a pierdut: leak

void f() {
    int* t = new int[3];
}                     // t dispare, blocul ramane: leak

Inainte sa scrii new, hotaraste cine detine blocul, adica cine va apela delete. Daca o functie intoarce un pointer la memorie noua, apelantul o detine, iar comentariul de deasupra functiei trebuie sa spuna asta.

Dangling pointers

Un dangling pointer retine o adresa la care nu mai exista un obiect valid. Doua moduri obisnuite de a obtine unul:

int* creeazaValoare() {
    int valoare = 10;
    return &valoare;  // gresit: valoare dispare la iesirea din functie
}

int* p = new int(5);
delete p;
*p = 6;               // gresit: blocul a fost eliberat

g++ te avertizeaza pentru primul caz (address of local variable returned). Citirea printr-un dangling pointer este comportament nedefinit: poate afisa vechea valoare, gunoi, sau programul se poate opri.

Verifica-te

1. Avem int a = 1; const int* p = &a;. Ce NU poti face?

2. Avem int a = 1, b = 2; int* const p = &a;. Care linie NU compileaza?

3. Ce afiseaza programul?
#include <iostream>
using namespace std;
int main() {
    int v[4] = {1, 2, 3, 4};
    int* p = v + 1;
    *(p + 1) = 10;
    p++;
    cout << *p << " " << v[2] << " " << p[-2];
}

4. Cum eliberezi memoria din int* v = new int[n];?

5. Care dintre urmatoarele produc memory leak?

6. Ce afiseaza programul?
#include <iostream>
using namespace std;
void mareste(int*& p, int& n) {
    int* q = new int[n + 1];
    for (int i = 0; i < n; i++) q[i] = p[i];
    q[n] = 100;
    delete[] p;
    p = q;
    n++;
}
int main() {
    int n = 2;
    int* v = new int[n]{5, 6};
    mareste(v, n);
    cout << n << " " << v[0] + v[2];
    delete[] v;
}

7. Ce este gresit la int* creeazaValoare() { int valoare = 10; return &valoare; }?

Scor: 0/7

Codul din repository: S02

Varianta in engleza