|
Pokazivači i reference
Teoretičari iz oblasti računarske tehnike mogu vas ubeđivati da je pomenuti problem rešen: Tjuring je odavno dokazao da se program napisan u bilo kojem računarskom jeziku (u ovom slučaju to je C), može konvertovati u bilo koji drugi jezik, ako ciljni jezik može da simulira Tjuringovu mašinu. Kako je to sa Javom očigledan slučaj (ja ne znam konkretno nijedan simulator za Tjuringovu mašinu u Javi, ali bi me jako začudilo da na Internetu ne postoji bar nekoliko takvih) - ako smatrate da bi taj pristup bio dovoljno dobar, možete slobodno preskočiti ostatak ovog teksta.
Ovde ćemo se baviti praktičnom stranom problema: kako postići da program napisan u C-u, koji sabira brojeve od 1 do 1000, kada se konvertuje u Javu i izvrši u vašem omiljenom browser-u, ne potraje "letnji dan do podne", koliko autor slobodno procenjuje da bi izvršavanje "Tjuringove verzije" potrajalo. Osnovni cilj nam je da rezultujući program što više liči na ono što bi programer napisao da mu je na raspolaganju bila Java umesto C-a.
Jedan od najvećih problema sa kojom se pisac jedne ovakve alatke susreće je nedostatak pokazivača u Javi, što je ujedno jedna od njenih prednosti. Java ne razume pojam direktno adresirane memorije, sva memorija se automatski zauzima (kada se instancira objekat na primer) i oslobađa (kada nestane memorije, pa garbage collector oslobodi prostor koji su zauzimali objekti koji se više ne koriste). Umesto pokazivača, Java ima reference, koje se mogu posmatrati kao vrsta pokazivača sa prilično ograničenim mogućnostima.
Postoji nekoliko bitnih razlika između pokazivača i referenci u Javi. Pre svega, aritmetika pokazivača, onakva kakva je dozvoljena u C-u, ne postoji u Javi. Na primer, u C-u možete da napišete:
int a[10] = {0,1,2,3,4,5,6,7,8,9};
int* p = a;
p += 15;
foo(*p);
|
Iz ovog primera se direktno vidi zašto je ova osobina pokazivača opasna: dodavanjem 15 na vrednost pokazivača se izlazi van okvira alociranog niza i vrednost na koju p ukazuje je nedefinisana. U Javi ovako nešto nije moguće; jedini način da se pristupi nizu je preko odgovarajuće promenljive, što bi u našem slučaju rezultovalo pozivanjem foo(a[15]), dakle prekida prilikom izvršavanja programa.
Java je, dalje, strogo tipizirani jezik; sve konverzije tipova su podložne proveri prilikom prevođenja i izvršavanja programa, pa česta konstrukcija koja se javlja u C-u nije dozvoljena:
int a[10];
double* p;
p = (double)a;
foo(p[3]);
|
Ovakav način pisanja programa je čest izvor grešaka koje je teško pronaći. Javin sistem tipova neće dozvoliti uzajamnu konverziju proizvoljnih tipova, pa ekvivalent gornjem programu napisan u Javi ne bi mogao ni da se prevede.
U Javi ne postoji mogućnost dobijanja adrese nekog objekta ili skalarne promenljive kao u C-u. Ovo je jedan od najčešćih uzroka grešaka u programu, tzv. "visećih referenci" kao što je:
int* foo(){
int result;
result = compute(...);
return (&result); }
|
U ovom slučaju funkcija foo() vraća pokazivač na promenljivu result; na prvi pogled je sve ispravno. Ali, result je lokalna promenljiva funkcije foo(), pa se ona alocira na steku pre izvršavanja funkcije, što znači da se okvir oslobađa nakon izvršavanja funkcije foo(). Rezultat poziva foo() će neko vreme pokazivati na korektnu vrednost, jer se taj prostor na steku ne "uništava" već se samo proglašava za slobodan - sve dok ne bude pozvana neka druga funkcija koja će upisati svoje podatke na to mesto. Pošto u Javi ne postoji način da dobijete adresu nekog objekta ili promenljive, ne postoji ni način da se formira "viseća referenca".
Sve ove osobine idu u prilog Javi, jer obavezuju programera na disciplinovanije programiranje, ali otežavaju konvertuju C-a u Javu. Mnogo veća "sloboda" koju omogućavaju pokazivači u C-u mora nekako da se prevede u ograničen kontekst referenci u Javi, što nije zadatak.
|