Pokazivači u jeziku 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
Svaki podatak koji se koristi u nekom programu mora biti definisan, tj. za njega se mora rezervisati memorija. Dodela vrednosti nekom podatku znači da se ta vrednost u binarnom obliku smesti na određenu memorijsku lokaciju. Pri tome je za podatak zauzeto onoliko bitova memorije koliko se zauzima za određeni tip podatka. Npr. za podatak tipa int u jeziku C/C++ to bi bilo 16 ili 32 bita.
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.Kada neki podatak zauzima više bajtova, pod adresom podatka podrazumeva se bajt sa najmanjim rednim brojem.
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
* identifikator_pokazivača
Posmatrajmo sledeći kod:
int a = 1;
int *pint;
pint = &a;
cout << a << endl;
cout << pint << endl;
Na standardnom izlazu:

Uveden je celobrojni podatak a i inicijalizovan na vrednost 1. Takođe je uveden i pokazivac pint na celobrojni podatak i on je inicijalizovan sa memorijskom adresom podatka a. Primetimo da se prilikom štampanja vrednosti pokazivača dobija veliki broj u heksadecimalnom zapisu. Adrese u memoriji su obično ovakve vrednosti.Opis tipa podatka na početku deklarativne naredbe označava tip pokazivanog podatka. U prethodnom je to int tj. pokazivač pokazuje na ceo broj. Ukoliko bi podatak bio tipa double onda bi se pokazivač na podatak tipa double definisao i inicijalizovao na sledeći način:
double x = 2;
double *pdb;
pdb = &x;
Dodela vrednosti podatku preko pokazivača na drugi podatak
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
U jeziku C/C++ dozvoljene su sledeće operacije nad pokazivačima:
- 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
Pretpostavimo da je definisan sledeći niz celih brojeva:
int A[7] = {5, -2, 3, 8, 11, 0, 25};
Da bi ispisali elemente niza korišćenjem pokazivača uvešćemo pokazivač pA i inicijalizovati ga memorijskom adresom prvog elementa niza:
int *pA = A;
Da bi odštampali vrednost elementa niza na koji pokazivač pokazuje treba upotrebiti *pA:
std::cout << *pA << "\n";
Da bi pokazivač menjao element na koji pokazuje tokom izvršavanja petlje, potrebno ga je inkrementirati, tj. povećati njegovu vrednost za 1:
pA++;
std::cout << *(pA) << "\n";
ili:
std::cout << *(pA + i) << "\n";
U prvom ciklusu, za i = 0 vrednost pokazivača, tj. adresa prvog elementa niza ima vrednost npr. 0xfdf0(heksa decimalni broj) vidi sliku 3. U svakom ciklusu vrednost pokazivača se pomeri za 1(pA++), što znači da će nova adresa biti veća za veličinu podatka u bajtovima na koji pokazuje pokazivač. Ta veličina u ovom slučaju iznosi 4 (int podatak ima veličinu od 4 bajta). Ova vrednost bi se dobila i sa sizeof(int).
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.

gde se promenljiva "i" menja tokom ciklusa.
Kompletan kod se može videti u nastavku:
#include <iostream>
using namespace std;
int main() {
int A[7] = {5, -2, 3, 8, 11, 0, 25};
int *pA = A;
for (int i = 0; i < 7; i++) {
cout << (pA + i) << "\n";
cout << *(pA + i) << "\n";
}
return 0;
}
Ako pokrenemo debuger i zaustavimo program unutar petlje, možemo da analiziramo kako se vrednost adrese, na koju pokazuje pokazivač menja tokom ciklusa, kada se vrednost pokazivača promeni za 1. Za i = 3 pokazivač se pomera, tako da pokazuje na 4. element u nizu, a to znači, da se adresa uvećala za 3 * 4 bajta, tj. sa vrednosti 0x61fdf0(0 je završna) promeni se na 0x61fdfc(c je završno i to predstavlja broj 12 u heksadecimalnom sistemu).

