POKAZIVAČI U JEZIKU C++
Sadržaj lekcije: Pokazivači u C++
Dobrodošli na lekciju posvećenu pokazivačima u jeziku C++! U ovoj lekciji ćemo detaljno objasniti šta su pokazivači, kako funkcionišu, kao i koje su njihove prednosti i mane. Upoznaćete se sa osnovnim konceptima, aritmetikom pokazivača, vezom između pokazivača i nizova, te načinima dinamičke alokacije memorije. Takođe, pokrićemo i modernije pristupe, kao što su pametni pokazivači, koji omogućavaju sigurnije upravljanje memorijom.
Uvod u pokazivače
Svaka memorijska lokacija ima neku svoju adresu. To je broj pridružen nekoj lokaciji. Najmanja memorijska lokacija koja može samostalno da se adresira je obično jedan bajt.
Pokazivač je prost podatak u koji može da se smesti adresa neke lokacije u memoriji.
Podaci na koje pokazuju pokazivači mogu biti različitih tipova.
Adresa nekog podatka u memoriji može da se dobije pomoću prefiksnog unarnog operatora &. Pogledaj više o operatorima na stranici operatori u jeziku C/C++.
Definisanje pokazivača
Posmatrajmo sledeći kod:
double x =2;
double *pdb;
pdb= &x;
Ako bi smo želeli da u prethodnom primeru uvedemo još jedan podatak tipa double npr. y i da inicijalizujemo ovu vrednost koristeći pokazivač na x, dodali bi sledeći kod:
double x=2;
double *pdb;
pdb= &x;
double y;
y= *pdb; //y je sada 2
Adresna aritmetika
- dodela vrednosti jednog pokazivača drugom
- dodavanje celobrojnog podatka na vrednost pokazivača i oduzimanje celobrojnog podatka od vrednosti pokazivača
- oduzimanje i upoređivanje dva pokazivača
- upoređivanje pokazivača nulom
Pokazivači i nizovi
std::cout << *(pA) << "\n";
U 4. ciklusu npr. i = 3, pa će vrednost memorijske adrese biti veća za 3*4=12 bajta, što je takođe prikazano na slici.
Kompletan kod se može videti u nastavku:
using namespace std;
int main() {
int *pA = A;
for (int i = 0; i < 7; i++) {
cout << *(pA + i) << "\\n";
return 0;
Pokazivači i stringovi
Da bi ispisali te znakove na ekranu, koristićemo petlju, a character-ima pristupiti pomoću pokazivača. Definišimo pokazivač na podatak tipa char i inicijalizujmo ga memorijskom adresom prvog elementa niza tekst:
using namespace std;
int main() {
char* pTekst = tekst;
while (*pTekst != '\\0') {
pTekst++; // Pomeranje pokazivača na sledeći znak (character)
return 0;
Pomeranje pokazivača, da pokazuje na sledeći znak, se ostvaruje povećanjem vrednosti pokazivača za 1. U ovom slučaju dužina char podatka je 1 bajt, pa će se adresa na koju pokazuje pokazivač promeniti u svakom ciklusu za 1 bajt a ne za 4 bajta, što je bio slučaj kod podataka tipa int.
Posle izvršenja programa, prikazaće se:
Testirajte svoj kod u editoru ispod!
Praktični Primeri: Dinamičko Alociranje Memorije
1. Kreiranje i oslobađanje dinamičkog niza:
using namespace std;
// Dinamičko alociranje memorije za niz
int main() {
// Popunjavanje niza vrednostima i ispis
for (int i = 0; i < 10; ++i) {
cout << niz[i] << " "; // Ispis elementa
// Oslobađanje memorije nakon korišćenja
delete[] niz; // Sprečava curenje memorije
return 0; // Kraj programa
2. Dinamički objekti sa konstruktorom i destruktorom:
using namespace std;
// Definicija klase Student
class Student {
public:
Student(string i) : ime(i) {} // Konstruktor koji postavlja ime studenta
~Student() {
int main() {
cout << s->ime << endl; // Ispis imena studenta
delete s; // Poziva destruktor i oslobađa memoriju
return 0; // Kraj programa
- Konstruktor: Kada se objekat Student kreira sa new, poziva se konstruktor Student(string i) koji postavlja ime studenta.
- Destruktor: Kada se objekat obriše sa delete, poziva se destruktor ~Student(). On ispisuje poruku da je objekat obrisan, što je korisno za praćenje života objekta u memoriji.
- Dinamičko alociranje: Korišćenjem new alociramo objekat na heap-u, a delete oslobađa tu memoriju.
3. Matrica kao pokazivač pokazivača:
using namespace std;
// Dinamička alokacija matrice kao pokazivač pokazivača
int main() {
// Alokacija kolona i popunjavanje matrice
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
// Ispis matrice
for (int i = 0; i < 3; ++i) {
cout << endl; // Novi red
// Oslobađanje memorije
for (int i = 0; i < 3; ++i) {
delete[] matrica; // Oslobađanje pokazivača na pokazivače
return 0;
4. Rad sa pokazivačem u funkciji
using namespace std;
// Funkcija za inicijalizaciju vrednosti
void inicijalizuj(int* p, int vrednost) {
int main() {
int* broj = new int;
// Poziv funkcije za inicijalizaciju
inicijalizuj(broj, 42);
// Ispis vrednosti
cout << *broj << " "; // Ispisuje 42
// Oslobađanje memorije
delete broj;
return 0;
- Funkcija inicijalizuj:
- Prima pokazivač p i vrednost vrednost.
- Direktno modifikuje vrednost na memorijskoj lokaciji na koju pokazuje p.
- Dinamička alokacija:
- Pokazivač broj alocira prostor za ceo broj u memoriji pomoću new int.
- Poziv funkcije:
- Funkcija inicijalizuj postavlja vrednost na memorijsku lokaciju, što omogućava kasniji pristup i manipulaciju tom vrednošću.
- Oslobađanje memorije:
- Memorija zauzeta sa new int mora se osloboditi pomoću delete kako bi se izbeglo curenje memorije.
5. Dinamička alokacija memorije za vektor
#include <vector>
using namespace std;
// Rad sa dinamički alociranim vektorom
int main() {
// Dodavanje elemenata u vektor
for (int i = 0; i < 5; ++i) {
// Ispis elemenata vektora
for (int i : vektor) {
return 0; // Kraj programa
6. Dinamička alokacija strukture podataka
using namespace std;
// Definisanje strukture
struct Osoba {
int godine; // Godine osobe
int main() {
// Dodela vrednosti članovima strukture
osoba->ime = "Marko";
osoba->godine = 25;
// Ispis informacija o osobi
cout << "Ime: " << osoba->ime << ", Godine: " << osoba->godine << endl;
// Oslobađanje memorije
delete osoba;
return 0;
Poređenje sa referencama
Primer:
using namespace std;
// Funkcija koja koristi pokazivač za izmenu vrednosti
void izmeniPokazivacem(int* ptr) {
// Funkcija koja koristi referencu za izmenu vrednosti
void izmeniReferencom(int& ref) {
int main() {
cout << "Početna vrednost: " << broj << endl;
// Koristeći pokazivač
izmeniPokazivacem(&broj); // Prosleđuje adresu promenljive
cout << "Nakon izmene pokazivačem: " << broj << endl;
// Koristeći referencu
izmeniReferencom(broj); // Prosleđuje referencu na promenljivu
cout << "Nakon izmene referencom: " << broj << endl;
return 0; // Završetak programa
- Pokazivač: Funkcija izmeniPokazivacem prima adresu promenljive (int* ptr) i koristi dereferenciranje (*ptr) da izmeni vrednost na toj adresi.
- Referenca: Funkcija izmeniReferencom koristi referencu (int& ref) koja je alias za promenljivu, omogućavajući direktnu izmenu vrednosti.
Razlika u korišćenju:
- Pokazivači zahtevaju eksplicitno prosleđivanje adrese (&) i dereferenciranje (*).
- Reference su intuitivnije jer se ponašaju kao obične promenljive bez dodatne sintakse.
Moderni standardi u C++: Pametni pokazivači i RAII
Od C++11 standarda uvedeni su pametni pokazivači kao što su std::unique_ptr i std::shared_ptr, koji značajno olakšavaju upravljanje dinamički alociranom memorijom. Oni primenjuju princip RAII (Resource Acquisition Is Initialization), što znači da se resursi automatski oslobađaju kada objekat izađe iz opsega. Ovaj pristup smanjuje rizik od curenja memorije i drugih grešaka povezanih sa ručnim upravljanjem memorijom (korišćenjem new i delete).
Prednosti korišćenja pametnih pokazivača:
- Automatsko oslobađanje memorije: Ne morate ručno pozivati delete ili delete[].
- Bezbednost: Izbegavaju se greške poput dvostrukog oslobađanja memorije.
- Jednostavnost koda: Kod postaje čitljiviji i održiviji, jer se upravljanje resursima vrši automatski.
Sledeći primer demonstrira kako se koriste std::unique_ptr i std::shared_ptr u praksi:
// Primer klase koja se koristi za demonstraciju
class Primer {
public:
void prikazi() {
std::cout << "Ovo je primer pametnog pokazivača." << std::endl;
}
};
// Glavna funkcija
int main() {
// Korišćenje std::unique_ptr: Garantuje jedinstveno vlasništvo nad objektom
std::unique_ptr<Primer> jedinstveniPokazivac = std::make_unique<Primer>();
jedinstveniPokazivac->prikazi();
// Korišćenje std::shared_ptr: Više pokazivača deli vlasništvo nad istim objektom
std::shared_ptr<Primer> deljeniPokazivac1 = std::make_shared<Primer>();
std::shared_ptr<Primer> deljeniPokazivac2 = deljeniPokazivac1; // Oba pokazivača dele isti objekat
deljeniPokazivac2->prikazi();
return 0;
}
Kao što se vidi, std::unique_ptr garantuje jedinstveno vlasništvo, dok std::shared_ptr omogućava deljenje vlasništva između više pokazivača. Ovaj moderni pristup čini kod sigurnijim i jednostavnijim za održavanje, što je posebno važno u kompleksnim projektima.
Dodatno objašnjenje:
Pametni pokazivači i RAII principi su ključni elementi modernog C++ programiranja, te je preporučljivo uvesti ovakve odeljke u postojeću dokumentaciju kako bi se čitaoci upoznali sa najnovijim praksama i standardima.
Detaljnija analiza grešaka prilikom rada sa pokazivačima
Prilikom rada sa pokazivačima u C++-u često se javljaju greške koje mogu dovesti do ozbiljnih problema u programu, kao što su curenje memorije i dangling pokazivači. U nastavku su predstavljeni primeri čestih grešaka i predložena rešenja i preventivne mere.
Curenje memorije (Memory Leak)
Curenje memorije se događa kada dinamički alocirana memorija nije pravilno oslobođena, što može rezultirati postepenim smanjenjem dostupne memorije i padom performansi programa.
// Primer curenja memorije
int* curenje() {
int* p = new int[10];
return p; // Memorija nije oslobođena, što može dovesti do curenja memorije
}
int main() {
int* niz = curenje();
/* Korišćenje niza */
delete[] niz; // Ako se ovaj delete izostavi, doći će do curenja memorije
return 0;
}
Rešenje: Koristite pametne pokazivače, poput std::unique_ptr ili std::shared_ptr, koji automatski oslobađaju memoriju kada objekt više nije u upotrebi.
// Rešenje sa std::unique_ptr
#include <memory>
#include <iostream>
std::unique_ptr<int[]> curenjeIspravljeno() {
return std::make_unique<int[]>( 10 );
}
int main() {
auto niz = curenjeIspravljeno();
// Niz će biti automatski oslobođen kada izađe iz opsega
std::cout << "Pametni pokazivači sprečavaju curenje memorije." << std::endl;
return 0;
}
Dangling pokazivači
Dangling pokazivači nastaju kada pokazivač referencira memorijski prostor koji je oslobođen ili je više nepostojeći. Korišćenje takvih pokazivača može dovesti do nepredvidivog ponašanja i rušenja programa.
// Primer dangling pokazivača
int* dangling() {
int a = 42;
return &a; // Vraća adresu lokalne promenljive, koja će biti uništena nakon izlaska iz funkcije
}
int main() {
int* p = dangling();
// Pokušaj pristupa oslobođenoj memoriji
std::cout << *p; // Nepouzdano, p ukazuje na nevalidnu memorijsku lokaciju
return 0;
}
Preventivne mere:
- Nikada ne vraćajte adresu lokalne promenljive iz funkcije.
- Ako je potrebno da funkcija vrati pokazivač, koristite dinamičku alokaciju ili pametne pokazivače.
- Nakon oslobađanja memorije, postavite pokazivač na nullptr kako biste sprečili neželjene pristupe.
Primena ovih preventivnih mera i korišćenje modernih tehnika upravljanja memorijom značajno smanjuje mogućnost grešaka pri radu sa pokazivačima u C++-u.
Dodatna dokumentacija
Za one koji žele da prodube svoje znanje o modernim C++ tehnikama i praksama, preporučujemo pregled sledećih resursa. Ovi resursi obuhvataju knjige, blogove i forume koji se redovno ažuriraju i pružaju primere iz stvarnog sveta.
-
Knjige:
- Effective Modern C++ – Scott Meyers
- C++ Primer (5th Edition) – Stanley B. Lippman, Josée Lajoie i Barbara E. Moo
- Modern C++ Programming Cookbook – Marius Bancila
-
Blogovi:
- ISO C++ – Zvanični blog o standardima i novostima u C++ zajednici.
- Fluent C++ – Blog sa savetima, trikovima i detaljnim analizama modernog C++ programiranja.
- Bartosz Filipek's Blog – Resurs sa tutorijalima i analizama savremenih tehnika u C++-u.
-
Forumi:
- Stack Overflow – C++ tag – Mesto gde možete postavljati pitanja i učiti iz iskustava drugih programera.
- Reddit /r/cpp – Aktivna zajednica koja diskutuje o svim aspektima C++ programiranja.
- C++ Forum – Forum za diskusije, savete i rešavanje problema u C++-u.
Ovi resursi nude širok spektar informacija – od osnova jezika do naprednih tehnika i praksi. Preporučujemo ih svima koji žele da budu u toku sa najnovijim standardima i da unaprede svoje programerske veštine u C++-u.
Prethodno
|< Dvodimenzionalni nizovi - matrice u c++ |
Sledeće
Funkcije u C++ >| |