Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

4 Petlje

Download as pdf or txt
Download as pdf or txt
You are on page 1of 40

Poglavlje 4

Ponavljanje dijelova koda -


Petlje

Petlja (eng. loop) je dio programa čije se izvršavanje ponavlja određeni broj
puta. Najjednostavniji primjer petlje smo već vidjeli: recimo da želimo na ekran
ispisati svoje ime 100 puta, možemo napisati sljedeći kod:
1 #include <stdio.h>
2 int main() {
3 int i;
4 for (i=0; i<100; i++) {
5 printf("Svoje ime\n");
6 }
7 return 0;
8 }

Da bismo znali da li je u našem programu potrebna petlja, trebamo sebi postaviti


pitanje: postoji li neki zadatak koji se ponavlja? U tom smislu prevod “petlja” možda
i nije baš najsretnije odabran. Na našem jeziku kažemo da je nešto zapetljano ako
se ne može razaznati šta je šta, ako je komplikovano. Primjer zapetljanog koda smo
vidjeli kada smo govorili o goto naredbi i špageti-kodu.
Upravo suprotno, petlje u programiranju služe baš tome da razjasne stvari i
olakšaju prepoznavanje logike koja stoji stoji iza nekog ponavljajućeg koda. Po
tome petlje su sličnije čvorovima. Mornari, izviđači i drugi poznaju nekoliko tipova
čvorova, pri čemu dobro znaju kada se koji koristi, koje su mu prednosti i mane itd.
Slično, iskusan programer prepoznaje petlju, ona mu olakšava da shvati šta je drugi
programer želio postići.
U programskom jeziku C postoje tri tipa petlji: for petlja, while i do-while
petlja. Svakoj od njih ćemo posvetiti po jedno poglavlje. Neki početnici u pro-
gramiranju su skloni da sve što se nalazi u vitičastim zagradama nazovu petljom
(a to je ustvari blok koda), pa tako često čujete frazu “if-petlja”. if nije petlja. if
je kontrolna struktura pomoću koje se postiže uslovno izvršenje i grananje koda.
Osnove računarstva (skripta) 2

Slika 4.1: Petlja vs. špageti kod

switch-case također nije petlja, to je kontrolna struktura koja služi za višestruki


izbor. Riječ “petlja” može se koristiti isključivo za dio koda koji se ponavlja.

4.1 for petlja


Prvi tip petlje koji smo upoznali je for petlja. for petlja se koristi kada želimo
ponoviti neki kod određeni unaprijed poznati broj puta.
Pogledajmo jedan jednostavan primjer for petlje:
1 #include <stdio.h>
2 int main() {
3 int i;
4 for (i=0; i<5; i++) {
5 printf("if nije petlja\n");
6 }
7 return 0;
8 }

Kada pokrenemo ovaj program, na ekranu dobijamo:


if nije petlja
if nije petlja
if nije petlja
if nije petlja
if nije petlja

Petlja se izvršila tačno 5 puta. Kontrolna promjenljiva i petlje dobila je vrijed-


nosti od 0 do 5 ne uključujući peticu i svaki put se povećala za 1, dakle [0,5) što je
ukupno pet vrijednosti. Kako bismo postigli da se petlja izvrši 8 puta umjesto 5?
Obratite pažnju na liniju sa pozivom printf funkcije:
5 printf("if nije petlja\n");

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 3

Čemu služi oznaka \n ? Šta bi se desilo smo je izostavili?


Možemo uočiti da je tijelo petlje zatvoreno u vitičaste zagrade:
4 for (i=0; i<5; i++) {
5 printf("if nije petlja\n");
6 }

Ove vitičaste zagrade označavaju blok koda o čemu smo pričali ranije. Rekli smo i
to da ako se samo jedna naredba nalazi u bloku, zagrade se mogu izostaviti. Koliko
ovdje imamo naredbi u bloku? Možemo li izostaviti zagrade?

4.1.1 Struktura for petlje


Na prethodnom primjeru možemo uočiti da se for petlja sastoji od tri dijela:
deklaracija kontrolne promjenljive, zaglavlje petlje i tijelo petlje.
1 #include <stdio.h>
2 int main() {
3 int i;
4 for (i=0; i<5; i++) {
5 printf("if nije petlja\n");
6 }
7 return 0;
8 }

Deklaracija kontrolne promjenljive je označena žutom bojom, zaglavlje plavom, a


