Ce face pointeri (*, &)
Fiecare variabilă se află undeva în memorie, la o adresă. &x dă adresa lui x, iar un pointer (int* p) este o variabilă care ține o astfel de adresă. *p („dereferențiere”) înseamnă „valoarea de la adresa din p”: citind sau scriind *p, lucrezi de fapt cu x.
Pentru obiecte, p->membru este prescurtarea pentru (*p).membru. Un pointer care nu arată spre nimic trebuie să fie nullptr; verifică-l înainte de folosire. Numele unui tablou se transformă în pointer la primul element, iar p + 1 arată spre elementul următor.
În C++ modern, pointerii „simpli” (T*) se folosesc doar pentru a observa obiecte care aparțin altcuiva. Pentru obiecte create dinamic folosește std::unique_ptr și std::shared_ptr, iar pentru parametri care nu pot lipsi, referințe.
Exemple
Exemplul 1: Adresă, dereferențiere, modificare
Adresa în sine nu este afișată, pentru că diferă la fiecare rulare; afișăm doar faptul că p arată spre stoc.
#include <print>
int main() {
int stoc = 20;
int* p = &stoc;
std::println("p arată spre stoc: {}", p == &stoc);
std::println("*p = {}", *p);
*p -= 5; // modificăm stoc prin pointer
std::println("stoc = {}", stoc);
}
Rezultat
p arată spre stoc: true
*p = 20
stoc = 15
Exemplul 2: Pointer ca parametru opțional
Funcția modifică variabila primită prin adresă. Când primește nullptr, pur și simplu nu scrie rezultatul.
#include <print>
bool imparte(int a, int b, int* rest) {
if (b == 0) return false;
if (rest != nullptr) {
*rest = a % b;
}
return true;
}
int main() {
int r = 0;
if (imparte(17, 5, &r)) {
std::println("Rest: {}", r);
}
std::println("Fără rest cerut: {}", imparte(17, 5, nullptr));
std::println("Împărțire la 0: {}", imparte(1, 0, &r));
}
Rezultat
Rest: 2
Fără rest cerut: true
Împărțire la 0: false
Exemplul 3: Pointeri și tablouri, săgeata ->
#include <print>
#include <string>
struct Oras {
std::string nume;
int populatie;
};
int main() {
int note[] = {7, 9, 10};
int* p = note; // pointer la primul element
std::println("{} {} {}", *p, *(p + 1), p[2]);
Oras iasi{"Iași", 271692};
Oras* o = &iasi;
o->populatie += 8;
std::println("{} are {} locuitori", o->nume, iasi.populatie);
}
Rezultat
7 9 10
Iași are 271700 locuitori
Greșeli frecvente
- Folosești un pointer neinițializat (
int* p; *p = 5;). Arată spre o adresă oarecare: comportament nedefinit. Inițializează-l mereu (int* p = nullptr;). - Păstrezi un pointer spre o variabilă locală după ce funcția s-a terminat, sau spre un element al unui
std::vectordupăpush_back(vectorul se poate muta în altă zonă de memorie). Pointerul devine „atârnat”. - Confunzi
*din declarație (int* p, „p este pointer”) cu*din expresie (*p, „valoarea de la p”). Înint* a, b;, doaraeste pointer;besteint.
Comenzi înrudite
referințe (&, &&): O referință este un alt nume pentru o variabilă existentă; tot ce faci cu referința se face de fapt cu variabila originală.nullptr: Valoarea unui pointer care nu arată spre nimic; înlocuiește vechile NULL și 0.new: Creează un obiect în memoria dinamică (heap) și returnează un pointer la el; obiectul trăiește până este distrus explicit cu delete.delete: Distruge un obiect creat cu new și eliberează memoria; scris ca = delete, interzice folosirea unei funcții.const: Marchează o valoare care nu mai poate fi modificată după inițializare; compilatorul oprește orice încercare de schimbare.std::unique_ptr: Pointer inteligent care deține singur un obiect și îl șterge automat când iese din domeniu.