петак, 09. октобар 2015.

Pokazivači u C++ programskom jeziku


Ukoliko idete na intervju za pisanje tehničke literature iz oblasti C i C++ programiranja za Microsoft korporaciju; šta tek da kažemo za radno mesto C++ programera; velika je verovatnoća da će vas pitati da im objasnite šta su to pokazivači. I pored toga što većina programera smatra da su pokazivači jedan od najmoćnijih alata i suština C i C++ programskog jezika jer manipulišu memorijom računara direktno, oni mogu biti zbunjujući i mnogim početnicima nije jasno zašto su oni u stvari i potrebni. Da bi ste razumeli pokazivače pre svega treba da razumete kako funkcioniše računarska memorija.


( Zvezdica * operator nam omogućava pristup vrednosti iz memorijske adrese )

Računarska memorija je kod svakog računara i kod drugih računarskih uređaja uvek podeljena na sekvencijalno označene memorijske adrese. Procesor razume samo brojeve i zbog toga procesor referiše lokacije u memoriji pomoću brojeva u heksadecimalnoj notaciji. Kompjuteri označavaju memoriju koristeći različite kompleksne šeme. Vi ne morate znati koji su to brojevi iako ih uvek možete odštampati upotrebom ampersanda &. Ovako: 

#include "stdafx.h"
#include <iostream>
using namespace std;

int main()
{
       short i = 8;

       cout << &i << endl << endl;

       system("PAUSE");
       return 0;

}

Kad pokrenete ovaj program, vaš rezultat će biti nešto poput ovog ali ne isto:

0104FC64 

Press any key to continue . . . 

Svaki put kad pokrenete program, ovaj broj dobićete drugi heksadecimalni broj memorijske adrese gde je računar smestio promenjivu i tipa short. Ali uvek će zauzimati 2 bita u memoriji koliko tip short zauzima za svoju vrednost. Zašto? Zato što računar uvek iznova rezerviše memorijsku adresu kad pokrenete vaš program dok rezervacija memorije zavisi od mnogo faktora i BIOS-a i celog vašeg operativnog sistema. Sad kad znate šta je i kako funkcioniše memorijska adresa, onda je lako pretpostaviti šta je pokazivač.

Šta je pokazivač? 


Pokazivač vam je najjednostavnije rečeno promenjiva koja čuva memorijsku adresu. Iako promenjive sadrže vrednosti, pokazivač jednostavno sadrži numeričku adresu promenjive. Što bi filozofski rekli pokazivač je način da se stigne do nečeg drugog. Pokazivači vam koriste npr. kada jednoj funkciji trebate da pošaljete veliku količinu podataka. U školskim primerima bi ste verovatno sve te podatke kopirali i predali funkciji. Ali to uzima mnogo memorije i mnogo je sporije. Jednostavnije je da umesto podataka funkciji pošaljete memorijsku adresu podataka, zatim pokazivače koristite umesto promenjivih koje sadrže podatke i uradite sve ono isto što ste hteli da uradite sa podacima u funkciji. Da, pokazivač jednostavno koristite kao promenjivu i sve operacije koje izvedete na pokazivaču će se odraziti i na promenjivu na koju pokazivač ukazuje. Pogledajte kako se pokazivač deklariše:

tip *ime;

Pokazivač se deklariše isto kao promenjiva, stime da se ispred naziva promenjive stavi karakter *. Međutim, samo deklaracija pokazivača nije dovoljna. Pokazivač se uvek mora inicijalizovati sa promenjivom na koju pokazuje. To se radi što se pokazivaču dodeli promenjiva ispred koje se dodeli ampersand &.

tip *ime;

ime = &promenjiva;

Ako bi ste poslali pokazivač bez inicijalizacije da vam prikaže na ekranu neku vrednost, verovatno očekujete da se na ekranu ispiše 0, dobićete grešku. Pogledajte program koji prezentuje najjednostavniju upotrebu pokazivača: 

#include "stdafx.h"
#include <iostream>
using namespace std;

