You are on page 1of 26

Osnove Raunarstva Analiza sedmog i osmog tutorijala

Akademska 2014/2015
Enil Paji

OSNOVE RAUNARSTVA
Analiza tutorijla 7 i 8
(koja veoma detaljno uvodi i objanjava pojam funkcije kroz veliki
broj primjera)

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Tematika tutorijala 8 jesu funkcije, novi koncept rezonovanja kda i pisanja istog. Moemo
primijetiti da svaku novinu koju uvedemo uvodimo da bismo sebi olakali pisanje kda i
stvorili nove mogunosti koje su do sada ili bile nedosutupne ili se moralo dosta posla uraditi
da bismo doli do njih.
Tako je i sa funkcijama, one nevjerovatno olakavaju pisanje kda, njegovu razumljivost i
itljivost, te predstavljaju gotovo univerzalno rjeenje za koritenje kda koji se ponavlja vie
puta sa razliitim parametrima.
ta je funkcija, tj. ta ona predstavlja? Funkciju moemo najlae definirati kao dio kda kojeg
moemo vie puta iskoristiti u drugim dijelovima kda. Kada uvedemo definiciju koja je vie
programerski karakteristina, bie nam jasnije ta ovo znai. Prije toga da malo, sa
filozofskog aspekta, prokomentiramo upotrebu funkija.
Moe se postaviti pitanje moe li se programirati bez funkcija?. Odgovor je da, teoretski.
Teoretski je sve to se isprogramira mogue staviti u jednu funkciju (kao to je teoretski
mogue sve petlje u programu zamijeniti samo jednom while petljom). A da li to treba tako
raditi? Apsolutno ne. Izbjegavanje pisanja funkcija nema gotovo nikakvog smisla, osim kada
se eli napisati neki jako ruan i neitljiv kd i to kada se provode neka takmienja sa tim
kriterijima. Znai, pisanje kda pomou funkcija je neto poeljno i jako se esto koristi.
Postoji nekoliko glavnih razloga koji nam govore zato koristiti funkcije:

Bolja itljivost i razumljivost kda. Ovo smo ve spomenuli. Kd kojeg vi napiete e


vrlo vjerovatno itati neko drugi; on e moda biti dijeljen izmeu vie ljudi tj. unutar
tima.
Omoguava nam lake pronalaenje greaka i ispravljanje kda. Kada imamo neki
obiman i komplikovan kd, vrlo je teko pronai greku u njemu, pogotovo ukoliko se
radi o tzv. prljavim grekama koje se teko detektiraju. Ukoliko pak taj kd podijelimo
u funkcije, mi moemo testirati ispravnost svake funkcije i utvrditi u kojoj funkciji se
nalazi greka.
DRY Don't repeat yourself ili prevedeno na bosanski jezik nemoj se ponavljati.
Ovo je jako vaan razlog zato da kd piemo pomou funkcija. Moemo neki dio
kda smjestiti u funkciju i tu funkciju pozivati odreen broj puta, to je puno
jednostavnije i krae. Vidjeemo kroz primjere kolika je korist od funkcij.
Dijeljenje kda zamislimo da nema funkcij i da mi svaki put kada elimo korisnika
pitati da unese neki broj piemo po nekoliko stotina linija kda. To bi predstavljalo
nonu moru za pisanje programa koji bi samo na ekranu ispisao Hello world!.
Umjesto toga mi koristimo neku, ve napisanu, funkciju (npr. printf) i vrlo
jednostavnim pozivom ispiemo eljeni tekst. Na ovaj nain je omogueno dijeljenje
kda kroz funkcije (uz pomo biblioteka u naem sluaju).

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Ispravke kda. Navedimo jednostavan primjer gdje mi u kdu, uz pomo petlji,


raunamo faktorijel nekog broja i raunamo ga na 15 mjesta (za razliite brojeve,
ovisno o potrebi). Naravno da smo tada napisali dio kda prvi put, a za ostala rauanja
smo ga doslovno prekopirali. Neka nakon odreenog vremena shvatimo da smo
napravili greku pri pisanju toga kda. Ispravke bismo morali uraditi na svih 15
mjesta. Ali, koritenjem funkcija, mi bismo trebali greku ispraviti samo na jednom
mjestu, to predstavlja totalnu modularnost za ispravljanje programa. Ne moraju to biti
greke, moemo eljeti da promijenimo funkcionalnost kda i slino...

Ovo su bili neki od glavnih razloga za pisanje koda funkcijama. Vidjeli smo kolika je njihova
korist.
Sada da definiemo ta je funkcija u programskom jeziku C (ista definicija vrijedi za veinu
drugih programskih jezika, kao to je i C++). To je zaseban dio kda, specifino deklarairan,
kojeg mi moemo iskoristiti vie puta. Ne samo to, nego moemo ga iskoristiti vie puta sa
razliitim parametrima, tako da dobijemo i razliito ponaanje. Ba kao prinf funkcija, nekad
ispiemo samo tekst, nekad tekst sa parametrima (pomou %d, %f i slino).
Svi smo do sada sigurno koristili ve napisane funkcije. Ispisivali smo tekst (printf), unosili
brojeve, znakove i ostalo (scanf), raunali korijen (sqrt) itd... Ali, da li smo do sada mi ikad
napisali funkciju? Moda ete pomisliti da niste, ali odgovor je da jeste. Pisali ste main
funkciju. Da, i main je funkcija, samo specijalne namjene (to je startna taka programa, tj.
program kad se pokrene, tu poinje njegovo izvravanje).
Pokaimo sada kako glasi generalna sintaksa funkcije
TIP ime_funcije (parametri)
{
/* Tijelo funkcije */
}

Ovdje za ime funkcije vrijede ista pravila kao i za ime varijable (mora se sadravati od slova
engleskog alfabeta, brojeva i donje crte, mora poinjati sa slovom itd.), ali ono to jo mora
vrijediti za ime funkcije jeste da je ono unikatno u cijelome programu. Znai, ne moemo
imati dvije funkcije sa istim imenom u naem programu. To znai da ne smijemo svoju
funkciju nazvati imenom neke funkcije koja se trenutno nalazi u naem programu.
Uzmimo kao primjer da smo ukljuili biblioteku <stdio.h>. Onda sadri definicije velikog
broja funkcija, meu njima i definicije za printf i scanf funkcije. Tako da su te funkcije
zapravo sadrane u naem programu to znai da mi ne smijemo napisati svoju funkciju koja
e se zvati printf (ili bilo koja druga funkcija koja se nalazi u ukljuenim bibliotekama).
Ime funkcije treba biti samodeskriptivno, odnosno da se iz samog imena funkcije moe,
barem povrno, shvatiti ta bi ta funkcija trebala da radi. Nakon imena funkcije obavezno
slijedi par obinih (malih) zagrada. Eventualno se unutar zagrada moe nalaziti lista
parametara ali to emo kasnije objasniti.
3

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Funkcija moe da vrati neku vrijednost, a TIP upravo predstavlja tip te vrijednosti koju
funkcija vraa (jo se naziva i povratna vrijednost). Tip povratne vrijednosti moe biti bilo
koji tip kojeg smo do sada obradili, tj. za tip povratne vrijednosti su dozvoljeni svi mogui
tipovi u C-u (osim nizova, funkcija ne moe vratiti niz, ali moe pokaziva na niz, to emo
vidjeti u sljedeoj analizi). Vidjeemo kasnije da tip povratne vrijednosti (inae se kae i tip
funkcije) moe biti i jedan specijalni tip u C-u, a to je void tip koji znai da funkcija ne vraa
nita.
Funkcija moe da prima parametre. Oni predstavljaju ulazne podatke u funkciju sa kojima
ona manipulira. Parametri funkcije se navode nakon njenog imena, unutar zagrada,
razdvajajui se zarezom. Svaki parametar mora imati tip i ime koje je unikatno za tu funkciju,
tj. njene parametre. Vidjeemo kasnije da parametri ipak ne moraju imati tip ili da ne moraju
imati ime, ali to su jako rijetke i vrlo nepreporuljive konstrukcije (pogotovo da parametri
nemaju tip).
Za sada nam je najlake funkciju predstaviti kao crnu kutiju, koja ima ulaze i izlaze:

