Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
1 1
1.1 Funkcije
Funkcije razbijaju velike raunske zadatke u manje celine i omoguavaju upotrebu ve postojeeg koda. Funkcije skrivaju detalje postupka od delova programa i time ine ceo program jasnijim i jednostavnijim za menjanje.
Funkcije mogu da vraaju vrednost (rezultat) pozivajuoj funkciji ili ne.
Definicija funkcije obuhvata: tip povratne vrednosti funkcije ili tip rezultata (ako se ne navede podrazumeva se int) ime funkcije listu formalnih parametara ili argumenata funkcije telo funkcije
Deklaracija (prototip) funkcije je definicija funkcije bez tela funkcije. Prototip funkcije zavrava se znakom ;.
Primer prototipa i definicije funkcije:
tip_rezultata ime_funkcije (tip param1, tip param2, ..., tip paramN);
tip_rezultata ime_funkcije (tip param1, tip param2, ..., tip paramN) { //telo funkcije }
Poziv funkcije unutar programa podrazumeva navoenje: imena funkcije liste stvarnih argumenata funkcije
Mehanizam za vraanje vrednosti iz pozvane funkcije predstavlja naredba return. Iza return moe se nalaziti bilo koji izraz.
return izraz;
Tip izraza se pretvara u tip rezultata ako je to neophodno. Takoe, iza return ne mora uopte da se navede neki izraz u tom sluaju se nijedna vrednost ne vraa pozivaocu.
1 Zasnovano na primerima kolega Milana Bankovia (www.matf.bg.ac.rs/~milan), Jelene Graovac (www.matf.bg.ac.rs/~jgraovac) i Stae Vujii (www.matf.bg.ac.rs/~stasa)
Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
2 Funkcija koja ne vraa nikakvu vrednost deklarie se da ima povratni tip void. Slino, ako funkcija nema argumente, u deklaraciji se umesto liste argumenata navodi void. Svi ranije pomenuti osnovni tipovi u C-u mogu biti povratni tipovi funkcije.
Primer 1: Napisati funkciju koja vri sabiranje dva cela broja i program koji testira rad ove funkcije.
#include <stdio.h>
/* Definicija funkcije */ int zbir(int a, int b) { return a+b; } int main() //pozivajuca funkcija { /* Poziv funkcije zbir */ int rezultat = zbir(3,5); printf("%d\n", rezultat);
return 0; }
NAPOMENA: Deklaracija funkcije moe da stoji nezavisno od definicije funkcije, ali mora da odgovara definiciji. Ako se funkcija definie posle glavne funkcije main(), na poetku programa (pre funkcije main) mora da postoji prototip funkcije (deklaracija funkcije). Dakle, deklaracija funkcije je neophodna u sluaju kada se njena definicija navodi nakon upotrebe date funkcije u kodu. Ako prototip ne postoji, bie izvrena implicitna deklaracija funkcije kada se ona prvi put pozove u nekom izrazu. Za takve funkcije se pretpostavlja da vraaju tip int i nita se ne pretpostavlja o njihovim argumentima, to moe da dovede do nepoklapanja.
Primer 2: Funkcija koja sabira dva broja (deklaracija stoji nezavisno od definicije).
#include <stdio.h>
int zbir(int, int); /* Deklaracija (prototip) funkcije */
int main() //pozivajuca funkcija { /* Poziv funkcije zbir */ int rezultat = zbir(3,5); printf("%d\n", rezultat);
return 0; Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
3 }
/* Definicija funkcije */ int zbir(int a, int b) { return a+b; }
Kada se u main() funkciji pojavi izraz: rezultat = zbir(3,5); realizuju se sledee akcije: - Rezervie se memorijski prostor za promenljive deklarisane u funkciji zbir(); - Formalnim parametrima se dodeljuju vrednosti stvarnih parametara: a=3; b=5; - Izvrava se telo funkcije; - Rezultat izraunavanja u funkciji se postavlja na mesto obraanja toj funkciji, odnosno dodeljuje se promenljivoj rezultat, i prelazi se na izvrenje sledee naredbe u funkciji main().
Primer 3: (Nalaenje maksimuma dva broja). Program pronalazi maksimum dva broja koji se uitavaju sa ulaza. Maksimum se izraunava u posebnoj funkciji. Program demonstrira upotrabu najjednostavnijih funkcija.
#include <stdio.h>
/* Funkcija max() prihvata dva broja i vraca njihov maksimum */ int max(int a, int b) { return a > b ? a : b; }
/* Funkcija main */ int main() { int a, b, m;
/* Ucitavamo dva cela broja */ printf("Uneti dva broja: "); scanf("%d%d", &a, &b);
/* Pozivamo funkciju max(), predajemo joj vrednosti promenljivih a i b, i njenu povratnu vrednost smestamo u m */ m = max(a,b);
/* Prikazujemo m */ printf("Maksimum je: %d\n", m);
Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
4 return 0; }
Primer 4: (Ojlerova funkcija). Program rauna vrednost Ojlerove funkcije pozitivnog celog broja unetog sa ulaza. Pod Ojlerovom funkcijom (n) podrazumevamo broj brojeva m, takvih da je 1 m < n i da su m i n uzajamno prosti. Ojlerova funkcija, kao i NZD(m, n) se raunaju u posebnim funkcijama.
#include <stdio.h>
/* Deklaracije funkcija (prototipovi) */ int nzd(int n, int m); int euler(int n);
/* main */ int main() { int n;
/* Unosimo ceo broj, a zatim izracunavamo njegovu Ojlerovu funkciju, i ispisujemo vrednost na izlaz */ printf("Uneti pozitivan ceo broj: "); scanf("%d", &n); printf("euler(%d) = %d\n", n, euler(n));
return 0; }
/* Ojlerova funkcija broja n */ int euler(int n) { int br = 0, m;
/* Prolazimo kroz sve prirodne brojeve m koji su manji od n, i za svaki koji je uzajamno prost sa n uvecavamo brojac */ for(m = 1; m < n; m++) if(nzd(n,m) == 1) br++;
/* Vracamo broj uzajamno prostih brojeva sa brojem n */ return br; }
/* Funkcija racuna nzd dva pozitivna broja primenom euklidovog algoritma. */ Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
5 int nzd(int n, int m) { int r;
/* Dokle god je m!=0, svodimo nzd(n,m) na nzd(m,r), gde je r = n % m. */ while(m) { r = n % m; n = m; m = r; } /* Nakon sto je m postalo 0, jasno je da je nzd(n,0) = n */ return n; }
Primer 6: (Stepen broja). Program rauna n-ti stepen unetog realnog broja. Raunanje ovog stepena se realizuje u posebnoj funkciji. Uitavanje i ispis se takoe izdvajaju u posebne funkcije.
#include <stdio.h>
/* Funkcija nema argumente, i ne vraca vrednost. Jedina njena uloga je da ispise pozdravnu poruku */ void ispisi_poruku() { printf("Program racuna n-ti stepen realnog broja!\n"); }
/* Funkcija ucitava sa ulaza realan broj, i vraca ga kao povratnu vrednost. Funkcija nema argumente */ double ucitaj_realan_broj() { double a; printf("Uneti realan broj: "); scanf("%lf", &a); return a; }
/* Funkcija ucitava sa ulaza ceo broj, i vraca ga kao povratnu vrednost. Funkcija nema argumente */ int ucitaj_ceo_broj() { int a; printf("Uneti ceo broj: "); Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
6 scanf("%d", &a); return a; }
/* Funkcija vraca n-ti stepen broja a */ double stepen(double a, int n) { double r; for(r = 1; n ; n--, r *= a);
return r; }
/* Funkcija prihvata realan broj, i prikazuje ga na izlazu, sa tri decimale. Funkcija ne vraca vrednost */ void prikazi_broj(double a) { printf("%5.3f\n", a); }
int main() { int n; double a;
ispisi_poruku(); /* Prikaz pozdravne poruke */ a = ucitaj_realan_broj(); /* Ucitava realan broj */ n = ucitaj_ceo_broj(); /* Ucitava ceo broj */ prikazi_broj(stepen(a, n)); /* Racuna stepen i ispisuje rezultat */
return 0; }
Primer 6: Verzija funkcije stepen() koja radi i za negativne izloioce.
#include <stdio.h>
double stepen(double a, int n) { int negativan = n < 0; double r;
if(negativan) n = -n; for(r = 1; n ; n--, r *= a);
return negativan ? 1.0/r : r; Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
7 }
int main() { double s = stepen(2.0, -1); printf(%f\n, s); return 0; }
Primer 7: Napisati funkciju koja izraunava zbir n-tih stepena brojeva od 1 do zadate granice i program koji ilustruje rad ove funkcije.
#include <stdio.h>
long zbir_stepena(int n, int granica);
int main() { printf("Zbir 2. stepena od 1 do 5 jeste %ld\n", zbir_stepena(2,5)); printf("Zbir 3. stepena od 1 do 5 jeste %ld\n", zbir_stepena(3,5)); printf("Zbir 4. stepena od 1 do 10 jeste %ld\n", zbir_stpena(4,10));
return 0; }
lont zbir_stepena(int n, int granica) { int i, j; /* brojaci u petljama */ long zbir = 0; long stepenovan;
/* Spoljasnji for ciklus obavlja sumiranje */ for(i = 1; i <= granica; zbir += stepenovan, ++i) /* Unutrasnji for ciklus obavlja stepenovanje */ for(stepenovan = 1, j = 1; j <= n; stepenovan *= i, ++j); return zbir; }
Ako izuzmemo korienje operatora zarez, funkcija moe da se napie jednostavnije na sledei nain:
lont zbir_stepena(int n, int granica) { int i, j; /* brojaci u petljama */ long zbir = 0; Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
8 long stepenovan;
/* Spoljasnji for ciklus obavlja sumiranje */ for(i = 1; i <= granica; ++i) { stepenovan = 1; /* Unutrasnji for ciklus obavlja stepenovanje */ for(j = 1; j <= n; ++j) stepenovan *= i; } return zbir; }
Ako iskoristimo funkciju stepen() iz prethodnog primera, ova funkcija moe jo jednostavnije da se napie:
lont zbir_stepena(int n, int granica) { int i, j; /* brojaci u petljama */ long zbir = 0;
/* Spoljasnji for ciklus obavlja sumiranje */ for(i = 1; i <= granica; ++i) zbir += stepen(i, n);
return zbir; }
Primer 8: Napisati program koji prikazuje sve proste brojeve u datom intervalu kojima je zbir cifara sloen broj. Interval se zadaje uitavanjem gornje i donje granice (dva prirodna broja). Brojeve prikazati u opadajuem poretku.
#include <stdio.h> #include <stdlib.h>
int prost(int n); /* Funkcija koja testira da li je broj n prost */
/* Svi prirodni brojevi (sem 1) imaju najmanje dva delioca: jedinicu i samog sebe. Brojevi koji nemaju drugih delilaca, sem ova dva, nazivaju se prostim brojevima. */
int zbir_cifara(int n); /* Funkcija koja racuna i vraca zbir cifara broja n */
int main() { Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
9 int donja, gornja; /* granice intervala */ int i; /* brojac u petlji */ int pom; /* posrednik u eventualnoj zameni */
/* Ucitavanje granica intervala */ scanf("%d%d", &donja, &gornja);
if(donja > gornja) /* Obezbedjivanje relacije donja <= gornja */ { pom = donja; donja = gornja; gornja = pom; }
/* Ispituje se da li je broj n prost tako sto se proverava da li ima delioce medju brojevima od 2 do n/2. Pri implementaciji se koristi tvrdjenje da je broj prost ako je jednak 2, ili ako je neparan i ako nema delitelja medju neparnim brojevima od 3 do n/2. */ int prost(int n) { int prost; /* Indikator slozenosti broja n */ int i; /* Potencijalni delitelj broja n */
/* Najmanji potencijalni kandidat za delitelje medju neparnim brojevima razlicitim od jedan */ i = 3;
while( prost && i <= n/2) { prost = n%i != 0; i = i + 2; /* Proveravamo kandidate za delitelje samo meu neparnim brojevima */ } return prost; }
int zbir_cifara(int n) { Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
10 int suma = 0; while( n > 0 ) { suma += n % 10; n /= 10; } return suma; }
1.1.2 Prenos parametara po vrednosti
Parametri se funkciji prenose po vrednosti, to znai da funkcija zapravo radi sa njihovim lokalnim kopijama. Sve promene koje funkcija naini nad parametrima, zapravo se odnose na lokalne kopije parametara, a ne na same parametre. Zakljuak je da se preneti parametri NE MOGU promeniti u telu funkcije.
Primer 9: Demonstracija prenosa parametara po vrednosti preneti parametri se ne mogu menjati.
#include <stdio.h>
void f(int x) { x *= 2; x++; }
int main() { int x = 3; f(x); printf("%d\n", x);
return 0; }
Promenjena vrednost lokalne kopije promenljive x u okviru funkcije f moe da se vrati kao rezultat rada funkcije. Promenljiva x i dalje zadrava vrednost koju je imala pre poziva funkcije f.
#include <stdio.h>
int f(int x) { return ++x; }
Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
11 int main() { int x = 3; printf("%d %d\n", x, f(x)); return 0; }
Izlaz: 3 4
1.2 Spoljanje (globalne) promenljive
C program sastoji se od skupa spoljanjih objekata, koji mogu biti: promenljive funkcije
Spoljanje promenljive su definisane izvan svake funkcije i zato su potencijalno na raspolaganju raznim funkcijama.
Funkcije su uvek spoljanje, jer C ne dozvoljava definisanje funkcija unutar drugih funkcija.
Po standardu, spoljanje promenljive i funkcije imaju to svojstvo da sve reference na njih po istom imenu, predstavljaju reference na istu stvar.
Prenoenje podataka izmeu funkcija: preko argumenata preko povratnih vrednosti preko spoljanjih promenljivih
NAPOMENA: Ukoliko se veliki broj promenljivih mora deliti izmeu funkcija, bolje je koristiti spoljanje promenljive nego dugake liste argumenata. Meutim, ovo treba paljivo primenjivati, jer u suprotnom se dobija program sa previe veza izmeu funkcija.
Poreenje spoljanjih i automatskih promenljivih:
Automatske promenljive su unutranje za funkciju. One nastaju kada otpoinje izvravanje funkcije i nestaju kada se ono zavrava. Spoljanje promenljive su permanentne i stoga zadravaju svoje vrednosti izmeu poziva funkcija. Dakle, ivotni ciklus ovih promenljivih je dui.
Primer 10 (Broja karaktera): Program ita tekst sa ulaza i broji velika i mala slova, cifre, beline i redove. Program demonstrira korienje globalnih podataka.
#include <stdio.h> Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
12
/* Deklaracija funkcije prebroj */ void prebroj();
/* Globalni brojaci. Podrazumevano su inicijalizovani nulom. */ int br_malih, br_velikih, br_cifara, br_belina, br_redova;
int main() { /* Pozivamo funkciju za prebrojavanje */ prebroj();
/* Prikazujemo vrednosti globalnih brojaca, koje je funkcija prebroj() izmenila */ printf("Broj malih slova: %d\n", br_malih); printf("Broj velikih slova: %d\n", br_velikih); printf("Broj cifara: %d\n", br_cifara); printf("Broj belina: %d\n", br_belina); printf("Broj redova: %d\n", br_redova);
/* Povratna vrednost funkcije main() */ return 0; }
/* Definicija funkcije prebroj() */ void prebroj() { int c;
Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
13 1.3 Statike promenljive
Deklaracija static, koja se primenjuje na spoljanju promenljivu ili funkciju, ograniava domet tog objekta na preostali deo izvorne datoteke.
Deklaracija static se moe primeniti i na unutranje promenljive. Unutranje statike promenljive su lokalne za odreenu funkciju ba kao i automatske promenljive, ali za razliku od njih, one nastavljaju da postoje i nakon zavretka funkcije. Dakle, one ne nastaju i ne nestaju sa svakim pozivom funkcije. Na taj nain obezbeuju privatnu, stalnu memoriju unutar funkcije.
1.4 Inicijalizacija
Spoljanje i statike promenljive se uvek inicijalizuju na nulu ukoliko nisu eksplicitno inicijalizovane. Inicijalizator mora biti konstantan izraz. Inicijalizacija se obavlja samo jednom, obino pre poetka izvravanja programa.
Automatske promenljive imaju nedefinisane (nepredvidive) poetne vrednosti ukoliko nisu eksplicitno inicijalizovane. Inicijalizator moe biti svaki izraz u kojem uestvuju prethodno definisane vrednosti, pa ak i pozivi funkcija. Inicijalizacija se obavlja svaki put prilikom izvravanja funkcije ili bloka.
Primer 11: Demonstracija ivotnog veka i oblasti vaenja promenljivih.
/* Umanjuje se lokalna promenljiva a. Globalna promenljiva sa istim imenom zadrzava svoju vrednost. */ void umanji() { /* Ova promenljiva a je nezavisna u odnosu na globalnu promenljivu a */ int a = 0; a--; Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet
14 printf("umanji::a = %d\n", a); }
void nestaticka_prom() { /* Nestaticke promenljive ne cuvaju vrednosti kroz pozive funkcije */ int s = 0; s++; printf("nestaticka promenljiva::s = %d\n", s); }
void staticka_prom() { /* Staticke promenljive cuvaju vrednosti kroz pozive funkcije. Inicijalizacija se odvija samo u okviru prvog poziva. */ static int s = 0; s++; printf("staticka promenljiva::s = %d\n", s); }
int main() { /* Promenljive lokalne za funkciju main */ int i; int x = 3;
printf("main::x = %d\n", x);
for(i=0; i<3; i++) { /* Promenljiva u okviru bloka je nezavisna od spoljne promenljive. Ovde se koristi promenljiva x lokalna za blok for petlje koja ima vrednost 5, dok originalno x i dalje ima vrednost 3 */ int x = 5; printf("for::x = %d\n", x); }
/* U ovom bloku x ima vrednost 3 */ printf("main::x = %d\n", x);
uvecaj(); umanji();
/* Globalna promenljiva a */ printf("main::a = %d\n", a);
/* Demonstracija statickih promenljivih */ Biljana Stojanovi | Univerzitet u Beogradu, Matematiki fakultet