int main()
{
       short i = 8;
       short *mypointer = &i;

       cout << "Value of the variable: " << i << endl;
       cout << "Memory address of the variable: " << &i  << endl;
       cout << "Value of the variable from the point: " << *mypointer << endl;
       cout << "Memory address of the variable from the point: " << mypointer << endl << endl;

       *mypointer += 2;
       cout << "Value of the variable after change pointers: " << i << endl << endl;

       system("PAUSE");
       return 0;

}

Kad pokrenete navedeni program, rezultat je:

Value of the variable: 8
Memory address of the variable: 005AFCD0
Value of the variable from the pointer: 8
Memory address of the variable from the pointer: 005AFCD0

Value of the variable after change pointers: 10

Press any key to continue . . .


Posle deklaracije pokazivača vi možete koristiti pokazivač bez zvezdice * (mypointer) da očitate memorijsku adresu pokazivača, ali ako želite da promenite vrednost u promenjivoj preko pokazivača, onda morate koristiti zvezdicu * (*mypointer). Inače se promena na pokazivaču neće odraziti na promenjivu. ( *mypointer += 2;) Ovde programeri prave najviše bagova jer menjaju pokazivač bez zvezdice * ( mypointer += 2;). Tako ne menjate vrednost promenjive niti kompajler prijavljuje grešku. Tako menjate adresu na koju pokazivač pokazuje i kompromitujete vaš kod.

Da li postoje pokazivači u C# programskom jeziku?

U C# programskom jeziku se pokazivači koriste konstantno ali je to tako lepo upakovano u sam programski jezik da vi i ne znate da vaš kod koristi pokazivače. U C# programskom jeziku nemate pointere, već koristite referentni tip ref ili out. Razlika je samo što referentni tip ref mora da bude inicijalizovan pre nego što se upotrebi dok out ne mora. Inače prednost C++ programskog jezika jeste baš u tome što vi na pokazivačima možete da vršite razne aritmetičke operacije na vrednost. Pokazivači mogu da pokazuju na nigde, zatim mogu biti ponovo dodeljeni neograničen broj puta i vi možete da vidite memorijsku adresu. 

Kad treba da koristim pokazivače, a kad ne?

Pokazivače treba koristiti kad god je to moguće i ima logike da se koriste. Koriste se kad hoćete da radite sa memorijskim adresama, pri rezervisanju i oslobađanju memorije, kad kreirate strukture, povezujete klase i objekte ili pravite redove i liste. Pokazivači najbolje daju rezultate kad implementirate algoritme i pravite strukture. Potrebno je da radite na mnogim projektima dok se ne uhodate kad je bolje koristiti pokazivač a kad ne. Pokazivači definitivno povećavaju veštine programera. Pogledajte na primer kako možemo da sortiramo niz brojeva uz pomoć funkcija koje koriste pokazivače:



#include "stdafx.h"
#include <iostream>
using namespace std;

void swap(short *mypointer1, short *mypointer2);

short myarray[8] = { 10, 5, 4, 17, 12, 9, 19, 1 };

int main()
{
       short low = 0;
       cout << "Array\t\t";

       for (short i = 0; i < 8; i++)
       {
             cout << myarray[i] << "\t";

       }

       cout << endl << endl;

       for (short j = 0; j < 8; j++)
       {
             // finds the lowest element and keeps it in low variable
             low = j;

             for (short k = j; k < 8; k++)
             {
                    if (myarray[k] < myarray[low]) low = k;
                    if (j != low) swap(&myarray[j], &myarray[low]);

             }

       }

       cout << "Sort \t\t";
       for (short l = 0; l < 8; l++)
       {
             cout << myarray[l] << "\t";

       }

       cout << endl << endl;
       system("PAUSE");
    return 0;

}

// swap function
void swap(short *mypointer1, short *mypointer2)
{
       short temp = *mypointer1;
       *mypointer1 = *mypointer2;
       *mypointer2 = temp;

}

Kad pokrenete ovaj program, rezultat je sortiran niz uz pomoć funkcije swap koja koristi pokazivače za zamenu brojeva. 

Array    10   5   4    17    12   9   19   1
 
Sort         1   4   5    9    10   12   17   19


Press any key to continue . . .

Kako to sve izgleda možete pogledati i na video-u:

  

( C++ Tutorial - 8. Pointers )