Ulazi

Funkcija

Izlazi

ta se nalazi u crnoj kutiji je ono to mi napiemo u tijelu funkcije. To je funkcionalnost koja


e predstavljati datu funkciju. Njena funkcionalnost tj. ono to e ona dati na izlazu zavisi od
ulaznih parametara. Preimo sada na konkretan primjer.
Primjer 1: Potrebno je napisati funkciju sa imenom Saberi koja prima dva parametra tipa int i
vraa zbir proslijeenih parametara.
int Saberi (int broj1, int broj2)
{
int rezultat = broj1 + broj2;
return rezultat;
}

Sada je potrebno analizirati ovu funkciju. Kao to vidimo, tip povratne vrijednosti je int (to se
moglo zakljuiti iz postavke jer su ulazi u funkciju bili tipa int, i povrati tip funkcije je int)
znai, ono to e funkcija vratiti (njen izlaz) e biti tipa int.
Ime funkcije je Saberi, i ono opisuje ta bi funkcija trebala da radi. Nakon imena slijedi mala
zagrada i lista parametara (koji predstavljaju ulaze). Svaki parametar ima svoj tip i ime. Ime
pramatetara mora biti jedinstveno meu svim parametrima, ali ono je vidljivo samo lokalno
(ne moe mu se pristupiti izvan ove funkcije). Kod nas su oba parametra tipa int (tako
postavka zadatka kae) i imaju imena broj1 i broj2.

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

U tijelu funkcije smo deklarirali varijablu rezultat i u nju smjestili zbir proslijeenih
parametara. Vano je napomenuti da je ova varijabla lokalna, tj. vidljiva je samo unutar nae
funkcije! Paralelu moemo povui sa pisanjem varijabli u nekom bloku, npr. u sljedeem
isjeku varijabla lok_var je lokalna varijabla i nije vidljiva izvan if bloka:
int main ()
{
int a = 0, b = 1, c; /* Lokalne varijable za main() funkciju */
if (a < b)
{
/* varijabla vidljiva samo u if bloku */
int lok_var = 0;
}
/* ovdje varijabla lok_var NIJE vidljiva, ne moemo joj
pristupiti */
return 0;
}

Ponovimo ta znai opseg (engl. scope) varijable. To je blok kda u kojem je varijabla
vidljiva, tj. moemo je koristiti (pristupati joj, mijenjati njenu vrijednost). Varijabla definisana
u nekom opsegu nije vidljiva van njega. Takoer, varijabla umire nakon to izlaska iz
njenog opsega. Pogledajmo primjer:
int main ()
{
/* varijablu 'a' vide svi blokovi u main() funkciji */
int a;
if (a < b)
{
/* varijabla 'b' je vidljiva samo unutar
ovog if bloka, {...}*/
int b;
/* u ovome bloku je vidljiva 'a' varijabla */
} /* <--- varijabla 'b' ovdje "umire" */
/* Ovdje varijabla 'b' nije vidljiva */
/* novi blok */
{
int c; /* vidljiva samo u ovome bloku */
} /* <--- varijabla 'c' ovdje "umire" */
/* Ovdje varijabla 'c' nije vidljiva */
return 0;
}