Pokazivači i stringovi
Neka je zadat string, odnosno niz char podataka na sledeći način:
char tekst[17] = "Danas je lep dan";
Elementi ovog niza su znakovi, s tim što je poslednji znak '\0'. Više o stringovima u jeziku C pročitajte u članku: C 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:
char *ptr = tekst;
Upotrebićemo while petlju, a znakovima pristupiti koristeći pokazivač na podatak tipa char. Kompletan kod je prikazan ispod.
#include <iostream>
using namespace std;
int main() {
char tekst[17] = "Danas je lep dan";
char *pTekst = tekst;
while (*pTekst != '\0') {
cout << *pTekst << "\n"; // Ispisuje znak na koji pokazuje pokazivač pTekst
pTekst++; // Pomeranje pokazivača na sledeći znak (character)
}
return 0;
}
Uslov koji se ispituje u while petlji je da vrednost koda tekućeg character-a bude različita od vrednosti koda poslednjeg character-a, dakle, da bude različit od '\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
Dinamičko alociranje memorije je ključan koncept u programiranju koji omogućava efikasno upravljanje memorijskim resursima tokom izvršavanja programa. Ova tehnika omogućava kreiranje promenljivih i struktura čija veličina nije unapred poznata, što je posebno korisno za rad sa velikim podacima, matricama, i dinamičkim strukturama poput lista ili stabala. U ovoj oblasti istražujemo praktične primere koji uključuju alokaciju, inicijalizaciju i oslobađanje memorije, uz objašnjenje kako izbeći uobičajene greške poput curenja memorije. Savladajte osnove kroz jasno objašnjene kodove i korak-po-korak uputstva!
1. Kreiranje i oslobađanje dinamičkog niza
Primer prikazuje osnovno dinamičko alociranje i oslobađanje memorije za niz, uključujući ispis elemenata.
#include <iostream>
using namespace std;
// Dinamičko alociranje memorije za niz
int main() {
int *niz = new int[10]; // Dinamička alokacija niza veličine 10
// Popunjavanje niza vrednostima i ispis
for (int i = 0; i < 10; ++i) {
niz[i] = i * 2; // Svaki element dobija vrednost i * 2
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
Primer ilustruje rad sa dinamički alociranom matricom kao nizom pokazivača na nizove, uključujući i oslobađanje memorije.
#include <iostream>
using namespace std;
// Definicija klase Student
class Student {
public:
string ime; // Atribut koji sadrži ime studenta
Student(string i)
: ime(i) {} // Konstruktor koji postavlja ime studenta
~Student() {
cout << "Student " << ime << " je obrisan." << endl; // Destruktor koji ispisuje poruku prilikom brisanja objekta
}
};
int main() {
Student *s = new Student("Marko"); // Dinamičko alociranje objekta Student
cout << s->ime << endl; // Ispis imena studenta
delete s; // Poziva destruktor i oslobađa memoriju
return 0; // Kraj programa
}
Objašnjenje:
- 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
Primer prikazuje kako se koristi dinamička alokacija memorije za matricu pomoću pokazivača na pokazivače. Ovaj pristup omogućava fleksibilno upravljanje memorijom za dvodimenzionalne strukture, ali zahteva pravilno oslobađanje memorije kako bi se izbeglo curenje.
#include <iostream>
using namespace std;
// Dinamička alokacija matrice kao pokazivač pokazivača
int main() {
int **matrica = new int *[3]; // Alokacija reda pokazivača
// Alokacija kolona i popunjavanje matrice
for (int i = 0; i < 3; ++i) {
matrica[i] = new int[4]; // Alokacija svakog reda
for (int j = 0; j < 4; ++j) {
matrica[i][j] = i + j; // Popunjavanje matrice vrednostima
}
}
// Ispis matrice
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 4; ++j) {
cout << matrica[i][j] << " "; // Ispis svakog elementa
}
cout << endl; // Novi red
}
// Oslobađanje memorije
for (int i = 0; i < 3; ++i) {
delete[] matrica[i]; // Oslobađanje svakog reda
}
delete[] matrica; // Oslobađanje pokazivača na pokazivače
return 0;
}
4. Rad sa pokazivačem u funkciji
Ovaj primer prikazuje kako funkcije mogu manipulisati dinamički alociranom memorijom koristeći pokazivače. Funkcija prima pokazivač kao parametar i omogućava direktnu modifikaciju vrednosti na memorijskoj lokaciji. Takav pristup omogućava efikasnu manipulaciju podacima u memoriji.
#include <iostream>
using namespace std;
// Funkcija za inicijalizaciju vrednosti
void inicijalizuj(int *p, int vrednost) {
*p = vrednost; // Postavlja vrednost na memorijsku lokaciju
}
int main() {
// Dinamička alokacija memorije za ceo broj
int *broj = new int;
// Poziv funkcije za inicijalizaciju
inicijalizuj(broj, 42);
// Ispis vrednosti
cout << *broj << " "; // Ispisuje 42
// Oslobađanje memorije
delete broj;
return 0;
}
Objašnjenja:
- 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
Treći primer ilustruje korišćenje dinamičke alokacije memorije za vektor i demonstrira operacije kao što su dodavanje elemenata i ispis sadržaja. Ovaj pristup je koristan kada veličina strukture podataka nije unapred poznata.
#include <iostream>
#include <vector>
using namespace std;
// Rad sa dinamički alociranim vektorom
int main() {
vector<int> vektor; // Deklaracija dinamičkog vektora
// Dodavanje elemenata u vektor
for (int i = 0; i < 5; ++i) {
vektor.push_back(i * 3); // Ubacuje vrednost i * 3 na kraj vektora
}
// Ispis elemenata vektora
for (int i : vektor) {
cout << i << " "; // Ispis svakog elementa
}
return 0; // Kraj programa
}
6. Dinamička alokacija strukture podataka
Četvrti primer pokazuje kako koristiti dinamičku alokaciju za strukturu podataka. Strukture su korisne za grupisanje povezanih podataka, dok se dinamička alokacija koristi za fleksibilno upravljanje memorijom.
#include <iostream>
using namespace std;
// Definisanje strukture
struct Osoba {
string ime; // Ime osobe
int godine; // Godine osobe
};
int main() {
Osoba *osoba = new Osoba; // Dinamička alokacija za strukturu
// 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
Pokazivači i reference su ključni koncepti u C++ jeziku koji omogućavaju indirektni rad sa promenljivama, ali imaju značajne razlike u sintaksi, funkcionalnosti i primeni. Pokazivač čuva adresu u memoriji i može se promeniti da pokazuje na drugi objekat, dok referenca predstavlja alias (pseudonim) za postojeći objekat i ne može biti reasignirana. Reference su jednostavnije za korišćenje i sigurnije, dok pokazivači pružaju veću fleksibilnost, posebno za dinamičko alociranje memorije. U nastavku ćemo detaljno objasniti ove razlike kroz praktične primere.
Primer
#include <iostream>
using namespace std;
// Funkcija koja koristi pokazivač za izmenu vrednosti
void izmeniPokazivacem(int *ptr) {
*ptr = 10; // Menja vrednost na koju pokazuje pokazivač
}
// Funkcija koja koristi referencu za izmenu vrednosti
void izmeniReferencom(int &ref) {
ref = 20; // Menja vrednost direktno kroz referencu
}
int main() {
int broj = 5; // Inicijalizacija promenljive
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
}
Objašnjenje:
- 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:
#include <iostream>
#include <memory>
// 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.
#include <iostream>
// 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.
Proveri znanje
Otvori kviz
Kviz: Pokazivači u C++-u
1. Šta je &x?
2. Ako je int *p = &x;, šta je *p?
3. Šta mora da prati svaki new?
4. Šta je nullptr?
| Prethodno |<Dvodimenzionalni nizovi - matrice u c++ |
Sledeće Funkcije u C++>| |
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.Kada neki podatak zauzima više bajtova, pod adresom podatka podrazumeva se bajt sa najmanjim rednim brojem.
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:
Primer prikazuje osnovno dinamičko alociranje i oslobađanje memorije za niz, uključujući ispis elemenata.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:
Primer ilustruje rad sa dinamički alociranom matricom kao nizom pokazivača na nizove, uključujući i oslobađanje memorije.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:
Primer prikazuje kako se koristi dinamička alokacija memorije za matricu pomoću pokazivača na pokazivače. Ovaj pristup omogućava fleksibilno upravljanje memorijom za dvodimenzionalne strukture, ali zahteva pravilno oslobađanje memorije kako bi se izbeglo curenje.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
Ovaj primer prikazuje kako funkcije mogu manipulisati dinamički alociranom memorijom koristeći pokazivače. Funkcija prima pokazivač kao parametar i omogućava direktnu modifikaciju vrednosti na memorijskoj lokaciji. Takav pristup omogućava efikasnu manipulaciju podacima u memoriji.using namespace std;
// Funkcija za inicijalizaciju vrednosti
void inicijalizuj(int* p, int vrednost) {
int main() {
int* broj = newint;
// 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
Treći primer ilustruje korišćenje dinamičke alokacije memorije za vektor i demonstrira operacije kao što su dodavanje elemenata i ispis sadržaja. Ovaj pristup je koristan kada veličina strukture podataka nije unapred poznata.#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
Četvrti primer pokazuje kako koristiti dinamičku alokaciju za strukturu podataka. Strukture su korisne za grupisanje povezanih podataka, dok se dinamička alokacija koristi za fleksibilno upravljanje memorijom.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 demonstracijuclass Primer {
public:
void prikazi() {
std::cout << "Ovo je primer pametnog pokazivača." << std::endl;
}
};
// Glavna funkcijaint 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 memorijeint* curenje() {
int* p = newint[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 memorijereturn 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 opsegastd::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čaint* 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 memorijistd::cout << *p; // Nepouzdano, p ukazuje na nevalidnu memorijsku lokacijureturn 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.
