You are on page 1of 18

Zeljko Juric: Principi programiranja /kroz program ski jezik C++/ Radni materijali - akademska godina 2009/10

15. Prenos parametara u potprograme


Upotreba globalnih promjenljivih eesto moze dovesti do gresaka u programima koje se tesko otkrivaju, s obzirom da imje vidokrug izuzetno velik, tako daje velika i mogucnost njihove upotrebe na pogresan naein. Takoder, upotreba globalnih promjenljivih u vise razlieitih potprograma dovodi do stvaranja neprirodne zavisnosti izmedu potprograma, koji ovise od imena dijeljenih globalnih promjenljivih. Medutim, do sada nam je upotreba globalnih promjenljivih bila jedini naein da izvrsimo razmjenu injormacija izmedu vise razlieitih potprograma. Srecom, C++ nudi mnogo prakticniji i sigurniji naCin razmjene informacija izmedu potprograma zasnovan na tzv. prenosu parametara, koji ne dovodi do stvaranja zavisnosti izmedu pojedinih potprograma. Potrebu za prenosom parametara ilustriraeemo na primjeru sljedeeeg programa koji racuna i stampa obim i povrsinu kruga:
#include <iostream> std;

using namespace const double

PI(3.141592654);

double poluprecniki

I I Stampa obim i povrsinu void ProracunajKrug() { cout "Obim: " 2 "Povrina: ", PI
int main() { cin poluprecnik; ProracunajKrug(); return 0;

kruga

sa poluprecnikom

"poluprecnik"

* PI * poluprecnik endl * poluprecnik * poluprecnik


II Glavni
program

endli

lako nema nikakve sumnje da ovaj program radi ispravno, u njemu se mogu uoeiti i brojni problemi, koji nisu vezani za njegovo junkcioniranje, vee za njegovu strukturu i mogucnost prilagoaavanja. Na prvom mjestu, komunikacija izmedu glavnog programa i potprograma "Proracunaj Krug" ostvarena je preko zajednicke globalne promjenljive "poluprecnik". Ukoliko se njeno ime promijeni, potprogram eeimati gresku, s obzirom da se oslanja na vrijednost nedeklarirane promjenljive. U nekom slozenijem okruZenju, moglo bi se cak desiti da izmjena imena promjenljive dovede do toga (sto je jos gore) da program formalno nema sintaksnu gresku, ali da radi pogresno (to se, na primjer, moze desiti ukoliko se u nekom drugom potprogramu neocekivano upotrebi ista promjenljiva, a da te cinjenice ovaj potprogram nije svjestan). To nije ono sto zaista zelimo. Nairne, potprogram bi trebao biti neovisna jedinica kOda, neovisna od ostatka programa koliko god je to moguce. Trebali bismo biti veoma pazljivi, i moZda bismo morali izmijeniti dio kOda, ukoliko bismo zeljeli da potprogram "Proracunaj Krug" prosto upotrijebimo u nekom drugom programu. Medutim, dobro napisan potprogram ne bi trebao nista da "zna" 0 tome sta se nalazi u ostatku programa, niti kako ee i gdje ee on biti upotrebljen u ostatku programa. Dobro napisan potprogram trebao bi sarno da "radi" posao koji mu je povjeren, bez "razmisljanja" 0 tome kome i zasto je taj po sao potreban. Na sliean problem nailazimo i ukoliko zelimo prosiriti nas program koji ispisuje pozdrav na ekranu, tako da mozemo zadati koliko puta zelimo da se ispiSe rijce "Pozdrav!". Ovo zadavanje trebamo izvrsiti u glavnom programu, koji poziva potprogram "IspisiPozdrav", prije njegovog poziva. Medutim, potprogram "IspisiPozdrav" mora imati nacin da sazna ovu vrijednost. Ukoliko se ograniCimo sarno na one sto smo do sada utvrdili, jedino sto mozemo uraditi je da iskoristimo globalne promjenljive, kao u sljedeeem primjeru:

XV-I

Zeljko Jurie: Principi programiranja !kroz program ski jezik C++/ Radni materijali - akademska godina 2009/10

#include

<iostream> std;

using namespace

int broj_ponavljanja; void IspisiPozdrav() { for(int i = 1; i <= broj_ponavljanja; int main() { cout "Koliko puta ~elite pozdrav? cin broj ponavljanja; IspisiPozdrav(); return 0; i++) cout "Pozdrav!\n";

";

Lose strane ovog programa su iste kao u prethoOOom primjeru. Rjesenje ovih problema nadeno je uvodenjem tehnike prenosa parametara. Pri tome se razlikuje pojamformalnih i stvarnih (iIi aktuelnih) parametara. Formalni parametri su specijalna vrsta lokalnih promjenljivih koje ne inicijalizira sam potprogram, vee njihovom inicijalizacijom upravlja onaj ko poziva potprogram. Za razliku od obicnih lokalnih promjenljivih, deklaracija formalnih parametara se navodi unutar zagrada koje se nalaze u zaglavlju potprograma. Inicijalne vrijeOOosti formalnih parametara zadaju se navodenjem zeljenih inicijalnih vrijednosti (koje se zovu stvami parametri) unutar zagrada prilikom poziva potprograma. Slijedi poboljsana verzija programa za racunanje obima i povrSine kruga, koja koristi ovu tehniku.
#include using <iostream> std;

namespace

const doub1e

PI(3.141592654); zadanim kao parametar

II Stampa obim i povrsinu kruga sa poluprecnikom void ProracunajKrug(double r) { cout "Obim: " 2 * PI * r endl; cout "Povr~ina: " PI * r * r endl;
int main() { double poluprecnik; cin poluprecnik; ProracunajKrug(poluprecnik); return 0;

II Glavni program

Koristenje parametara omogueava potprogramu "Proracunaj Krug" da koristi svoje vlastito loka/no ime za polupreenik kruga (u navedenom primjeru "r") koje ne mora nmno imati nikakve veze sa imenom promjenljive (u navedenom primjeru "pol uprecni k") koju koristi glavni program (koji poziva ovaj potprogram) za cuvanje informacije 0 polupreeniku kruga. Prilikom poziva potprograma "ProcarunajKrug", tekuea vrijednost promjenljive "poluprecnik" (stvami parametar) koristi se za inicijalizaciju lokalne promjenljive (formalnog parametra) "r". Drugim rijeeina, vrijeOOost promjenljive "poluprecnik" se kopira u formalni parametar "r". Poput svih drugih lokalnih promjenljivih (osim statickih), formalni parametri su vidljivi sarno unutar odgovarajueeg potprograma i automat ski se unistavaju nakon zavrSetka potprograma. Formalni parametri ne mogu biti staticki. Kao sto je vee receno, formalni parametri se deklariraju unutar samog zaglavlja potprograma. Deklaracija parametara se cesto naziva popis parametara (eng!. parameter list). Iz izlozenog slijedi da su formalni parametri jedinstveni za konkretan potprogram, oOOosno oni su jeOOoznacno defmirani samim zaglavljem potprograma:

XV-2

Zeljko Juric: Principi programiranja /kroz programski jezik C++/ Radni materijali - akademska godina 2009/1 0

popis parametara ~
void ProracunajKrug(double

formalni parametar S druge strane, stvami parametri se navode prilikom poziva potprograma odgovarajuce formalne parametre:
ProracunajKrug(poluprecnik)

r)

sa ciljem da inicijaliziraju

stvarni parametar U navedenom primjeru, stvami parametar "poluprecnik" kopira se u formalni parametar "r". S obzirom da se stvami parametri navode prilikom poziva, a isti potprogramje moguce pozvati koliko god puta zelimo, u svakom pozivu moguce je zadati drugaeije vrijednosti stvamih argumenata, odnosno stvami argumenti nisu jedinstveno odreaeni sarnim potprogramom. U tom slueaju, prilikom svakog izvrsavanja potprograma, formalni parametri ce imati drugaeije poeetne vrijednosti (odredene vrijednostima stvamih parametara). Za razliku od formalnih parametara koji su promjenljive, stvami parametri ne moraju biti promjenljive, vec mogu biti bilo koje vrijednosti ispavnog tipa, sto ukljueuje promjenljive, konstante iIi izraze. Stoga su pozivi poput sljedecih sasvim korektni (uz pretpostavku da postoji i promjenljiva "precnik" tipa "double"):
ProracunajKrug(poluprecnik); ProracunajKrug(7 * 3.18 - 2.27); ProracunajKrug(3.15); ProracunajKrug(precnik / 2);

Broj stvamih parametara koji se prenose u potprogram mora bili jednak broju formalnih parametara (u nasem primjeru jedan), osim u slucaju postojanja tzv. podrazumijevanih parametara, 0 kojima cemo govoriti nesto kasnije. Svaki stvami parametar mora odgovarati po tipu odgovarajucem formalnom parametru. Na primjer, u prethodnom primjeru, formalni parametar "r" je tipa "double", isto kao i stvarni parametar "poluprecni k".Neslaganje u tipu parametara je dozvoljeno jedino u slucajevima u kojima je podrzana automatska konverzija tipa iz tipa stvamog parametra u tip formalnog parametra. Na primjer, ako je formalni parametar tipa "doub1e", stvami parametar moze biti tipa "int", s obzirom da postoji automatska konverzija (promotivne prirode) iz tipa "int" u tip "doub1e". Nacelno je moguce i obmuto, tj. proslijediti stvami parametar tipa "doub1e" potprogramu koji posjeduje formalni parametar tipa "int", s obzirom da je takoder podrZana i automatska konverzija iz tipa "doub1e" u tip "int". Medutim, moramo biti svjesni da ce u ovom slucaju doci do odsjecanja decimala prilikom prenosa stvamog parametra u formalni. Potprogram "Stampaj PrazneLinij e" u sljedecem primjeru ispisuje "n" praznih linija, gdje je "n" formalni parametar:
void StampajPrazneLinije(int n) { for(int i = 1; i <= n; i++) cout endl;

Potprogram mozemo kasnije pozvati bilo gdje u programu. Na primjer, kada nam zatreba 5 praznih linija, mozemo napisati:
StampajPrazneLinije(5);

Opisani nacin prenosa parametara pri kojem se vrijednost stvamog parametra kopira u odgovarajuci formalni parametar, naziva se prenos po vrijednosti (eng!. passing by value). Kasnije cemo vidjeti da

XV-3

Zeljko Jurie: Principi programiranja Ikroz program ski jezik C++/ Radni materijali - akademska godina 2009/1 0

jezik C++ podnava i drugi naein prenosa parametara, nazvan prenos po referenci (engl. passing by reference), mada u jeziku c++ ova razlika u prenosu nije toliko striktne forme koliko u nekim drugim jezicima kao sto je Pascal. Strogo reeeno, C++ zapravo i ne podrZava prenos po referenci, ali se on moze simulirati tako sto se kao formalni parametar upotrijebi specijalna vrsta objekata nazvana referenca. 0 ovome cemo govoriti u kasnijim poglavljima. Pored termina parametar, koristi se i termin argument. Mada su termini parametar i argument sinonimi, u literaturi se veoma eesto kada se upotrijebi termin "parametar" bez konkretne specifikacije da li se radi 0 formalnom iii stvamom parametru eesce misli naformalni parametar, dokje pri upotrebi termina argument obmuto (tj. eesce se misli na stvami argument). Na primjer, dosta eesto se govori 0 "parametrima koje potprogram prima", odnosno 0 "argumentima koje prosljedujemo potprogramu". U slueaju kada potprogram ima vise parametara, i stvami i formalni parametri razdvajaju se zarezom. Pri tome se pri navodenjuformalnih parametara tip svakog od njih mora navesti odvojeno, kao u sljede6em primjeru programa:
#include <iostream> std; p, int q) {

using namespace

void IspisiZbir(int cout p + q; int main() { IspisiZbir(3, return 0;

2);

Ovaj program ce, naravno, ispisati broj "5". S druge strane, sljede6a definicija nije ispravna:
void IspisiZbir(int cout p + q; p, q) {

Razlog za ovu prividnu nekonzistentnost izmedu naeina deklaracije formalnih parametara i deklaracije obienih lokalnih promjenljivih lezi u einjenici da se, pod odredenim okolnostima, imena formalnih parametara mogu izostaviti, tako daje neophodna individualna tipizacija svakog formalnog parametra. Formalni i stvami parametar u principu mogu imati ista imena, ali treba voditi raeuna da se i u tom slueaju radi 0 razliCitim objektima, a da su njihova istovjetna imena samo stvar slueaja. Dakle, eak i ukoliko formalni i stvarni parametar slueajno imaju isto ime (npr. "n"), formalni parametar "n" je neovisan od stvamog parametra "n", mada se pri pozivu potprograma "IspisiPozdrav" stvami parametar "n" kopira u formalni parametar "n". Ovo je ilustrirano u sljede6em (potpuino legalnom) primjeru, koji predstavlja modificirani program za ispis pozdrava zadani broj puta, uz tehniku prenosa parametara:
#include <iostream> std; "Pozdrav!\n";

using namespace

void IspisiPozdrav(int n) { for(int i = 1; i <= n; i++) cout int main() { int n; cout "Koliko puta cin n; IspisiPozdrav(n); return 0;

ieli pozdrav?

";

XV-4

Zeljko Jurie: Principi programiranja Ikroz program ski jezik CHI Radni materijali - akademska godina 2009/1 0

Ovdje se prilikom poziva potprograma "IspisiPozdrav" stvarni parametar "n" (koji je lokalna promjenljiva potprograma "main") kopira u jormalni parametar "n" (koji je lokalna promjenljiva potprograma "IspisiPozdrav"). Sljedeei primjer ee nas uvjeriti da stvami i formalni parametri predstavljaju razlicite objekte (iako se stvarni kopira u formalni) cak i kada imaju isto ime:
#include <iostream> std; n)

using namespace

void Potprogram(int cout n; n t= 3; cout n; int main() { int n(S); cout n; Potprogram (n); cout n; return 0;

Svoje rezonovanje provjerite tako sto eete odgovoriti na pitanje sta ispisuje ovaj program. Ispravan odgovor je "5585". Takoder, radi provjere da li ste shvatili cinjenicu da su formalni i stvarni parametri neovisni objekti probajte analizirati sta ee ispisati sljedeei (prilicno konfuzan) program. Ispravan odgovor je "255225":
#include <iostream> std; a, int b) {

using namespace

void Potprogram(int cout a b; int main() ( int a (2), b (S) ; cout a b; Potprogram(b, a); cout a b; return 0;

Prenos parametara po vrijednosti moze se koristiti kada god zelimo prenijeti neku informaciju u potprogram, pri cemu nas dalje ne zanima sta ee taj potprogram uraditi sa prenesenom informacijom (tj. da Ii ee prenesena informacija pretrpiti izmjene iIi ne; to je privatna stvar potprograma). Osnovna svrha parametara je da ucine potprogram generalnijim, tako da se on lakse moze ponovo iskoristiti u drugim programima. Na primjer, u programu za ispis sahovske table imali smo potprogram nazvan "Stampaj Zvj ezdice" koji je stampao "sirina_polj a" zvjezdica na ekranu, pri cemu je "sirina _polj a" bila globalna promj enlj iva:
void StampajZvjezdice() { for(int i = 1; i <= sirina_polja I I Stampa "sirina_polja" ; itt) cout "."; zvjezdica

Ovaj potprogram moze biti poboljsan, uvodenjem formalnog parametra (nazvanog npr. "br zv", skraeeno od "broj zvjezdica") koji omogueava da potprogram ne mora nista "znati" 0 tome kakva se imena promjenljivih koriste u ostatku programa:
void StampajZvjezdice(int br zv) ( II Stampa for(int i = 1; i <= br zv; itt) cout "."; "br zv" zvjezdica

xv-s

Zeljko JOOe: Principi programiranja Ikroz program ski jezik CHI Radni materijali - akademska godina 2009/1 0

Ovim je potprogram postao mnogo generalniji, jer omogucava stampanje onoliko zvjezdica koliko zelimo, kada god to zatraZimo. Pri tome, zeljeni broj zvjezdica jednostavno zadajemo prilikom poziva potprograma. Na primjer, StampajZvjezdice(5); Primijetimo da pored toga sto potprogram "Stampaj zvj ezdice" ne mora znati kako se zovu promjenljive u ostatku programa, onaj ko poziva potprogram (glavni program u nasem slucaju) takoder ne mora nista da zna 0 tome kako se zove formalni parametar potprograma (to je "privatna stvar" potprograma ). U istom programu postoji takoder i potprogram nazvan "Stampaj Razmake" koji stampa nekoliko razmaka. Njegov k6d identican je kao u potprogramu "Stampaj Zvj ezdice", izuzev sto na objekat "cout" saljemo razmak, a ne zvjezdicu. Dodajuci jos jedan parametar, mozemo postici da isti potprogram radi oba srodna posla:
II Stampa "br zn" znakova "znak"

void StampajZnakove(int br_zn, char znak) for(int i = 1; i <= br_zn; i++) cout

{ znak;

Ovo je jos generalniji potprogram, koji stampa proizvoljan broj bilo kojeg znaka koji zelimo. Na primjer, pozivom Stampaj Znakove
(4, 'A');

odStampacemo cetiri slova 'A'. Isto tako, pozivom StampajZnakove(sirina_polja,


'*');

odstampacemo onoliko zvjezdica koliko iznosi trenutna vrijednost promjenljive "sirina

_polj

a".

Kada koristimo prototipove potprograma koji imaju parametre, njihova imena nije neophodno navoditi i u prototipu, jer je kompajleru dovoljno da zna njihov broj i tip u trenutku kada se potprogram poziva. Tako je sljedeei programpotpuno korektan: #include using int <iostream> namespace std; int);

main (void) { void IspisiZbir(int, IspisiZbir(3, 2); return 0; p,

void IspisiZbir(int cout p + q;

int

q) {

Nije greska navesti imena parametara i u prototipu, tako da bi u prethodnom programu sasvim legalan bio i prototip void IspisiZbir(int p, int
q);

U stiStini, kako su imena formalnih parametara potpuno nebitna kada se radi 0 prototipu, kompajler ih potpuno ignorira. Kao posljedica te cinjenice, imena parametara u prototipu potprograma i u stvamoj defmiciji potprograma uopce se ne moraju slagati. Na primjer, u prethodnom programu bio bi bez ikakvih problema prihvacen i prototip poput sljedeeeg (bez obzira sto se u definiciji potprograma formalni parametri zovu "p" i "q"):

XV-6

Zeljko Juric: Principi programiranja /kroz programski jezik C++/ Radni materijali - akademska godina 2009/10

void IspisiZbir(int

prvi

sabirak,

int drugi

sabirak);

Opisanu osobinu programeri cesto koriste, dajuCi mnogo deskriptivnija imena parametrima u prototipu nego u samoj realizaciji potprograma. Nairne, ukoliko je prototip dovoljno deskriptivan, njegovim se posmatranjem cesto moze zakljuCiti sta potprogram radi, bez potrebe za analizom same realizacije potprograma. S druge strane, upotreba isuvise dugackih naziva parametara u samom potprogramu bila bi prilicno zamoma. Sljedeei primjer ilustrira potprogram "Stampaj TablicuMnozenj a" sa dva parametra "m" i "n", koji stampa tablicu mnozenja za sve proizvode oblika m x i pri cemu i uzima vrijednosti od 1 do n. Na primjer, ako pozovemo ovaj potprogram pozivom "Stampaj TablicuMnozenj a (3, 5)",bice ispisana tablica mnozenja za proizvode 3 x 1,3 x 2, 3 x 3, 3 x 4 i 3 x 5:
void StampajTablicuMnozenja(int for(int i 1; i <= n; i++) cout m " x " i m, int n)

" = "

m * i

endl;

Parametri mogu biti bUo kojeg legalnog tipa (ukljucujuci i nizovne tipove, 0 cemu cemo govoriti nesto kasnije). U sljedecem primjeru koristimo parametar koji je pobrojanog tipa. Definiran je pobrojani tip "Dani", i potprogram "Stampaj Kalendar" sa dva parametra. Prvi parametar "broj_ dana" je tipa "int", dok je drugi parametar "pocetni _dan" tipa "Dani". Parametar "broj_ dana" odreduje broj dana u mjesecu, parametar "pocetni _ dan" odreduje dan u sedrnici kojim zapocinje taj mjesec, a potprogram "Stampaj Kalendar" stampa kalendar za taj mjesec. U programu je definiran i glavni program koji ilustrira kako se poziva taj potprogram za slucaj kada ze1imo odstampati kalendar za mjesec koji ima 31 dan, a pocinje srijedom:
#include #include <iostream> <iomanip> std; Utorak, Srijeda, Cetvrtak, Petak,

using namespace

enum Dani {Ponedje1jak, Subota, Nedjelja};

void StampajKalendar(int broj_dana, Dani pocetni_dan) cout " PUS CPS N\n" setw(3 * pocetni dan) ""; for(int j = 1; j <= broj_dana; j++) cout setw(3) j; if(pocetni_dan != Nedjelja) pocetni dan Dani(pocetni dan + 1); else { pocetni_dan Ponedjeljak; cout endl;

int main() { StampajKalendar(31, return 0;

Srijeda);

Kao efekat izvrsavanja ovog programa dobieemo sljedeei ispis:


PUS (;
P S N

6 7 13 14 20 21 27 28

1 234 5 8 9 10 11 12 15 16 17 18 19 22 23 24 25 26 29 30 31

XV-7

Ze1jko Jurie: Principi programiranja Ikroz programski jezik c++! Radni materijali - akademska godina 2009/10

U ovom programu, uz pomoc "setw" manipulatora ispisujemo odredeni broj praznih mjesta, da bismo doveli poziciju za ispis ispod slova koje predstavlja odgovarajuci dan. Kako je svaka kolona siroka 3 znaka, broj dodatnih praznina koje treba ispisati jednak je trostrukoj vrijednosti rednog broja odgovarajuceg dana (rOOnibrojevi dana poeinju od nule, sto nam upravo odgovara, jer za slueaj da je poeetni dan ponedjeljak, nikakvi dopunski razmaci nisu potrebni). Nakon toga, "for" petljom ispisujemo sve brojeve redom od 1 do vrijednosti parametra "broj_dana", aZurirajuci pri tom svaki put promjenljivu "pocetni _dan" tako da ukazuje na sljedeci dan, osim za slueaj kada je njena vrijednost "Nedj elj a". U tom slueaju, vrijednost promjenljive "pocetni dan" vraeamo nazad na vrijednost "Ponedj elj ak", uz ispis jednog praznog reda, eime zapravo prelaZirno na ispis novog reda kalendara. U prethodnom primjeru, deklarirali smo tip "Dani" sa globalnom vidljivoscu, samim tim sto smo ga deklarirali izvan svih blokova. Stoga je ovaj tip dostupan i u potprogramu "Stampaj Kalendar" i u glavnoj funkciji "main", sto nam je upravo i potrebno. Da smo tip "Dani" deklarirali unutar potprograma "Stampaj Kalendar", on ne bi bio vidljiv nigdje izvan tijela tog potprograma, sto znaei da pobrojana konstanta "Srij eda" ne bi bila vidljiva u "main" funkciji (tako da program ne bi uopee radio). Treba napomenuti da se, za razliku od deklaracija globalnih promjenljivih, deklaracija tipova sa globalnom vidljivoseu ne smatra stetnom, i eesto je veoma potrebna. Nairne, dok se informacije mogu razmjenjivati izmedu potprograma putem prenosa parametara, ne postoji sliean naein kojim bi potprogrami mogli moousobno razmjenjivati tipove, stoga je upotreba tipova sa globalnom vidljivoscu eesto jedino rjesenje. Veoma j e vaZno napomenuti da iako j ezik C++ garantira da ee vrij ednosti svih stvamih parametara biti izraeunate prije nego 8tO njihove vrijednosti budu proslijedene formalnim parametrima (odnosno prije poeetka izvrSavanja potprograma), jezik C++ ne propisuje redoslijed kojim ce stvami parametri biti izracunati. Mada redoslijOO izraeunavanja stvarnih parametara u veeini slueajeva uopee nije bitan, on moze biti znaeajan u slueaju kada stvarni parametri predstavljaju izraze koji sadrZe bocne efekte. Na primjer, razmotrimo sljedeei jednostavni potprogram koji prosto ispisuje vrijednosti svojih formalnih parametara:
void Potprogram(int a, int b) { cout a " " b endl;

Pretpostavimo sada da smo izvrSili sljedeei poziv:


int x (5) ; Potprogram(x++, x++);

Kakav ee biti ispis nakon izvrsavanja ovog potprograma? Odgovor uopee nije propisan standardom jezika C++, odnosno ispis se ne moze predvidjeti! Jedino sto je garantirano je da ee oba izraza "x++" biti izvrSena, tako da ee na kraju promjenljiva "x" sigumo imati vrijednost 7. Medutim, nije definirano da Ii ee prvo biti izraeunat lijevi ili desni izraz "x++". Ukoliko se prvo izraeuna lijevi izraz, u formalni parametar "a" ee biti prenesena vrijednost 5 (ne zaboravimo da je vrijednost izraza "x++" vrijednost promjenljive "x" prije uveeanja), au parametar "b" vrijednost 6, tako da ee ispis biti "5 6". Medutim, ukoliko se prvo izraeuna desni izraz, dobieemo ispis "65". Ne smijemo pomisliti da problem nastaje iskljueivo zbog upotrebe dva boena efekta u istoj naredbi (8to se svakako smatra nOOozvoljenim). Nairne, podjednako je problematiean i sljedeei poziv:
int x(5); Potprogram(x++, x);

Nairne, nije tesko provjeriti da bi, u zavisnosti od redoslijeda izvrSavanja, mogli dobiti ispis "5 6" ili "55". Sliene probleme bismo imali i u sljOOeeem pozivu, u kojem bismo, zavisno od redoslijeda izraeunavanja stvarnih parametara, mogli dobiti ispis "46" iIi "7 4":
int x (5) ; Potprogram(x
4,

x + 2);

XV-8

Zeljko June: Pnncipi programiranja /kroz program ski jezik C++/ Radni materijali - akademska godina 2009/10

Zbog spomenutih nedoumica, standardomjezika C++ je zabran}eno u nekom od stvamih parametara koristiti promjenljivu nad kojom se u nekom drugom stvamom parametru istog potprograma obavlja bocni efekat. Ova zabrana nije sintaksne prirode, tako da ee kompajler dopustiti ovakve pozive, ali njihov efekat nije predvidljiv. Naravno da je standard jezika C++ mogao propisati redoslijed izracunavanja stvarnih parametara (npr. slijeva nadesno). Meoutim, komitet za standardizaciju je smatrao da je nametanje redoslijeda nepotrebno ogranicenje za autore kompajlera, s obzirom da postoje racunarske arhitekture kod kojih je izracunavanje slijeva nadesno ejikasnije od izracunavanja zdesna nalijevo, dok postoje i racunarske arhitekture kod kojih vrijedi obmuto. Nemojte pokusavati da utvrdite koju strategiju koristi Vas kompajler, pa da se ubuduee oslanjate na utvroenu strategiju. Na prvom mjestu, tako napisan program ne mora raditi ukoliko ga probate prevesti nekim drugim kompajlerom. Sto je jos gore, po standardu kompajleri imaju puno pravo da u zavisnosti od okolnosti izaberu redoslijed izracunavanja parametara. Drugim rijecima, ukoliko ste eksperimentiranjem utvrdili da kompajler koji koristite izracunava parametre zdesna nalijevo, ne mora znaciti da on to radi uvijek. Nairne, mnogi kompajleri mogu promijeniti svoj ustaljeni redoslijed izracunavanja parametara ukoliko u nekoj konkretnoj situaciji zakljuce da ee promjena redoslijeda dovesti do efikasnijeg prevedenog kOda! U jeziku C++ je podrzana moguenost da se prilikom pozivanja potprograma navede manji bro} stvamih parametara nego sto iznosi broj formalnih parametara. Medutim, to je moguee sarno ukoliko se u definiciji potprograma na neki nacin naznaci kakve ee pocetne vrijednosti dobiti oni formalni parametri koji nisu inicijalizirani odgovarajueim stvarnim parametrom, s obzirom da formalni parametri uvijek mora}u biti inicijalizirani. Da bismo pokazali kako se ovo postize, razmotrimo sljedeei potprogram sa tri parametra, koji na ekranu iscrtava pravougaonik od znakova sa visinom i sirinom koje se zadaju kao prva dva parametra, dok treei parametar predstavlja znak koji ee se koristiti za iscrtavanje pravougaonika: void CrtajPravougaonik(int VlSlna, int sirina, for(int i = Ii i <= visinai i++) { for(int j = Ii j <= sirinai j++) cout cout endli char znaki znak) {

Recimo, ukoliko zelimo iscrtati pravougaonik formata 5 x 8 sastavljen od zvjezdica, koristieemo poziv: CrtajPravougaonik(5,
8,
'*')i

Pretpostavimo sada da u veeini slucajeva zelimo za iscrtavanje pravougaonika koristiti zvjezdicu, dok neki drugi znak zelimo koristiti sarno u iznimnim slucajevima. Tada stalno navooenje zvjezdice kao tre6eg parametra pri pozivu potprograma mozemo izbjeei ukoliko formalni parametar "znak" proglasimo za podrazumijevani (eng!. default) parametar (mada je preciznije reci parametar sa podrazumijevanom pocetnom vrijednoscu). To se postize tako sto se iza imena formalnog parametra navede znak "=" iza kojeg slijedi vrijednost koja ee biti iskoristena za inicijalizaciju formalnog parametra u sluca}u da se odgovara}uCi stvami parametar izostavi. Slijedi modificirana verzija potprograma "Crtaj Pravougaonik" koja koristi ovu ideju: void CrtajPravougaonik(int visina, int sirina, for(int i = 1; i <= visinai i++) { for(int j = Ii j <= sirinai j++) cout cout endli char
znaki

znak

, * ')

Sa ovakvom definicijom potprograma, poziv poput CrtajPravougaonik(5,


8)i

postaje sasvim legalan, bez obzira sto je broj stvarnih argumenata manji od broja formalnih argumenata. Nairne, u ovom slucaju formalni parametar "znak" ima podrazumijevanu vrijednost * koja ee biti
I I,

XV-9

Zeljko Jurie: Principi programiranja /kroz program ski jezik C++/ Radni materijali - akademska godina 2009/1 0

iskoristena za njegovu inicijalizaciju, u slucaju da se odgovarajuci stvarni parametar izostavi. Stoga ce prilikom navedenog poziva, formalni pararnetar "znak" dobiti vrijednost '*', tako da ce taj poziv proizvesti isti efekat kao i poziv
CrtajPravougaonik(5, 8, '*');

Treba napomenuti da se podrazumijevana vrijednost formalnog pararnetra koristi za njegovu inicijalizaciju sarno u slucaju da se izostavi odgovarajuCi stvami argument prilikom poziva potprograma. Tako ce, ukoliko izvrSimo poziv
CrtajPravougaonik(5, 8, '0');

formalni pararnetar "znak" dobiti vrijednost stvarnog pararnetra '0', odnosno dobieemo pravougaonik iscrtan od znakova '0'. VaZno je napomenuti da se pri zadavanju podrazumijevanih vrijednosti mora koristiti sintaksa sa znakom "=", a ne konstruktorska sintaksa sa zagradama, koja je preporucena pri obicnoj inicijalizaciji promjenljivih. Stoga se zaglavlje prethodnog programa nije moglo napisati ovako:
void CrtajPravougaonik(int visina, int sirina, char znak('*'))

Moguee je imati i vise pararnetara sa podrazumijevanom vrijednoseu. Medutim, pri tome postoji ogranicenje da ukoliko neki pararnetar ima podrazumijevanu vrijednost, svi pararnetri koji se u listi formalnih pararnetara nalaze desno od njega moraju takoaer imati podrazumijevane vrijednosti (razloge za ovo ogranicenje uvidje6emo uskoro). Odavde slijedi da u slucaju da sarno jedan pararnetar ima pOdrazumijevanu vrijednost, to moze biti sarno posljednji parametar u listi formalnih parametara. Sljede6i primjer ilustrira varijantu potprograma "Crtaj Pravougaonik" u kojem se javljaju dva parametra sa podrazumijevanim vrijednostima:
void CrtajPravougaonik (int visina, int sirina for(int i = 1; i <= visina; i++) { for(int j = 1; j <= sirina; j++) cout cout endl;

= 10, char znak =


znak;

'*')

Stoga ovaj potprograrn mozemo pozvati sa tri, dva ilijednim stvarnim argumentom, na primjer:
CrtajPravougaonik(5, Crtaj Pravougaonik (5, CrtajPravougaonik(5); 8, '+'); 8);

II Crtaj Pravougaonik (5, 8, II Crtaj Pravougaonik (5, 10,

'*'); '*');

U komentarima je prikazano kako bi izgledali odgovarajuei vrijednosti parametara.

pozivi bez koristenja podrazumijevanih

Moguee je i da svi pararnetri imaju podrazumijevane vrijednosti. Takav potprogram je moguee pozvati i bez navodenja ijednog stvarnog argumenta (pri tome se zagrade, koje oznacavaju poziv potprograma, ne smiju izostaviti, vee sarno ostaju prazne, kao u slucaju potprograma bez pararnetara). U slucaju da se koriste prototipovi, eventualne podrazumijevane vrijednosti parametara navode se samo u prototipu, ali ne i u defmiciji potprograma, inace ee kompajler prijaviti gresku (kao i pri svakoj drugoj dvostrukoj definiciji). Za prethodni potprograrn, prototip bi mogao izgledati recimo ovako.
void CrtajPravougaonik (int visina, int sirina

= 10, char znak

'*');

S obzirom da se imena parametara u prototipovima ignoriraju i mogu se izostaviti, sasvim je legalan i sljede6i prototip (koji djeluje pomalo cudno, jer izgleda kao da se tipovima dodjeljuju vrijednosti):

XV-IO

Zeljko Juric: Principi programiranja /kroz programski jezik C++/ Radni materijali - akademska godina 2009/10

void

CrtajPravougaonik(int,

int

= 10, char = '*');

Vjerovatno je upravo mogucnost ovakvih sintaksnih konstrukcija razlog zbog cega se za inicijalizaciju podrazumijevanih parametara ne smije koristiti konstruktorska sintaksa. Teoretski, podrazumijevane vrijednosti je moguce definirati u definiciji potprograma a ne u prototipu. Medutim, u tom slucaju ukoliko se pri pozivu potprograma izostavi odgovarajuei stvarni argument, kompajler ce prijaviti gresku ukoliko nije prethodno vidio Citavu definiciju potprograma, jer u suprotnom nece znati da odgovarajuei argument uopce ima podrazumijevanu vrijednost. Mogucnost da vise od jednog parametra ima podrazumijevane vrijednosti osnovni je razlog zbog kojeg nije dozvoljeno da bilo koji parametar ima podrazumijevane vrijednosti, nego sarno parametri koji cine zavrsni dio liste formalnih parametara. Nairne, razmotrimo sta bi se desHo kada bi bio dozvoljen potprogram poput sljedeceg, u kojemprvi i treti parametar imaju podrazurnijevane vrijednosti: void OvoNeRadi(int a 1, int b, int c = 2) cout a " " b " " c endl;
}

Ovakav potprogram bi se ocigledno mogao pozvati sa tri, dva ilijednim stvamim argumentom. Pri tome su pozivi sa tri iIi jednim argumentom posve nedvosmisleni. Medutim, u slucaju poziva sa dva stvarna argumenta (odnosno, u slucaju kada je jedan od argumenata izostavljen), javlja se dvosmislica po pitanju ko}i je argument izostavljen (prvi ili treci). los vece dvosmislice mogle bi nastati u slucaju jos veceg broja parametara, od kojih neki imaju podrazumijevane vrijednosti, a neki ne. U jeziku C++ ovakve dvosrnislice su otklonjene striktnim ogranicavanjem koji parametri mogu imati podrazurnijevane vrijednosti, a koji ne mogu. U nacelu, podrazurnijevane vrijednosti ne moraju biti konstante vee mogu biti i izrazi. NaZalost, kako vidokrug formalnih parametara pocinje tek unutar tijela odgovarajueeg potprograma, nije moguce u podrazurnijevanoj vrijednosti iskoristiti vrijednost nekog drugog parametra koji se nalazi u listi formalnih parametara, cak i ukoliko se on nalazi lijevo od parametra kojem zadajemo vrijednost. Tako nije moguee napisati potprogram sa zaglavljem poput sljedeceg, u kojem bi podrazurnijevana vrijednost formalnog parametra "sirina" trebala da budejednaka vrijednosti formalnog parametra "visina": void Crtaj Pravougaonik (int visina, int sirina
= visina, char

znak =

'*')

Mozemo reci da zbog opisanog problema sa ogranicenom vidljivoscu, eventualne promjenljive koje bi se mogle upotrijebiti u izrazima kojim se inicijaliziraju podrazurnijevani parametri mogu biti samo globalne promjenljive, cija se primjena svakako ne preporucuje. Stoga su podrazurnijevane vrijednosti gotovo uvijek konstantne velicine. Srecom, u nastavku cemo vidjeti da postoji drugi naCin da se ostvari efekat koji smo zeljeli postici gore prikazanom (neispravnom) definicijom. U jeziku C++ je dozvoljeno imati vise potprograma sa istim imenima, pod uvjetom da je iz nacina kako je potprogram pozvan moguce nedvosmisleno odrediti koji potprogram treba pozvati. Neophodan uvjet za to je da se potprograrni koji imaju ista imena moraju razlikovati iIi po broju parametara, iIi po tipu odgovarajueih formalnih parametara, iii i po jednom i po drugom. Ova mogucnost naziva se preklapanje iii preopterecivanje (eng!. overloading) potprograma (funkcija). Na primjer, u sljedecem primjeru imamo preklopljena dva potprograma istog imena "P1" koji ne rade nista korisno (sluze sarno kao demonstracija preklapanja): void P1(int a) { cout "Jedan parametar: void P1(int a, int b) { cout "Ova parametra: " a endl;

"

" i "

endl;

XV-II

Zeljko Jurie: Principi programiranja /kroz program ski jezik C++/ Radni materijali - akademska godina 2009/10

U ovom slueaju se radi 0 preklapanju po broju parametara. Stoga su legalna oba sljedeea poziva (pri eemu ce u prvom pozivu biti pozvan drugi potprogram, sa dva parametra, a u drugom pozivu prvi potprogram, sajednim parametrom):
PI (3, 5) PI (3) ;
i

Sljedeei primjer demonstrira preklapanje po tipu parametara. Oba potprograma imaju isto ime "P2" i oba imaju jedan formalni parametar, ali im se tip formalnog parametra razlikuje:
void P2(int a) { cout "Parametar void P2(double a) { cout "Parametar

tipa int: "

endl;

tipa double:

"

endl;

Jasno je da ce od sljedeca eetiri poziva, uz pretpostavku da je "n" cjelobrojna promjenljiva, prva dva poziva dovesti do poziva prvog potprograma, dok ce treei i eetvrti poziv dovesti do poziva drugog potprograma:
P2 (3) ;

P2(1 + n / 2); P2 (3. ) ; P2(3.14 * n / 2.21);

Ovi primjeri jasno ukazuju na znaeaj pojma tipa vrijednosti u jeziku C++, i potrebe za razlikovanjem podatka "3" (kojije tipa "int") i podatka "3." (kojije tipa "double"). Prilikom odredivanja koji ce potprogram biti pozvan, kompajler prvo pokusava da pronade potprogram kod kojeg postoji potpuno slaganje po broju i tipu izmedu formalnih i stvarnih parametara. Ukoliko se takav potprogram ne pronade, tada se pokuSava ustanoviti indirektno slaganje po tipu parametara, oOOosno slaganje po tipu uz pretpostavku da se izvrSi automatska pretvorba stvarnih parametara u navedene tipove formalnih parametara (uz pretpostavku da su takve automatske pretvorbe dozvoljene, poput pretvorbe iz tipa "char" u tip "int"). Ukoliko se ni nakon toga ne uspije uspostaviti slaganje, prijavljuje se greska. U slucaju da se potpuno slaganje ne pronade, ada se indirektno slaganje moze uspostaviti sa vise razliCitih potprograma, daje se prioritet slaganju koje zahtijeva "logicniju" oOO08no "manje drasticnu" konverziju. Tako se konverzija izjeOOog cjelobrojnog tipa u drugi (npr. iz tipa "char" u tip "int") iii iz jeOOog realnog tipa u drugi (npr. iz "float" u "double") smatra "logienijom" odnosno "direktnijom" od konverzije iz cjelobrojnog u realni tip. Stoga ce, za slucaj prethodnog primjera, poziv
P2 (' A');

u kojem je stvarni parametar tipa "char", dovesti do poziva potprograma "P2" sa formalnim parametrom tipa "int", s obzirom da je konverzija iz tipa "char" u tip "int" neposrednija nego (takoder dozvoljena) konverzija u tip "double". Takoder, konverzije u ugradene tipove podataka smatraju se logienijim od konverzija u korisnicke tipove podataka, koje cerno upoznati kasnije. Medutim, moze se desiti da se indirektno slaganje moze uspostaviti sa vise razlicitih potprograma, preko konverzija koje su medusobno podjednako logicne. U tom slucaju smatra se da je poziv nejasan (engl. ambiguous), i prijavljuje se greska. Na primjer, ukoliko postoje dva potprograma istog imena od kojih jedan prima parametar tipa "float" a drugi parametar tipa "double", bice prijavljena greska ukoliko kao stvarni parametar upotrijebimo podatak tipa "int" (osim ukoliko postoji i treei potprogram istog tipa koji prima parametar cjelobrojnog tipa). Nairne, obje moguce konverzije iz tipa "int" u tipove "float" i "double" podjednako su logicne, i kompajler ne moze odluciti koji potprogram treba pozvati. Na ovakve nejasnoee vee smo ukazivali pri opisu matematickih funkcija iz biblioteke "cmath",
XV -12

Zeljko Juric: Principi programiranja /kroz programski jezik C++/ Radni materijali - akademska godina 2009/10

kod kojih nastaju upravo opisane nejasnoee u slucaju da 1m se kao stvarni argumenti proslijede cjelobrojne vrijednosti. Uz izvjestan oprez, moguee je rnijesati tehniku koriStenja podrazurnijevanih parametara, preklapanja po broju parametara i preklapanja po tipu parametara. Oprez je potreban zbog Cinjenice da se kombiniranjem ovih tehnika poveeava moguenost da nepamjom formiramo definicije koje ee dovesti do nejasnih poziva. Na primjer, razmotrimo sljedeea dva potprograma istog imena "P3":
void P3(int a) { cout "Jedan parametar: " a

endl;

void P3(int a, int b = 10) { cout "Dva parametra: "

" i "

endl;

Jasno je da je, UZ ovako defmirane potprograme, poziv poput "P3 (10)" nejasan, jer ne postoji mogucnost razgranicenja da Ii se radi 0 pozivu prvog potprograma, iIi pozivu drugog potprograma sa izostavljenim drugim argumentom. U oba slucaja ostvaruje se potpuno slaganje tipova. Medutim, uz neophodnu dozu opreza, moguce je formirati korisne potprograme koji kombiniraju opisane tehnike. Na primjer, razmotrirno sljedeee potprograme:
void CrtajPravougaonik(int V1Slna, int sirina, char for(int i = 1; i <= visina; i++) { for(int j = 1; j <= sirina; j++) cout znak; cout endl; znak
'*') {

void CrtajPravougaonik(int V1Slna, char znak = '*') for(int i = 1; i <= visina; i++) { for(int j = 1; j <= visina; j++) cout znak; cout endl;

Prvi od ova dva potprograma je vee razmotreni potprogram za crtanje pravougaonika, a drugi potprogram je njegova neznatno modificirana varijanta (bolje receno, specijalni slucaj) koji iscrtava pravougaonik sajednakom sirinom i visinom (tj. kvadrat). Sa ovako definiranim potprograrnima moguci su pozivi poput sljedeeih, pri cemu se u prva dva slucaja poziva prvi potprogram, au trecem i cetvrtom slucaju drugi potprogram:
CrtajPravougaonik(5, CrtajPravougaonik(5, CrtajPravougaonik(5, CrtajPravougaonik(5); 10, '+'); 10); '+');

Narocito je interesantno razmotriti drugi i treci poziv. Iako oba poziva imaju po dva stvama argurnenta, drugi poziv poziva prvi potprogram, jer se sa prvim potprogramom ostvaruje potpuno slaganje tipova stvamih i formalnih argumenata uz pretpostavku da je treci argument izostavljen. S druge strane, treei poziv poziva drugi potprograrn, jer se potpuno slaganje tipova stvamih i formalnih argurnenata ostvaruje sa drugim potprogramom, dok je sa prvirn potprogramom moguee ostvariti sarno indirektno slaganje, u slucaju da se izvrsi konverzija tipa "char" u tip "int". Iz izlozenog je jasno da se preklapanje potprograma moze koristiti kao altemativa koristenju podrazurnijevanih vrijednosti parametara, i da je pornoeu preklapanja moguce postiCi efekte koji nisu izvodljivi upotrebom podrazurnijevanih vrijednosti. Medutim, treba obratiti pamju da se kod preklapanja ne radi 0 jednom, nego 0 vise razliCitih potprograma, koji sarno imaju isto ime. Ovi potprograrni koji dijele isto ime trebali bi da obavljaju slicne zadatke, inace se postavlja pitanje zasto bi uopce koristili isto irne za potprograme koji rade razlicite stvari. Inace, prije nego sto se odlucimo za preklapanje, treba

XV -13

Zeljko Jurie: Principi programiranja /kroz programski jezik C++/ Radni materijali - akademska godina 2009/1 0

razmisliti da li je zaista potrebno koristenje istog imena, jer prevelika upotreba preklapanja moze dovesti do zbrke. Na primjer, u prethodnom primjeru mozda je parnetnije drugi potprogram nazvati "CrtajKvadrat", jer on u suStini zaista iscrtava kvadrat (koji doduse jeste specijalan slucaj pravougaonika, tako da i ime "Crtaj Pravougaoni k" ima opravdanja, u smislu da se drugi potprogram moze smatrati kao specijalan slueaj prvog). Naroeito su spoma preklapanja po tipu parametara, jer je dosta upitno zbog eega bi trebali imati dva potprograrna istog imena koji prihvataju argumente razlicitog tipa. Ukoliko ovi potprogrami rade razlicite stvari, trebali bi imati i razlicita imena. Stoga se preklapanje po tipu obicno koristi u slucaju kada vise potprograrna logicki gledano obavlja isti zadatak, ali se taj zadatak za razlicite tipove izvodi na razlicite nacine (npr. postupak racunanja stepena ab osjetno se razlikuje za slucaj kada je b cijeli broj i za slucaj kada je b realan). Sa primjerima ispravno upotrijebljenih preklapanja susretaeemo se u kasnijim poglavljima. Kao opeu preporuku, u slucajevima kada se isti efekat moze postiei preklapanjem i upotrebom parametara sa podrazumijevanim vrijednostima, uvijek je bolje i sigumije koristiti pararnetre sa podrazumijevanim vrijednostima. Preklapanje moze biti veoma korisno, ali sarno pod uvjetom da tacno znarno sta i zasto radimo. U suprotnom,upotreba preklapanja sarno dovodi do zbrke. Treba naglasiti da pararnetre sa podrazumijevanim vrijednostima treba koristiti samo ako su sve smislene. Na primjer, ako irnamo potprogram "F" koji se moze pozvati ili sa dva cjelobrojna argumenta iIi bez argumenata pri cemu se tada podrazumijeva da oba argumenta irnaju vrijednosti 0, ali ne i sa sarnojednim argumentom, tada nije dobro definirati ovaj potprograrn kao
varijante koje se mogu dobiti izostavljanjem stvarnih parametara
void F(int x
=

0, int

yO)

jer ee se tako napisan potprograrn moei pozvati i sa jednim argumentom, sto nije predvideno. Umjesto toga, treba koristiti preklapanje po broju argumenata. Da se izbjegne dupliranje kOda, u preklopljenoj verziji bez argumenata moze se pozvati preklopljena verzija sa argumentima:
void F() { F (0,0) ;

Kod prograrna koji sadrZe mnostvo potprograrna, neophodno je poznavati strukturu programa, odnosno informaciju 0 tome koje potprograme on sadrZi, i kakva je pozivna hijerarhija (tj. koji potprogrami zovu koje potprograrne). Pregledan nacin za prikazivanje strukture prograrna predstavljaju tzv. strukturni dijagrami. Na primjer, sljedeei dijagram oznacava da potprograrn "A" poziva potprograrne "B" i "c":

Struktumi dijagrami mogu takoder prikazivati i prenos parametara. Na primjer, sljedeei dijagrarn prikazuje da glavni program poziva potprogram "Proracunaj Krug" i da mu pri tome prenosi vrijednost promjenljive "pol uprecni k" kao pararnetar:

XV-14

Zeljko Jurie: Principi programiranja /kroz program ski jezik C++/ Radni materijali - akademska godina 2009/10

Ne treba mijesati struktume dijagrame sa tzv. dijagramima toka (engl. flowcharts). Naime, strukturni dijagrami prikazuju samo hijerarhiju potprograma, a ne i algoritam kako pojedini potprogrami djeluju (niti kako funkcioniraju). Mehanizam prenosa parametara je od kljucnog znacaJa za razvoj programa. Naime, u dobro napisanom programu, niti jedan dio programa ne bi trebao da ima pristup onim promjenljivim koje mu nisu potrebne (ovaj princip postaje jos izrazajniji u tzv. objektno zasnovanom pristupu programiranju, koji cemo razmatrati kasnije). Na primjer, sa gledista onog ko poziva potprogram, svaki potprogram treba djelovati kao "cma kutija" (pod ovim pojmom se u tehnici obicno podrazumijeva uredaj za koji mozemo utvrditi sta radi, ali ne i kako radi, s obzirom da nam je njegova unutrasnjost posve nedostupna). Naime, onaj ko poziva potprogram treba samo da potprogramu preda ispravne parametre i da im prepusti da odrade svoj po sao, ne ulazeti u to kako ce oni to uraditi. S druge strane, potprogrami samo primaju parametre od pozivaoca, i ne tiee ih se sta radi ostatak programa. Oni se brinu samo kako da obave zadatak koji im je povjeren. Takoder, u dobro napisanom programu, ni jedan potprogram ne bi trebao da radi vise razliCitih poslova (svakom poslu treba dodije1iti poseban potprogram). Mehanizam koji obezbjeduje ispunjenje ovih principa naziva se sakrivanje informaeija (engl. information hiding). U jeziku C++ ovaj mehanizam se ostvaruje tako sto svaku promjenljivu definiramo tako da joj je vidokrug sto je god moguce manji. Da se izbjegne upotreba globalnih promjenljivih u funkcijama, treba intenzivno koristiti parametre. Ovaj koncept je ilustriran na poboljsanoj verziji modularnog programa za prikaz sahovske table, u kojem je izbjegnuta upotreba global nih promjenljivih, koristenjem mehanizma prenosa parametara:
#include <iostream> std; '), Zvjezdica('*'); II Glavni program int); II Prototip potprograma II Sirina II Visina kvadratnag kvadratnog
"S tampaj Tabl u"

using namespace

const char Razmak(' int main () void

StampajTablu(int,

int m; int ni

palja polja

cout "Ovaj program prikazuje ahovsku tablu.\n\n" "Unesite irinu svakog kvadrata, u znakovima: "; cin m; cout "Unesite visinu svakog kvadrata, u linijama: cin n; cout "\n\n"; StampajTablu(n, m); return 0; II Stampa sahavsku tablu void StampajTablu(int visina, int sirina) void StampajNeparniRed(int, int); void Stampaj ParniRed (int, int); for (int i = 1; i <= 4; i++) { StampajNeparniRed(visina, sirina); StampajParniRed(visina, sirina);

";

II Stampa neparni red void StampajNeparniRed(int visina, int sirina) { void StampajLinijuNeparnogReda(int); for(int i = 1; i <= visinai i++) StampajLinijuNeparnogReda(sirina);

XV-IS

Zeljko Jurie: Principi programiranja Ikroz program ski jezik C++/ Radni materijali - akademska godina 2009/1 0

II Stampa parni red void StampajParniRed(int visina, int sirina) { void StampajLinijuParnogReda(int): for(int i = 1; i <= visina; i++) StampajLinijuParnogReda(sirina): II Stampa liniju neparnog reda void StampajLinijuNeparnogReda(int sirina kvadrata) void StampajZnakove(int, char); for(int i = 1; i <= 4; i++) { StampajZnakove(sirina kvadrata, Razmak); StampajZnakove(sirina kvadrata, Zvjezdica);
}

cout

endl:

II Stampa liniju parnog reda void StampajLinijuParnogReda(int sirina kvadrata) void StampajZnakove(int, char): for(int i = 1; i <= 4: i++) { StampajZnakove (sirina_kvadrata, Zvjezdica): StampajZnakove (sirina kvadrata, Razmak):
}

cout

endl:

II Stampa niz znakova void StampajZnakove(int broj znakova, char znak) { for(int i = I: i <= broj znakova: i++) cout znak:

Slijedi i kompletan strukturni dijagram za ovaj program (treba obratiti painju da se imena parametara u strukturnom dijagramu ne moraju poklapati sa imenima parametara u programu, jer je cilj samo ukazati na znace,ye parametara, a ne i na njihova stvarna imena):

~ visina ~ sirina

StampajLinijuParnogReda ~ visina ~znak

StampajLinijuNeparnogReda

V
znak

visina

Primijetimo da strukturni dijagram ne opisuje koristeni algoritam, 5tO je vee ranije istaknuto. Algoritmi za svaku funkciju prikazanu na strukturnom dijagramu moraju biti opisani koristenjem

XV-16

Ze1jko Jurie: Principi programiranja Ikroz program ski jezik C++I Radni materijali - akademska godina 2009/10

pseudo-k6da (nacin koji smo vee u vise navrata koristili za opis funkcioniranja pojedinih dijelova programa). Mada su se nekada za opisivanje algoritama intenzivno koristili dijagrami toka, njihova upotreba se danas izbgjegava, jer se smatra da oni nisu prilagodeni konceptima modemih programskih jezika i da njihova upotreba podstice nemodulami pristup u razvoju programa. Na ovom mjestu je neophodno naglasiti da modulami programi nisu niti najkraCi niti najefikasniji, ali su sigumo lakSi i za razumijevanje i za odriavarije (tj. za eventualne modifikacije, korekcije, dopune itd.) od odgovarajueih nemodulamih programa. Na primjer, sljedeei (nemodulami) program sigumo je dosta kraei od prethodno napisanog modulamog progrdma, ali je tezi za razurnijevanje (koristi cetiri petlje jedna unutar druge), tezi je za izmjene, a narocito je losa strana sto se ni jedan dio ovog programa ne moze upotrebiti kao potprogram u nekom drugom programu:
#include <iostream> std;

using namespace

int main() { int m, n; cout "Ovaj program prikazuje ahovsku tablu.\n\n" "Unesite irinu svakog kvadrata, u znakovima: "; cin m; cout "Unesite visinu svakog kvadrata, u linijama: cin n; cout "\n\n"; for(int i = 1; i <= 4; i++) { for(int j = 1; j <= n; j++) { for (int k 1; k <= 4; k++ ) { for(int 1 1; 1 <= m; 1++) cout " "; for(int 1 1; 1 <= m; 1++) cout "*";
}

";

cout
}

endl;

for(int j 1; j <= n; j ++) { 1; k <= 4; k++) { for(int k 1; 1 <= m; 1++) cout for(int 1 for(int 1 1; 1 <= m; 1++) cout
}

"*";

" ";

cout
}

endl;

return

0;

Upotreba malih ulozenih petlji koje koriste promjenljivu "1" kao brojac mogla bi se izbjeei koristenjem manipulatora "setw" i "setfill". Ova modifikacija ostavlja se citatelju odnosno Citateljki za vjeibu. Treba naglasiti da su efikasnost, kratkoca i modularnost tri potpuno opreena zahtjeva. U vrijeme kada su racunari bili spori i kada je kapacitet radne memorije bio mali, efikasnost i kratkoca su bili dominantni zahtjevi. Danas, kada je od svih racunarskih "komponenti" ubjedljivo najskuplji radni sat programera, primami zahtjev je modularnost. Pokazuje se da je, uz dobar kompajler, duzina izvrsne verzije (tj. nakon kompajliranja) modulamog programa tek neznatno veca od dtiZine ekvivalentnog nemodulamog programa, a razlika u efikasnosti je skoro neprimjetna. U kasnijim poglavljima cemo se upoznati sa principima objektno zasnovanog i objektno orijentiranog dizajna koji su jos pogodniji za razvoj velikih programa (sa aspekta jasnoee, razumijevanja i mogucnosti odrZavanja), mada su sa aspekta kratkoee i efikasnosti programa ovi principi jos nepovoljniji. Bez obzira na to, danas se gotovo svi iole veei programi projektiraju koristeci ove principe. Osobina modulamih programa koja omogueava da se jednom napisani potprogrami mogu upotrebljavati u drugim programima dovodi do velike ustede u vremenu prilikom razvoja obimnih programskih paketa. S druge strane, skraCivanje programa obicno zahtijeva oslanjanje na neke "trikove"

XV -17

Zeljko Jurie: Principi programiranja /kroz program ski jezik C++/ Radni materijali - akademska godina 2009/ 10

specifiene za problem koji se rjesava, sto eini razradeni algoritam potpuno "vezanim" za problem koji se rjeSava. Pogledajmo, na primjer, sljedecu verziju programa za iscrtavanje sahovske table. On je nesumnjivo izuzetno kratak, i koristi sarno dvije petlje, ali i prilicno tezak za razumijevanje, jer se zasniva na nekim "karakteristienim" svojstvima "sare" koju eini sahovska tabla. Takoder, ovaj program koristi i prilieno "konfuzni" temarni operator "? : ". Nemojte se previse zabrinjavati ukoliko ne uspijete razumjeti kako radi ovaj program. On je vise "mozgalica" za enigmatieare nego primjer kako bi trebalo pisati dobre programe:
#include using <iostream> std;

namespace

int main() { int ill, n; cout "Ovaj program prikazuje ahovsku tablu.\n\n" "Unesite irinu svakog kvadrata, u znakovima: "; cin m; cout "Unesite visinu svakog kvadrata, u linijama: cin n; cout "\n\n"; for(int i = 0; i < 8 * n; i++) { for(int k = 0; k < 8 * m; k++) cout ((i / n + k / m) % 2 ? "*" ""); cout endl;
}

";

return

0;

Na primjeru ovog programa takoder mozemo vidjeti i da su kratkoca i efikasnost medusobno sukobljeni zahtjevi. Tako je ovaj program vjerovatno "sampion kratkoce", ali je manje efikasan od prethodnog, jer se u ovom programu unutar unutraSnje petlje izvrSavajutri operacije dijeljenja. Kako se unutrasnja petlja izvrSava 8 m puta a spoljaSnja 8 n puta, slijedi da se u ovom programu izvrSava ukupno 192 m n dijeljenja (koje je relativno spora operacija), dok se u prethodnom programu ne izvrSava niti jedno dijeljenje! Efikasnost ovog programa se moze osjetno popraviti (uz zadrZavanje kratkoce) upotrebom manipulatora "setw" i "setfill", sto prepuStamo enigmatski nastrojenim eitateljima i eitate1jkamakao korisnu vjezbu.

XV -18

You might also like