Poto je varijabla rezultat lokalna, ona e umrijeti na kraju funkcije. Ovo e biti jako vano
kada budemo spominjali funkcije koje vraaju pokazivae na neke varijable. Znai, varijabla
rezultat se kreira kada se pozove funkcija (vidjeemo kasnije kako bismo ovu funkciju mogli
pozvati), a ona se uniti kada ova funkcija vrati vrijednost tj. kada izaemo iz ove funkcije.

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Sa naredbom return rezultat; mi smo iz funkcije vratili vrijednost (tj. izlaz) koja
predstavlja zbir proslijeenih brojeva. Ovu vrijednost moemo iskoristiti kasnije u nekoj
drugoj funkciji, ali moemo je i ignorirati (vie o ovome kasnije).
Takoer, varijable koje su parametri ove funkcije (varijable broj1 i broj2) su takoer lokalne.
Njih moemo mijenjati u funkciji, itati im vrijednost itd. One nisu vidljive izvan ove
funkcije, poto je njihov opseg zapravo blok kda u kojem piemo implementaciju funkcije.
Sada da razmotrimo kako bismo ovu finkciju iskoristili. Njeno iskoritavanje se naziva poziv
funkcije koji se sastoji od navoenja imena funkcije i prosljeivanja joj parametara.
Pogledajmo cijeli primjer:
#include <stdio.h>
int Saberi (int broj1, int broj2)
{
int rezultat = broj1 + broj2;
return rezultat;
}
int main ()
{
int b1 = 0;
printf ("Unesite broj 1: ");
scanf ("%d", &b1);
int b2 = 0;
printf ("Unesite broj 2: ");
scanf ("%d", &b2);
int zbir;
/* Poziv funkcije */
zbir = Saberi (b1, b2);
/* Ispis njenog rezultata, zbir varijabla
printf ("%d + %d = %d", b1, b2, zbir);
return 0;
}

*/

Nakon kompajliranja kda i pokretanja programa, to bi moglo izgledati ovako.

Razmotrimo sada nekoliko stvari. Prvo da ponovo analiziramo poziv funkcije. Mi smo izlaz
funkcije (njenu povratu vrijednost) dodijelili varijabli zbir. Kao ulaz funkciji smo proslijedili
varijbale b1 i b2. Izvravanje je ilo ovim tokom: prvo je funkcija prihvatila varijable b1 i b2,
nakon toga je zbir njihovih vrijednosti smjestila u varijablu rezultat (koja je vidljiva samo
funkciji Saberi, ali nije u ostalim funkcijama, tako ni u main funkciji) i na kraju je funkcija
vratila vrijednost varijable rezultat a tu vraenu vrijednost smo smjestili u varijablu zbir.

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Druga stvar koju trebamo analizirati jeste terminologija. Kada smo pisali Saberi funkciju,
spominjali smo da ona prihvata paramatre. To su varijable koje predstavljaju ulaz u tu
funkciju. Te varijable se nazivaju parametrima kada mi definiemo funkciju. Eh, kada mi tu
funkciju pozivamo, mi njoj kao ulaz prosljeujemo neke varijable. Te varijable se nazivaju
argumenti funkcije. Pogledajmo generalan primjer:
/* Deklaracija funkcije */
int funkcija (parametri)
{
/* Tijelo funkcije */
}
...
/* Poziv funkcije */
x = funkcija (argumenti);

Trea stvar, koja je jako bitna, jeste da smo mi funkciju Saberi napisali iznad funkcije main.
Ovo je jako vano, da funkcija koja e biti pozivana (u naem sluaju Saberi) bude
deklarisana prije (iznad) funkcije u kojoj se ta funkcija poziva (prije main funkcije u naem
sluaju). Vidjeemo kasnije da se i ovo moe zaobii uvoenjem tzv. prototipova, ali za sada,
ako funkcija A poziva funkciju B, tada funkcija B mora biti deklarisana prije funkcije A (ili
barem najavljena pomou ve spomenutih prototipova). Iz ovoga slijedi da sve funkcije
moraju biti napisane (ili najavljene) prije main funkcije, poto e ih ona pozivati.
Primijetimo da se nakon return naredbe u funkciji ne mora nai neka varijabla. To moe biti i
izraz. Pa smo funkciju saberi mogli krae napisati:
int Saberi (int broj1, int broj2)
{
return broj1 + broj2;
}

Naravno, dobili bismo isti efekat.


Kada pozivamo funkciju, mi njoj ne moramo proslijediti varijable; moemo i izraze ili ak
rezultate drugih funkcija. Npr.
int a = 10, b = 7, c = 3;
int rez1 = Saberi (a * a + b - 3 * c, 2 * c - 1);

U ovome sluaju prvo se izraunavaju izrazi a * a + b - 3 * c i 2 * c - 1. Onda se


to prosljeuje funkciji, tj. deklariraju se lokalne varijable broj1 i broj2. U njih se smjeste
rezultati tih izraza i iz funkcije se vraa zbir tih varijabli smjetajui ga u rez1 varijablu (koja
je deklarirana u main funkciji).
Napomenimo da nema pravila kojim se redoslijedom izraunavaju argumenti koji su
proslijeeni funkciji. Standard jezika C (kao i jezika C++) kae da je to nespecificirano
ponaanje i kompajler ima pravo da evaluira izraze kako hoe. Pogledajmo primjer:

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

int x = 10;
int rez = Saberi (++x, x);

Da li e funkciji Saberi biti proslijeene vrijednosti 11 i 11, ili e biti proslijeeno 11 i 10 mi


ne znamo, to je do kompajlera da odlui. Ono to sigurno znamo jeste da e ++x biti izvreno
prije nego to bude smjeteno u lokalnu varijablu broj1 a da li e biti u varijablu broj2
smjetena vrijednost ++x ili x to nije specificirano.
Zanimljivo je da funkcija uvijek mora imati svoje tijelo (koje moe biti prazno), stoga nije
dozvoljeno sljedee (to smo pisali kod npr. for petlji, if konstrukcija i slino):
int Saberi (int broj1, int broj2)
return broj1 + broj2; /* NIJE dozvoljeno! */

Sada navedimo par funkcija sa razliitim imenima, parametrima i povratnim tipovima.


double Kvadriraj (double x)
{
return x * x;
}
int DajPosljednjuifru (int n)
{
return n % 10;
}
double Diskriminanta_1 (double a, double b, double
{
return b * b - 4.0 * a * c;
}
double Diskriminanta_2 (double a, double b, double
{
/* Funkcija 'Kvadriraj' mora biti definisana
double b_na_2 = Kvadriraj (b); /*Poziv
return b_na_2 - 4.0 * a * c;
}
double Diskriminanta_3 (double a, double b, double
{
/* Kompaktnija verzija */
return Kvadriraj (b) - 4.0 * a * c;
}

c)

c)
prije ove funkcije! */
ve napisane funkcije*/
c)

Funkcije Diskriminanta_2 i Diskriminanta_3 pozivaju funkciju Kvadriraj, to predstavlja


poziv jedne funkcije unutar druge funkcije. Takvo neto imamo kada pozovemo npr. printf
funkciju u main funkciji.
Primjer 2: Potrebno je napisati kd za program koji e raunati faktorijel nekog broja. Broj
mora biti u intervalu [0, 12]. Koristiti funkciju sljedeeg prototipa:
long Faktorijel (int n)

Ovdje smo mogli ponovo vidjeti pojam prototipa. Prototip je zapravo samo deklaracija
funkcije, bez njenog tijela! Prototip za nau funkciju Saberi bi izgledao ovako:
int Saberi (int broj1, int broj2);

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Primijetimo znak taka-zarez (;) nakon prototipa. ta zapravo predstavlja prototip funkcije?
To je, pojednostavljeno reeno, njena najava. Mi prototipom najavljujemo funkciju govorei
postoji funkcija sa tim imenom, tim tipovima parametara i povratnom vrijednou toga tipa.
Na taj nain kompajler e znati da takva funkcija postoji. Ovo emo malo bolje pojasniti na
primjeru rjeavanja ovoga zadataka. Od nas se u postavci trai takva funkcija i mi emo je i
napisati.

#include <stdio.h>
long Faktorijel (int n)
{
long F = 1; /* Varijabla ciju cemo vrijednost vratiti */
int a = 0;
for (a = 1; a <= n; ++a)
F *= a; /* Racunamo faktorijel */
return F; /* Vracamo vrijednost od 'F' - izlazimo iz funkcije */
}
int main ()
{
int br = 0;
printf ("Unesite broj [0, 12]: ");
scanf ("%d", &br);
if (br < 0 || br > 12)
{
printf ("Unos pogresan!");
return 0;
}
/* Rezultat funkcije direktno prosljeujemo printf funkciji */
printf ("Faktorijel: %i! = %ld\n", br, Faktorijel(br));
return 0;
}

Ovdje je ogranienje za n = 12 zbog toga to je 13! broj ija vrijednost je prevelika za


varijablu tipa long. Analizirajmo sada primjer iznad. Deklarirali smo funkciju Faktorijel. Njen
povratni tip je long, a tip jedinog parametra je int. U njoj smo deklarirali dvoje lokalne
varijable F i i, izraunali faktorijel broja proslijeenog kao parametar te vratili tu vrijednost.
Poto funkciju Faktorijel pozivamo u main funkciji, mi smo je deklarirali iznad nje.
U main funkciji, gdje pozivamo Faktorijel funkciju, smo direktno proslijedili rezultat funkcije
Faktorijel funkciji printf. To se moglo i glomaznije napisati, poput:
/* Rezultat funkcije smjestimo u varijablu */
long rezultat = Faktorijel(br);
printf ("Faktorijel: %i! = %ld\n", br, rezultat);

