O pokazivačima - Malo detaljnije
Zašto koristimo pokazivače?
Pokazivači su vrlo važan koncept u svim programskim jezicima. Svaki podatak se čuva u memoriji na nekoj adresi. Pokazivač nam kaže na kojoj adresi je naš podatak i kog je on tipa. Programski jezik C nam daje slobodu da podacima pristupamo direktno preko promenljivih i preko adresa korišćenjem pokazivača.
Pokazivači su jedna od glavnih prednosti jezika C. Oni služe za direktan pristup memoriji računara. Već smo rekli da kada deklarišemo neku promenljivu, ma koji bio njen tip, sistem mora da rezerviše potrebnu količinu memorije u koju će smeštati određene vrednosti. Da bi davali vrednost promenljivoj ili da bi smo njenu vrednost „čitali“ koristimo ime te promenljive. Ako pak deklarišemo pokazivač on će „pokazivati“ na lokaciju neke promenljive unutar memorije i dakle sadržaće adresu promenljive na koju pokazuje, a ne vrednost koja se nalazi na toj adresi.
...
Pokazivači i adrese
Memorija računara organizovana je u niz uzastopnih bajtova. Uzastopni bajtovi mogu se tretirati kao jedinstven podatak. Na primer, dva (ili četiri, u zavisnosti od sistema) uzastopna bajta mogu se tretirati kao jedinstven podatak celobrojnog tipa.
Pokazivači predstavljaju tip podataka u C-u takav da su vrednosti ovog tipa memorijske adrese. U zavisnosti od sistema, adrese obično zauzimaju četiri (ranije dva, a u novije vreme i osam) bajta.
Pokazivačke promenljive (promenljive pokazivačkog tipa) su promenljive koje sadrže memorijske adrese.
Iako su pokazivačke vrednosti (adrese) u suštini celi brojevi, pokazivački tipovi se striktno razlikuju od celobrojnih.
Takođe, jezik C razlikuje više pokazivačkih tipova i tip pokazivača se određuje na osnovu tipa podataka na koji pokazuje. Ovo znači da pokazivači implicitno čuvaju informaciju o tipu onoga na šta ukazuju, sa izuzetkom pokazivača tipa void koji nema informaciju o tipu podataka na koji ukazuje.
Tip pokazivača koji ukazuje na podatak tipa int zapisuje se int *. Slično važi i za druge tipove.
Prilikom deklaracije, nije bitno da li postoji razmak između zvezdice i tipa ili zvezdice i identifikatora i kako god da je napisano, zvezdica se vezuje uz identifikator, što je čest izvor grešaka.
int *p1;
int* p2;
int* p3, p4;
Dakle, u ovom primeru, p1, p2 i p3 su pokazivači koji ukazuju na int dok je p4 običan int.
Kako bi pokazivačka promenljiva sadržala adresu nekog smislenog podatka potrebno je programeru dati mogućnost odredivanja adresa objekata.
-
Unarni operator & (koji zovemo „operator referenciranja“ ili „adresni operator”) vraća adresu svog operanda. On može biti primenjen samo na promenljive i elemente, a ne i na izraze ili konstante.
-
Unarni operator * (koji zovemo „operator dereferenciranja“) se primenjuje na pokazivačku promenljivu i vraća sadržaj lokacije na koju ta promenljiva pokazuje, vodeći računa o tipu.
Simbol * koristi i za označavanje pokazivačkih tipova i za operator dereferenciranja i poželjno je jasno razlikovanje ove njegove dve različite uloge.
Ukoliko pokazivač ukazuje na neku promenljivu, posredno se menja i njen sadržaj.
Na primer, nakon dodela
int a=10, *p;
p = &a;
*p = 5;
promenljiva p ukazuje na a, a u lokaciju na koju ukazuje p je upisana vrednost 5. Time je i vrednost promenljive a postala 5.
Dereferencirani pokazivač nekog tipa, može se pojaviti u bilo kom kontekstu u kojem se može pojaviti podatak tog tipa. Na primer, u datom primeru ispravna bi bila i naredba *p = *p+a+3
Još jednom naglasimo da pokazivački i celobrojni tipovi različiti.
Tako je, na primer, ako je data deklaracija
int *pa, a;,
naredni kôd neispravan pa = a;.
Takođe, neispravno je i a = pa;, kao i pa = 1234.
Jedini izuzetak je 0 koja se može tumačiti i kao ceo broj i kao pokazivač. Tako je moguće dodeliti nulu pokazivačkoj promenljivoj i porediti pokazivač sa nulom. Simbolička konstanta NULL se često koristi umesto nule, kao jasniji indikator da je u pitanju specijalna pokazivačka vrednost. NULL je definisana u zaglavlju <stdio.h> i ubuduće ćemo uvek koristiti NULL kao vrednost za pokazivač koji ne pokazuje ni na šta smisleno. Pokazivač koji ima vrednost NULL nije moguće dereferencirati. Pokušaj dereferenciranja dovodi do greške tokom izvršavanja programa (najčešće “segmentation fault”).
Generički pokazivački tip (void*):
U nekim slučajevima, poželjno je imati mogućnost “opšteg” pokazivača, tj. pokazivača koji može da ukazuje na promenljive različitih tipova. Za to se koristi tip void*. Izraze ovog tipa je moguće eksplicitno konvertovati u bilo koji konkretni pokazivački tip (čak se u C-u, za razliku od C++-a, vrši i implicitna konverzija prilikom dodele). Međutim, na ravno, nije moguće vršiti dereferenciranje pokazivača tipa void* jer nije moguće odrediti tip takvog izraza kao ni broj bajtova u memoriji koji predstavljaju njegovu vrednost.