Zeljko Juric

Embed Size (px)

Citation preview

  • 8/3/2019 Zeljko Juric

    1/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 globalnihpromjenljivih. 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

    using namespace std;

    const double PI(3.141592654);

    double poluprecniki

    I I Stampa obim i povrsinu kruga sa poluprecnikom "poluprecnik"

    void ProracunajKrug() {

    cout "Obim: " 2 * PI * poluprecnik endl "Povrina: ", PI * poluprecnik * poluprecnik endli

    int main() {

    cin poluprecnik;ProracunajKrug();

    return 0;

    II Glavni program

    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" 0tome kome i zasto je taj posao 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

  • 8/3/2019 Zeljko Juric

    2/18

    Zeljko Jurie: Principi programiranja !kroz program ski jezik C++/

    Radni materijali - akademska godina 2009/10

    #include

    using namespace std;

    int broj_ponavljanja;

    void IspisiPozdrav() {

    for(int i = 1; i

  • 8/3/2019 Zeljko Juric

    3/18

    Zeljko Juric: Principi programiranja /kroz programski jezik C++/

    Radni materijali - akademska godina 2009/1 0

    popis parametara

    ~void ProracunajKrug(double r)

    t formalni parametar

    S druge strane, stvami parametri se navode prilikom poziva potprograma sa ciljem da inicijalizirajuodgovarajuce formalne parametre:

    ProracunajKrug(poluprecnik)

    tstvarni 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 formalnomparametru. 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

  • 8/3/2019 Zeljko Juric

    4/18

    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 argumentsinonimi, u literaturi se veoma eesto kada se upotrijebi termin "parametar" bez konkretne specifikacije

    da li se radi 0formalnom 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

    using namespace std;

    void IspisiZbir(int p, int q) {cout p + q;

    int main() {

    IspisiZbir(3, 2);

    return 0;

    Ovaj program ce, naravno, ispisati broj "5". S druge strane, sljede6a definicija nije ispravna:

    void IspisiZbir(int p, q) {

    cout 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

    using namespace std;

    void IspisiPozdrav(int n) {

    for(int i = 1; i

  • 8/3/2019 Zeljko Juric

    5/18

    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

    using namespace std;

    void Potprogram(int n)

    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

    using namespace std;

    void Potprogram(int a, int b) {

    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

  • 8/3/2019 Zeljko Juric

    6/18

    Zeljko JO O e: 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 zovupromjenljive 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 namespace std;

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

    void IspisiZbir(int p, int q) {cout p + 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 0prototipu, 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

  • 8/3/2019 Zeljko Juric

    7/18

    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 m, int n)

    for(int i 1; i

  • 8/3/2019 Zeljko Juric

    8/18

    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 razmjenjivatitipove,

    stoga je upotreba tipova sa globalnom vidljivoscueesto 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 nastajeiskljueivo 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

  • 8/3/2019 Zeljko Juric

    9/18

    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 uvijekmora}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, char znak) {for(int i = Ii i

  • 8/3/2019 Zeljko Juric

    10/18

    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 pozivproizvesti 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 = 10, char znak = '*') {

    for(int i = 1; i

  • 8/3/2019 Zeljko Juric

    11/18

    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 stvarnaargumenta (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 sepreklapanje 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: " a endl;

    void P1(int a, int b) {cout "Ova parametra: " a " i " b endl;

    XV-II

  • 8/3/2019 Zeljko Juric

    12/18

    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) i

    PI (3) ;

    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 tipa int: " a endl;

    void P2(double a) {

    cout "Parametar tipa double: " a 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 formalnimparametrom 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

  • 8/3/2019 Zeljko Juric

    13/18

    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: " a " i " b 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 znak

    for(int i = 1; i

  • 8/3/2019 Zeljko Juric

    14/18

    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 jedosta 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 preklapanjepo tipu obicno koristi u slucaju kada vise potprograrna logicki gledano obavlja isti zadatak, ali se tajzadatak 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 ispravnoupotrijebljenih preklapanja susretaeemo se u kasnijim poglavljima. Kao opeu preporuku, u slucajevimakada se isti efekat moze postiei preklapanjem i upotrebom parametara sa podrazumijevanimvrijednostima, 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. Usuprotnom,upotreba preklapanja sarno dovodi do zbrke.

    Treba naglasiti da pararnetre sa podrazumijevanim vrijednostima treba koristiti samo ako su svevarijante koje se mogu dobiti izostavljanjem stvarnih parametara 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 nijedobro definirati ovaj potprograrn kao

    void F(int x= 0, int yO) {

    jer ee se tako napisan potprograrn moei pozvati i sa jednim argumentom, sto nije predvideno. Umjestotoga, 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. kojipotprogrami 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 dijagrarnprikazuje da glavni program poziva potprogram "Proracunaj Krug" i da mu pri tome prenosivrijednost promjenljive "pol uprecni k" kao pararnetar:

    XV-14

  • 8/3/2019 Zeljko Juric

    15/18

    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 ialgoritam 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 posao, 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 verzijimodularnog programa za prikaz sahovske table, u kojem je izbjegnuta upotreba global nih promjenljivih,

    koristenjem mehanizma prenosa parametara:

    #include

    using namespace std;

    const char Razmak(' '), Zvjezdica('*');

    int main ()

    void StampajTablu(int, int);

    int m ;

    int ni

    II Glavni program

    II Prototip potprograma "Stampaj Tabl u"

    II Sirina kvadratnag palja

    II Visina kvadratnog 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

  • 8/3/2019 Zeljko Juric

    16/18

    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

  • 8/3/2019 Zeljko Juric

    17/18

    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

    using namespace std;

    int m ain() {

    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

  • 8/3/2019 Zeljko Juric

    18/18

    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 jenesumnjivo izuzetno kratak, i koristi sarno dvije petlje, ali i prilicno tezak za razumijevanje, jer sezasniva 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 namespace std;

    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 medusobnosukobljeni zahtjevi. Tako je ovaj program vjerovatno "sampion kratkoce", ali je manje efikasan odprethodnog, 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 neizvrSava niti jedno dijeljenje! Efikasnost ovog programa se moze osjetno popraviti (uz zadrZavanje

    kratkoce) upotrebom manipulatora "setw" i "setfill", sto prepuStamo enigmatski nastrojenimeitateljima i eitate1jkamakao korisnu vjezbu.

    XV -18