Ali vidjeli smo da mi varijablu rezultat koristimo samo pri ispisu (u printf funkciji) pa nam
ona i ne treba. Zbog toga smo rezultat Faktorijel funkcije direktno proslijedili printf funkciji,
ime smo utedili liniju kda.
Takoer, da ponovimo, kada ispisujemo (ili unosimo) varijablu tipa long pomou printf i
scanf funkcije, koristimo %ld.
9

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Navedimo sada i verziju rjeenja koje implementira Faktorijel funkciju nakon main funkcije.
#include <stdio.h>
/* Prototip (najava) funkcije. Primijetimo ; nakon prototipa */
long Faktorijel (int n);
int main ()
{
int br = 0;
printf ("Unesite broj [0, 12]: ");
scanf ("%d", &br);
if (br < 0 || br > 12)
{
printf ("Unos pogresan!");
return 0;
}
/* Rezultat funkcije direktno prosljeujemo printf funkciji */
printf ("Faktorijel: %i! = %ld\n", br, Faktorijel(br));
return 0;
}
/* Implementacija funkcije */
long Faktorijel (int n) /* Zaglavlje, samo ova linija */
{
long F = 1; /* Varijabla ciju cemo vrijednost vratiti */
int a = 0;
for (a = 1; a <= n; ++a)
F *= a; /* Racunamo faktorijel */
return F; /* Vracamo vrijednost od 'F' - izlazimo iz funkcije */
}

Ovo rjeenje e imati isti efekat kao i prethodno. Ono to je vano zapamtiti jeste da prototip
funkcije mora u potpunosti odgovarati deklaraciji funkcije. Znai, ime, tipovi ulaznih
parametara kao i povratni tip moraju biti identini i kod prototipa i kod zaglavlja funkcije
(gdje istu implementiramo piemo). Spomenuli smo pojam zaglavlja funkcije, pa da ga sada
objasnimo. Razlika izmeu prototipa i zaglavlja funkcije je ta to nakon prototipa obavezno
slijedi znak taka zarez, a nakon zaglavlja funkcije obavezno slijedi implementacija funkcije.
Takoer primijetimo da nam imena parametara u prototipu ne trebaju uopte! Zaista,
kompajleru su kod prototipa bitni samo tipovi parametara, ali ne i njihova imena, jer mi ne
moemo nita korisno sa njima uraditi. Stoga je prototip mogao da glasi i ovako:
long Faktorijel (int);

Ovo je onaj sluaj kojeg smo spomenuli, parametri funkcije ne moraju imati imena (i kod
zaglavlja funkcije, nakon kojeg slijedi njena implementacija, mi moemo imati bezimene
parametre, ali onda ne postoji nain da ih mi iskoristimo; taj primjer je prikazan ispod). Drugi
sluaj (koji je nepoeljan) kada parametri nemaju tip e biti objanjen kasnije, i to samo
radi potpunosti ovog dokumenta.
int Funkcija (int a, int b, int)
{
/* Nemamo naina da pristupimo 3. parametru! */
return a * a * a + b - 2;
}

10

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Moe se postaviti pitanje moe li da funkcija ne vraa nita? Da samo izvri dio kda i to je
to?. Odgovor je da, moe. Ukoliko funkcija ne vraa nita onda se ona naziva procedurom.
Procedure (tj. funkcije koje ne vraaju nita) piemo isto kao i obine funkcije, samo to im
kao povratni tip navodimo void. Rije void kada bi se prevela sa engleskog ima znaenje
nita, praznina, pa tako funkcija koja vraa void zapravo ne vraa nita. Pokaimo to na
primjeru.
Primjer 3: napisati program koji od korisnika trai da unese n brojeva (n se unosi sa tastature,
mora biti u intervalu [1, 15]) i smjeta ih u niz. Potrebno je da se taj niz ispie u obrnutom
poretku. Za upit korisnika Unesite i-ti elemenat niza koristiti proceduru tj. funkciju koja ne
vraa nita.
#include <stdio.h>
void Ispisi (int i)
{
printf ("Unesite %i. elemenat niza. Niz[%i] = ", i + 1, i);
}
int main ()
{
int n = 0;
int niz [15];
printf ("Unesite broj n: ");
scanf ("%d", &n);
if (n < 1 || n > 15)
{
printf ("Unos pogresan!\n");
return 0;
}
int i;
for (i = 0; i < n; ++i)
{
Ispisi(i); /* Poziv procedure */
scanf ("%i", &niz[i]);
}
/* Ispis niza u obrnutom poretku: */
printf ("Ispis elemenata niza u obrnutom redoslijedu:\n");
for (i = n - 1; i >= 0; --i)
printf ("%i, ", niz[i]);
return 0;
}

Naa funkcija Ispisi je imala povratni tip void pa je to onda procedura. U njoj samo ispiemo
tekst koji korisnika pita da unese i-ti broj na i-1-vom indeksu.
Zanimljiv je poziv procedure. Nju pozivamo tako to joj navedemo ime i u zagradama
argumente. Poto ona ne vraa nita, nije validno pisati int x = Ispisi (i);. Ako tako
neto uradimo, dobiemo sljedeu greku kompajlera: C:\Users\...\main.c|20|error: void
value not ignored as it ought to be|.
Prije nego to napiemo jo neke razlike izmeu funkcije i procedura, napomenimo da nije
mogue deklarirati varijablu tipa void, iako je i void tip jezika C (on je ipak specijalni tip).
Vidjeemo kasnije (u sljedeoj analizi) da je mogue deklarirati varijablu koja je pokaziva na
11

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

tip void, ali o tome neemo govoriti ovdje. Sada da pokaemo kakvu greku e nam
kompajler javiti ukoliko pokuamo deklarirati varijablu tipa void, tj. ukoliko pokuamo
sljedee: void x;. Greka izgleda ovako: C:\Users\...\main.c|21|error: variable or field 'x'
declared void|.
Sada da se osvrnemo na razliku izmeu procedur i funkcij. Jezik C (kao ni jezik C++) ne
pravi razliku izmeu procedure i funkcije. Tehniki, jedina razlika je povratni tip (to povlai
i da funkcija mora imati return naredbu, a procedura ne mora). Mi rezultat koji vrati funkcija
moemo ignorirati. Uzmimo primjer nae Saberi funkcije. Moemo pisati ovako neto:
int main()
{
...
Saberi (a, b); /* Umjesto rezultat = Saberi (a, b); */
/* I rezultat sljedecih izraza je ignoriran! */
5 + 3;
a + b;
a + 1;
return 0;
}