tijelo crvenom.
for petlja mora imati kontrolnu promjenljivu ili brojač. Kontrolna promjenljiva,
kao i svaka druga, može imati bilo kakvo ime koje želite, ali iz istorijskih razloga
uobičajeno je da se kontrolne promjenljive za for petlje imenuju i , j , k ... Kontrolna
promjenljiva prebrojava koliko puta se petlja izvršila. Ona će najprije poprimiti
vrijednost 0, zatim 1, 2 itd. a kada dostigne vrijednost 5 petlja će se prekinuti, pa
se na taj način osigurava da se for petlja izvrši tačno 5 puta.
Inače u originalnom C89 standardu predviđeno je da se sve promjenljive moraju
deklarisati na početku funkcije, dok je u C++u čak preporučeno da se deklarišu tamo
gdje se koriste. C++ omogućuje da se promjenljiva deklariše u samom zaglavlju for
petlje, ovako:
for (int i=0; i<5; i++) {

U C-u ovo nije dozvoljeno, dakle primjer iznad ne predstavlja validan C kod i neće
se kompajlirati (osim ako niste greškom kreirali C++ projekat umjesto C projekta).
Zaglavlje petlje definiše koliko puta će se petlja izvršiti, na način da se određuje
početna i krajnja vrijednost kontrolne promjenljive i , kao i korak za koji će se i
povećati u svakom prolazu kroz petlju. U ranijem primjeru vidimo da je početna
vrijednost i nula, da se petlja prekida kada i postane 5 (što znači da 5 nije
uključeno u petlju), te da se u svakom prolazu i poveća za jedan ( i++ ). Kako
bismo izmijenili ovu for petlju tako da je i broj 5 uključen?

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 4

Tijelo petlje je blok naredbi koje se ponavljaju. Sve ono što je u tijelu petlje
će se ponavljati, ono što nije – neće. Prije i poslije tijela petlje ne navodi se znak
tačka-zarez. Ako napišemo:
for (int i=0; i<5; i++) ; {
printf ("i je %d\n", i);
}

ispisaće se samo jednom “i je 5” iz sljedećeg razloga: tijelo petlje je prvi blok


naredbi koji se nalazi nakon zaglavlja petlje, a znak tačka-zarez je validan blok koji
je prazan (naredba koja ne radi ništa). Dio u vitičastim zagradama ustvari se nalazi
izvan (poslije) for petlje.
Posvetimo se malo detaljnije zaglavlju petlje.
for (i=0; i<5; i++) {

Zaglavlje for petlje nalazi se unutar običnih (malih) zagrada, a sastoji se opet od tri
dijela razdvojena znakom tačka-zarez (eng. semicolon). Generalno, da bi for petlja
bila validna moraju se u zaglavlju javiti tačno dva tačka-zareza. Ta tri dijela su:
inicijalizacija (označena žutom bojom), uslov (označen plavom bojom) i ažuriranje
(označeno crvenom bojom).
Inicijalizacija je neka naredba koja će se izvršiti samo jednom prije ulaska u
petlju. Ona služi za postavljanje početne vrijednosti kontrolne promjenljive. Npr. u
primjeru iznad i=0 je postavilo kontrolnu promjenljivu na njenu početnu vrijednost
nula.
Sve dok je uslov ispunjen petlja će se ponavljati. Kada uslov prestane biti
ispunjen, petlja se prekida a izvršavanje se nastavlja poslije petlje (tj. iza tijela
petlje). U pravilu uslovom se definiše krajnja vrijednost kontrolne promjenljive npr.
iznad je stavljeno i<5 što znači da će i rasti dok ne dostigne vrijednost pet.
Konačno, ažuriranje je neka naredba koja će se izvršiti prilikom svakog pro-
laza kroz petlju, poslije tijela petlje, a služi kako bi se definisao korak sa kojim će
kontrolna promjenljiva rasti. U našem primjeru i++ označava da se i pri svakom
prolazu kroz petlju povećava za jedan.
Pogledajmo neke primjere.
Koliko puta će se izvršiti sljedeće petlje:
for (i=1; i<8; i++) { ...
for (i=100; i<200; i++) { ...
for (i=1; i<=8; i++) { ...
for (i=8; i<1; i++) { ...

U posljednjem slučaju petlja se neće izvršiti nijednom, jer uslov petlje nije is-
punjen odmah na početku. Postavili smo i na 8 što nije manje od 1 tako da uslov
već u startu nije ispunjen. To znači da se tijelo petlje neće izvršiti niti jednom, neće
se uopšte na ekranu ispisati da "if nije petlja".
Za kraj ove diskusije o strukturi for petlje pogledaćemo ovaj programčić:
1 #include <stdio.h>
2 int main() {

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 5

3 int i,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=0; i<n; i++) {
7 printf("if nije petlja!\n");
8 }
9 return 0;
10 }

Ovdje korisnik najprije sa tastature unosi cijeli broj n a zatim se na ekranu ispiše
tekst “if nije petlja” n puta. Tu možemo vidjeti primjer for petlje koja se izvršava
neki proizvoljan broj puta koji korisnik definiše svojim unosom, a ako korisnik unese
negativan broj petlja se naravno neće izvršiti niti jednom.

4.1.2 Kako funkcioniše for petlja?


Sada pogledajmo ovaj jednostavni program koji je vrlo sličan prethodnom.
1 #include <stdio.h>
2 int main() {
3 int i,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=0; i<n; i++) {
7 printf("i je %d\n", i);
8 }
9 printf("poslije petlje %d\n", i);
10 return 0;
11 }

Probajte odgonetnuti šta će ovaj program ispisati na ekran ako korisnik unese n=5 .
Možete li?

Sada pogledajte rješenje:


i je 0
i je 1
i je 2
i je 3
i je 4
poslije petlje 5

Da bismo shvatili zašto je program ispisao baš ovo, trebamo razumjeti precizno kako
funkcioniše for petlja odnosno kojim redom se izvršavaju naredbe. Proći ćemo kroz
program korak po korak.
U liniji 3 programa deklarisane su promjenljive i i n . U ovom trenutku one su
neinicijalizovane. U liniji 5 korisnik unosi n sa tastature, a rekli smo da će unijeti
broj 5. Da vidimo šta se dešava dalje.

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 6

6 for (i=0; i<n; i++) {

U liniji 6 programa nalazi se zaglavlje for petlje koje se izvršava ovako: najprije se
dešava inicijalizacija, tj. postavlja se i na 0. Zatim se provjerava uslov tj. da li je
i<n odnosno da li je 0<5. Pošto je uslov ispunjen prelazi se na tijelo petlje. Tijelo
petlje u liniji 7 ispisuje poruku i je ... , u ovom trenutku promjenljiva i još uvijek
ima vrijednost 0 pa će se ispisati i je 0 .
Ovim je tijelo petlje završeno. Tek kada se izvrše sve naredbe u tijelu petlje
izvršava se ažuriranje kontrolne promjenljive koje se nalazi u zaglavlju for petlje.
Naredba za ažuriranje glasi i++ pa će se i povećati za 1 i postaće 1.
Sada se ponovo provjerava uslov, da li je i<n tj. 1<5, što je ispunjeno. Ponovo
se izvršava tijelo petlje, tj. ispisuje se i je 1 . Ponovo se izvršava ažuriranje, pa i
postaje 2 itd.
Preskočimo sada na posljednji prolaz for petlje u kojem se ispisala poruka i je 4 .
Nakon izvršenja tijela petlje (ispisa poruke i je 4 ) izvršava se ažuriranje for petlje
i++ , i se povećava za 1 i postaje 5. Zatim se provjerava uslov i<n , pošto je i=5 i
n=5 uslov NIJE ispunjen (5 nije manje od 5) i petlja se prekida. U ovom trenutku
izvršavanje for petlje skače na kraj petlje tj. na prvu naredbu koja se nalazi poslije
tijela petlje. To je naredba:
9 printf("poslije petlje %d\n", i);

U ovom trenutku i je 5 pa se ispisuje poruka poslije petlje 5 .


Šta iz ovog primjera možemo zaključiti?
1. Kontrolna promjenljiva će u prvom prolazu kroz petlju imati vrijednost koja
je dodijeljena u polju inicijalizacije.
2. U posljednjem prolazu kroz petlju uslov je još uvijek ispunjen, pa ako smo
stavili uslov i<n , i će imati vrijednost n-1 .
3. Po izlasku iz petlje, kontrolna promjenljiva će imati vrijednost n odnosno
imaće prvu vrijednost za koju uslov više nije ispunjen.
Ovo nam ostavlja razne zgodne mogućnosti pri radu sa petljama odnosno da možemo
znati kako i kada se petlja završila, kao što ćemo vidjeti kasnije.

4.1.3 Zloupotrebe zaglavlja for petlje


Ako znamo tačno kada se izvršava koje polje zaglavlja for petlje, to nam ostavlja
mogućnost da ih koristimo na razne druge načine osim onog koji smo vidjeli naprijed.
Ovo smo nazvali “zloupotrebom” iz razloga što se na taj način umanjuje čitljivost
čitavog programa. Naravno, takav program će i dalje raditi ispravno.
Prije svega, svako od polja zaglavlja for petlje može biti prazno. For petlja je
validna dok god u zaglavlju imamo dva tačka-zareza, a između njih se ne mora
nalaziti ništa. Recimo pogledajte ovaj program:
1 #include <stdio.h>
2 int main() {

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 7

3 int i=0,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (; i<n; i++) {
7 printf("i je %d\n", i);
8 }
9 printf("poslije petlje %d\n", i);
10 return 0;
11 }

U odnosu na prethodni primjer, samo smo inicijalizaciju promjenljive i pomakli na


mjesto gdje je ona i deklarisana, čime je polje za inicijalizaciju u zaglavlju for petlje
ostalo prazno. Često ćete vidjeti i ovakav kod:
for (i; i<n; i++) {

Ovo nije greška striktno govoreći, ali ćete u odgovarajućoj liniji dobiti upozorenje
kompajlera
Statement with no effect

koje označava da imate kod koji ne radi ništa. Naime, ako navedete samo naziv
promjenljive, sam taj naziv je ujedno i izraz koji ima određenu vrijednost, ali mi sa
tom vrijednošću nismo uradili ništa, baš kao da ste u zasebnoj liniji napisali naredbu:
i;

Zapamtite da je prvo polje zaglavlja for petlje naredba koja se izvršava prije ulaska
u petlju. Kompajler vas tu upozorava da niste nešto slučajno zaboravili pošto je un-
esena naredba beskorisna. Da bismo izbjegli poplavu upozorenja (koja je generalno
loša jer se mogu slučajno preskočiti ozbiljne greške), bolje je da pobrišemo ovo i
koje je ujedno i nepotrebno jer je dozvoljeno imati prazno polje zaglavlja.
Također i ažuriranje može biti prazno. Gdje bismo trebali u prethodnom primjeru
prebaciti i++ kako bi polje zaglavlja za ažuriranje postalo prazno, a da program
zadrži istu funkciju?
Na kraju, i sam uslov može biti prazan. Petlja koja glasi ovako:
for (;;) {

naziva se beskonačna petlja. Beskonačna petlja će se izvršavati sve dok korisnik ne


prekine program. No moguće je i da programer prekine izvršavanje na neki način
kao što ćemo vidjeti kasnije. Dakle beskonačne petlje nisu potpuno beskorisne.
Nakon što smo potpuno ispraznili zaglavlje for petlje, u njega možemo dodati
neke druge stvari. Npr. pogledajte ovaj program:
1 #include <stdio.h>
2 int main() {
3 int i=0,n;
4 printf("Unesite broj n: ");
5 for (scanf("%d", &n); i<n; printf("i je %d\n", i++));

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 8

6 printf("poslije petlje %d\n", i);


7 return 0;
8 }

Ovaj program i dalje radi potpuno isto što i prethodnih nekoliko primjera. Da biste
prepoznali inicijalizaciju, uslov i ažuriranje for petlje, samo tražite znak tačka-zarez.
Radi lakšeg razumijevanja obojićemo ih bojama kao i ranije:
1 #include <stdio.h>
2 int main() {
3 int i=0,n;
4 printf("Unesite broj n: ");
5 for (scanf("%d", &n); i<n; printf("i je %d\n", i++));
6 printf("poslije petlje %d\n", i);
7 return 0;
8 }

Dakle, pošto je inicijalizacija naredba koja se izvršava samo jednom prije početka
petlje, tu smo ubacili poziv funkcije scanf jer nam je to potrebno samo jednom.
Uslov je ostao isti kao i ranije. Operacija ažuriranja nam je sada kompletno tijelo
petlje sa svojom printf funkcijom. Možete uočiti da se unutar poziva printf
funkcije i uvećava za jedan, pri čemu smo koristili postfiksni operator ++ kako
bismo osigurali da se na ekranu ispiše neuvećana vrijednost.
Konačno, zelenom bojom je označeno tijelo petlje koje se sastoji samo od znaka
tačka-zarez! Naime, pošto smo kompletno tijelo uspjeli uvući u zaglavlje petlje, tijelo
nam je sada prazno. Ponekad u kodu se dešava da želimo označiti blok koda koji je
prazan tj. koji ne sadrži niti jednu naredbu. To možemo uraditi na dva ravnopravna
načina. Jedan je naravno da stavimo samo otvorenu i zatvorenu vitičastu zagradu
for (scanf("%d", &n); i<n; printf("i je %d\n", i++)) { }

a drugi je da navedemo znak tačka-zarez. Znak tačka-zarez bez drugih oznaka


predstavlja naredbu koja ne radi ništa (u programiranju su takve naredbe poznate
kao null-naredbe ili NOP).

4.1.4 Petlje sa različitim koracima


U svim dosadašnjim primjerima korak u petlji je bio jedan, što naravno ne mora
biti. Probajmo recimo u našem ranijem primjeru staviti korak 3:
1 #include <stdio.h>
2 int main() {
3 int i,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=0; i<n; i+=3) {
7 printf("i je %d\n", i);
8 }
9 printf("poslije petlje %d\n", i);

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 9

10 return 0;
11 }

Obratite pažnju na polje ažuriranja u zaglavlju petlje. i+=3 je naredba koja koristeći
kombinovani operator += uvećava i za 3. Isto bi značenje imala i naredba i=i+3 .
Obratite pažnju da je ažuriranje naredba. Da ste napisali samo i+3 ovako:
for (i=0; i<n; i+3) {

u pitanju bi bila beskonačna petlja jer se i nikada ne bi mijenjalo, program bi do u


beskonačnost ispisivao i je 0 .

3 Naredba i+3 ne mijenja vrijednost i, dok i+=3 mijenja.

Neka je korisnik unio broj 10. Program će sada ispisati:


Unesite broj n: 10
i je 0
i je 3
i je 6
i je 9
poslije petlje 12

Vidimo da se u svakom koraku i povećalo za tri. Odakle sada 12 na kraju? U


posljednjem prolasku kroz petlju i je bilo 9. Zatim se izvršilo ažuriranje, i se
povećalo za 3 i postalo 12. Sada će program evaluirati uslov i<n , pa pošto 12
nije manje od 9 petlja će se prekinuti i izvršavanje nastaviti u naredbi koja ispisuje
“poslije petlje”.
Drugim riječima, 12 je prva vrijednost promjenljive i za koju uslov više nije bio
ispunjen. Još o ovome kroz neke primjere.
Zadatak 1. Napisati program koji omogućuje korisniku da unese cijeli
broj n, a zatim na ekranu ispisuje sve parne brojeve na intervalu rn, n2 s.
Naravno mogli bismo ovo riješiti tako što napišemo petlju koja prolazi kroz sve
brojeve na intervalu rn, n2 s a zatim provjeravamo da li je svaki od tih brojeva paran.
1 #include <stdio.h>
2 int main() {
3 int i,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=n; i<=n*n; i++) {
7 if (i%2 == 0)
8 printf("%d\n", i);
9 }
10 return 0;
11 }

Ali možemo li iskoristiti korak kako bismo uzimali svaki drugi broj?

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 10

1 #include <stdio.h>
2 int main() {
3 int i,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=n; i<=n*n; i+=2) {
7 printf("%d\n", i);
8 }
9 return 0;
10 }

Problem je što ne znamo da li je broj n paran ili nije. Ako broj n nije paran,
program iznad će ispisati sve neparne brojeve na intervalu, a ne sve parne. Rješenje
je da prije for petlje uvećamo n ako je neparno.
1 #include <stdio.h>
2 int main() {
3 int i,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 i=n;
7 if (n%2 == 1) i++;
8 for (; i<=n*n; i+=2) {
9 printf("%d\n", i);
10 }
11 return 0;
12 }

Zadatak 2. Napisati program koji omogućuje korisniku da unese cijeli broj


x, a zatim provjerava da li je x stepen dvojke (takav broj da je x “ 2n gdje
je n neki prirodan broj) te ispisuje poruku JESTE ako je uslov ispunjen.
Ovaj zadatak bi se mogao riješiti tako što koristimo funkciju logaritma ili da se
pokuša tražiti ostatak pri dijeljenju x sa dva. A možemo probati i ovako: proći ćemo
kroz sve stepene dvojke koji su manji ili jednaki x i ako je na kraju rezultat jednak
x-u ispisati traženu poruku.
1 #include <stdio.h>
2 int main() {
3 int i,x;
4 printf("Unesite broj n: ");
5 scanf("%d", &x);
6 for (i=1; i<=x; i*=2) {
7 if (i==x) printf("JESTE");
8 }
9 return 0;
10 }

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 11

4.1.5 Opadajuća for petlja


Prirodno je postaviti pitanje: može li korak biti negativan? Odgovor je naravno
da može, a takvu petlju nazivamo opadajuća. Primjer korištenja opadajuće petlje
se može vidjeti ovdje:
1 #include <stdio.h>
2 int main() {
3 int i,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=n; i>0; i--) {
7 printf("i je %d\n", i);
8 }
9 printf("poslije petlje %d\n", i);
10 return 0;
11 }

Ako korisnik unese broj 5 izlaz programa će izgledati ovako:


i je 5
i je 4
i je 3
i je 2
i je 1
poslije petlje 0

Šta možemo zaključiti o opadajućim petljama iz ovog primjera?


• početna vrijednost za i je 5;
• u svakom prolazu i se smanjuje za 1;

• petlja se izvršava sve dok je i veće od 0; pošto je početna vrijednost 5, uslov


mora biti ispunjen za tu početnu vrijednost, u suprotnom se petlja neće niti
jednom izvršiti;
• pošto smo stavili uslov i>0 u posljednjem prolazu i će biti 1;

• po izlasku iz petlje kontrolna promjenljiva dobija prvu vrijednost za koju uslov


više nije ispunjen, a to je 0.

4.1.6 Naredbe break i continue


Došlo je vrijeme da predstavimo dvije nove naredbe C programskog jezika koje
služe specifično za kontrolu rada petlji, a to su naredbe break i continue.
Naredba break (eng. prekini) služi za prekid petlje. Ustvari ovu naredbu smo
već spominjali kada smo govorili o switch-case strukturi. Zaista, break naredba ima
dva značenja u programskom jeziku C. Unutar switch-case strukture, naredba break

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 12

označava završetak slučaja, a izvršavanje programa se nastavlja iza završetka switch-


case bloka. Unutar petlje, break označava prekid petlje, a izvršavanje programa će
“skočiti” iza završetka petlje.
Ako se naredba break ne nalazi niti u switch-case bloku niti u petlji, dobićete
kompajlersku grešku! Često se kod početnika može vidjeti da pokušavaju
prekinuti čitav program naredbom break.

Prekid programa se može postići naredbom return unutar main


funkcije, a u drugim funkcijama može se koristiti funkcija exit iz biblioteke
stdlib.h .
Primjer upotrebe naredbe break možete vidjeti u ovom programu:
1 #include <stdio.h>
2 int main() {
3 int i=0,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (;;) {
7 printf("i je %d\n", i);
8 i++;
9 if (i>=n) break;
10 }
11 printf("poslije petlje %d\n", i);
12 return 0;
13 }

Ovaj program je ekvivalentan primjeru koji smo proučavali ranije. Sada se ko-
risti beskonačna for petlja iz koje se izlazi naredbom break . Uslov je invertovan
(sjetimo se da je >= negacija od <) jer se u ovom programu petlja prekida kada je
uslov ispunjen, a ranije smo u zaglavlju for petlje navodili uslov za koji će se petlja
ponavljati sve dok je taj uslov ispunjen.
Naredba continue (eng. nastavi) preskače sve preostale naredbe do kraja petlje
i prelazi na sljedeću iteraciju petlje. Zbog toga je sam naziv ove naredbe često zbun-
jujući jer nije najjasnije šta se to nastavlja. Pogledajmo sljedeći primjer upotrebe
naredbe continue:
1 #include <stdio.h>
2 #include <math.h>
3 int main() {
4 int i;
5 double x,y;
6 for (i=0; i<100; i++) {
7 x = sin(i/3.);
8 if (x<=0) continue;
9 y = i + sqrt(x);
10 printf("i=%d x=%f y=%f\n’’, i, x, y);
11 }

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 13

12 return 0;
13 }

U ovom programu, za svaku vrijednost i na intervalu [0,99] računa se određeni


matematički izraz, pod uslovom da takav izraz ima smisla, odnosno ako je potko-
rijena veličina x pozitivan broj. Za vrijednosti i “ 0, 1, 2...9, x će biti pozitivno.
Već za i “ 10 važi i{3 “ 3.3333..., pa je x “ sin 3.333 “ ´0.187 ď 0. U ovom
slučaju će se izvršiti naredba continue , što znači da se neće računati y=i+sqrt(x)
(odnosno neće se računati korijen negativnog broja), niti će se ispisati poruka na
ekran. Izvršavanje programa preskače na naredbu i++ čime i postaje 11 i zatim se
izvršava sljedeća iteracija petlje, tj. računa se sin(11/3.) što će opet biti negativan
broj itd. Za i=30 će x ponovo biti pozitivno itd.
Da bismo dodatno utvrdili razumijevanje naredbi break i continue, pogledajmo
još jedan primjer:
1 #include <stdio.h>
2 int main() {
3 int k,h=0;
4 for (k=0; k<70; k++) {
5 if(k%6==0) continue;
6 h++;
7 if (k==6 || k==12)
8 break;
9 }
10 printf("h=%d",h);
11 return 0;
12 }

Ovaj program će na ekranu ispisati broj 58. Da bismo razumjeli zašto, proći ćemo
kroz njega liniju po liniju.
Kao što znamo, for petlja se izvršava 70 puta. Brojač h će se uvećati za jedan u
svakom prolazu kroz petlju (linija 6), osim kada se izvrši naredba continue u liniji 5
budući da ona preskače naredbu h++ . Ova naredba continue će se izvršiti za svako
k čiji je modulo 6 jednak nuli, odnosno za sve brojeve djeljive sa 6 kao i za nulu
(pošto je 0%6=0 ). Na intervalu [0,70) takvih brojeva ima ukupno 12:
0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66.

Dakle, naredba h++ će se izvršiti 70-12=58 puta. Što se tiče naredbe break ona
se neće nikada izvršiti jer su brojevi 6 i 12 djeljivi sa 6 (vidimo ih na prethodnom
spisku) pa će u oba slučaja naredba continue preskočiti naredbu break.
Ako program modifikujemo tako da piše:
7 if (k==6 || k==12 || k==17)

vidimo da 17 nije djeljivo sa 6 pa će se program prekinuti kada k bude 17. Petlja


će se izvršiti 18 puta (za vrijednosti k=0,1,2,...,17 – vrijednost 17 moramo uključiti
jer će break nastupiti tek nakon naredbe h++ ). Na ovom intervalu će se tri puta
desiti continue tako da će program ispisati broj 15.

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 14

4.1.7 Višestruke (ugniježdene) petlje


U bloku naredbi koje čine tijelo petlje mogu se nalaziti bilo kakve validne naredbe
programskog jezika C. Dakle, u bloku se može nalaziti i druga petlja. Šta bi takav
program mogao značiti? Pa ako znamo da pomoću for petlje ponavljamo neki blok
n puta, ako se u tom bloku nalazi druga for petlja koja ponavlja neki drugi blok k
puta, onda će taj unutrašnji blok biti ponovljen ukupno n*k puta.
Napravićemo jedan školski primjer programa koji radi sa dvostrukom petljom
kako bismo bolje razumjeli šta se u takvoj petlji dešava sa kontrolnim promjenljivim.
1 #include <stdio.h>
2 int main() {
3 int i,j;
4 for (i=0; i<5; i++) {
5 for (j=0; j<5; j++) {
6 printf("i=%d,j=%d\n", i, j);
7 }
8 }
9 return 0;
10 }

Prvo što moramo uočiti je da se u ugniježdenoj petlji ne može koristiti ista


kontrolna promjenljiva. Da smo u unutrašnjoj petlji koristili promjenljivu i ovako:
for (i=0; i<5; i++) {
for (i=0; i<5; i++) {
...

ona bi u samoj toj unutrašnjoj petlji dostigla vrijednost 5. Dakle po završetku


unutrašnje petlje i bi bilo 5, pa bi se odmah zatim i vanjska petlja završila jer uslov
i<5 više nije ispunjen, dakle dio označen sa tri tačke bi se izvršio sve skupa 5 puta.
Inače, možete slobodno koristiti istu kontrolnu promjenljivu za veći broj petlji dok
god te petlje nisu ugniježdene.
Dakle da vidimo šta će ispisati prethodni program u kojem smo korektno koristili
promjenljive i i j :
i=0,j=0
i=0,j=1
i=0,j=2
i=0,j=3
i=0,j=4
i=1,j=0
i=1,j=1
...
i=4,j=3
i=4,j=4

Ukupno biće ispisano 25 poruka. Najprije će i postati 0. Zatim se izvršava kom-


pletna j -petlja (obično se tako skraćeno naziva petlja čija je kontrolna promjenljiva

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 15

j ). Ta petlja će proći kroz sve vrijednosti j od 0 do 4 i završiti se, ispisujući prvih


pet poruka. Po završetku ove petlje j ima vrijednost 5 (što nam nije bitno), a i i
dalje ima vrijednost 0. Zatim se izvršava naredba i++ kojom i postaje 1. Pošto je
i i dalje manje od 5, ponovo se izvršava kompletna j -petlja. U zaglavlju j -petlje
j se vraća na polaznu vrijednost 0 i zatim prolazi kroz vrijednosti [0,5) itd. Ovaj
ciklus se završava kada i postane 5, a j je također 5.
Dvostruku petlju je najlakše shvatiti kroz neke primjere kao što je sljedeći.
Zadatak 3. Napraviti program koji omogućuje korisniku da unese prirodan
broj n, a zatim na ekranu iscrtava kvadrat ispunjen znakovima zvijezda (*)
čije su sve stranice dugačke po n zvijezda.

Unesite broj n: 5
*****
*****
*****
*****
*****

Kod rješavanja ovog zadatka možemo primijeniti opštu strategiju rješavanja pro-
gramskih zadataka, koja je inspirisana latinskom uzrečicom “zavadi pa vladaj”:

3 Ako imate složen problem koji ne znate (isprve) riješiti, podijelite ga na jed-
nostavnije probleme koje znate riješiti.

Čak i ako ne uspijete riješiti sve dijelove zadatka, barem imate nešto što je u
praksi često bolje nego ništa. Ostatak problema možete podijeliti sa vašim kolegama,
ili naći neko polu-gotovo rješenje pretraživanjem interneta.
U ovom slučaju možemo zadatak podijeliti ovako:

• Podzadatak 1: Nacrtati jedan red od n zvjezdica.

– Da bismo ovo uradili trebamo ponoviti zadatak ispisa jedne zvjezdice n


puta.

• Podzadatak 2: Ponoviti podzadatak 1 (ispis jednog reda) n puta.

Važno je zapamtiti da računar obavlja zadatke koje smo mu dali jedan po jedan,
pa je dovoljno strpljivo navoditi rješenja i ponavljati ih potreban broj puta dok ne
dođemo do konačnog rezultata.
Dakle, kako ćemo ispisati jednu zvjezdicu na ekranu? Kôd koji obavlja taj
zadatak glasi printf("*"); – ovom naredbom smo ispisali jednu zvjezdicu. Sada
nju trebamo ponoviti n puta. Za ponavljanje nekog zadatka n puta koristimo for
petlju:
for (i=0; i<n; i++)
printf("*");

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 16

Time smo dobili n zvjezdica što čini jedan red. Ostaje da ovaj zadatak ispisa jednog
reda ponovimo n puta. Možemo li kompletan kod naveden iznad staviti u još jednu
petlju, ovako?
for (i=0; i<n; i++)
for (i=0; i<n; i++)
printf("*");

Ovo nije ispravno jer u ugniježdenoj petlji koristimo istu kontrolnu promjenljivu.
Moramo zamijeniti i sa j
for (i=0; i<n; i++)
for (j=0; j<n; j++)
printf("*");

Podsjetimo se da blok koda ne moramo ograditi vitičastim zagradama ako se


u njemu nalazi samo jedna naredba. printf("*"); je samo jedna naredba tako da
u j -petlji nemamo vitičastih zagrada. No sama j -petlja u svojoj cjelosti također
predstavlja samo jednu naredbu! Iz perspektive C-a naredbe koje podrazumijevaju
blok kao što su for i if i same čine jednu jedinstvenu naredbu, pa ako je to jedina
naredba u bloku taj blok ne moramo ograditi vitičastim zagradama. Generalno, veća
je vjerovatnoća da ćete isključiti neku naredbu iz bloka jer niste stavili zagrade nego
da ćete izazvati sintaksnu grešku.
Ovo već liči na funkcionalan kod. Napišimo i ostatak programa da provjerimo
kako radi.
1 #include <stdio.h>
2 int main() {
3 int i,j,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=0; i<n; i++)
7 for (j=0; j<n; j++)
8 printf("*");
9 return 0;
10 }

Probajmo pokrenuti ovaj program i unijeti recimo broj 3:


Unesite broj n: 3
*********

Program je ispisao devet zvjezdica, ali se sve te zvjezdice nalaze u istom redu. Zašto?
Da li smo negdje zatražili od računara da pređe u novi red? Nismo. Potrebno
je nakon svakog reda zvjezdica ispisati po jedan znak \n . Gdje trebamo dodati
odgovarajući printf ? Ako to uradimo ovako:
1 #include <stdio.h>
2 int main() {
3 int i,j,n;

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 17

4 printf("Unesite broj n: ");


5 scanf("%d", &n);
6 for (i=0; i<n; i++)
7 for (j=0; j<n; j++)
8 printf("*");
9 printf("\n");
10 return 0;
11 }

nećemo imati nikakvog efekta jer će se novi red ispisati na kraju programa gdje je
nevidljiv. Ovakva popravka:
1 #include <stdio.h>
2 int main() {
3 int i,j,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=0; i<n; i++) {
7 for (j=0; j<n; j++) {
8 printf("*");
9 printf("\n");
10 }
11 }
12 return 0;
13 }

će pak proizvesti sljedeći rezultat:


Unesite broj n: 3
*
*
*
*
*
*
*
*
*

Drugim riječima dobijamo po jedan novi red nakon svake zvjezdice. Ispravno rješenje
je sljedeće: j -petlja predstavlja ispis jednog reda, pa znak \n treba ispisati nakon
j -petlje ali unutar i -petlje koja odbrojava redove, tj. ovako:

1 #include <stdio.h>
2 int main() {
3 int i,j,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=0; i<n; i++) {

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 18

7 for (j=0; j<n; j++) {


8 printf("*");
9 }
10 printf("\n");
11 }
12 return 0;
13 }

Zadatak 4. Napraviti program koji na ekranu ispisuje tablicu množenja


prvih 10 prirodnih brojeva.

1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
...

Tablica množenja zahtijeva da svaki broj [1,10] pomnožimo sa svakim brojem


[1,10]. Ukupno na ekranu trebamo ispisati 100 brojeva. U prvom redu nalaze se
brojevi 1,2,3,...,10 pomnoženi brojem 1, u drugom redu se nalaze isti ti brojevi
pomnoženi brojem 2 itd. Ovdje možemo uočiti sličnost sa prethodnim programom.
Jedan red tablice ispisujemo petljom:
for (i=1; i<=10; i++)
printf("%d", i*k);

pri čemu je k neki broj sa kojim množimo sve članove tog reda. Možemo uočiti
da petlja prolazi kroz sve vrijednosti i P r1, 10s te da smo printf funkciji direktno
proslijedili rezultat množenja brojeva i i k . Ostaje da uočimo da ova promjenljiva
k takođe poprima sve vrijednosti na intervalu [1,10] te da na kraju svakog reda
ispišemo \n .
1 #include <stdio.h>
2 int main() {
3 int i,j;
4 for (i=1; i<=10; i++) {
5 for (j=1; j<=10; j++) {
6 printf("%d", i*j);
7 }
8 printf("\n");
9 }
10 return 0;
11 }

Ovo će na ekranu proizvesti dosta zbrkan ispis:


12345678910
2468101214161820
...

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 19

Problem je što između pojedinačnih brojeva nemamo nikakvog razmaka. Možemo


jednostavno dodati jedan razmak u printf nakon formata %d :
6 printf("%d ", i*j);

To bi nam dalo sljedeći ispis:


1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
...

Ovo već počinje ličiti na tablicu množenja, ali brojevi nisu lijepo poravnati. Da
bismo dobili lijepo poravnanje kao u primjeru, uočavamo da će najveći broj u ovoj
tablici biti 100, dakle trocifren, tako da je dovoljno da za svaki broj rezervišemo po
4 mjesta. Preostaje da prepravimo poziv funkcije printf na sljedeći način:
6 printf("%4d", i*j);

i time ćemo dobiti ispis kao u postavci zadatka.

4.2 while petlja


Nakon što smo se detaljno upoznali sa for petljom, možemo proučiti i drugi tip
petlje u C-u a to je while petlja. Za razliku od for petlje, while petlja se koristi
kada broj izvršenja nije unaprijed poznat tj. ne zavisi od neke konkretne konstante
ili promjenljive.
Riječ while se prevodi kao “sve dok” – “sve dok je uslov ispunjen, ponavljaj tijelo
petlje”. Sintaksa while petlje je sljedeća:
while (uslov) tijelo;

Sve dok je uslov ispunjen, tijelo petlje se ponavlja, a petlja se završava kada uslov
više nije ispunjen. Obratite pažnju da je sa while petljom vrlo lako napraviti beskon-
ačnu petlju! To će se desiti ako se u tijelu petlje ne dešava ništa što bi promijenilo
istinitost uslova. Npr. ako napišemo:
while (x>0) y++;

ovo će sigurno biti ili beskonačna petlja ili petlja koja se ne izvrši nijednom. Ako
je uslov x>0 ispunjen prvi put, biće ispunjen i svaki sljedeći put jer se x ne mijenja
nigdje u petlji. Pogledajmo malo složeniji primjer:
1 #include <stdio.h>
2 int main() {
3 int x, suma=0;
4 while (suma <= 100) {
5 printf("Unesite broj: ");
6 scanf("%d", &x);
7 suma += x;

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 20

8 }
9 printf("suma je %d", suma);
10 return 0;
11 }

Značenje while petlje ovdje je “sve dok je suma manja ili jednaka od 100 ponavljaj
unos broja x i dodaj ga na sumu”. U ovom primjeru ne možemo znati kakve
brojeve će unositi korisnik, suma može odmah prekoračiti broj 100 a može se unositi
beskonačno mnogo brojeva (ako korisnik bude unosio i negativne brojeve).
for petlja je istovremeno jednostavnija i složenija od while petlje. Jednostavnija
je zato što – ako se ispravno koristi – na prvi pogled možemo vidjeti koliko će se
puta izvršiti. Složenija je zato što se for petlja sastoji od nekoliko dijelova koji se
ne izvršavaju onim redom koji su navedeni, nego se sa uslova skače na tijelo petlje
pa nazad na ažuriranje.
Kod while petlje, analiza je vrlo jednostavna. Ako je uslov ispunjen, izvršava se
tijelo petlje te se program vraća na uslov. Ako nije, izvršenje skače na prvu naredbu
poslije tijela petlje.
Ako želimo namjerno napraviti beskonačnu petlju pomoću petlje while, samo se
podsjetimo da je u C-u jedinica logička vrijednost istine. Pa ako napišemo:
while(1)

ova petlja će biti beskonačna pošto će uslov u petlji uvijek imati vrijednost “istina”.

4.2.1 Transformacija for u while i obrnuto


Možemo uočiti da je while petlja ustvari ekvivalentna for petlji. Svaka for petlja
se može pretvoriti u while petlju i obrnuto. Zašto onda postoje različite petlje?
Odgovor leži u nečemu što smo do sada ponovili više puta: većina programskih
struktura postoji prije svega zato da olakšaju čitanje i razumijevanje programa,
odnosno da smanje pojavu grešaka i olakšaju njihovu popravku.
for petlja olakšava razumijevanje programa u kojem se može tačno znati broj
ponavljanja nekog koda i raspon vrijednosti koje treba poprimiti kontrolna prom-
jenljiva. Tamo gdje nam ovaj broj nije unaprijed jasan, ili gdje nam nije bitna
vrijednost kontrolne promjenljive, koristimo while petlju.
Transformacija for petlje u while petlju i obrnuto je korisna vježba za razumije-
vanje obje vrste petlji. Krenućemo od jednostavnog primjera koji smo koristili više
puta ranije:
for (i=0; i<n; i++) {
printf("i je %d", i);
}

Kao što znamo, možemo dobiti program koji obavlja istu funkciju ako inicijalizaciju
prebacimo ispred petlje, a ažuriranje postavimo u tijelo petlje nakon ostalih naredbi:
i=0;
for (; i<n; ) {
printf("i je %d", i);

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 21

Slika 4.2: Transformacija for petlje u while petlju i obrnuto

i++;
}

A for petlja koja u zaglavlju sadrži samo uslov je ustvari while petlja! Odnosno
možemo napisati:
i=0;
while (i<n) {
printf("i je %d", i);
i++;
}

Generalno, svaku for petlju možemo pretvoriti u while petlju na sljedeći način
(Slika 4.2): inicijalizaciju (označenu crvenom bojom) prebacimo ispred petlje, ažuri-
ranje (označeno zelenom bojom) prebacimo u tijelo petlje nakon ostalih naredbi, a
uslov (označen plavom bojom) ostaje gdje jeste i sada postaje uslov while petlje.
Moguća je i transformacija u obrnutom smjeru, pri čemu tražimo u okolnom
kodu naredbe koje bi mogle biti kandidati za inicijalizaciju i ažuriranje, pa ako ih ne
nađemo možemo ta polja for petlje ostaviti i praznim.

4.3 do-while petlja


Mnogi početnici u programiranju ne razlikuju do-while petlju od while petlje.
Ustvari, u pitanju je potpuno različit tip petlje koja igrom slučaja koristi istu ključnu
riječ while . U mnogim jezicima se ova petlja zove repeat-until da bi se izbjegla
ovakva zabuna.

3 Razlika između while i do-while petlje je u tome što se while petlja ne mora
izvršiti niti jednom, dok će se do-while petlja uvijek izvršiti barem jednom.

Recimo možemo napisati ovakav kod:


int x=5;
while (x<3) {
printf("Lozinka glasi: califragilisticexpialidocius\n");
}

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 22

Slika 4.3: Transformacija while petlje u do-while petlju

Ova petlja se neće izvršiti niti jednom, korisnik nikada neće vidjeti lozinku jer uslov
x<3 u datom trenutku sigurno nije ispunjen. Izvršenje programa preskače kompletno
tijelo petlje i nastavlja sa prvom naredbom nakon petlje. Da smo umjesto toga stavili
do-while petlju:
1 int x=5;
2 do {
3 printf("Lozinka glasi: califragilisticexpialidocius\n");
4 } while (x<3);

korisnik bi sigurno ugledao lozinku bez obzira da li je uslov ispunjen ili ne. Ispun-
jenost uslova utiče isključivo na to koliko puta će se petlja ponoviti.
Iz ovoga proizlazi da ne možemo jednostavno pretvoriti do-while petlju u while
petlju, morali bismo ponoviti kompletan kod petlje (Slika 4.3).
Ovaj dupli kod možemo izostaviti samo ako znamo da je uslov B sigurno ispunjen
kada prvi put ulazimo u while petlju. do-while petlja upravo služi da bismo izbjegli
nepotrebne inicijalizacije promjenljivih koje osiguravaju barem jedno izvršenje petlje.
Obratimo pažnju na sintaksu do-while petlje koja se može vidjeti na primjeru
iznad: nakon ključne riječi do slijedi tijelo petlje (blok naredbi), zatim ključna riječ
while , uslov naveden u običnim (malim) zagradama, te na kraju znak tačka-zarez.
Treba naglasiti da se znak tačka-zarez javlja tek na kraju! Nema ga poslije riječi
do ili prije riječi while .
Da bismo pokazali kako se koristi do-while petlja, podsjetimo se jednog programa
koji smo koristili za demonstraciju rada switch-case strukture. Srezaćemo program
na interesantni dio:
1 #include <stdio.h>
2 int main() {
3 int mjesec;
4 printf("Unesite mjesec: ");
5 scanf("%d", &mjesec);
6 if (mjesec < 1 || mjesec > 12) {
7 printf("Neispravan mjesec");
8 return 0;
9 }
10 if (mjesec == 1) ...

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 23

U programu smo morali osigurati da se promjenljiva mjesec nalazi na intervalu


[1,12]. To smo tada mogli uraditi tako što u slučaju neispravnog mjeseca prekinemo
program ili možemo zatražiti ponovni unos naredbom goto . Sada smo naučili petlje,
pa možemo iskoristiti while petlju da bismo ponavljali unos mjeseca sve dok je on
neispravan (dok je izvan intervala [1,12]).
#include <stdio.h>
int main() {
int mjesec;
while (mjesec < 1 || mjesec > 12) {
printf("Unesite mjesec: ");
scanf("%d", &mjesec);
}
...

Ovaj program sadrži grešku. Naime, uporedili smo vrijednost promjenljive mjesec
sa brojevima 1 i 12 prije nego što je promjenljiva dobila ikakvu vrijednost. Ovo
je pristup neinicijalizovanoj promjenljivoj! Iako će u velikoj većini slučajeva ovaj
program raditi ispravno, postoji određena vjerovatnoća da će promjenljiva dobiti
vrijednost na intervalu [1,12] tako da korisnik nikada neće ni dobiti priliku da unosi
mjesec.
Ovaj problem možemo riješiti na nekoliko načina. Možemo staviti još jedan unos
promjenljive mjesec prije petlje koji će se sigurno izvršiti:
1 #include <stdio.h>
2 int main() {
3 int mjesec;
4 printf("Unesite mjesec: ");
5 scanf("%d", &mjesec);
6 while (mjesec < 1 || mjesec > 12) {
7 printf("Unesite mjesec: ");
8 scanf("%d", &mjesec);
9 }
10 ...

Ali ovo bi dovelo do ponavljanja koda. Svako ponavljanje koda smatra se štetnim.
Nekada je lakše rješenje uraditi copy-paste određenog koda onoliko puta koliko vam
je potreban, ali to treba izbjegavati kada god je moguće. Pored toga što nepotrebno
produžuje program i otežava čitljivost, ponavljanje koda može voditi ka greškama
jer kada uočite grešku u kodu, sada tu istu grešku morate popraviti na više mjesta,
a velika je vjerovatnoća da ćete preskočiti neko od tih mjesta.
Možete i postaviti inicijalnu vrijednost promjenljive mjesec na neku vrijednost
koja osigurava da će se while petlja izvršiti barem jednom:
1 #include <stdio.h>
2 int main() {
3 int mjesec=0;
4 while (mjesec < 1 || mjesec > 12) {

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 24

5 printf("Unesite mjesec: ");


6 scanf("%d", &mjesec);
7 }
8 ...

Pošto smo postavili mjesec na nulu, znamo da će se petlja izvršiti barem jednom.
Ovo je također jedna vrsta koda koji je ispravan i daje tačan rezultat, ali je primjer
loše prakse u programiranju koja se naziva magična konstanta. Naime, ovaj primjer
je dovoljno jednostavan da je jasno šta se ovdje dešava, ali u složenijim programima
programer koji analizira taj program može postaviti pitanje: šta predstavlja ta nula?
Zašto baš nula? Zašto ne 1000 ili -1? Navođenje bilo kakve konstantne vrijed-
nosti u kodu u pravilu zahtijeva komentar koji objašnjava zašto je baš ta vrijednost
odabrana.
Ako malo razmislimo, u oba ova primjera mi smo ustvari simulirali rad do-while
petlje. U primjeru sa ponavljanjem koda, mi smo upravo obavili transformaciju
while u do-while petlju. U primjeru sa mjesec=0 , postavili smo početnu vrijednost
promjenljive mjesec da bismo osigurali da se while petlja izvrši barem jednom. Pravo
pitanje koje će iskusniji programer postaviti početniku koji koristi magične konstante
glasi “šta želiš postići?” Ako želimo postići da se petlja izvrši barem jednom, za to
se koristi do-while petlja. Pa ćemo prepraviti program tako da se koristi upravo ta
petlja:
1 #include <stdio.h>
2 int main() {
3 int mjesec;
4 do {
5 printf("Unesite mjesec: ");
6 scanf("%d", &mjesec);
7 } while (mjesec < 1 || mjesec > 12);
8 if (mjesec == 1) {
9 ...

Vidimo da u ovom primjeru nema potrebe za magičnim konstantama niti za pon-


avljanjem kôda.

4.4 Primjeri zadataka sa petljama


U nastavku ćemo proći korak po korak kroz različita rješenja nekih interesantnih
zadataka iz programiranja u kojima se koriste isključivo petlje. Na ovaj način ćemo
kroz primjere naučiti veći broj različitih “trikova” koji nam u nastavku našeg učenja
programiranja mogu biti vrlo korisni.

4.4.1 Crtanje oblika na ekranu ASCII karakterima


Prilikom učenja programiranja susrešćete se sa određenim brojem primjera gdje
se na ekranu iscrtava određeni oblik sastavljen od pluseva, zvjezdica, tačaka i sl. pri
čemu su možda dimenzije oblika unesene putem tastature. Koja je svrha ovakvih

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 25

Slika 4.4: Redoslijed kretanja po scan-linijama

zadataka, ako znamo da se već dugo vremena nikakvi ozbiljni programi niti igre ne
rade na ovaj način?
Crtanje 2D oblika je jedan vrlo praktičan primjer kroz koji možemo razumjeti
način rada programa, a pogotovo dvostrukih petlji. Vidjećemo kasnije da će nam to
biti korisno kada budemo učili matrice i druge višedimenzionalne strukture. Mnogi
lakše razumiju apstraktne pojmove na vizualan način, ako ih vide ilustrovane nekim
crtežom.
Prilikom rješavanja ovih zadataka vrlo je važno shvatiti da se mi iz C programa
ne možemo (na standardan način) pozicionirati na proizvoljnu lokaciju na ekranu.
Ne možemo ići prema lijevo niti prema gore. Moguće je koristiti znak \b za brisanje
jednog karaktera, ali ovo nam u pravilu nije previše korisno. Ako želimo pomjeriti
kursor prema dolje ispisujemo znak za novi red \n , ali obratite pažnju da se nakon
ispisa \n više ne možemo vratiti nazad prema gore. Kretanje u desno postižemo
ispisom znaka razmaka: printf(" ");
Dakle kada razmišljamo o rješenju uvijek moramo imati na umu da se kursor
kreće red po red, odozgo prema dolje, a unutar jednog reda s lijeva na desno. Ovaj
smjer kretanja odgovara tzv. scan-linijama koje se koriste inače za osvježavanje
sadržaja na ekranima (Slika 4.4).
Zadatak 5. Nacrtati trougao sastavljen od zvjezdica kao na primjeru čije
su katete dugačke po n zvjezdica (broj n unosi korisnik).

Unesite broj n: 5
*
**
***
****
*****

Ovdje ćemo se podsjetiti opšteg principa rješavanja programskih zadataka, pa


ćemo dati jednu njegovu varijantu:

3 Ako ne znate uraditi ono što se traži, uradite dio koji znate a zatim probajte
prepravljati program prema ciljanom rješenju.

Ono što znamo uraditi je nacrtati kvadrat čije stranice čini n zvjezdica jer smo
to uradili u zadatku 4.3 (poglavlje 4.1.7).

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 26

Unesite broj n: 5
*****
*****
*****
*****
*****

Programski kod glasi:


1 #include <stdio.h>
2 int main() {
3 int i,j,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=0; i<n; i++) {
7 for (j=0; j<n; j++) {
8 printf("*");
9 }
10 printf("\n");
11 }
12 return 0;
13 }

Na koji način se ovaj program razlikuje od željenog? U slučaju kvadrata, u


svakom redu imamo n zvjezdica, dok kod trougla u prvom redu imamo 1 zvjezdicu,
u drugom 2 zvjezdice itd. Možemo reći da u nekom opštem i -tom redu imamo
i zvjezdica. Uočavamo da je unutrašnja j -petlja (linije 7 i 8) zadužena za ispis
jednog reda zvjezdica, te da je njen krajnji opseg n. Ostaje da nađemo to i koje
nam definiše broj zvjezdica – a to je upravo ista promjenljiva i koju imamo u liniji
6! Naime, u prvom redu kvadrata i će biti 0, u drugom će biti 1 itd. Nama je
potrebna u prvom redu jedna zvjezdica, u drugom dvije itd. pa možemo pisati:
7 for (j=0; j<i+1; j++) {

ili
7 for (j=0; j<=i; j++) {

Zadatak 6. Napraviti program koji na ekranu iscrtava trougao sastavljen


od zvjezdica kao na primjeru, čije su katete dugačke po n zvjezdica (broj n
unosi korisnik).

Unesite broj n: 5
*****
****
***
**
*

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 27

Tekst zadatka je isti, ali se oblik razlikuje tj. trougao je rotiran. Na koji način
možemo ispisati ovako rotiran trougao?
Krenimo opet analizirati svaki pojedinačni red trougla. U prvom redu imamo 5
zvjezdica, dakle n zvjezdica, u drugom redu imamo 4 zvjezdice (n-1), u trećem 3
(n-2) itd. Dalje uočavamo da je svaki red pomaknut u desnu stranu. Da se ne bi
desilo da su zvjezdice pomaknute ulijevo, prije svakog reda trebamo ispisati određeni
broj razmaka. Koji je to broj?

1. U prvom redu ispisujemo 0 razmaka i n zvjezdica


2. U drugom redu ispisujemo 1 razmak i n-1 zvjezdica
3. U trećem redu ispisujemo 2 razmaka i n-2 zvjezdica
4. ...

Možemo uočiti da u nekom i -tom redu treba ispisati i razmaka i n-i zvjezdica,
pri čemu i kreće od 0, a to je upravo ono što nam treba. Rješenje glasi:
1 #include <stdio.h>
2 int main() {
3 int i,j,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6 for (i=0; i<n; i++) {
7 for (j=0; j<i; j++)
8 printf(" ");
9 for (j=0; j<n-i; j++)
10 printf("*");
11 printf("\n");
12 }
13 return 0;
14 }

Petlja u linijama 7 i 8 ispisuje i razmaka, a petlja u linijama 9 i 10 ispisuje n-i


zvjezdica, sve skupa se ponavlja n puta petljom koja počinje u liniji 6.
Zadatak 7. Napraviti program koji na ekranu iscrtava šuplji kvadrat
kao na slici sastavljen od znakova tačka pri čemu je svaka stranica kvadrata
dugačka n znakova (korisnik unosi broj n sa tastature).

Unesite broj n: 5
.....
. .
. .
. .
.....

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 28

Znamo da se ispis vrši red po red, pa uočavamo najprije da su prvi i posljednji


red međusobno jednaki, i sastoje se od n tačaka. Ako pogledamo redove “u sredini”,
i oni su međusobno jednaki. Svaki od ovih srednjih redova sastoji se od: jedne
tačke, n-2 razmaka te na kraju još jedna tačka. Broj redova u sredini oblika je n-2 .
Ova dvica potiče od činjenice da smo isključili prvi i posljedni red. Dakle rješenje
zadatka bi glasilo ovako:
1 #include <stdio.h>
2 int main() {
3 int i,j,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6

7 for (i=0; i<n; i++)


8 printf(".");
9 printf("\n");
10

11 for (i=0; i<n-2; i++) {


12 printf(".");
13 for (j=0; j<n-2; j++)
14 printf(" ");
15 printf(".\n");
16 }
17

18 for (i=0; i<n; i++)


19 printf(".");
20 printf("\n");
21

22 return 0;
23 }

Kôd u linijama 7-9 ispisuje prvi red tačaka koji se završava prelaskom u novi red
( \n ). Linije 18-20 predstavljaju posljednji red i možemo vidjeti da su identične. U
linijama 11-16 ispisuje se sredina, pri čemu jedan red sredine ispisuju linije 12-15
(tačka, n-2 razmaka, tačka i \n ), a taj red se ponavlja n-2 puta. Radi lakšeg
čitanja koda ostavili smo prazne redove u linijama 6, 10, 17 i 21.
Postoji i drugačiji pristup rješavanju zadataka sa crtanjem oblika na ekranu.
Možemo na prostor za ispis postaviti koordinatni sistem:
Unesite broj n: 5
.....
. .
. .
. .
.....

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 29

Ishodište koordinatnog sistema je u gornjem lijevom uglu jer ispis kreće slijeva
nadesno, odozgo prema dole. Vodoravna koordinata je j a uspravna je i . Sada
zadatak postaje da kreiramo logičku (Booleovu) funkciju f pi, jq koja ima vrijednost
istina ako treba na koordinatama pi, jq ispisati tačku, a neistina ako ne treba tj.
ako treba ispisati razmak. Ne smijemo zaboraviti \n na kraju svakog reda.
for (i=0; i<n; i++) {
for (j=0; j<n; j++) {
if (f(i,j))
printf(".");
else
printf(" ");
}
printf("\n");
}

Pošto još uvijek nismo radili funkcije, logički uslov za ispis tačke ćemo uglaviti u kod
umjesto f(i,j) . Vidimo da se tačka treba ispisati ako je i nula (gornja stranica)
ili ako je i=n-1 (donja stranica) ili j=0 (lijeva stranica) ili j=n-1 (desna stranica).
Prevedeno u C to nam daje sljedeći kompletan program:
1 #include <stdio.h>
2 int main() {
3 int i,j,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6

7 for (i=0; i<n; i++) {


8 for (j=0; j<n; j++) {
9 if (i==0 || i==n-1 || j==0 || j==n-1)
10 printf(".");
11 else
12 printf(" ");
13 }
14 printf("\n");
15 }
16

17 return 0;
18 }

Zadatak 8. Napisati program koji omogućuje unos pozitivnog cijelog broja


n>4 (zabraniti unos brojeva manjih ili jednakih broju 4), a zatim na ekranu
ispisuje veliko štampano slovo N sastavljeno od znakova * čije je širina i
visina n znakova. Primjeri za n=5 i n=6:

* * * *
** * ** *

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 30

* * * * * *
* ** * * *
* * * **
* *

Kao u prethodnom primjeru sa koordinatnim sistemom uočavamo da se znak


zvjezdica ispisuje ako je j=0, j=n-1 ili j=i (dijagonala). Zabranu unosa broja n ď 4
postižemo do-while petljom.
1 #include <stdio.h>
2 int main() {
3 int i,j,n;
4 do {
5 printf("Unesite broj n: ");
6 scanf("%d", &n);
7 } while (n<=4);
8 for (i=0; i<n; i++) {
9 for (j=0; j<n; j++) {
10 if (j==0 || j==n-1 || j==i)
11 printf("*");
12 else
13 printf(" ");
14 }
15 printf("\n");
16 }
17 return 0;
18 }

4.4.2 Sume
Sljedeći čest tip zadataka sa petljama su programi koji trebaju izračunati neku
matematičku sumu. Pa pogledajmo na primjeru kako se rješavaju ovakvi zadaci.
Zadatak 9. Napisati program koji izračunava arctanh x – hiperbolički
arkus tangens nekog broja x – koristeći sljedeću aproksimativnu formulu:

x3 x5
arctanh x “ x ` ` ` ... (4.1)
3 5
Računari ne mogu predstaviti brojeve sa beskonačnom preciznosti. Zbog toga
se iracionalne vrijednosti (kakve su u pravilu rezultati trigonometrijskih funkcija)
izračunavaju tako što se pronađe neki red koji konvergira ka željenoj vrijednosti, a
zatim se izračunava njegova konačna suma. Broj članova sume n određuje se tako
da krajnji rezultat bude dovoljno tačan za potrebe zadatka.
Dakle, ako je zadatak definisan kao iznad, korisnik ustvari treba unijeti dvije
vrijednosti: broj x koji je u pravilu realan broj, te prirodan broj n koji predstavlja
broj članova sume.

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 31

Opšti princip rješavanja zadataka sa sumama je sljedeći: najprije moramo za-


pisati sumu u obliku:
n
ÿ x2i´1
arctanh x “ (4.2)
i“1
2i ´ 1
Ovakav zapis nam je potreban zato što je njime izražen opšti “i-ti” član sume. Zatim
trebamo taj opšti član zapisati koristeći C sintaksu:
clan = pow(x, 2*i-1) / (2*i-1)

Ovdje moramo obratiti pažnju na nekoliko stvari. Najprije, kada u matematici


napišemo 2i ´ 1 između 2 i i se nalazi operacija množenja koja je izostavljena. U
C-u operator množenja je zvjezdica i ne možemo ga preskočiti, tj. ako napišemo
2i-1 u našem programu dobićemo sintaksnu grešku. Dalje, razlomak kao takav je
operacija dijeljenja, ali moramo paziti da brojnik i nazivnik stavimo u zagrade jer
bismo u suprotnom dobili
x2i´1
i´1 (4.3)
2
Stepenovanje u C-u ne postoji kao operacija nego moramo koristiti funkciju pow
definisanu u biblioteci math.h . Funkcija pow uvijek vraća vrijednost tipa double, čak
i kad su oba operanda cjelobrojne vrijednosti, tako da ovdje neće doći do zaokruži-
vanja. Ovo sve bi nam trebalo biti poznato od ranije.
Treći i posljednji korak je izračunavanje sume n ovakvih članova. To postižemo
tako što kreiramo for petlju u kojoj za svako i P r1, ns izračunavamo i-ti član i
dodajemo ga na promjenljivu suma :
for (i=1; i<=n; i++) {
clan = pow(x, 2*i-1) / (2*i-1);
suma += clan;
}

Kada pogledamo sumu datu u zadatku, možemo uočiti da se u njoj javljaju


isključivo neparni brojevi. Ustvari, 2i ´ 1 je samo matematički način da izrazimo
činjenicu da je neki broj neparan. Naš program možemo učiniti bržim i lakšim za
čitanje tako što izračunavanje izraza 2*i-1 ne radimo u svakom prolazu kroz petlju:
for (i=1; i<2*n; i+=2) {
clan = pow(x,i) / i;
suma += clan;
}

U ovoj varijanti u svakom prolazu kroz petlju i se povećava za 2, pa pošto smo


počeli od broja 1 i će poprimati neparne vrijednosti. Broj članova sume treba biti
n što smo osigurali uslovom i<2*n .
Kompletno rješenje zadatka glasi ovako:
1 #include <stdio.h>
2 int main() {
3 int i,n;

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 32

4 double suma=0, x, clan;


5 printf("Unesite broj x: ");
6 scanf("%lf", &x);
7 do {
8 printf("Unesite broj clanova sume: ");
9 scanf("%d", &n);
10 } while (n<=0);
11 for (i=1; i<2*n; i+=2) {
12 clan = pow(x,i) / i;
13 suma += clan;
14 }
15 printf("Suma iznosi: %g", suma);
16 return 0;
17 }

Možemo uočiti da su promjenljive suma , x i clan tipa double , jer za velike vrijed-
nosti n povećana preciznost može imati efekta na rezultat, te smo koristili format
%lf za unos. Petljom do-while osigurali smo da je vrijednost n pozitivna pošto C
nema tip podataka za prirodne brojeve. Na kraju koristimo format %g za ispis čime
postižemo da će se ispisati onoliko decimala koliko bude potrebno.
Zadatak 10. Napisati program koji izračunava broj π na k tačnih decimala,
po sljedećoj aproksimativnoj formuli:

1 1 1 1
π “ 4 ¨ p1 ´ ` ´ ` ...q (4.4)
3 5 7 9
Koliko članova sume n nam je potrebno da bismo imali k tačnih decimala?
Uočavamo da je svaki sljedeći član sume date iznad manji od prethodnog. Dakle
kako raste naše i dolazimo do članova koji su toliko mali da ne bi trebali imati
efekta na prvih k decimala. Konkretno, kada član sume postane manji od 10´k
možemo prekinuti petlju.
Ovdje treba napomenuti da čak ni sa tipom double ne možemo predstaviti više
od 6-7 tačnih decimala. Kada radimo sa brojevima manjim od 0.000001 greška
zbog nepreciznosti predstavljanja realnih brojeva u memoriji postaje tolika da se
lako može desiti da upadnemo u beskonačnu petlju. No za potrebe rješavanja ovog
zadatka pretpostavićemo da je tip double precizniji nego što realno jeste.
Suma u skraćenom obliku glasi:
n
ÿ 1
π “4¨ p´1qi (4.5)
i“1
2i ´ 1

Ovdje p´1qi je matematička notacija koja označava da se mijenja predznak razlomka


(p´1q0 “ 1, p´1q1 “ ´1, p´1q2 “ 1, p´1q3 “ ´1 ...). Opšti član možemo
izračunati direktno pomoću funkcije pow :
clan = pow(-1,i) / (2*i-1)

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 33

ali ovo bi bilo prilično neefikasno rješenje jer bismo u svakom prolasku kroz petlju
radili stepenovanje, što nije potrebno. Umjesto toga možemo pisati ovako:
clan = predznak / (2*i-1);
predznak = -predznak;

pri čemu ćemo početnu vrijednost promjenljive predznak staviti na 1. Dakle u


svakom prolasku kroz petlju ova promjenljiva će mijenjati vrijednost u -1, pa 1, -1,
1... Ostaje još da kao u prethodnom zadatku uočimo da je 2i ´ 1 oznaka za neparan
broj. Konačno rješenje glasi:
1 #include <stdio.h>
2 #include <math.h>
3 int main() {
4 int k, i=1, predznak=1;
5 double suma=0, clan, limit;
6 do {
7 printf("Unesite broj decimala: ");
8 scanf("%d", &k);
9 } while (k<=0 || k>7);
10 limit = pow(10, -k);
11 do {
12 clan = (double)predznak/i;
13 predznak = -predznak;
14 i += 2;
15 suma += clan;
16 } while (clan>=limit);
17 suma *= 4;
18 printf("Suma iznosi: %g", suma);
19 return 0;
20 }

Kada član sume postane manji od vrijednosti limit (a to je 10´k ), prekidamo


petlju. Za centralni dio koda koristili smo do-while petlju jer nije unaprijed poznat
broj članova sume, a besmisleno je da se petlja ne izvrši barem jednom. Moramo
paziti da su predznak i i cijeli brojevi, pa u liniji 12 dodajemo (double) da bismo
izbjegli odsjecanje. Ne zaboravite da se izračunata suma na kraju treba pomnožiti
sa 4.
Zadatak 11. Izračunati vrijednost sume:

x2 x3
S “x` ` ` ... (4.6)
2! 3!
Ova suma konvergira jer funkcija faktorijela raste brže od eksponencijalne funkcije.
Faktorijel se definiše ovako:
k! “ 1 ¨ 2 ¨ 3 ¨ ... ¨ k (4.7)
Dakle k! možemo izračunati sljedećim C kodom:

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 34

fakt=1;
for (i=2; i<=k; i++)
fakt *= i;

Postavljanje promjenljive fakt na 1 je bitno jer je jedinica neutralna za množenje.


Da smo pisali fakt=0; rezultat bi uvijek bio nula. Množenje broja sa 1 je nepotrebno
pa petlja može početi od dvojke.
i
Iz naprijed navedenog je vrlo lako odrediti opšti član xi! pa bi rješenje glasilo:
1 #include <stdio.h>
2 int main() {
3 int i,j,n,fakt;
4 double suma=0, x, clan;
5 printf("Unesite broj x: ");
6 scanf("%lf", &x);
7 do {
8 printf("Unesite broj clanova sume: ");
9 scanf("%d", &n);
10 } while (n<=0);
11 for (i=1; i<=n; i++) {
12 /* Faktorijel broja i (i!) */
13 fakt = 1;
14 for (j=2; j<=i; j++)
15 fakt *= j;
16 clan = pow(x,i) / fakt;
17 suma += clan;
18 }
19 printf("Suma iznosi: %g", suma);
20 return 0;
21 }

Ovo rješenje je potpuno tačno, ali ga možemo uraditi znatno efikasnije. Pogledajmo
sljedeće rješenje:
1 #include <stdio.h>
2 int main() {
3 int i,j,n,fakt;
4 double suma=0, x, clan=1;
5 printf("Unesite broj x: ");
6 scanf("%lf", &x);
7 do {
8 printf("Unesite broj clanova sume: ");
9 scanf("%d", &n);
10 } while (n<=0);
11 for (i=1; i<=n; i++) {
12 clan *= x/i;
13 suma += clan;
14 }

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 35

15 printf("Suma iznosi: %g", suma);


16 return 0;
17 }

Pogledajte u liniji 12 kako izračunavamo opšti član. Svaki član sume jednak je
prethodnom članu pomnoženom sa x{i:

xi´1 x xi
¨ “ (4.8)
pi ´ 1q! i i!

Sada u petlji imamo jedno sabiranje, jedno množenje i jedno dijeljenje, dok smo
ranije imali još i stepenovanje i petlju za računanje faktorijela. Početna vrijednost
člana je 1 (linija 4) kako bi u prvom prolazu bio pomnožen sa x{1 i dobijamo prvi
član sume x.
Prilikom rješavanja zadataka u kojima se pojavljuje faktorijel, obratite pažnju da
funkcija faktorijela raste izuzetno brzo, pa već za vrijednosti oko 13! prekoračujemo
opseg vrijednosti koje možemo predstaviti tipom int .

4.4.3 Prosti brojevi, problemi tipa “za svako x”


Prosti brojevi se vrlo često javljaju zadacima iz programiranja, a pogotovo u
raznim algoritmima iz oblasti kriptografije, jer je to jedan od matematičkih prob-
lema koji je (za sada) nemoguće riješiti računarski bez korištenja petlje. Ako otkri-
jete rješenje bez korištenja petlje, u svakom slučaju se javite jer biste se mogli
izuzetno obogatiti! Uobičajena definicija prostog broja kakvu nalazimo u školskim
udžbenicima glasi:

3 Broj je prost ako je djeljiv isključivo brojem 1 i samim sobom.

Pregledajući studentske radove redovno možemo vidjeti ovakav kôd:


if (n%1 == 0 && n%n == 0)
print("Prost");
else
print("Slozen");

Problem ovdje je što ste previdjeli riječ isključivo u tekstu definicije. Svaki broj
je djeljiv sa 1 i sa samim sobom, ali prosti brojevi nisu djeljivi ni sa kojim drugim
brojem, pa bi sa stanovišta programiranja bolja bila sljedeća definicija: Broj je prost
ako NIJE djeljiv niti sa jednim brojem između 1 i tog broja.
Često se može vidjeti i ovakvo rješenje:
if (n%2 != 0 && n%3 != 0 && n%5 != 0 && n%7 != 0)
print("Prost");
else
print("Slozen");

Ponekad se uslovi nastavljaju, pa studenti znaju ispisati i više od pola stranice teksta
nabrajajući brojeve sa kojima prost broj nije djeljiv. Studentu svaka čast na trudu

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 36

ali ovakvo rješenje ponovo nije tačno. To možemo vidjeti iz sljedećeg primjera:
uzmimo sve brojeve koje je student nabrojao u svom uslovu i nađimo neki broj koji
je veći od svih tih brojeva i koji je prost (u primjeru iznad to bi bilo 11). Nazovimo
taj broj x. Broj x2 je složen broj jer je djeljiv sa x, no on nije djeljiv niti sa jednim
od brojeva navedenih u kôdu! Dakle, ako bi korisnik unio broj n “ 112 “ 121, kôd
naveden iznad bi za taj broj ispisao da je prost što nije tačno.
Pa će zatim student probati popraviti svoj kod na sljedeći način:
if (n%2 != 0 && n%3 != 0 && n%5 != 0 && n%7 != 0 && n%(int)sqrt(n) != 0)
print("Prost");
else
print("Slozen");

(posljednji poduslov provjerava da li je broj djeljiv sa svojim kvadratnim korijenom),


što ponovo nije tačno jer u našem primjeru iznad umjesto x2 možemo uzeti x3 , pa x5
itd. Ukratko: nemoguće je provjeriti da li je neki broj n prost a da ne provjerimo da
li je?n djeljivo sa svim brojevima na intervalu p1, nq, odnosno najmanje na intervalu
r2, ns.
Naoružani ovim znanjem možemo probati da realizujemo program koji provjerava
da li je broj prost:
#include <stdio.h>
int main() {
int i,j,n;
printf("Unesite broj n: ");
scanf("%d", &n);

for (i=2; i<n; i++) {


if (n%i == 0)
printf("Slozen");
else
printf("Prost");
}

return 0;
}

Na osnovu izgleda programa već znate da je i on pogrešan. Da bismo razumjeli


zašto, pokrenimo ovaj program i unesimo npr. broj 8:
Unesite broj n: 8
Slozen
Prost
Slozen
Prost
Prost
Prost

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 37

Problem ovdje je što je program nastavio provjeravati djeljivost za brojeve 3, 4 itd.,


što nije potrebno jer već kada ustanovimo da je 8 djeljivo sa 2 znamo da je taj broj
složen i ne moramo provjeravati dalje.
Prepravićemo program tako da se on prekine čim ustanovimo da je broj složen:
for (i=2; i<n; i++) {
if (n%i == 0) {
printf("Slozen");
break;
} else {
printf("Prost");
}
}

Sada će za broj 8 program dati tačan rezultat:


Unesite broj n: 8
Slozen

No za broj 9 imamo:
Unesite broj n: 9
Prost
Slozen

Šta se ovdje desilo? Prođimo kroz program korak po korak. Najprije je i imalo
vrijednost 2 pa smo provjeravali da li je 9 djeljivo sa 2. Pošto nije, uslov nije ispunjen
pa se na ekranu ispisalo Prost . Vidimo da ovo nije ispravno jer je 9 složen broj,
pa se tekst Prost ne treba ispisivati u petlji za svaku vrijednost i sa kojom n nije
djeljivo, nego samo na kraju petlje. Možemo ovako prepraviti program:
for (i=2; i<n; i++) {
if (n%i == 0) {
printf("Slozen");
break;
}
}
printf("Prost");

No sada za broj 9 imamo:


Unesite broj n: 9
Slozen
Prost

jer se tekst Prost ispisuje za svako uneseno n bilo ono prost ili složen broj. Želimo
da se tekst Prost ispiše samo ako broj n nije djeljiv niti sa jednim i , odnosno ako
se break nikada nije izvršilo. Kako to možemo znati? Pa podsjetimo se poglavlja
4.1.6 gdje smo rekli da vrijednost kontrolne promjenljive i poslije petlje može biti
korisna da znamo da se petlja završila bez prekida. Time dolazimo do kompletnog
tačnog rješenja zadatka:

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 38

1 #include <stdio.h>
2 int main() {
3 int i,j,n;
4 printf("Unesite broj n: ");
5 scanf("%d", &n);
6

7 for (i=2; i<n; i++) {


8 if (n % i == 0) {
9 printf("Slozen");
10 break;
11 }
12 }
13 if (i == n)
14 printf("Prost");
15

16 return 0;
17 }

Provjera da li je broj prost spada u klasu problema “za svako x” – u matematici


se ovo označava sa @x. Broj n je prost ako i samo ako za svako x na intervalu (1,n)
važi da n nije djeljivo sa x. Matematički to možemo zapisati ovako:

prostpnq ðñ @x P p1, nq, x P N, x ffl n (4.9)

Zadatke tipa “za svako x” rješavamo na sljedeći način:


1. Pretpostavimo da uslov važi za svako x.
2. Koristeći for petlju prolazimo kroz sve moguće vrijednosti x.
3. Ako nađemo takvo x da uslov ne važi, zaključujemo da uslov zadatka nije
ispunjen i prekidamo petlju.
Korak 1. se obično realizuje koristeći logičku promjenljivu. U programskom jeziku
C ne postoji logički (boolean) tip podataka, nego umjesto njega koristimo int pri
čemu 1 označava istinu a 0 neistinu.
Dakle, program koji provjerava da li je neki broj n prost možemo napisati i ovako:
7 prost = 1; /* logicka promjenljiva */
8 for (i=2; i<n; i++)
9 if (n % i == 0) {
10 prost = 0;
11 break;
12 }
13 }
14 if (prost)
15 printf("Prost");
16 else
17 printf("Slozen");

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 39

Ovo rješenje je bolje od prethodnog jer ga lakše možemo iskoristiti u nekom većem
programu npr. ako se traži da prebrojimo sve proste brojeve na nekom intervalu.
Također često imamo i zadatke tipa “postoji barem jedan x” – a matematička
oznaka je Dx. Recimo provjera da li je broj složen spada u ovaj tip zadatka. Broj
n je složen ako i samo ako na intervalu (1,n) postoji barem jedno x takvo da je n
djeljivo s njim. Matematički to možemo zapisati

slozenpnq ðñ Dx P p1, nq, x P N, x  n (4.10)

Zadatak tipa Dx je invertovana verzija zadatka @x, odnosno broj je složen ako nije
prost i obrnuto. Drugim riječima, ako za svako x važi suprotan uslov od traženog,
to znači da nije tačno da postoji barem jedan x za koji važi traženi uslov. Algoritam
je u biti isto samo se mijenja značenje logičke istine i neistine (0 i 1):
1. Pretpostavimo da ne postoji nijedno x koje zadovoljava uslov.
2. Koristeći for petlju prolazimo kroz sve moguće vrijednosti x.
3. Ako nađemo takvo x za koje uslov važi, zaključujemo da je uslov zadatka
ispunjen i prekidamo petlju.

4.4.4 Razdvajanje broja na cifre

Zadatak 12. Napisati program koji određuje najveću cifru broja.

Unesite broj: 28147


Najveca cifra je: 8

Nekoliko napomena vezanih za ovaj zadatak: Ovdje ćemo raditi isključivo sa


tipom int pošto nepreciznost realnih tipova pravi probleme sa primjenom algoritma
koji ćemo opisati u nastavku. Neki studenti pokušavaju ovaj zadatak riješiti tako što
koriste nizove ili stringove, pa ako u primjeru iznad umjesto cijelog broja unosimo
string dobijamo neki sasvim različit problem, no nije teško promijeniti tekst zadatka
tako da rješenje sa stringom nije moguće.
Da bismo riješili zadatak moramo napraviti petlju koja prolazi kroz uneseni broj
cifru po cifru. Da bismo dobili posljednu cifru broja možemo koristiti ostatak pri
dijeljenju (modulo) sa 10. S druge strane, ako radimo cjelobrojno dijeljenje sa 10
doći će do odsijecanja svih cifara osim posljednje. Ove dvije operacije možemo
postaviti u petlju. Npr. ako je broj x=28147 , imaćemo:
• c=x%10=7 , x=x/10=2814

• c=x%10=4 , x=x/10=281

• c=x%10=1 , x=x/10=28

• c=x%10=8 , x=x/10=2

• c=x%10=2 , x=x/10=0

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.
Osnove računarstva (skripta) 40

Promjenljiva c je redom poprimala vrijednosti cifara broja 28147 od manje vrijednih


ka većim, tj. zdesna nalijevo. Petlja se završava kada x postane nula. Dakle rješenje
zadatka glasi:
1 #include <stdio.h>
2 int main() {
3 int x, c, max=0;
4 printf("Unesite broj: ");
5 scanf("%d", &x);
6

7 while (x != 0) {
8 c = x % 10;
9 if (c>max) max=c;
10 x = x / 10;
11 }
12 printf ("Najveca cifra je: %d", max);
13

14 return 0;
15 }

Obratimo pažnju na par stvari. Za početnu vrijednost promjenljive max smo uzeli 0
što je sigurna vrijednost jer sigurno niti jedna cifra neće biti manja od nule. Uslov
petlje glasi x!=0 a ne x>0 jer bi ova druga varijanta radila netačno ako korisnik
unese negativan broj. No u tom slučaju će i c biti negativno, pa bismo u liniji 8
trebali uzeti:
8 c = abs(x % 10);

te uključiti potrebnu biblioteku stdlib.h .


Za vježbu probajte uraditi i ovakve zadatke:
Zadatak 13. Napisati program koji izračunava sumu cifara broja.

Zadatak 14. Napisati program koji sve parne cifre u broju zamjenjuje
cifrom 0. Pošto cjelobrojni tipovi u C-u ne mogu predstaviti brojeve koji
počinju nulama, sve parne cifre koje se nalaze s lijeve strane broja trebaju
biti obrisane. Ako su sve cifre broja parne, rezultat treba biti broj 0.

Umnožavanje ove skripte i njena upotreba van Elektrotehničkog fakulteta Sarajevo


nisu dozvoljeni.

You might also like