Sa ovim smo mi prosto ignorirali ono to je funkcija Saberi vratila. To nam ovdje nije bilo od
koristi, ali ukoliko funkcija uradi i neki sporedni efekat, pored vraanja vrijednosti, onda mi
moemo ignorirati njenu povratnu vrijednost.
Linija koju smo zaokruili, a + 1;, ne mijenja vrijednost varijable a! Ona samo vrijednost
varijable a sabere sa brojem 1 i rezultat toga se ignorira! Da smo htjeli poveati vrijednost
varijable a za 1 pisali bismo a = a + 1 ili a++ odnosno ++a. Takoer jo jednom
napomenimo da izraz a = a++ predstavlja boni efekat i nije regularan. U tome izrazu mi
vrijednost varijable a mijenjamo dva puta (jednom kada je inkrementiramo tj. a++ a drugi
put kada dodjeljujemo).
Primjer funkcije koja uradi neki sporedni efekat ali i vrati vrijednost je, nama poznata, scanf
funkcija. Do sada nismo iskoritavali povratnu vrijednost scanf funkcije, ali na sljedeem
primjeru pokaimo kako da znamo da li je unos bio ispravan.
Ime varijable koja se prosljeuje kao argument funkciji nema ama ba nikakve veze sa
imenom parametra te funkcije. Npr. razmotrimo funkciju koja e prihvatati dva parametra, a i
b i ispisivati ih. Mi emo u main funkciji deklarirati varijable takoer sa imenima a i b, ali
emo funkciji proslijediti b i a. Pogledajmo:
#include <stdio.h>
void Ispisi (int a, int b)
{
printf ("Iz funkcije ISPISI:\n");
printf ("a = %i\n", a);
printf ("b = %i\n", b);
}

12

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

int main ()
{
int a = 10, b = 5;
printf ("Iz funkcije MAIN:\n");
printf ("a = %i\n", a);
printf ("b = %i\n", b);
printf ("\nPozivamo funkciju Ispisi (b, a):\n\n");
Ispisi(b, a);
return 0;
}

Varijabla a iz funkcije Ispisi e primiti vrijednost varijable b iz main funkcije, a varijabla b iz


Ispisi funkcije e primiti vrijednost varijable a iz main funkcije.
Nakon kompajliranja i pokretanja, to e izgledati ovako:

Primjer 4: Napisati program koji od korisnika trai unos 2 realna broja a i b i jedan cijeli broj
n. Program treba izraunati broj c po formuli = 7 + , i = 1..n. (broj c je suma svih ci
brojeva). Pri tome, kao unos moe biti proizvoljan, tj. korisnik moe unositi i slova i
specijalne znakove i slino. Va program treba da detektira kada je unos bio pogrean te da u
sluaju istog ispie greku i zavri sa radom.

#include <stdio.h>
int main ()
{
double a, b, c = 0;
int n;
printf ("Unesite brojeve a, b i c: ");
/*
scanf funkcija vrati broj
uspjesno unesesenih parametara.
Mi unosimo tri broja, i scanf funkcija
bi trebala vratiti broj 3.
*/
int rez = scanf ("%lf %lf %d", &a, &b, &n);
if (rez != 3)
{
printf ("Niste unijeli 2 realna broja i jedan cijeli!\n\n");
return 0;

13

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

}
int i;
for (i = 1; i <= n; ++i)
c += 7.0 * a + b;
printf ("C je: %f", c);
return 0;
}

Ako namjerno napravimo pogrean unos (npr. umjesto broja unesemo slovo ili neki tekst),
onda e nas to program upozoriti:

Mi smo prije, dok smo koristili scanf funkciju, ignorirali njenu povratnu vrijednost. To je u C
jeziku (kao i u C++-u) dozvoljeno, jer on ne pravi striktnu razliku izmeu procedura i funkcija
(proceduru smatra samo specijalnim sluajem funkcije). Vidjeli smo da povratna vrijednost
funkcije scanf moe biti korisna. ta je sa printf funkcijom? Ona vraa broj ispisanih znakova,
npr. ukoliko ispiemo Zdravo OR! ona e vratiti broj 10. Slijedi da nam povratna vrijednost
ove funkcije i nije ba od neke koristi i ona se veinom ignorira.
Primjer 5: Prepravite Primjer 4 tako da program, nakon pogrenog unosa, ponovo od
korisnika trai da unese brojeve.
Ukoliko naivno pokuamo rijeiti ovaj program tako da umjesto izlaza ponovo korisnika
pitamo, biemo negativno iznenaeni. Npr. sljedee rjeenje bi moglo izgledati kao da radi,
ali vidjeemo da ono ipak ne radi.
#include <stdio.h>
int main ()
{
double a, b, c = 0;
int n, rez;
do
{
printf ("Unesite brojeve a, b i c: ");
rez = scanf ("%lf %lf %d", &a, &b, &n);
if (rez != 3)
printf ("Pogresan unos!\n\n");
}
while (rez != 3);
int i;
for (i = 1; i <= n; ++i)
c += 7.0 * a + b;
printf ("C je: %f", c);
return 0;
}

14

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Ukoliko kd iznad kompajliramo, pokrenemo i unesemo loe podatke, ono e ispisati da je


unos pogrean. Ali, dok se bude traio ponovni unos, tada program upasti u beskonanu
petlju. Zbog ega? Pa zbog toga to funkcija standardni izlaz (stdin) postavi u pogrenom
(neispravnom) stanju nakon to je dolo do pogrenog unosa. Problem kod unosa jeste taj to
mi unos zavravamo sa unosom novog reda (pritiskanjem Enter tipke). Eh, i novi red ima
svoju oznaku, a to je '\n'. Nakon to mi pogreno unesemo neki tekst, u spremniku (engl.
buffer) je ostao znak '\n' i nakon to ponovo korisnika pitamo unos, ono e iz spremnika
proitati '\n' i naravno javiti da je unos netaan. Zbog toga program ulazi u beskonanu petlju:

Znai, mi na neki nain moramo standardni ulaz dovesti u ispravno stanje. Najlake je to
uraditi tako to emo napisati funkciji Ocisti koja e to raditi, a pozivati emo je kada znamo
da je dolo do pogrenog unosa. Uradimo sada ovaj zadatak na ispravan nain, pri tome emo
funkciju Obrisi napisati nakon main funkcije, ali emo njen prototip napisani prije main
funkcije.
#include <stdio.h>
void Obrisi();
int main ()
{
double a, b, c = 0;
int n, rez;
do
{
printf ("Unesite brojeve a, b i c: ");
rez = scanf ("%lf %lf %d", &a, &b, &n);
if (rez != 3)
{
printf ("Pogresan unos!\n\n");
Obrisi(); /* Ocistimo ulazni tok */
}
}
while (rez != 3);
int i;
for (i = 1; i <= n; ++i)
c += 7.0 * a + b;

15

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

printf ("C je: %f", c);


return 0;
}
void Obrisi()
{
char c;
while (7)
{
/* Pokupimo sve znakove do \n (kupimo i njega) */
/* EOF - End Of File predstavlja specijalan
znak koji indicira kraj datoteke */
c = getchar();
if (c == '\n' || c == EOF)
return;
}
}

Rjeenje prikazano iznad je ispravno. Koristi funkciju getchar (nalazi se u <stdio.h>


biblioteci) koja uzima znak po znak iz ulaznog spremnika, i na taj nain ga prazni.
Funkcija Obrisi se mogla i kompaktnije napisati:
void Obrisi()
{
char c;
while ((c = getchar()) != '\n' && c != EOF);
/* Odnosno (sa eksplicitno napisanim praznim tijelom):
while ((c = getchar()) != '\n' && c != EOF) {}
*/
}

Pogledajmo kako bi sada izgledalo iznad napisano nakon kompajliranja i pokretanja:

Postoje dvije jako zanimljive stvari u iznad napisanoj funkciji. Prva je da smo u while petlji, u
njenom zaglavlju, u varijablu c smjetali proitani znak (pomou getchar funkcije) i onda ga
poredili sa '\n' i EOF. To je naravno dozvoljeno, isto kao to smo u for petlji vrili dodjelu
varijabli, npr. for (i = 0; ...).

16

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Druga zanimljiva stvar je da funkcija (tj. procedura) Obrisi nije primala nikakve parametre! I
to je dozvoljeno da nam funkcija ne prima nikakve parametre. Kada se takva funkcija
poziva, obavezno moramo navesti i par malih zagrada () nakon imena funkcije. Drugim
rijeima, poziv Obrisi funkcije izgleda ovako:
Obrisi();

A nikako ovako:
Obrisi;

Druga varijanta (bez para zagrada) je samo pokaziva na funkciju (tj. adresa mainskog kda
te funkcije). O pokazivaima na funkcije e biti rijei u Analizi tutorijala koja obuhvata
pokazivae.
Sada da spomenemo jednu razliku izmeu jezika C i C++ u deklarisanju funkcija bez
paramatara. Naime, kada mi napiemo funkciju kao to smo napisali Obrisi funkciju, tj. na
sljedei nain:
TIP Funkcija ()
{
/* Tijelo funkcije */
}

Tada to u jeziku C ne znai da funkcija ne prima parametre. To u C-u zapravo znai da


funkcija prima neogranien broj parametara nepoznatog tipa! Zaista, toj funkciji moemo
proslijediti bilo koji broj parametara, bilo kojeg tipa. Pogledajmo primjer:

#include <stdio.h>
int Funkcija ()
{
return 10;
}
int main ()
{
int n = 5;
1
int x = Funkcija(10, n, 2.5, 'c', "neki tekst", n * n);
printf ("x = %d\n", x);
2
int y = Funkcija("veeeliki tekst", n / 2, n - 1);
printf ("y = %d\n", y);
3
int z = Funkcija(n, 1);
printf ("z = %d\n", z);
return 0;
}

U kdu iznad smo istu funkciju pozvali tri puta, sa razliitim brojem parametara, i razliitim
tipovima parametara. Ovi parametri se jednostavno ignoriu, isto kao da i nisu napisani.
17

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Naravno, kd iznad e, nakon kompajliranja i pokretanja, ispisati x = 10, y = 10 i z =


10 (svako u novom redu), bez obzira na proslijeene parametre.
Postavlja se pitanje da li postoji ikakav nain da mi napiemo funkciju koja ba ne prima
nikakve parametre, tj. da funkciju ne smijemo pozivati sa prozivoljnim parametrima?
Odgovor je pozitivan. To moemo uraditi tako da stavimo kljunu rije void kao jedini
parametar funkcije, ime smo funkcionalno rekli funkcija prima void, tj. ne prima nita.
Drugim rijeima, deklariranje funkcije Funkcija gdje zabranjujemo njeno pozivanje sa bilo
kakvim parametrima emo uraditi na sljedei nain:
int Funkcija (void)
{
return 10;
}

Ukoliko sada pokuamo pozvati ovu funkciju, ali navedemo bilo kakav parametar, kompajler
e nam javiti greku: C:\Users\...\main.c|10|error: too many arguments to function
'Funkcija'|. Na ovaj nain smo efektivno zabranili pozivanje funkcije sa parametrima
odnosno argumentima. Naravno, ovu funkciju ne smijemo pozvati niti sa void kao
argumentom, tj. nije dozvoljeno sljedee: int x = Funkcija(void);. Nije dozvoljeno ni
da nam funkcija prima dva ili vie parametara tipa void, tj. neto poput ovoga ne radi:
int Funkcija (void, void) /* NE RADI! */
{
return 10;
}

Bie javljena sljedea greka: C:\Users\...\main.c|3|error: 'void' must be the only


parameter|.
Konano, nije dozvoljeno ni imenovati void parametar, tj. neto poput ovoga ne radi:
int Funkcija (void param) /* NE RADI! */
{
return 10;
}

Dobiemo sljedeu greku: C:\Users\...\main.c|3|error: parameter 1 ('param') has


incomplete type|.
Sada da spomenemo razliku izmeu jezik C i C++ o kojoj smo priali. Name, za razliku od
C-a, u C++-u kada stavimo samo prazan par zagrada: int Funkcija (), tada je to potpuno
isto kao da smo napisali int Funkcija (void). Zbog toga onako deklarirana funkcija
nema znaenje da prihvata neogranien broj parametara nepoznatog tipa nego jednostavno ne
prihvata niti jedan parametar.
U jeziku C, glavni tip je int. To znai da, ako mi ne specificiramo povratni tip funkcije, on e
biti int tipa:
18

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Funkcija (int a, double b) /* Nije preporuljivo raditi ovo!!! */


{
return a * b;
}

Povratni tip iznad napisane funkcije je int, ali ovo je strogo nepreporuljivo raditi. Isto vrijedi
i za parametre funkcije; ukoliko im ne navedemo tip, smatrati e se da je su oni tipa int. Ovo
vrijedi jedino ukoliko niti jednom parametru ne navedemo tip:
int Funkcija (a, b, c, d) /* Nije preporuljivo raditi ovo!!! */
{
return a + b * b c * d;
}

Parametri a, b, c i d e automatski biti int tipa. Da smo samo jednom parametru naveli tip (a
ostalima ne), kompajler bi nam javio greku. Za gore napisanu funkciju kompajler e nas
upozoriti: C:\Users\...\main.c|3|warning: type of 'a' defaults to 'int' [-Wmissing-parametertype]|. Postoje mnogi nedostaci iznad napisane funkcije, prvi meu njima jeste da mi ovu
funkciju moemo pozvati bez parametara, sa samo jednim parametrom, samo sa dva, a
moemo je pozvati i sa neogranienim brojem parametara. Zbog toga je pisanje funkcije bez
povratnog tipa ili pisanje funkcije ne navodei tipove njenim parametrima nepreporuljivo, i
treba ga po svaku cijenu izbjegavati. Ovdje je samo navedeno radi kompletnosti materijala i
da saznamo neto novo o jeziku C.
Napomenimo da jezik C ne podrava preklapanje funkcija (engl. function overloading), tj. ne
dozvoljava da imamo vie funkcija sa istim imenom a razliitim tipom parametara. Za razliku
od C-a, jezik C++ to dozvoljava.
Takoer, jezik C++ dozvoljava da funkcija ima opcionalne parametre, tj. parametre koji imaju
zadanu vrijednost i ne moraju se navoditi prilikom pozivanja. Jezik C ni to nedozvoljava, tako
da ako napiemo neto poput sljedeeg, ono nee biti dozvoljeno u C-u:
void Funkcija (int param1, int opcionalni = 10) /* Ne radi u C-u! */
{
/* tijelo funkcije */
}

Razmotrimo sada ta se deava sa argumentima koji su proslijeeni funkciji. Pogledajmo


jednostavan primjer:
#include <stdio.h>
void Funkcija (int n)
{
n = 2;
}
int main ()
{
int broj = 10;
printf ("Varijabla 'broj' PRIJE poziva funkcije je %d\n", broj);
Funkcija (broj);

19

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

printf ("Varijabla 'broj' NAKON poziva funkcije je %d\n", broj);


return 0;
}

Vano je da znamo da se argumenti funkcijama prosljeuju po vrijednosti, tj. funkcija dobiva


njihovu kopiju. Bilo kakva izmjena proslijeenih varijabli u funkcijama je zapravo izmjena
njihovih kopija, te originalne varijable nee biti izmijenjene. Primjer iznad nakon
kompajliranja i pokretanja izgleda ovako:

Moe se postaviti pitanje da li funkcija ima ikakvog naina da izmijeni varijablu druge
funkcije? Odgovor je da, postoji nain. Te funkcije kao parametre prihvataju pokazivae na
varijable, a mi im pri pozivu moramo proslijediti adrese varijabli. Kao primjer moemo uzeti
scanf funkciju kojoj smo morali slati adrese varijabli (uz pomo adresnog operatora &), a
funkcija scanf je mijenjala vrijednosti tih varijabli. O ovome vie u sljedeoj analizi koja
pokriva gradivo iz pokazivaa.
Sada je potrebno razmotriti prosljeivanje nizova funkcijama. Prije svega moramo
napomenuti da, kada mi proslijedimo niz funkciji, mi smo zapravo proslijedili pokaziva na
prvi elemenat toga niza. Za sada emo to zvati samo pokazivaom, bez objanjavanja ta on
predstavlja. Kako je i pokaziva obina varijabla, i ona e biti proslijeena po vrijednosti, tj.
funkcija e dobiti kopiju pokazivaa. Ali ta e biti sa onim na to on pokazuje, tj. ta e biti
sa nizom? Eh, sada dolazimo do jedne jako vane stvari: mi nikada funkciji ne moemo
proslijediti kopiju niza! Kada god proslijedimo niz funkciji, mi smo zapravo proslijedili
pokaziva na njegov prvi elemenat, tj. proslijeen je originalni niz. Sve izmjene nad nizom
koje napravimo u funkciji, odraziti e se na originalni niz. Pogledajmo primjer:
#include <stdio.h>
void Funkcija (int niz[]) /* Deklaracija parametra */
{
niz[0] = 100;
}
int main ()
{
int niz_intova[4] = {1, 2, 3, 4}, i;
printf ("Niz PRIJE poziva funkcije: \n\n");
for (i = 0; i < 4; ++i)
printf ("%i, ", niz_intova[i]);
Funkcija(niz_intova); /* Prosljeivanje niza, samo ime! */
printf ("\n\nNiz NAKON poziva funkcije: \n\n");
for (i = 0; i < 4; ++i)
printf ("%i, ", niz_intova[i]);
printf ("\n");
return 0;

20

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Niz, kao parametar funkcije se normalno deklarira. Stavi se ime niza i par uglastih zagrada.
Kada pozivamo funkciju i prosljeujemo niz, navodimo samo ime niza!
Program iznad demonstrira da funkcija moe izmijeniti originalni niz:

Ime niza, upotrijebljeno samo za sebe (znai bez indeksiranja) predstavlja pokaziva na prvi
elemenat toga niza. Mi smo funkciju mogli pozvati i ovako:
Funkcija(&niz_intova [0]);

Primijetimo da smo proslijedili adresu prvog elementa. Isto tako, spomenuli smo da funkcija
prima pokaziva na prvi elemenat, pa smo umjesto int niz[] mogli pisati i ovo:
void Funkcija (int *niz)

Primjer 6: napisati funkciju koja e pravilno ispisivati proslijeeni niz. Elementi niza trebaju
biti ispisani unutar vitiastih zagrada, razdvojeni zarezom tako da se nakon posljednjeg
elementa ne nalazi zarez. Primjer, za elemente niza 1, 2, 0, 4, funkcija treba ispisati {1, 2, 0,
4}.

Iz postavke zadatka vidimo da funkcija (nazovimo je IspisiNiz) treba primati niz. Ova
funkcija zapravo treba primiti jo jedan paramatar, a to je veliina niza. Poto smo rekli da mi
funkciji prosljeujemo samo adresu prvog elementa niza, ta funkcija nema ama ba nikakv
nain da sazna koliko taj niz ima elemenata! Zbog toga mi funkciji, pored niza, moramo
proslijediti i informaciju o broju elemenata toga niza. Takvo je pravilo gotovo uvijek, i kada
se kae funkcija prima niz smatra se da ta funkcija treba primati i dodatni parametar koji
predstavlja veliinu toga niza. (Ipak, u jeziku C++, postoji trik da se sazna veliina niza tj.
broj elemenata niza proslijeenog funkciji. To koristi stvari koje jezik C nema poput template
konstrukcija i referenci).
#include <stdio.h>
void IspisiNiz (int niz[], int velicina)
{
printf ("{");
int i;

21

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

for (i = 0; i < velicina - 1; ++i)


printf ("%i, ", niz[i]);
if (velicina > 0)
printf ("%i", niz[velicina - 1]);
printf ("}");
}
int main ()
{
int niz[] = {1, 2, 7, -2, 0, 4, 5, 8};
IspisiNiz(niz, 8);
return 0;
}

Izgled bi mogao biti ovakav:

Zanimljiva stvar jeste da mi pomou funkcije IspisiNiz moemo ispisati samo dio niza, to je
ponovan dokaz da je pisanje kda funkcijama praktino. Ukoliko liniju gdje pozivamo
funkciju IspisiNiz promijenimo da izgleda kao ispod, mi emo ispisati samo dio niza.
Napomenimo da samo ime niza znai adresa prvog elementa, pa tako moemo ispisati niz
odakle elimo:
IspisiNiz(&niz[3], 3);

Nakon pokretanja, program bi ispisao sljedee (4., 5. i 6. elemenat niza):

Znai, funkcije ne samo da skrauju pisanje istog kda vie puta, nego je taj kd skalabilan i
ovisi od ulaznih parametara (moemo postii razliite efekte nad istim kdom).
Primjer 7: Napisati dvije funkcije, prva sa nazivom Suma koja rauna sumu niza tj. zbir
njegovih elemenata, a druga Udvostruci koja svaki elemenat niza udvostrui. Potrebno je da
se izrauna suma nekog niza (ne mora se unositi), zatim da se ta suma ispie, pa se onda
udvostrui svaki elemenat niza, i na kraju ponovo izrauna suma niza i ispie.
#include <stdio.h>
double Suma (double niz[], int vel)
{
double s = 0;
int i;
for (i = 0; i < vel; ++i)
s += niz[i];
return s;

22

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

}
void Udvostruci (double niz[], int vel)
{
int i;
for (i = 0; i < vel; ++i)
/* Isto kao: niz[i] = niz[i] * 2.0 */
niz[i] *= 2.0;
}
int main ()
{
double niz[5] = {1, 1.1, 2.3, 4.1, 7.7};
double suma1 = Suma (niz, 5);
printf ("Suma1 = %.3f\n", suma1);
printf ("Pozivam funkciju (proceduru) 'Udvostruci'!\n");
Udvostruci (niz, 5);
double suma2 = Suma (niz, 5);
printf ("Suma2 = %.3f\n", suma2);
return 0;
}

Jo smo se jednom uvjerili da funkcija moe izmijeniti vrijednosti niza. Takoer smo jo
jednom vidjeli da je funkciji potreban i dodatnni parametar koji predstavlja veliinu niza.
Analizirajmo sada prosljeivanje 2D nizova funkcijama. Generalno, sintaksa je slina kao i
kod obinih, 1D nizova, ali postoji jedno pravilo. Kada god prosljeujemo viedimenzionalni
niz (dimenzije n > 1), mi moramo u parametru funkcije navesti sve dimenzije toga niza osim
prve. Razlog za to je tehnike prirode, a u osnovi je svaki multidimenzionalni niz u memoriji
predstavljen kao 1D niz. Da bismo izraunali adresu nekog elementa u 2D nizu, mi moramo
znati njegovu drugu dimenziju, tj. broj kolona 2D niza odnosno matrice. Isto vrijedi i za 3D
niz, 4D niz i slino.
Primjer 8: potrebno je napraviti program koji e ispisati proslijeeni 2D niz dimenzija n5.
Znai, taj 2D niz (matrica) e sigurno imati 5 kolona, a broj redova nije poznat.

#include <stdio.h>
void IspisiMatricu (int mat[][5], int br_redova)
{
int i = 0, j;
for (; i < br_redova; ++i)
{
for (j = 0; j < 5; ++j)
printf ("%4i", mat[i][j]);
printf ("\n");
}
}
int main ()
{
int matrica[2][5] = {{1, 2, 3, 4, 5}, {5, 4, 3, 2, 1}};
IspisiMatricu(matrica, 2);
return 0;

23

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Nakon kompajliranja i pokretanja to e izgledati ovako:

Zakljuak: uvijek moramo eksplicitno napisati sve dimenzije osim prve kada prosljeujemo
multidimenzionalne nizove.
Napomenimo jo da standard jezika C (kao i jezika C++) ne dozvoljava pisanje funkcije u
funkciji! Nije dozvoljeno ovo:
int Funkcija1 (int a)
{
/* ... */
void Funkcija2 (char b) /* Ne moe ovako! */
{
/* ... */
}
}

Ipak, prototip unutar funkcije smijemo pisati. On predstavlja najavu funkcije 1 ali samo
funkciji 2. Takav prototip se naziva i lokalnim prototipom.
#include <stdio.h>
int main ()
{
int n = 10;
int Kvadriraj(int); /* Lokalni prototip! */
int n2 = Kvadriraj(n);
return 0;
}
int Kvadriraj (int x)
{
return x * x;
}

Za kraj, dotaknimo se pojma rekurzivne funkcije, tj. funkcije koja poziva samu sebe. Funkcija
moe pozivati drugu funkciju, to smo vidjeli par puta do sada (npr. u main funkciji pozivamo
scanf funkciju). Eh, ukoliko funkcija poziva samu sebe, to se naziva rekurzijom, a takva
funkvija rekurzivnom funkcijom. Naravno, treba definisati i granicu rekurzije, inae bi
funkcija pozivala samu sebe neogranien broj puta. Moramo navesti neki sluaj kada emo
izai iz rekurzije tj. terminirati rekurziju. Ovdje se neemo previe time baviti nego emo
spomenuti rekurziju radi kompletnosti i upoznavanja studenata sa tim pojmom.

24

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Primjer 9: Napisati funkciju koja rauna faktorijel broja koritenjem rekurzije.


long Faktorijel_Rekurzija (int n)
{
/* Faktorijel je matematicki definisan ovako:
n! := 1,
n! := n * (n - 1),

za n < 2
za n >= 2

n pripada N u {0}
Sprovedimo ovu definiciju u programski kd:
*/
if (n < 2)
return 1;
return n * Faktorijel_Rekurzija(n - 1);
}

Na sluaj gdje smo prekidali funkciju (tj. prekidali rekurziju) jeste ako je n < 2.

Primjer 10: Potrebno je od korisnika traiti unos cijelog broja n. Broj n mora biti u intervalu
[3, 99] i mora biti djeljiv sa 3. Ukoliko jedan od ovih uslova nije zadovoljen, treba ispisati
greku i zatraiti ponovo unos. Program nakon toga treba od korisnika da zatrai unos n
realnih brojeva i smjesti ih u niz. Na kraju, program treba da izrauna n/3 diskriminanti. Za
koeficijente a, b i c svake diskriminante se uzimaju elementi niza takvi da se uzima svako m-ti
elemenat, gdje m predstavlja korak (opisan primjerom ispod)
Primjer za n = 9
Diskriminanta 1:
Diskriminanta 2:
Diskriminanta 3:

i
a
a
a

brojeve 1, 2,
= 1, b = 4, c
= 2, b = 5, c
= 3, b = 6, c

3, 4, 5, 6, 7, 8, 9:
= 7
= 8
= 9

U zadatku je obavezno koristiti funciju za raunanje diskriminante sa sljedeim prototipom:


double Diskiminanta (double a, double b, double c)
Brojeve ispisivati zaokrueno na dvije decimale, pri tome broj ukupno treba da zauzme 5
mjesta. Svaku diskriminantu ispisivati u novom redu. Vie detalja pogledati na slici ispod za
n = 12:

25

Osnove Raunarstva Analiza sedmog i osmog tutorijala


Akademska 2014/2015
Enil Paji

Ovaj zadatak nema veze sa maloprije opisanim rekurzivnim rjeavanjem. Ovdje samo
iskoritavamo jednostavnost kda koji koristi funkcije.
#include <stdio.h>
#define BR_EL 99
double Diskriminanta (double a, double b, double c)
{
return b * b - 4.0 * a * c;
}
int main ()
{
double niz [BR_EL];
int a, n;
while (7)
{
printf ("Unesite koliko zelite brojeva [3, 99], mora biti
djeljivo sa 3: ");
scanf ("%i", &n);
if (n < 3 || n > 99 || n % 3 != 0)
printf ("\tUnos pogresan!\n");
else
break;
}
for (a = 0; a < n; ++a)
{
printf ("Unesite %i. broj: ", a + 1);
scanf ("%lf", &niz[a]);
}
for (a = 0; a < n / 3; ++a) /* n/3 je diskriminanti */
{
int korak = n / 3;
double A, B, C;
A = niz[a];
B = niz[a + korak];
C = niz[a + 2 * korak];
double D = Diskriminanta (A, B, C);
printf ("Diskriminanta: b^2-4ac za A: %-5.2f B: %-5.2f C:
%-5.2f iznosi D = %-5.2f\n", A, B, C, D);
}
return 0;
}

26

You might also like