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

Sadržaj

Predgovor drugom izdanju ............................................................................................. I


Sadržaj .......................................................................................................................... III
1. TEORIJSKI DIO .................................................................................................. 1
1.1 Programski jezici. Istorijat i terminologija...................................................... 2
1.1.1 Programski jezici...................................................................................2
1.1.2 Istorijat programskih jezika...................................................................3
1.1.3 Terminologija ........................................................................................6
1.1.4 Podaci ....................................................................................................8
1.1.5 Potprogrami ...........................................................................................9
1.2 Programski jezik C ........................................................................................ 12
1.2.1 Jedan jednostavan program: od teksta do izvršenja ............................12
1.2.2 Osnovni sintaksni elementi .................................................................15
1.2.2.1 Identifikatori...................................................................................15
1.2.2.2 Konstante .......................................................................................15
1.2.2.3 Specijalni simboli ...........................................................................16
1.2.2.4 Rezervisane (ključne) riječi............................................................16
1.2.2.5 Stringovi .........................................................................................17
1.2.2.6 Komentari.......................................................................................17
1.2.2.7 Bjeline ............................................................................................17
1.2.3 Operatori u programskom jeziku C .....................................................18
1.2.4 Pregled tipova podataka ......................................................................19
1.2.4.1 Tip int ..........................................................................................20
1.2.4.2 Tipovi float i double .................................................................22
1.2.4.3 Tip char ........................................................................................23
1.2.4.4 Još malo o operatorima ..................................................................25
1.2.4.5 Implicitna i eksplicitna konverzija tipova ......................................27
1.2.5 Osnovno o printf i scanf ...............................................................28
1.2.6 Struktura programa..............................................................................31
1.2.7 Naredbe u C-u .....................................................................................32
1.2.7.1 Naredba pridruživanja ....................................................................32
1.2.7.2 Blok naredbi ...................................................................................33
1.2.7.3 Naredbe uslovnog izvršavanja .......................................................33
1.2.7.4 Ciklusi (petlje)................................................................................35
1.2.7.5 Naredbe continue i break..........................................................36

III
1.2.7.6 Naredba goto ............................................................................... 37
1.2.8 Pokazivači .......................................................................................... 38
1.2.9 Nizovi ................................................................................................. 40
1.2.9.1 Jednodimenzioni nizovi................................................................. 40
1.2.9.2 Višedimenzioni nizovi................................................................... 42
1.2.9.3 Inicijalizacija nizova ..................................................................... 44
1.2.9.4 Kombinovane deklaracije nizova i pokazivača ............................. 45
1.2.10 Rad sa stringovima ............................................................................. 46
1.2.10.1 Neke korisne funkcije za rad sa stringovima............................... 48
1.2.10.2 Rad sa većim brojem stringova ................................................... 49
1.2.11 Funkcije .............................................................................................. 51
1.2.11.1 Prototipovi funkcija ..................................................................... 55
1.2.11.2 Funkcije i nizovi .......................................................................... 55
1.2.11.3 Funkcije i matrice ........................................................................ 57
1.2.11.4 Kombinovane deklaracije nizova, pokazivača i funkcija. ........... 59
1.2.11.5 Pokazivači na funkcije ................................................................ 59
1.2.11.6 Funkcija main .............................................................................. 61
1.2.11.7 Rekurzivne funkcije .................................................................... 62
1.2.12 Pretraživanje i sortiranje nizova ......................................................... 64
1.2.12.1 Algoritmi za pretraživanje nizova ............................................... 64
1.2.12.2 Algoritmi za sortiranje nizova ..................................................... 66
1.2.13 Složenost algoritama .......................................................................... 68
1.2.13.1 Analiza najgoreg i prosječnog slučaja ......................................... 70
1.2.13.2 Rast složenosti. Veliko O ............................................................ 70
1.2.13.3 Studija slučaja: Broj podnizova sa parnom sumom .................... 72
1.2.14 Ključne riječi typedef i const ............................................................. 75
1.2.15 Pretprocesor........................................................................................ 76
1.2.16 Važnije programske biblioteke i odgovarajuće funkcije .................... 78
1.2.16.1 stdio.h ..................................................................................... 78
1.2.16.2 string.h ................................................................................... 81
1.2.16.3 ctype.h ..................................................................................... 81
1.2.16.4 math.h........................................................................................ 81
1.2.16.5 limits.h ................................................................................... 82
1.2.16.6 stdlib.h ................................................................................... 82
1.2.17 Karakteristike promjenljivih .............................................................. 82
1.2.17.1 Lokalne i globalne promjenljive.................................................. 82
1.2.17.2 Dinamičke i statičke promjenljive ............................................... 83
1.2.17.3 Eksterne promjenljive ................................................................. 84
1.2.17.4 Ključne riječi register, auto i volatile ............................. 85
1.2.18 Dinamička alokacija i dealokacija ...................................................... 85
1.2.19 Enumeracija........................................................................................ 87
1.2.20 Strukture ............................................................................................. 87
1.2.20.1 Deklaracija strukturnih promjenljivih. Operator . ....................... 88

IV
1.2.20.2 Pokazivači na strukture. Operator ‐> ...........................................90
1.2.20.3 Strukture i funkcije.......................................................................92
1.2.21 Unije ....................................................................................................93
1.2.22 Polja bitova .........................................................................................94
1.2.23 Rad sa fajlovima..................................................................................95
1.2.23.1 Otvaranje fajla ..............................................................................95
1.2.23.2 Upis i čitanje iz fajla. Kretanje kroz fajl. Zatvaranje fajla ...........97
1.2.23.3 Brisanje i promjena imena fajla ...................................................99
1.2.23.4 Redirekcija ...................................................................................99
1.2.24 Još malo o standardima C99 i C11 ....................................................100
1.2.24.1 Tipovi podataka bool i complex .............................................101
1.2.24.2 Cjelobrojni tipovi fiksne veličine ...............................................102
1.2.24.3 inline funkcije ........................................................................102
1.2.24.4 Složeni literali ............................................................................103
1.2.24.5 Funkcija zna svoje ime i još ponešto..........................................104
1.2.24.6 Standard C11 i njegove novine ..................................................105
1.3 Linkovani tipovi podataka .......................................................................... 107
1.3.1 Liste...................................................................................................107
1.3.1.1 Kreiranje jednostruko povezane liste ...........................................108
1.3.1.2 Umetanje čvora u unutrašnjost liste .............................................109
1.3.1.3 Brisanje repa liste .........................................................................110
1.3.1.4 Brisanje čvora iz unutrašnjosti liste .............................................110
1.3.1.5 Dvostruko povezane liste .............................................................111
1.3.1.6 Umetanje čvora u unutrašnjost dvostruko povezane liste ............111
1.3.1.7 Brisanje čvora iz unutrašnjosti dvostruko povezane liste ............112
1.3.1.8 Specijalni tipovi listi ....................................................................112
1.3.1.9 Primjer programa za rad sa listom ...............................................113
1.3.2 Grafovi ..............................................................................................117
1.3.2.1 Predstavljanje grafova ..................................................................118
1.3.2.2 Problem najkraćeg puta ................................................................119
1.3.3 Stabla .................................................................................................121
2. ZBIRKA ZADATAKA .................................................................................... 127
2.1 Kontrola toka programa .............................................................................. 128
2.2 Nizovi .......................................................................................................... 141
2.3 Matrice ........................................................................................................ 146
2.4 Stringovi ...................................................................................................... 158
2.5 Funkcije....................................................................................................... 172
2.6 Rekurzivne funkcije .................................................................................... 191
2.7 Sortiranje ..................................................................................................... 197
2.8 Dinamička alokacija i dealokacija .............................................................. 207
2.9 Fajlovi ......................................................................................................... 214
V
2.10 Strukture i liste ............................................................................................231
2.11 Grafovi ........................................................................................................257
2.12 Binarno stablo .............................................................................................265
2.13 Igre ..............................................................................................................275
Literatura ....................................................................................................................299

VI
Predgovor drugom izdanju

Pred vama se nalazi drugo izdanje udžbenika iz programskog jezika C sa pratećom


zbirkom zadataka. Udžbenik je nastao kao rezultat dugogodišnjeg rada autora u okviru
predmeta Programiranje I, koji se izučava na drugoj godini Elektrotehničkog fakulteta
Univerziteta Crne Gore. Ovaj udžbenik mogu da koriste i studenti drugih fakulteta koji
se srijeću sa programerskom problematikom, kao i programeri zainteresovani za
savladavanje najvažnijih pojmova strukturnog programiranja i programskog jezika C.
Dobar dio udžbenika se može koristiti i u nastavi u gimnazijama i srednjim stručnim
školama koje u obrazovnim programima obrađuju ovaj programski jezik.
Struktura udžbenika je nepromijenjena u odnosu na prvo izdanje, tj. udžbenik
počinje kratkim istorijatom programskih jezika i pregledom programerske
terminologije. Nakon toga je dat teorijski dio koji detaljno opisuje programski jezik C.
Najveći dio materijala pripada pratećoj zbirci zadataka, čijih 13 poglavlja prate
redosljed izlaganja teorije. U odnosu na prvo izdanje, teorijski dio je dopunjen dodatnim
objašnjenjima i primjerima, poglavljem sa složenošću algoritama, kao i novinama koje
donose C99 i C11, standardi programskog jezika C. Zbirka zadataka je revidirana i
dodat je jedan broj novih primjera kojima se ilustruje rješavanje praktičnih
programerskih problema. Kritike i sugestije čitalaca na prvo izdanje su uzete u obzir u
ovom izdanju udžbenika. Ovom prilikom im se zahvaljujemo.
Kako čitati udžbenik? Preporučujemo kombinovani prolazak kroz teoriju i
odgovarajuća poglavlja iz zbirke. Kako se materijal izlaže linearno, treba pratiti
redosljed izlaganja u teorijskom dijelu. Čitaoci koji imaju predznanje iz programskog
jezika C mogu da preskoče poznate im dijelove materijala, ali se ne preporučuje
preskakanje čitavih poglavlja. Teorijski dio obiluje primjerima, dijelovima
programskog kôda i čitavim programima. Ove primjere je poželjno čitati i tumačiti uz
teoriju, dok se preporučuje samostalna izrada zadataka iz zbirke. Čitanje gotovih
rješenja iz zbirke se preporučuje samo u slučaju da napori da se samostalno riješe
problemi nijesu urodili plodom. Na osnovu svog iskustva, autori zaključuju da puno
bolji učinak ima samostalno rješavanje jednog jednostavnog problema od desetine
pročitanih rješenja kompleksnijih problema. Neizbježan faktor učenja programiranja je
ispravljanje sopstvenih grešaka. Autori zadržavaju pravo da svoje studente više puta
podsjete na ove zaključke.
Značajan trud je uložen da bi se eliminisale greške, ali je zasigurno jedan broj ostao
i u ovom izdanju. Stoga su sva zapažanja i sugestije dobrodošli i mogu se uputiti
autorima lično ili elektronskom poštom na adrese slobdj@ac.me, igordj@ac.me i
pvesna@ac.me.

I
Čitaocima želimo uspješno savladavanje programskog jezika C i nadamo se da će
im ovaj udžbenik biti od koristi. Konačno, ne zaboravite tajnu učenja programiranja –
programiranje se najbolje uči programirajući!

Podgorica, septembar 2018.


AUTORI

II
12 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

1.2 Programski jezik C

1.2.1 Jedan jednostavan program: od teksta do izvršenja

Uradimo, za početak, jedan jednostavan program. Tekst ovog programa, koga još
nazivamo i izvorni kôd (eng. source code) je dat u nastavku.
#include<stdio.h>

int main()
{
int a,b;
a=11;
b=7;
printf("Zbir brojeva %d i %d je %d",a,b,a+b);
return 0;
}
Tekst programa se unosi u editoru teksta, što može biti Notepad, Wordpad ili bilo
koje drugo okruženje za programski razvoj koje uključuje editor teksta. Fajlovi koji
sadrže C izvorni kôd imaju ekstenziju .c.
Nakon unošenja programskog teksta, pristupa se njegovom pretprocesiranju, koje
podrazumijeva niz operacija koje se izvršavaju nad samim izvornim kôdom, kao što su
zamjene dijelova teksta drugim tekstom, uklanjanje bjelina itd.
Nakon pretprocesiranja, slijedi prevođenje programa iz izvornog u mašinski kôd,
tj. kompajliranje. Dobijeni mašinski kôd je sa relativnim adresama programskih
instrukcija i naziva se objektni kôd.
U sljedećem koraku se vrši povezivanje (linkovanje) dobijenog objektnog kôda sa
mašinskim kôdom funkcija koje koristimo u našem programu, a koje se nalaze u raznim
bibliotekama. Rezultat povezivanja je jedna programska cjelina koja sadrži sve
procesorske instrukcije potrebne za pravilno izvršenje programa.
Na osnovu integralnog objektnog kôda programa, kreira se izvršni program u
apsolutnim adresama i smješta se u RAM memoriju. Ovaj dio procedure obavlja loader.
Konačno, program se izvršava tako što procesor uzima instrukciju po instrukciju
izvršnog kôda iz RAM memorije i izvršava ih.
Prethodno opisani koraci kreiranja i izvršenja jednog C programa su prikazani na
slici 1. Posmatrajući sve ove korake, može se steći utisak da je čitav proces vrlo
komplikovan. Međutim, zahvaljujući postojanju integrisanih razvojnih okruženja (eng.
Integrated Development Environment - IDE), unos, editovanje i prevođenje programa
1.2 P ROGRAMSKI JEZIK C 13

je značajno olakšano. Naime, integrisano razvojno okruženje je softverski alat koji


omogućava potpuni razvoj programa (otuda riječ integrisan) u smislu:
 unosa i modifikacije izvornog kôda (često olakšanog auto-complete opcijom),
 prevođenja u mašinski kôd (dakle, ima uključen kompajler),
 testiranja programa,
 izvršenja programa i
 otklanjanja grešaka (eng. error debugging).
Od integrisanih razvojnih okruženja za programski jezik C, pomenućemo
najpopularnije Eclipse, NetBeans, CodeBlocks, C++ Builder (nekadašnji Borland
C++Builder), Dev C++, CodeLite.

Slika 1. Koraci kreiranja i izvršenja jednog C programa.

Vratimo se našem programu. Nakon što prevedemo i pokrenemo program, na


komandnoj liniji operativnog sistema (ili konzoli), biće ispisan sljedeći tekst:
Zbir brojeva 11 i 7 je 18
Time je ovaj program uspješno izvršen.
Napravimo kratak pregled bitnih elemenata jednog C programa. Krenimo od prve
linije, tj. #include<stdio.h>. U pitanju je instrukcija kojom se u naš kôd uključuju
14 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

potrebne programske biblioteke. Konkretno, u ovom slučaju, uključiće se biblioteka


stdio, koja sadrži korisne funkcije za rad sa ulazno-izlaznim uređajima računara
(tastatura je standardni ulazni, a monitor standardni izlazni uređaj). Preciznije, uključiće
se samo funkcije koje koristimo u našem programu. Ovo uključivanje se vrši prije
samog prevođenja programa i dio je pretprocesora.
Slijedi int main(){...}. U pitanju je glavna funkcija u programu, od koje kreće
izvršenje programa. Naime, jedan C program se sastoji od funkcija i promjenljivih.
Funkcije sadrže instrukcije koje određuju koje će operacije biti izvršene, a promjenljive
služe da pamte podatke nad kojima se vrše te operacije. U C programu se može naći
proizvoljan broj funkcija, ali je funkcija main obavezan dio programa i početna tačka
izvršenja programa. Riječ int ispred imena main označava da je tip podatka koji
funkcija main vraća cjelobrojan.
Linija int a,b; predstavlja deklaraciju (navođenje) promjenljivih koje koristimo
u programu. Sve promjenljive koje koristimo moramo deklarisati prije prvog korišćenja.
Deklaracija podrazumijeva navođe imena i tipa promjenljive. Ovdje smo naveli dvije
cjelobrojne promjenljive, a i b. O dozvoljenim imenima i tipovima više riječi kasnije.
Linije a=11; i b=7; predstavljaju izvršne instrukcije, konkretno operacije dodjele.
Programski jezik C je bogat operacijama koje se mogu izvršavati nad podacima.
Naredba printf(...); predstavlja poziv funkcije printf, koja služi za prikaz
(ili štampu) podataka na standardnom uređaju za izlaz (monitor). Za korišćenje ove
funkcije, potrebno je uključiti biblioteku stdio.h, što je urađeno na početku programa.
Konačno, naredba return 0; predstavlja bezuslovni prekid izvršenja funkcije
main uz vraćanje cjelobrojne vrijednosti 0. Već smo rekli da int ispred main()
označava da funkcija main vraća cjelobrojnu vrijednost. Ova vrijednost se vraća
operativnom sistemu i ima značenje izlaznog statusa. Nula se tumači kao uspješan
završetak programa, a svaka druga vrijednost signalizira završetak uslijed greške.
Uočite da se sve programske linije završavaju tačkom-zarez. To je obaveza u
programskom jeziku C, sa par izuzetaka kad se treba izostaviti. Tačka-zarez završava
naredbu i omogućava da se više naredbi grupiše u istom redu. Tako naredbe:
a=11;
b=7;
možemo grupisati u
a=11; b=7;
Ovim završavamo kratak pregled osnovnih elemenata C programa. Nastavljamo sa
detaljnim upoznavanjem ovog programskog jezika.
20 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

MATLAB podržava, moraju da deklarišu promjenljive u smislu njihovog pripadanja


određenim tipovima podataka. Takođe, većina programskih jezika mora da ima
informaciju o veličini nizova i matrica sa kojima radi. Razlog je u načinu komuniciranja
sa memorijom računara kojoj se mora naglasiti prostor za smještaj promjenljivih.
Programski jezik C posjeduje 10 tipova podataka od kojih se tri nazivaju osnovnim,
dok se ostalih sedam naziva izvedenim tipovima. Osnovni tipovi podataka su: cijeli broj
(int), realan broj (float ili double) i karakter (char). Izvedeni tipovi podataka su:
 neodređeni tip (void),
 pokazivač (eng. pointer),
 nabrajanje (enumeracija),
 niz (niz može biti bilo kog tipa i iz niza su izvedene matrice, tj. višedimenzioni
nizovi, kao i stringovi - nizovi karaktera),
 struktura (struct), iz koje se mogu izvoditi polja bitova, liste i drugi složeni tipovi
podataka),
 unija (union) i
 fajl (FILE).
U naredne tri sekcije ćemo proći kroz osnovne tipove podataka, dok ćemo kasnije
obraditi i ostale.

1.2.4.1 Tip int


Već smo napomenuli osnovne činjenice oko tipa podataka int. Svaka promjenljiva
tipa int (kao i bilo kojeg drugog tipa) mora biti deklarisana (najavljena za korišćenje).
Razlog za ovo je potreba računara da zauzme memoriju za promjenljive, pri čemu
veličine memorije zavisi od tipa promjenljive. Pored standardnog tipa int, programski
jezik C dozvoljava definiciju "kratkog" int-a (short int ili samo short) i "dugog"
int-a (long int). Odrednice short i long se nazivaju modifikatori i definišu zauzetu
memoriju u bajtovima, odnosno minimalni i maksimalni cijeli broj koji može biti
predstavljen datom promjenljivom. Tip short int zahtijeva 2 bajta memorije i ima
opseg vrijednosti od -215 do 215-1, dok long int zahtijeva 4 bajta memorije i ima
opseg vrijednosti od -231 do 231-1. Veličina tipa int zavisi od platforme (arhitekture
računara i kompajlera) na kojoj se implementira program, i može biti 2 ili 4 bajta. Ovo
je jedna od problematičnih računskih zavisnosti koja se mora otkloniti dobrim
programerskim stilom. Ako se želi naglasiti da je neka konstanta ili broj koji se
pridružuje promjenljivoj "duga", to se radi dodavanjem sufiksa L, na primjer 60L.
Pored short i long, mogu se definisati još dva modifikatora, signed za
"označene" i unsigned za "neoznačene" promjenljive. Modifikator signed se može
izostaviti kao podrazumjevan (eng. default). Odrednica unsigned ispred int, short
int ili long int promjenljive znači da ta promjenljiva ne može uzeti negativne
vrijednosti, već da može biti veća od nule ili jednaka nuli. Tada se mijenja i maksimalni
i minimalni broj koji ova promjenljiva može sadržati. Znate li kako?
1.2 P ROGRAMSKI JEZIK C 21

Promjenljiva tipa int se u programu deklariše (najavljuje za korišćenje) kao:


int i;
Ova najava se obično vrši na početku programa ili bloka naredbi prije bilo koje druge
naredbe, što je bila i obaveza do standarda C99. Od standarda C99 nadalje, deklaracija
se može vršiti bilo gdje u programskom kôdu, ali prije prvog korišćenja promjenljive.
Kada se kreira ovakva promjenljiva, ona zauzima nezauzete memorijske lokacije (one
memorijske lokacije koje nijesu zauzete sistemskim promjenljivima, drugim
promjenljivima i kodom programa). U tom trenutku na zauzetoj lokaciji mogu se
nalaziti bilo kakvi podaci. U cilju spriječavanja da naša promjenljiva uzima i u jednom
trenutku besmislene vrijednosti, ponekad se direktno sa njenom deklaracijom vrši i
inicijalizacija (zadavanje početne vrijednosti). Na primjer:
int i=0;
istovremeno sa kreiranjem dodijeljuje vrijednost promjenljivoj. long int promjenljive
se mogu deklarisati na sljedeće načine:
long int b;
long b;
U drugom obliku, int nismo naglašavali. Isto važi za short i unsigned. Važno je
znati da unutar istog dijela programa, tj. bloka naredbi, ne mogu biti deklarisane
promjenljive sa istim imenom, jer bi to "zbunjivalo" kompajler. Postoji mogućnost
definisanja promjenljivih sa istim imenom u različitim programskim funkcijama, kao i
u različitim blokovima iste funkcije. O ovome će više riječu u poglavlju 1.2.17.
Ako želimo da deklarišemo više int promjenljivih, to možemo uraditi sa jednom
ili više naredbi. Naime, dozvoljena je naredba:
int i=7,b=0,c;
kojom se inicijalizuju cjelobrojne promjenljive i na 7 i b na 0 i kreira, ali ne i
inicijalizuje, promjenljiva c istog tipa. Vrijednost neke promjenljive se može
inicijalizovati i pomoću neke druge promjenljive koja je već definisana, na primjer:
int i;
int j=i+2;
ili čak:
int i,j=i+2;
Nije dozvoljena inicijalizacija tipa:
int j=i;
int i;
jer u trenutku deklarisanja promjenljive j ne postoji memorijski objekat koji predstavlja
promjenljivu i.
22 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Sa standardom C99, uvodi se tip cjelobrojne promjenljive long long int, koji
zauzima 8 bajtova i čiji je opseg vrijednosti od od -263 do 263-1. Pri deklaraciji long
long int promjenljive se može izostaviti riječ int. Takođe, i kod ovog tipa ključna
riječ unsigned mijenja opseg vrijednosti, gdje nisu dozvoljene negativne vrijednosti.
U tabeli 4 su dati cjelobrojni tipovi podataka, broj bajtova koji zauzimaju i odgovarajući
opsezi vrijednosti.
Pored dekadnih brojeva, postoji mogućnost zapisivanja cjelobrojnih oktalnih
brojeva, koji uvijek počinju sa nulom (npr. 074) i heksadecimalnih brojeva, koji počinju
sa 0x (npr. 0x3c).
Na kraju, recimo par riječi o binarnoj reprezentaciji int tipova. Za memorijsko
predstavljanje označenih int tipova, koristi se zapis cijelih brojeva sa dvojnim
komplementom. Tako, binarne kombinacije short int tipa, koji zauzima 2 bajta, su
date u nastavku (baza brojnog sistema je data u donjem indeksu broja):
0000 0000 0000 00002 = 010
0000 0000 0000 00012 = 110
0000 0000 0000 00102 = 210

0111 1111 1111 11112 = 3276710
1000 0000 0000 00002 = -3276810
1000 0000 0000 00012 = -3276710

1111 1111 1111 11112 = -110
Kod neoznačenih int tipova, najmanja vrijednost je 0, a najveća 2M-1, gdje je M
broj bita koji zauzima predmetni tip. U ovom slučaju, binarne kombinacije unsigned
short int tipa su:
0000 0000 0000 00002 = 010
0000 0000 0000 00012 = 110
0000 0000 0000 00102 = 210

1111 1111 1111 11102 = 6553410
1111 1111 1111 11112 = 6553510
Kod ostalih int tipova se samo mijenja broj bita, a princip predstavljanja je isti.
Na osnovu binarne reprezentacije, protumačite šta će se dogoditi ako povećate za 1
vrijednost int promjenljive koja sadrži najveći cijeli broj koji se može upisati u nju.

1.2.4.2 Tipovi float i double


U programskom jeziku C, float i double podrazumijevaju brojeve sa pokretnim
zarezom (eng. floating point numbers). Ove brojeve ćemo, zbog mogućnosti da sadrže
i decimalni dio broja, nazivati realnim brojevima, i zapisivaćemo ih kao 0.234 ili ‐
4.23e4, gdje e4 podrazumjeva 104. Tip float zauzima 4 bajta (jednostruka
28 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

promjenljivoj na lijevoj strani izraza. Ovakav metod konverzije se naziva implicitnom


(podrazumijevanom) konverzijom. Vrši se kada nema poklapanja tipova između izraza
na lijevoj i na desnoj strani. Takođe se obavlja prilikom prosljeđivanja argumenta
funkciji koja očekuje argument nekog drugog tipa. Ovo može da predstavlja problem i
da dovede do neočekivanih rezultata. Treba predvidjeti ovakve situacije i izvršiti
eksplicitnu konverziju podataka. Eksplicitna konverzija iz jednog u drugi tip podataka
se obavlja cast operatorom. Ovaj operator se primjenjuje na način (tip)izraz, gdje
tip može biti bilo koji od tipova podataka. Na primjer, (int)1.6 odbacuje decimalni
dio i vraća vrijednost 1.
Prilikom implicitne konverzije podataka, tip podatka koji zauzima manji
memorijski prostor se pretvara u onaj koji zauzima veći. Na primjer, prilikom operacija
u kojima učestvuju cijeli i realni brojevi dolazi do konverzije promjenljive tipa int u
tip float. Kao primjer prethodno rečenog, razmatrajmo sljedeće naredbe:
int a=1;
float b,c=4.2;
b=a+c;
Prije operacije sabiranja dolazi do konverzije cjelobrojne vrijednosti a=1 u realan broj
1.0. Naravno, to ne znači da promjenljiva a mijenja svoj tip, već da se samo vrijednost
koju ta promjenljiva ima i koja se koristi u izrazima konvertuje u viši tip.

1.2.5 Osnovno o printf i scanf

Funkcije printf i scanf služe za prikazivanje proizvoljnog teksta i rezultata na


ekranu i učitavanje podataka sa tastature, respektivno. Ove funkcije nijesu dio bazične
specifikacije programskog jezika C, već su dio programske biblioteke stdio.h
(STanDard Input/Output). Da bi mogli da koristite ove dvije funkcije, na početku
programa je potrebno navesti pretprocesorsku direktivu #include<stdio.h>. U
suprotnom, ove funkcije će u daljem tekstu programa ostati nepoznate programskom
prevodiocu i dovešće do greške prilikom prevođenja. Skupina naredbi koja počinje sa #
nazivaju se pretprocesor (ili pretprocesorske naredbe), a zbog korišćenja znaka #
ponekad se nazivaju tarabe. Tarabe se navode na početku programa. Detaljno ćemo
raditi tarabe i važnije programske biblioteke kasnije.
Funkcija printf služi za štampanje podataka. Njen opšti oblik je:
printf("string", promjenljive_konstante_izrazi_poziviFunkcija);
Funkcija štampa string na ekranu, dok se dijelovi stringa navedeni sa znakom % ispred
mijenjaju sa konstantama, odnosno vrijednostima promjenljivih, izraza ili funkcija
navedenih u drugom dijelu funkcije. Na primjer, funkcija
printf("%d %d %f tekst %c\n",in,60,fl,ch);
štampa cijeli broj in (oznaka %d znači da je broj koji se štampa cio broj), pa broj 60,
zatim broj u pokretnom zarezu (oznaka %f označava brojeve u pokretnom zarezu), zatim
1.2 P ROGRAMSKI JEZIK C 29

se štampa string "tekst" i karakter ch (%c označava da će na tom mjestu biti


odštampan karakter), i na kraju \n označava prelazak u novi red (funkcija printf
nakon izvršavanja ne prelazi automatski u novi red). Parovi karaktera %d, %f i %c se
nazivaju specifikatori formata (eng. format specifiers). Koliko imamo specifikatora
formata unutar stringa toliko treba biti izraza (konstanti, promjenljivih, izraza, poziva
funkcija) razdvojenih zarezima nakon stringa. Neće doći do greške ukoliko to nije
zadovoljeno, ali nema smisla koristiti funkciju printf na taj način.
U tabeli 5 je dat detaljan pregled specifikatora formata u programskom jeziku C.

Specifikator Tip
Cio broj. %d i %i su sinonimi pri štampi, ali se razlikuju prilikom
učitavanja funkcijom scanf (%i će interpretirati broj kao oktalni ako
%d, %i
počinje sa 0, odnosno kao heksadecimalni ako počinje sa 0x, dok %d to
neće uraditi).
%ld, %li long int
%lld, %lli long long int
Neoznačen cijeli broj (unsigned int). Može se kombinovati sa dugim
%u
formatima, npr. %lu, %llu.
%o Neoznačen cijeli broj kao oktalni broj.
Neoznačen cijeli broj kao heksadecimalni broj. %x koristi mala slova, dok
%x, %X
%X koristi velika.
float ili double u standardnom zapisu ([‐]ddd.ddd).
%f, %F Podrazumijevani broj decimalnih mjesta je 6. Formati %f i %F imaju vrlo
male razlike i to u načinu štampe specijalnih vrijednosti beskonačno i NaN.
double vrijednosti. Kod printf se ne mora navoditi l, tj. dovoljno je
%lf, %lF %f, prilikom štampanja double vrijednosti. Kod scanf se mora koristiti
%lf prilikom učitavanja double vrijednosti.
float ili double u eksponencijalnoj formi ([‐]d.ddd e[+/‐]ddd).
%e, %E Razlike %e i %E su samo u veličini slova e prilikom štampe, tj. %e
prikazuje e, dok %E prikazuje E.
float ili double u standardnoj ili eksponencijalnoj formi, zavisno koja
je pogodnija. %g koristi mala slova, dok %G koristi velika. Ovaj tip se
%g, %G razlikuje od %f i %F po tome što završne nule u decimalnom dijelu broja
nisu uključene, i po tome da se decimalna tačka ne prikazuje kod cijelih
brojeva.
%s String
%c Karakter
%p Pokazivač, u formatu zavisnom od implementacije.
Tabela 5. Specifikatori formata u programskom jeziku C.

Pored navedenih, dozvoljeni specifikator formata za cijele brojeve je %bd, gdje je


b cijeli broj, koji podrazumjeva prikazivanje cijelog broja sa b mjesta. Ukoliko je b veće
od broja cifara broja koji se štampa, razlika će se dopuniti spejsovima sa lijeve strane,
tj. broj će biti centriran udesno u okviru b polja. U suprotnom, broj će biti prikazan bez
30 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

dodatnih spejsova, čak i kada je b manje od broja cifara broja. Lijevo poravnanje broja
u okviru b polja se može dobiti na sljedeći način %‐bd. Na primjer, naredba
printf("%10d\n%‐10d\n%2d",12345,12345,12345);
će odštampati sljedeći tekst:
12345
12345
12345
U slučaju realnih brojeva, format %bf, gdje je b cijeli broj, podrazumjeva prikaz
decimalnog broja sa ukupno b mjesta (uključujući decimalnu tačku i predznak). Ukoliko
je b manje od ukupnog broja polja potrebnih za zapis broja, broj će se podrazumijevano
prikazati, tj. na način %f, uz podrazumijevanih šest decimalnih mjesta. Format %a.bf,
gdje su a i b cijeli brojevi, podrazumjeva prikaz decimalnog broja sa ukupno a mjesta
(uključujući decimalnu tačku i predznak) i b decimala. Po prirodi stvari, očekivano je
da je a veće od b. Ovdje takođe možemo mijenjati poravnanje broja u okviru a polja na
način %‐a.bf. Na primjer, naredbe:
double a=123.4567;
printf("%9f\n%‐13.4f\n%12.2f\n%.8f",‐a,a,a,a);
će odštampati sljedeći tekst:
‐123.456700
123.4567
123.46
123.45670000
Unutar drugog dijela naredbe mogu se nalaziti i matematički izrazi. Na primjer,
printf("%d\n", j++);
će prikazati vrijednost j i zatim je uvećati za 1. Takođe, mogu biti i pozivi funkcija koje
vraćaju vrijednost. Dobra praksa, ipak, preporučuje izbjegavanje ovih opcija jer mogu
dovesti do nečitkosti programa. Postoji još jedna opasnost kod ovakvog korišćenja
naredbe printf, a to je redosljed predaje argumenata, koji je sa desna na lijevo. To
znači da će naredbe:
j=1;
printf("%d %d\n",j,j++);
prikazati na ekranu broj 2, pa 1 i preći u novi red. Naime, prvi predati argumenat je
desni, koji nakon predaje biva uvećan za jedan, pa tek lijevi, koji je tada 2.
Funkcija scanf služi za unos, tj. učitavanje podataka. Njen oblik je sličan obliku
funkcije printf, s tim što se umjesto vrijednosti argumenata koje se štampaju navode
adrese promjenljivih u koje upisujemo učitane podatke. Oblik je sljedeći:
scanf("specifikatori_formata", adrese_promjenljivih);
pri čemu važe specifikatori formata dati u tabeli 5. Naredba
1.2 P ROGRAMSKI JEZIK C 35

Radi ubrzavanja izvršavanja switch naredbe, poželjno je u vrhu bloka staviti


slučajeve sa najvećom vjerovatnoćom pojave, a pri dnu one sa manjom vjerovatnoćom.
Slično važi za if...else if...else naredbu.

1.2.7.4 Ciklusi (petlje)


Veoma često postoji potreba da se dio programa izvršava više puta. Ovaj dio
programa se tada realizuje u okviru ciklusa (petlje). Programski jezik C raspolaže sa
većim brojem naredbi kojima se može realizovati ciklus.
Prva ovakva naredba je while. Njena sintaksa je:
while(uslov)
naredba ili blok naredbi;
Dok je uslov logički tačan izvršava se naredba ili blok naredbi. Sve naredbe
koje se izvršavaju u okviru while, ili neke druge petlje, čine tijelo petlje. Ukoliko
uslov nije logički tačan kad dođemo do while petlje, tijelo petlje se neće izvršiti
nijednom, već nastavljamo sa prvom naredbom nakon petlje (tj. preskačemo petlju).
Slično kao kod if naredbe, ako je samo jedna naredba dio while petlje, nepotrebno je
navoditi vitičaste zagrade. Takođe, nakon while uslova se ne stavlja tačka-zarez, čime
bi se naredba ili blok naredbi odvojili od petlje.
Slična while petlji je do...while petlja, čiji je oblik:
do
naredba ili blok naredbi;
while(izraz);
Razlika u odnosu na while je što se naredba ili blok naredbi izvrše
jednom, pa se tek onda provjerava logički uslov. Naime, kod while petlje se može
desiti da se tijelo petlje ne izvrši nijednom. Uočiti da se kod do...while ciklusa stavlja
tačka-zarez nakon while uslova.
Treća vrsta petlje je for petlja sa sintaksom
for(izraz1;izraz2;izraz3)
naredba ili blok naredbi;
for petlja se drugačije naziva i brojačka petlja, jer se obično koristi kada je poznat broj
izvršavanja tijela petlje, ili kako se to još naziva broj iteracija. U tom smislu, izrazi dati
u zaglavlju for petlje, tj. unutar zagrada, definišu početnu i krajnju vrijednost brojača
(promjenljive po kojoj se izvršava petlja) i zakon njegove promjene. Preciznije, izraz1
služi za postavljanje početne vrijednosti brojača petlje. Petlja se izvršava dok je izraz2
logički tačan, a izraz3 definiše način na koji se brojač mijenja, tako da jednog trenutka
izraz2 ne bude logički tačan. Na primjer, petlja
for(i=1;i<=5;i++)
printf("%d\t%d\t%d\n",i,i*i,i*i*i);
36 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

će odštampati sljedeći tekst:


1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
Petlja se izvršila tačno 5 puta, pri čemu brojač i uzima vrijednosti 1, 2, 3, 4 i 5. Vrlo
bitno je uočiti da se iz petlje izlazi sa vrijednošću brojača za koju izraz2 nije logički
tačan. U ovom slučaju, za vrijednost i=6 se izlazi iz petlje, i tu vrijednost će
promjenljiva i imati nakon petlje. Česta je greška pretpostaviti da, po izlasku iz petlje,
brojač ima posljednju vrijednost za koju se tijelo petlje izvršavalo. Uočite u prethodnom
primjeru korišćenje tabulatora \t u okviru printf funkcije, koji, na jednostavan način,
omogućava prikaz teksta u više kolona.
Kod for petlje se može, po potrebi, izostaviti neki od izraza. Ako se izostavi
izraz2 tada se ne provjerava logički uslov, odnosno petlja postaje beskonačna. Slično
je i kod while petlje, ako se unutar zagrada upiše broj različit od nule, npr. while(1).
Iako su na prvi pogled beskonačne petlje besmislene, one imaju primjenu u različitim
programerskim tehnikama.
U zaglavlju for petlje može se navesti više naredbi razdvojenih zarezima (koma
operatorima) unutar jednog izraza, na primjer:
for(i=0,j=0;i<10;i++,j++)
U prethodnoj naredbi smo vidjeli upotrebu koma operatora. Nijesu svi zarezi koji se
upotrebljavaju u programskom jeziku C koma operatori. Naime, zarezi se koriste i za
razdvajanje sintaksnih elemenata kao što su promjenljive prilikom deklarisanja.
Zapamtite da se ciklične naredbe mogu ugnježdivati, odnosno da se jedna može
nalaziti unutar druge, ali da se ne mogu presjecati, tj. ne može se spoljašnja petlja
završiti prije unutrašnje!

1.2.7.5 Naredbe continue i break


Naredba continue prekida tekuću iteraciju petlje i uzrokuje prelazak na sljedeću
iteraciju, tj. na izvršavanje tijela petlje za narednu vrijednost brojača. Dakle, naredbe
tijela petlje iza continue se preskaču. Kod while petlje se nakon naredbe continue
prelazi na ispitivanje uslova petlje.
Naredba break prekida izvršavanje petlje, a izvršavanje programa se nastavlja sa
prvom naredbom nakon petlje. Ukoliko se break nalazi unutar tijela ugniježdenih
petlji, tada prekida izvršavanje samo one petlje u kojoj se nalazi, a ne svih petlji. Sa
naredbom break smo se već sreli kod naredbe switch. Naredba break se ne smije
naći van tijela petlje ili naredbe switch, tj. takva situacija uzrokuje sintaksnu grešku.
Razliku između continue i break ilustrujmo na sljedećem primjeru. Petlja
1.2 P ROGRAMSKI JEZIK C 37

for(i=1;i<7;i++)
{
if(i==4)
continue;
printf("%d ",i);
}
će štampati 1 2 3 5 6, dok će petlja
for(i=1;i<7;i++)
{
if(i==4)
break;
printf("%d ",i);
}
štampati 1 2 3.

1.2.7.6 Naredba goto


Naredba goto je naredba bezuslovnog skoka. Njena sintaksa je:
goto labela;
...
labela: naredba;
labela predstavlja oznaku (identifikator) naredbe na koju se skače sa goto. Sve
naredbe između goto i labela se preskaču. Dobra programerska praksa nalaže
izbjegavanje korišćenja ove naredbe. Razlozi su u teškom prepravljanju programa koji
sadrže ovu naredbu. Programi napisani sa naredbom goto mogu biti veoma nepregledni
i nejasni. Korišćenje naredbe goto za ulazak u ciklus bi proizvelo grešku. Ipak, u nekim
situacijama, ova naredba može pružiti elegantno rješenje za određeni zadatak. Na
primjer, sa goto se može jednostavno iskočiti iz većeg broja ugniježdenih petlji. Sve u
svemu, naredbe goto i continue treba izbjegavati jer narušavaju strukturnu
organizaciju programa.
Programski jezici tipa Fortran i BASIC nemaju ovako bogat spisak naredbi za
kontrolu toka programa. Naime, u doba kreiranja ovih jezika još nije postojala
standardizacija ovih naredbi. Stoga se u ovim programskim jezicima naredba goto
koristi veoma često. Svi noviji jezici imaju sličan spisak naredbi za kontrolu toka
programa i veoma sličnu fleksibilnost. Napomenimo da naredba switch nije postojala
u nekim programskim jezicima, pa je fleksibilnost pokazana u jeziku C dovela do njene
šire primjene i uvođenja u novije verzije drugih jezika.
38 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

1.2.8 Pokazivači

Pokazivač (eng. pointer) je promjenljiva koja sadrži memorijsku adresu nekog


podatka. Pretpostavimo da smo kreirali cjelobrojnu promjenljivu x. Adresa memorijske
lokacije na kojoj se nalazi promjenljiva x se može sačuvati u nekoj pokazivačkoj
promjenljivoj y. Naravno, i ta pokazivačka promjenljiva ima svoju adresu u memoriji.
Deklaracija pokazivačke promjenljive (u našem slučaju y) se u programskom jeziku C
vrši korišćenjem operatora *, tj.:
int x;
int *y; /* Moguće je int x, *y; */
U trenutku kreiranja (deklarisanja), pokazivač y može da sadrži adresu bilo koje lokacije
u memoriji. Uočimo da je x cijeli broj, *y je takođe cijeli broj, dok je y pokazivač na
cijeli broj. Ponekad se kaže da je y tipa int *.
Upisivanje adrese promjenljive x u pokazivač y se vrši naredbom y=&x. Prisjetimo
se operatora referenciranja &, koji je unaran, i znači adresa od. Taj operator treba
razlikovati od odgovarajućeg za operacije nad bitovima, koji je binaran. Takođe, *y u
deklaraciji treba razlikovati od množenja.
Pristupanje sadržaju memorijske lokacije čija je adresa upisana u pokazivaču y vrši
se operatorom dereferenciranja (posrednog pristupa) *, tj. *y predstavlja sadržaj
memorijske lokacije sa adresom u y. Operacija dereferenciranja omogućava čitanje i
promjenu sadržaja date memorijske lokacije. Posmatrajmo sljedeću sekvencu naredbi:
int x=7,*y;
y=&x;
printf("%d %d\n",x,*y);
*y=12;
printf("%d %d",x,*y);
Ova sekvenca rezultuje u ispisu:
7 7
12 12
Nakon naredbe y=&x, pokazivač y ukazuje na memorijsku lokaciju u kojoj je smještena
cjelobrojna promjenljiva x, pa *y u funkciji printf daje sadržaj te lokacije, tj.
vrijednost promjenljive x. Naredbom *y=12 vršimo promjenu sadržaja date
memorijske lokacije, a samim tim i vrijednosti promjenljive x. Znači, potpuno isti efekat
na vrijednost promjenljive x imaju naredbe x=12 i *y=12, ukoliko y ima vrijednost &x.
Odavde potiče naziv operacije posredan pristup, tj. promjenljivoj x smo pristupili
posredno, preko pokazivača y, i promijenili joj vrijednost.
Za inicijalizaciju pokazivačkih promjenljivih postoje dva razumna načina. Prvi
način smo već vidjeli u prethodnom primjeru i to je inicijalizacija pokazivača na adresu
prethodno definisane promjenljive:
int x;
40 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

int x=67,*p1,*p2;
p1=&x;
p2=p1+4;
printf("%d",p2‐p1);
će se odštampati broj 4 na ekranu, što znači da se između p1 i p2 nalaze 4 memorijske
lokacije veličine jednog int podatka.
Pokazivače ne možemo međusobno sabirati, dijeliti i množiti!
Pokazivači su podržani u malom broju programskih jezika, npr. COBOL, BASIC,
Fortran, C, C++, D, dok noviji jezici uglavnom ne podržavaju rad sa pokazivačima.
Sličan konceptu pokazivača je koncept reference, koji se koristi u objektno-
orijentisanom programiranju. Fleksibilnost rada sa pokazivačima i često korišćene
operacije će biti razmatrane u narednom poglavlju.

1.2.9 Nizovi

Niz (eng. array) je uzastopna grupa memorijskih lokacija u kojima se nalaze


promjenljive istoga tipa. Najčešće korišćeni su jednodimenzioni nizovi, koje još
zovemo vektorima ili samo nizovima, a od višedimenzionih se najviše koriste
dvodimenzioni nizovi, tzv. matrice.

1.2.9.1 Jednodimenzioni nizovi


Deklaracija tipa:
tip a[5];
rezerviše 5 uzastopnih mjesta za podatke tipa tip u memoriji računara. Deklaracija
zahtjeva da se navede broj elemenata niza kako bi program mogao da izvrši alokaciju
memorije. Ovako deklarisan niz se još naziva i statički deklarisan niz. Slika 2 prikazuje
sekvencijalnu memorijsku reprezentaciju cjelobrojnog niza a od 5 elemenata:

Slika 2. Memorijska reprezentacija niza a od 5 elemenata.

Elementima niza a se pristupa korišćenjem operatora [] i rednog broja elementa


niza (još se zove indeks elementa), koji se kreće od 0 do 4, tj. a[0], a[1], a[2], a[3]
i a[4]. Uočite da indeksiranje niza počinje od 0 i da završava sa indeksom koji je za
jedan manji od broja elemenata u nizu, tj. dužine niza. Sa elementima niza u ovom
1.2 P ROGRAMSKI JEZIK C 41

zapisu se mogu izvršavati operacije kao i sa bilo kojim drugim promjenljivima istog
tipa, tj. operacije tipa:
a[1]+=3;
b=a[2]*a[3];
c=(a[0]>2)?3:1‐a[1];
Indeks niza ne mora biti cjelobrojna konstanta, već to može biti proizvoljan cjelobrojni
izraz koji rezultuje cijelim brojem iz intervala [0, N-1], gdje je N dužina niza. Na
primjer, sasvim je regularno koristiti zapis tipa:
a[b‐c]+=2;
gdje izraz b‐c daje cijeli broj iz pomenutog intervala.
Važno je znati da programski jezik C ne vrši provjeru vrijednosti indeksa što se tiče
pripadnosti datim granicama, već da se može pristupiti i elementu van tog opsega, npr.
a[N+2] ili čak a[‐4]. Pristupanje "elementu niza" van granica niza implicira i
mogućnost promjene vrijednosti date memorijske lokacije, u kojoj može biti smještena
neka druga promjenljiva iz našeg programa, kôd programa ili nešto treće. U nadležnosti
je programera da uzme u obzir ovu osobinu prilikom pisanja programa koji rade sa
nizovima, kako ne bi mijenjao sadržaj onih memorijskih lokacija koje ne želi da mijenja.
Adrese pojedinih elemenata niza se dobijaju primjenom operatora & na te elemente,
tj. &a[0], &a[1] itd. što se može koristiti prilikom učitavanja niza naredbom scanf.
U programskom jeziku C postoji alternativna konvencija za zapis adresa elemenata niza
a. Naime, samo ime niza a ima vrijednost adrese niza, tj. ime niza se može tumačiti kao
pokazivač na niz. Tačnije, a ima vrijednost adrese prvog bajta prvog elementa niza
(strelica na slici 2). Adresa drugog elementa niza se može dobiti sa a+1, trećeg sa a+2
itd. Znači, važi relacija a+i=&a[i]. Ovakav zapis je posljedica činjenice da se elementi
niza ređaju u memoriji jedan za drugim, kao i činjenice da jezik C podržava sabiranje
pokazivača sa cjelobrojnom konstantom. Kako ime niza a ima vrijednost tipa
pokazivača na int, a+1 predstavlja adresu sljedećeg podatka u nizu, koja je za 4 bajta
(pretpostavimo da tip int zauzima 4 bajta) veća od adrese a. Na primjer, ukoliko se
prvi element cijelobrojnog niza a nalazi na lokaciji 1000, onda će drugi element biti na
adresi a+1=1004, treći na adresi a+2=1008 itd. Kada se operator dereferenciranja *
primjeni na vrijednost a+i, dobija se vrijednost i-tog elementa niza, tj. važi relacija
*(a+i)=a[i], ili konkretno *a=a[0], *(a+1)=a[1], *(a+2)=a[2] itd. Uočimo da
smo koristili zagrade za *(a+i), jer bi bez zagrada *a+i zapravo značilo a[0]+i,
zbog većeg prioriteta operacije * u odnosu na +.
Uzimajući u obzir prethodno rečeno, učitavanje elemenata cjelobrojnog niza a,
dužine N, možemo izvršiti na sljedeće načine:
for(i=0;i<N;i++)
scanf("%d",&a[i]);
ili
1.2 P ROGRAMSKI JEZIK C 47

Slika 4. Memorijska reprezentacija stringa "Prvi cas".

Operator sizeof("Prvi cas") će vratiti vrijednost 9, jer je toliko bajtova


zauzeto ovim stringom. Sa druge strane, funkcija strlen("Prvi cas"), koja
određuje dužinu stringa, će vratiti broj 8, jer je to broj karaktera koji postoje unutar
stringa, ne računajući terminacioni karakter. Funkcija strlen je definisana u okviru
biblioteke string.h.
Stringovi se deklarišu kao niz karaktera, npr.
char c[20];
Jasno je da je c dimenziono (konstantni) pokazivač na char. Prilikom deklaracije
stringa, potrebno je uzeti u obzir i terminacioni karakter. Stringovi se mogu učitavati
pomoću funkcije scanf, na sljedeći način:
scanf("%s",c);
gdje se koristi %s kao specifikator formata. Ispred imena stringa ne navodimo operator
& zato što samo ime stringa ima vrijednost adrese stringa. Za razliku od numeričkih
nizova, čiji se elementi učitavaju jedan po jedan, čitav string se učitava odjednom.
Slično, čitav string se može odštampati u jednom koraku, koristeći funkciju printf:
printf("%s",c);
Inicijalizacija stringa se vrši na jedan od sljedeća dva načina:
char c[20] = "Program";
char c[] = "Program";
U prvom slučaju, za string je zauzeto 20 bajtova u memoriji, bez obzira što smo ga
inicijalizovali stringom od 7 karaktera, dok je u drugom slučaju za string zauzeto 8
bajtova (sedam slova i terminacioni karakter). Dozvoljen je, ali i rijetko korišćen
sljedeći oblik inicijalizacije:
char c[20]={'P','r','o','g','r','a','m','\0'};
kada se navode pojedinačni karakteri kao elementi stringa, i kad se terminacioni
karakter mora navesti kao i ostali, što nije u slučaj u inicijalizaciji sa stringom.
48 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Ako se učitavanje stringa vrši pomoću funkcije scanf koristeći specifikator


formata %s, tada string ne može da sadrži bjeline, što je ponekad nepogodno. Kasnije
ćemo vidjeti kako se može otkloniti ovaj nedostatak funkcije scanf. Kao alternativu,
umjesto scanf možemo koristiti funkciju gets, kojom se učitava string do prvog
karaktera Enter. Ova funkcija je definisana u okviru stdio.h i poziva se na način
gets(c). U paru sa gets, imamo funkciju puts, koja štampa string argument (poziva
se sa puts(c)) i automatski prelazi u novi red, što ne radi funkcija printf.
Kao ilustraciju rada funkcija gets i puts, i njihovo poređenje sa scanf i printf,
respektivno, posmatrajmo sljedeći dio kôda:
char s[30];
puts("Unijeti string: ");
gets(s);
printf("Ucitani string je %s\n",s);
printf("Unijeti string: ");
scanf("%s",s);
printf("Ucitani string je %s",s);
i njegovo izvršavanje sa učitanim stringom "Marko Polo".
Unijeti string:
Marko Polo
Ucitani string je Marko Polo
Unijeti string: Marko Polo
Ucitani string je Marko
Čitaocu prepuštamo tumačenje dobijenog rezultata.

1.2.10.1 Neke korisne funkcije za rad sa stringovima


Pored opisanih funkcija za rad sa stringovima, programske biblioteke jezika C
posjeduju i veliki broj drugih funkcija. Ovdje ćemo pomenuti neke od njih. Primjena
nekih od ovih funkcija, iako veoma logična, biće jasnija tek pošto se upoznamo sa
funkcijama. Za učitavanje karaktera sa tastature koristi se funkcija getch(), u obliku
c=getch(), čime se izvršavanje programa zaustavlja do pritiska na bilo koji taster,
kada se to što je pritisnuto smiješta u promjenljivu c. Za štampanje jednog karaktera
koristi se funkcija putch(c).
Kopiranje sadržaja stringa a u string b (dakle, a i b moraju dimenziono biti
pokazivači na char) se obavlja funkcijom strcpy, na način strcpy(b,a).
Nadovezivanje stringa a na string b se vrši naredbom strcat(b,a). Poređenje
sadržaja stringova se obavlja naredbom strcmp(a,b), koja vraća vrijednost koja je
manja od nule ako je a<b, nulu za a=b i vrijednost veću od nule za a>b. Stringovi se
porede karakter po karakter u skladu sa ASCII tabelom karaktera. Ako je, po ASCII
kôdu, prvi karakter stringa a manji od prvog karaktera stringa b, tada je string a manji
od stringa b, a ako su isti procedura se nastavlja za sljedeće karaktere. Stringovi su
jednaki ako su im jednaki svi karakteri.
1.2 P ROGRAMSKI JEZIK C 51

novu tekuću poziciju, ali samo u slučaju da je učitan niz karaktera, odnosno da je size
različito od nule. Na kraju smo dali jednostavnu petlju za prikazivanje sadržaja ovog
niza karaktera, tj. pojedinih stringova unutar njega.

1.2.11 Funkcije

U uvodnoj glavi smo se upoznali sa osnovnim pojmovima vezanim za potprograme.


U programskom jeziku C postoji samo jedna vrsta potprograma koja se naziva funkcija.
Posmatrajmo funkciju main() na lijevoj strani slike 5. Tri naredbe, naredbaX,
naredbaY i naredbaZ, se ponavljaju. Ove tri naredbe možemo izdvojiti u okviru
zasebnog bloka naredbi, tom bloku damo ime, u ovom primjeru fun, i te naredbe u
okviru funkcije main jednostavno pozivamo tim imenom. Imenovani blok naredbi, koji
izvršava određeni zadatak i poziva se po imenu, se naziva funkcija. Ukoliko naredbe
zavise od određenih promjenljivih, te promjenljive ćemo proslijediti funkciji u zagradi
nakon imena funkcije, a prije početka bloka.

Slika 5. Grupisanje naredbi u formi funkcije.

Program organizovan u više funkcija je lakše održavati od jedne integralne


funkcije. Prvi razlog za to je smanjen obim programskog kôda i bolja preglednost, jer
se naredbe koje se ponavljaju u integralnoj funkciji pojavljuju samo jednom u
višefunkcijskom programu. Drugi razlog je olakšano editovanje programa, tj. izmjena
naredbi koje se ponavljaju. Ako se te naredbe nalaze u zasebnoj funkciji, na samo
jednom mjestu vršimo izmjenu, za razliku od integralne funkcije gdje to moramo raditi
više puta, čime se povećava vjerovatnoća da ćemo negdje preskočiti da izvršimo
potrebne izmjene. Treći, vrlo bitan razlog da se programske funkcionalnosti grupišu u
okviru posebnih blokova je mogućnost korišćenja tih funkcionalnosti i u drugim
programima, na taj način promovišući ponovnu upotrebu kôda (eng. code reusability),
jedan od ključnih principa korektnog razvoja softvera. Funkcija predstavlja osnovni
52 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

gradivni blok programa, i može se koristiti u razvoju većeg broja programa. Takođe,
srodne funkcije se mogu grupisati u programske biblioteke.
Definicija funkcije ima oblik:
tip_rezultata ime(tip_1 arg_1, ..., tip_n arg_n)
{
tijelo funkcije
}
gdje:
 tip_rezultata predstavlja tip podatka koji funkcija vraća kao rezultat
izvršavanja. Ukoliko funkcija ne vraća nikakav podatak, potrebno je deklarisati kao
void ime(tip_1 arg_1, ..., tip_n arg_n);
 ime predstavlja ime funkcije, koje mora biti pravilan identifikator (može sadržati
mala i velika slova, cifre, donju crtu, i ne može početi cifrom);
 tip_1 arg_1, ... , tip_n arg_n predstavlja listu ulaznih parametara funkcije,
koji se odvajaju zarezima. Funkcija ne mora imati argumente i tada navodimo samo
zagradu, tj. tip_rezultata ime().
Unutar vitičastih zagrada se navodi tijelo funkcije koje se sastoji od deklaracije
promjenljivih i izvršnih naredbi. Dio funkcije prije bloka naredbi, tj.
tip_rezultata ime(tip_1 arg_1, ..., tip_n arg_n)
se naziva zaglavlje funkcije ili potpis funkcije (eng. function signature).
Ilustrujmo način izvršavanja funkcije na skici jednog jednostavnog programa.
int main()
{
/* Naredbe1 */
fun();
/* Naredbe3 */
}

tip_rezultata fun()
{
/* Naredbe2 */
}
Program započinje izvršavanje od funkcije main(). U njoj se izvršavaju
Naredbe1, do poziva funkcije fun(), kada se prelazi na izvršavanje bloka naredbi koji
se nalazi u okviru nje (Naredbe2). Nakon izvršenja funkcije fun, kontrola se vraća
funkciji main(), kada se izvršavaju preostale Naredbe3.
Funkcije mogu da vraćaju vrijednost, koja je onog tipa koji je specificiran tipom
funkcije. Vrijednost koju funkcija vraća prosljeđuje se pozivajućoj funkciji naredbom
return koja ujedno predstavlja tačku izlaska iz funkcije, tj. prekid izvršavanja
1.2 P ROGRAMSKI JEZIK C 53

funkcije. Drugim riječima, ako se u pozvanoj funkciji nalaze naredbe nakon return,
one se neće izvršiti.
Funkcija može imati argumente nad kojima se vrše odgovarajuće operacije. Svaki
od argumenata ima odgovarajući tip, s time što je u C-u funkciji moguće proslijediti
podatak koji ne odgovara tipu argumenta funkcije, kada dolazi do implicitne konverzije
podataka. Ovo bi trebalo izbjegavati i uvoditi cast operator, tj. sprovoditi slaganje
argumenata po tipu, gdje god je to moguće.
Posmatrajmo primjer funkcije koja računa zbir dva cijela broja i taj zbir vraća kao
rezultat:
int zbir1(int x,int y)
{
return x+y;
}
Ova funkcija se poziva (u funkciji main ili nekoj drugoj funkciji) na sljedeći način:
a=zbir1(b,c);
U pozivajućoj funkciji, promjenljive a, b i c se nazivaju stvarni argumenti ili samo
argumenti, dok se u definiciji funkcije zbir1 promjenljive x i y nazivaju formalni
argumenti ili parametri funkcije. Takođe, u pozivajućoj funkciji smo umjesto imena b
i c mogli koristiti x i y, čime dobijamo istoimene stvarne i formalne argumente. Pošto
se pozivajuća i pozvana funkcija nikad ne izvršavaju istovremeno, ne dolazi do konflikta
imena promjenljivih, tako da je ova situacija potpuno regularna. Drugim riječima, ime
x u pozivajućoj i pozvanoj funkciji se ne odnosi na isti entitet u memoriji. Kao
posljedica toga, promjene na formalnim argumentima ne utiču na vrijednost stvarnih
argumenata. Ilustrujmo to na sljedećem programu:
#include<stdio.h>

int main()
{
int a=3,b=4;
int c=fun(a,b);
printf("Zbir (main) je %d, a zbir (fun) je %d",a+b,c);
}

int fun(int x,int y)


{
x++;
y++;
return x+y;
}
Prethodni program će odštampati tekst:
Zbir (main) je 7, a zbir (fun) je 9
1.2 P ROGRAMSKI JEZIK C 55

Konačno, važno je zapamtiti da unutar tijela jedne funkcije ne može biti definisana
druga funkcija. Dakle, definicije funkcija čine liniju. Naravno, unutar tijela jedne
funkcije može biti pozvana druga funkcija.

1.2.11.1 Prototipovi funkcija


Definicija funkcije se u tekstu programa može navesti prije ili poslije njenog
poziva. I jedan i drugi princip se mogu smatrati prirodnim, mada se obično koristi drugi
princip. U radu sa funkcijama, uobičajen pristup je da se definicije funkcija navode
poslije funkcije main, a da se prije funkcije main navedu prototipovi funkcija. Prototip
funkcije je vrlo sličan zaglavlju, sa dvije male razlike:
 ne moraju se navoditi formalni argumenti (ako se navedu, kompajler ih ignoriše), i
 prototip se mora završiti tačkom-zarez.
Na primjer, za funkciju
int zbir1(int x,int y)
{
return x+y;
}
prototip je
int zbir1(int,int);
Prototipovi omogućavaju vrlo bitnu kontrolu poziva funkcija u fazi prevođenja.
Naime, može se desiti da se prethodno definisana funkcija zbir1 pozove sa jednim ili
tri argumenta umjesto dva, što može dovesti do neočekivanih rezultata ili pucanja
programa. Uvođenjem prototipova, informišemo kompajler u fazi prevođenja koje se
funkcije pojavljuju, sa kojim argumentima i u kom redosljedu, pa on na osnovu toga
može vršiti provjeru ispravnosti poziva funkcija. U slučaju poziva funkcije sa
neodgovarajućim brojem argumenata, kompajliranje se prekida uz odgovarajuću
poruku greške.

1.2.11.2 Funkcije i nizovi


Do sada prezentirane funkcije ne odgovaraju na fundamentalno pitanje: Kako je
moguće realizovati funkciju koja će vršiti operacije nad nizom, tj. elementima niza? To
se u Pascal-u vršilo prosljeđivanjem kopije niza potprogramu. Taj način pozivanja se
naziva pozivanje po referenci (eng. call by reference). Međutim, programski jezik C
posjeduje samo poziv po vrijednosti (eng. call by value) kao način prosljeđivanja
argumenata funkciji. Stoga postoji samo jedan način da se niz proslijedi funkciji, a to
prosljeđivanjem adrese niza tj. pokazivača na niz. Poznajući adresu niza u memoriji,
funkcija može da obavlja radnje čitanja i upisa vrijednosti u elemente niza. U uvodnoj
glavi smo napomenuli da prosljeđivanje argumenta po vrijednosti potprogramu ne može
da izmjeni te vrijednosti. Ako se potprogramu proslijedi adresa promjenljive kao
62 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

ime_programa abc def ghi


Pošto se program poziva sa 4 argumenta, uključujući ime programa ime_programa,
argc će imati vrijednost 4, dok će niz argv imati sljedeće elemente:
argv[0]="ime_programa"
argv[1]="abc"
argv[2]="def"
argv[3]="ghi"
Dakle, prvi element argv[0] je sam naziv programa (koji uključuje i putanju na disku
do njega), dok su drugi ostali argumenti u formi stringova.
Ilustrujmo rad sa argumentima komandne linije na primjeru programa koji sa
komandne linije učitava cijele brojeve, sabira ih i štampa njihov zbir.
#include<stdio.h>

int main(int argc,char *argv[])


{
int i,suma=0;
for(i=1;i<argc;i++)
suma+=atoi(argv[i]);
printf("Suma je %d\n",suma);
}
Pretpostavljajući da je naziv programa test, poziv programa sa komandne linije,
praćen argumentima cijelim brojevima, i odgovarajući odziv programa, bi mogli biti
C:\test\bin\Debug>test 1 2 3 4 5 6 7 8 9
Suma je 45
Tumačenje programa ostavljamo čitaocu.
Napomenimo na kraju da su imena argc i argv opciona, i da umjesto njih možemo
koristiti bilo koji ispravan identifikator. Ipak, uobičajena konvencija je koristiti ova
imena.

1.2.11.7 Rekurzivne funkcije


U uvodnom izlaganju smo istakli da u programskom jeziku C postoji mogućnost
da funkcija poziva samu sebe. Ovo se naziva rekurzijom i veoma je efikasna
programerska praksa. Kao jedan primjer možemo navesti funkciju koja računa faktorijel
datog prirodnog broja n:
int fakt(int n)
{
if(n>0)
return n*fakt(n‐1);
else
return 1;
}
1.2 P ROGRAMSKI JEZIK C 63

Kao što znamo, faktorijel prirodnog broja n je jednak n!=n×(n-1)×(n-2)×...×1. Faktorijel


broja n se rekurzivno može zapisati kao n!=n×(n-1)!, pri čemu je 0!=1. Ovu činjenicu
koristimo u izradi fakt funkcije.
Rekurzivne funkcije se sastoje iz dva dijela: rekurzivnog i baznog. U rekurzivnom
dijelu, imamo rekurzivni poziv funkcije, ali sa drugačijom vrijednošću argumenata, koje
će jednog trenutka dovesti do izvršavanja baznog dijela. U baznom dijelu, rješenje se
izražava nerekurzivno, i ovaj dio predstavlja kraj rekurzivnih poziva funkcija. Od ove
tačke se vraćamo unazad kroz prethodne rekurzivne pozive, formirajući konačno
rješenje problema.
Posmatrajmo izvršenje funkcije fakt(5). Na osnovu definicije ove funkcije,
zaključujemo da:
 fakt(5) vraća vrijednost 5*fakt(4)
 fakt(4) vraća vrijednost 4*fakt(3)
 fakt(3) vraća vrijednost 3*fakt(2)
 fakt(2) vraća vrijednost 2*fakt(1)
 fakt(1) vraća vrijednost 1*fakt(0)
 fakt(0) vraća vrijednost 1
Prvih pet poziva odgovara rekurzivnom, a posljednji baznom dijelu rekurziju. Iako je u
rekurzivnom dijelu navedeno da funkcija "vraća vrijednost", ona ne vraća nikakvu
vrijednost sve dok se ne razriješi koju vrijednost vraća funkcija iz rekurzivnog poziva.
Tako, da bi mogli da odredimo šta vraća fakt(5), prvo treba da odredimo šta vraća
poziv fakt(4), itd. Jedino se za poziv fakt(0) može reći da vraća vrijednost 1.
Nakon izvršenja baznog dijela rekurzije, kaskadno se vraćamo nazad i formiramo
vrijednost faktorijela. Sad imamo sljedeći tok:
 fakt(0) vraća vrijednost 1
 fakt(1) vraća vrijednost 1*fakt(0), tj. 1
 fakt(2) vraća vrijednost 2*fakt(1), tj. 2
 fakt(3) vraća vrijednost 3*fakt(2), tj. 6
 fakt(4) vraća vrijednost 4*fakt(3), tj. 24
 fakt(5) konačno vraća vrijednost 5*fakt(4), tj. 120.
Rekurzija ima jedan veliki nedostatak, koji ćemo ilustrovati na primjeru funkcije
fakt. Naime, u našem primjeru za n=5, glavni program ili neka druga funkcija prvo
pozivaju funkciju fakt(5). Prilikom poziva te funkcije, na stek se smještaju podaci
vezani za promjenljive iz pozivajuće funkcije. Zatim funkcija fakt(5) poziva funkciju
fakt(4), pa se promjenljive iz funkcije fakt(5) smještaju na stek itd. Kod velikog
broja n, ovo može biti memorijski vrlo zahtjevno. Kod složenijih funkcija ovo može biti
i sporo zbog potrebe da se alocira/dealocira veliki broj podataka na steku. Stoga,
64 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

programer treba da procjeni složenost funkcije i da se opredijeli za elegantnost


(rekurzija) ili efikasnost u rješavanju.

1.2.12 Pretraživanje i sortiranje nizova

1.2.12.1 Algoritmi za pretraživanje nizova


U bazama podataka, aplikacijama za obradu teksta, kao i u brojnim drugim
inženjerskim aplikacijama, koriste se algoritmi za pretraživanje i sortiranje.
Cilj algoritama za pretraživanje je da se odredi da li u datom nizu/kolekciji postoji
traženi podatak i da se vrati njegova pozicija (indeks). Suštinski postoje dva različita
algoritma za pretraživanje:
 direktna pretraga i
 binarno pretraživanje.
U direktnoj pretrazi, prolazimo kroz niz, element po element, i provjeravamo da li
je tekući element jednak traženom. Ako jeste, vraćamo indeks tekućeg elementa i
procedura se završava. Ako nije, prelazimo na sljedeći element. Ukoliko ne pronađemo
traženi element, vraćamo broj koji ne može predstavljati regularan indeks elementa,
recimo -1. Kako ova pretraga uključuje elementarnu logiku, često se naziva i brute force
pretraga. U nastavku je data funkcija koja implementira direktno pretraživanje.
int direktna(int niz[],int n,int x)
{
int i;
for(i=0;i<n;i++)
if(niz[i]==x)
return i;
return –1;
}
Binarno pretraživanje spada u kategoriju podijeli-pa-vladaj (eng. divide-and-
conquer) algoritama, kod kojih se problem razlaže na dva ili više potproblema (podijeli
dio), sve dok problem ne postane dovoljno jednostavan da se može direktno riješiti
(vladaj dio). Ovo razlaganje je obično rekurzivno. Rješenja potproblema se nakon toga
kombinuju, dajući rješenje početnog problema.
Binarno pretraživanje polazi od pretpostavke da je niz sortiran. Uzmimo da je u
pitanju neopadajući redosljed. Posmatramo srednji element niza (srednji po poziciji) i
poredimo ga sa traženim brojem. Ako su jednaki, vraćamo indeks srednjeg elementa i
procedura se završava. Ako nijesu jednaki, provjerava se da li je srednji element veći
od traženog. Ako je veći, pretraživanje lokalizujemo na prvu polovinu niza, a ako je
manji na drugu polovinu niza. Lokalizovanje vršimo zahvaljujući činjenici da je niz
sortiran. Preciznije, ako je niz sortiran u neopadajući redosljed i ako je srednji element
veći od traženog, onda su svi elementi nakon srednjeg takođe veći od traženog broja. Sa
druge strane, ako je srednji element manji od traženog, onda su svi elementi prije
1.2 P ROGRAMSKI JEZIK C 65

srednjeg takođe manji od traženog elementa. Proceduru nastavljamo na prvoj ili drugoj
polovini niza, koji su pojedinačno manji od čitavog niza (podijeli dio). Kada tekuća
polovina niza ima jedan ili dva elementa, vršimo direktno poređenje tih elemenata sa
traženim (vladaj dio).
Ilustrujmo binarno pretraživanje na primjeru niza
1 3 6 10 15 21 28 36 45
pretpostavljajući da tražimo broj 36. Srednji element ovog niza je 15. Kako je on manji
od traženog broja, pretragu lokalizujemo na podniz ovog niza između elemenata 21 i 45
(druga polovina niza), odnosno zanemarujemo prvu polovinu niza jer je traženi element
sigurno veći od svih elemenata u toj polovini. Srednji element gornje polovine je 28
(može biti i 36, što je stvar unapred dogovorene konvencije). Kako je 28 manje od 36,
pretragu lokalizujemo na podniz ovog niza između elemenata 36 i 45. Pošto ovaj podniz
ima dva elementa, vršimo direktno poređenje tih elemenata sa traženim, i pošto ga
pronalazimo vraćamo indeks 7.
U nastavku je data rekurzivna funkcija binarno koja implementira binarno
pretraživanje niza.
int binarno(int niz[],int start,int end,int x)
{
if(start>end)
return ‐1;
else if(start==end||start==end‐1)
{
if (niz[start]==x||niz[end]==x)
return (niz[start]==x)?start:end;
else
return ‐1;
}
int sred=start+(end‐start)/2;
if (niz[sred]==x)
return sred;
if (niz[sred]>x)
return binarno(niz,start,sred‐1,x);
else
return binarno(niz,sred+1,end,x);
}
Ova funkcija se poziva na sljedeći način:
binarno(niz,0,n‐1,x)
gdje je n dužina niza niz, a x je traženi broj. Čitaocu prepuštamo tumačenje funkcije.
66 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

1.2.12.2 Algoritmi za sortiranje nizova


U mnogim programskim aplikacijama sortiranje nekog niza elemenata je važan
problem. Mi ćemo ovdje samo elementarno "načeti" ovu problematiku upoznavši vas
sa nekoliko osnovnih algoritama za sortiranje. Kao prvi primjer algoritama za sortiranje,
uzmimo sortiranje niza cijelih brojeva u rastući redosljed (od najmanjeg ka najvećem)
metodom uzastopnih minimuma. Naime, ova metoda radi tako se što poredi element sa
indeksom 0 sa svim ostalim elementima u nizu i svaki put kad je taj element veći od
tekućeg elementa vršimo zamjenu njihovih pozicija. Kada prođemo kroz čitav niz,
najmanji element niza se nalazi na prvom mjestu u nizu. Zatim uzimamo drugi element,
tj. element sa indeksom 1, i poredimo ga sa svim elementima nakon njega (indeksi veći
od 1), vršeći zamjenu svaki put kada naiđemo da je on veći od tekućeg elementa.
Procedura se ponavlja za svaku poziciju u nizu osim za posljednju. Program ima oblik:
#include<stdio.h>

int main()
{
int n,temp,i,j,a[100];
puts("Unijeti duzinu niza:");
scanf("%d",&n);
puts("Unijeti elemente niza:");
for(i=0;i<n;i++)
scanf("%d",a+i);
for(i=0;i<n‐1;i++)
for(j=i+1;j<n;j++)
if(a[i]>a[j])
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
for(i=0;i<n;i++)
printf("%d ",a[i]);
}
Ako posmatramo niz {4 6 1 5 8 2 7}, on će nakon svake zamjene elemenata
izgledati (elementi koji mijenjaju poziciju u narednom koraku su boldovani):
4 6 1 5 8 2 7
1 6 4 5 8 2 7
1 4 6 5 8 2 7
1 2 6 5 8 4 7
1 2 5 6 8 4 7
1 2 4 6 8 5 7
1 2 4 5 8 6 7
1 2 4 5 6 8 7
1 2 4 5 6 7 8
68 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

for(i=0;i<n;i++)
printf("%d ",a[i]);
}
Ukoliko posmatramo niz iz prethodnog primjera, nakon svake zamjene elemenata
u bubble sort algoritmu, niz će izgledati (elementi koji mijenjaju poziciju u narednom
koraku su boldovani):
4 6 1 5 8 2 7
4 1 6 5 8 2 7
4 1 5 6 8 2 7
4 1 5 6 2 8 7
4 1 5 6 2 7 8
1 4 5 6 2 7 8
1 4 5 2 6 7 8
1 4 2 5 6 7 8
1 2 4 5 6 7 8
Odredimo broj operacija poređenja u ova dva algoritma. Za oba algoritma, logika
je sljedeća: u prvom prolasku kroz niz imamo n-1 poređenje, u drugom n-2, ..., u
zadnjem 1 poređenje. Kad se saberu sva ova poređenja, tj. (n-1)+(n-2)+...+2+1, dobija
se ukupno n(n-1)/2 poređenja. Sa stanovišta složenosti, tj. broja operacija poređenja
potrebnih za sortiranje, ova dva algoritma su među najsloženijim. Više riječi o
složenosti algoritama će biti u poglavlju 1.2.13.
Još jednom napominjemo da su ovo dva elementarna algoritma za sortiranje. Pored
njih, postoji vrlo velik broj drugih algoritama, od kojih će neki, kao što su sortiranje sa
umetanjem (eng. insertion sort) i brzo sortiranje (eng. quick sort), biti obrađeni u
pratećoj zbirci.

1.2.13 Složenost algoritama

Nijesu svi algoritmi koji rješavaju neki problem jednako kvalitetni. Jedna od mjera
kvaliteta algoritama, odnosno programa, je složenost. Tipovi složenosti su:
 Vremenska složenost Odnosi se na vrijeme potrebno za izvršavanje algoritma.
Proporcionalna je broju operacija (aritmetičkih, logičkih ili nekih drugih) koje
algoritam izvršava. Dakle, procjena vremenske složenosti se svodi na brojanje
operacija u algoritmu.
 Prostorna složenost Odnosi se na memorijski prostor koji zauzimaju podaci u
algoritmu i određuje se sabiranjem veličine memorijskih lokacija koje zauzimaju
promjenljive upotrijebljene u algoritmu.
 Komunikaciona složenost Odnosi se na potrebnu komunikaciju procesora sa
periferijama, diskovima i memorijom prilikom izvršavanja programa. Ova
složenost je posebno značajna kod paralelnih izvršavanja programa kada razni
procesori međusobno komuniciraju.
1.2 P ROGRAMSKI JEZIK C 69

Mi ćemo se detaljnije zadržati na vremenskoj složenosti, po kojoj je vrijeme


izvršavanja algoritma proporcionalno broju operacija u algoritmu, tj.
vrijeme_izvršavanja = broj_operacija × konstantni_interval.
Generalno, ovo ne važi za malu količinu ulaznih podataka, već samo za veliki obim
podataka. Teško je odrediti svaku operaciju u algoritmu, a teško je generalno i porediti
operacije kao što su sinus ugla, množenje i npr. operacije poređenja.
Kod sekvence se broje operacije na koje se naiđe. Na primjer, za sljedeću sekvencu
naredbi:
a=x+y+z;
b=a+5;
c=a*b;
se može reći ima složenost od 3 sabiranja, 1 množenja i 3 dodjele vrijednosti. Složenost
operacije dodjele se često zanemaruje u odnosu na ostale operacije.
Kod selekcije јe situacija nešto složenija jer se ne može znati unaprijed koji će uslov
biti ispunjen i samim tim koja će se grana izvršiti. Obično se razmatra grana sa najviše
operacija, pa bi u slučaju sljedeće if...else naredbe
if(a>3)
c=a+b+3;
else
c=a+1;
složenost bila 1 poređenje i 2 sabiranja.
Kod ciklusa se broj operacija tokom jednog izvršavanja ciklusa množi sa brojem
ponavljanja ciklusa. Na primjer, ciklus
i=1;
while(i<=N)
{
a=a+b;
i=i+1;
}
će imati složenost 2N sabiranja i N+1 poređenje (uslov izvršavanja petlje), dok će ciklus
for(i=0;i<N;i+=2)
{
a=a*b+i;
}
imati složenost N sabiranja, N/2+1 poređenje i N/2 množenja.
Kod ugnježdenih ciklusa, slična je logika, s tim što se prvo odredi broj operacija
najdubljeg ciklusa, pa se taj broj množi sa brojem iteracija prvog sljedećeg ciklusa itd.
Na primjer, sljedeći dio kôda
70 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

for(i=1;i<=N;i++)
{
for(j=1;j<=N;j++)
{
K operacija
}
}
ima složenost od N2K operacija (broj iteracija unutrašnjeg ciklusa je N2), plus N2+N
inkrementiranja promjenljivih i i j, i N2+2N+1 poređenje.

1.2.13.1 Analiza najgoreg i prosječnog slučaja


Sami ulazni podaci mogu značajno uticati na složenost. Na primjer, sortiranje
relativno sortiranog niza zahtijeva manje operacija od niza sa nasumično raspoređenim
elementima ili niza koji je sortiran u suprotnom redosljedu. Ukoliko znamo nešto o
ulaznim podacima, može se vršiti sofisticiranija analiza.
Što raditi kada ne postoji jedinstvena veza ulaznih podataka i složenosti? Postoje
dvije strategije:
 Analiza najgoreg slučaja (eng. worst case analysis) Uzme se najgori mogući slučaj
i kaže se da algoritam ima toliku vremensku složenost u najgorem slučaju (ovo je
češća strategija).
 Analiza prosječnog slučaja (eng. average case analysis) Za svaki mogući obim
ulaznih podataka treba znati koliko operacija je potrebno. Takođe, treba znati sa
kojom se vjerovatnoćom taj podatak pojavljuje. Prosječan broj operacija je:
K

pN
i 1
i i  p1 N1  p2 N 2  ...  pK N K ,

gdje pi predstavlja vjerovatnoću ulaznog podatka datog obima, dok Ni predstavlja


broj operacija za dati obim ulaznog podatka. Za vjerovatnoće važi:
K

p
i 1
i  p1  p2  ...  pK  1.

Kao primjer određivanja složenosti, uzmimo program koji radi sa parnim i


neparnim brojevima. Pretpostavimo da se parni i neparni brojevi pojavljuju sa jednakom
vjerovatnoćom. Ukoliko je broj operacija kod parnih brojeva reda veličine N, a kod
neparnih 2N, najgora složenost je 2N, a prosječna N/2+2N/2=3N/2.

1.2.13.2 Rast složenosti. Veliko O


Rast složenosti algoritma sa povećanjem veličine ulaza je korisna mjera za procjenu
i poređenje algoritama. Rast funkcija se obično opisuje koristeći veliko O notaciju (eng.
big-O notation).
72 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

 U zbiru proizvoljnog broja funkcija, najbrža određuje složenost zbira. Na primjer,


složenost funkcije f(n)=12log(n)+log3(n)+4nlog(n)+33n2+n3/100 je O(n3).
Dakle, cilj određivanja složenosti algoritma je pronaći najmanju jednostavnu
funkciju g(n) za koju je f(n) reda O(g(n)). Funkcije g(n) koje se često srijeću su date u
nastavku (poređane su po složenosti):
 1 – konstantna složenost
 log(n) – logaritamska složenost
 n – linearna složenost
 n log(n) – linearitmična (eng. linearithmic) složenost
 n2 – kvadratna složenost
 n3 – kubna složenost
 2n – eksponencijalna složenost
 n! – faktorijel složenost
U tabeli 6 je dat rast složenosti nekih često korišćenih algoritama.

Operacija Rast složenosti


Proizvod matrica O(n2)
Ugnježdene for petlje (k nivoa) O(nk)
Direktna pretraga O(n)
Binarna pretraga O(log2n)
Ponovljeni minimum O(n2)
Bubble sort O(n2)
Merge sort O(n log2n)
Generisanje svih podskupova datog skupa O(2n)
Generisanje svih permutacija datog skupa O(n!)
Tabela 6. Rast složenosti nekih algoritama.

Uočite da je složenost ponovljenog minimuma i bubble sort algoritma reda O(n2),


gdje je n broj elemenata niza, pri čemu razmatramo složenost u zavisnosti od operacija
poređenja. Njihova stvarna složenost je n(n+1)/2, ali na osnovu polinomijalnog pravila
određivanja složenosti, zaključujemo da je to reda O(n2).

1.2.13.3 Studija slučaja: Broj podnizova sa parnom sumom


Posmatrajmo sljedeći problem. Dat je niz prirodnih brojeva X, dužine N. Treba
odrediti koliko ima podnizova ovog niza, sastavljenih od uzastopnih elemenata, čija je
suma paran broj. Dakle, treba naći broj uređenih parova (i,j), gdje je 0 ≤ i ≤ j ≤ N-1, za
koje je suma X[i]+X[i+1]+···+X[j-1]+X[j] paran broj. Na primjer, niz X={1,2,3,4} ima
4 podniza sa parnom sumom: {1,2,3}, {1,2,3,4}, {2} i {4}.
Ilustrovaćemo 3 rješenja ovog problema, čije su složenosti O(N3), O(N2) i O(N),
počev od najsloženijeg.
78 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

pretpocesorske direktive je ifdef kojim se provjerava da li je neki makro definisan


naredbom define i ako jeste izvršava neku operaciju. Takođe, dozvoljeno je i ifndef
kojim se neka operacija izvršava ako taj makro nije definisan. Ove makrodefinicije se
obično koriste za podešavanje programa parametrima okruženja, izmjene izvornog koda
u raznim fazama programiranja itd. Na primjer, provjeri se da li je neki parametar vezan
za operativni sistem definisan i ako jeste izvrši određena operacija, ili ako nije uključena
neka programska biblioteka koja sadrži definiciju neke konstante - uključiti je, itd.
Pretprocesorska naredba #error tekst naređuje pretprocesoru da ispiše tekst
u okviru poruke o greški. Sa ovim se ne iscrpljuje broj pretprocesorskih direktiva kao i
interesantnih opcija kod istih, ali ćemo se zadržati samo na ovim.

1.2.16 Važnije programske biblioteke i odgovarajuće funkcije

U okviru programskih kompajlera jezika C uključen je veliki broj programskih


biblioteka. Mi smo se do sada upoznali samo sa nekoliko funkcija koje pripadaju
programskim bibliotekama stdio.h i string.h. U nastavku opisujemo važnije
programske biblioteke i često korišćene funkcije iz tih biblioteka.

1.2.16.1 stdio.h
Tako smo se iz programske biblioteke stdio.h upoznali sa getchar (učitavanje
karaktera sa tastature), gets (učitavanje stringa sa tastature), putchar (štampanje
karaktera na ekranu), puts (štampanje stringa na ekranu), printf (standardni izlaz
podataka), scanf (standardni unos podataka). Upoznali smo se i sa simboličkom
konstantom NULL.
Pre nego krenemo sa opisom drugih funkcija, vratimo se na funkciju scanf i njene
napredne opcije. Funkcija scanf ima zaglavlje:
int scanf(const char *format, ...)
Već smo vidjeli da argument format predstavlja string koji može sadržati karaktere
bez bjelina, bjeline i specifikatore formata ulaznih podataka (%d, %f, %s itd.). Funkcija
vraća cijeli broj koji predstavlja broj uspješno učitanih podataka. U suprotnom, funkcija
vraća negativnu vrijednost ili EOF, u zavisnosti od greške koja se desila. Specifikator
formata ima opšti oblik:
%[*][širina][dužina]tip
Zagrade [] označavaju da je polje opciono (ne mora se navesti). Objašnjenja pojedinih
dijelova su:
 * označava da se ulazni podatak učitava, ali se ignoriše, tj. ne dodjeljuje nijednoj
promjenljivoj.
1.2 P ROGRAMSKI JEZIK C 79

 širina je maksimalan broj karaktera koji se učitava. Na primjer, %2d označava


promjenljivu tipa int sa dvije cifre, dok %4f označava float promjenljivu sa
ukupno četiri karaktera (počev od cifre najveće težine).
 dužina dodatno određuje tip promjenljive u smislu veličine. Na primjer, %hd
označava promjenljivu tipa short int, dok %ld označava promjenljivu tipa long
int. Prisjetite se da se double vrijednosti moraju učitavati sa specifikatorom %lf.
 tip je karakter koji predstavlja tip podatka koji se učitava (d i i za cijele brojeve,
f, e i g za realne brojeve itd.).
Na primjer, sljedeći dio koda:
int x,y;
float z;
scanf("%2d %f %*d %d",&x,&z,&y);
printf("%d %d %f",x,y,z);
će za unijete brojeve 1234, 56 i 789 štampati
12 789 34.000000
Protumačite sami dobijene vrijednosti promjenljivih x, y i z, kao i gdje se izgubila
unijeta vrijednost 56.
Izuzetno korisna opcija kod funkcije scanf je tzv. specifikator opsega (eng.
scanset specifier). Ovaj specifikator omogućava učitavanje određenog opsega
karaktera, sve dok se ne učita karakter koji ne pripada tom opsegu. Sintaksa
specifikatora opsega je:
%[opseg]
gdje se unutar zagrade navode svi "poželjni" karakteri za učitavanje. Na primjer,
%[aeiou] znači da učitavamo karaktere 'a', 'e', 'i', 'o' i 'u', i da prekidamo
učitavanje kad naiđe karakter koji nije nijedan od pobrojanih. Interval susjednih
karaktera se označava sa crticom, npr. %[0‐9] znači da učitavamo karaktere sve dok
ne naiđe karakter koji nija cifra. Pojedinačni karakteri i intervali se mogu kombinovati,
pa tako %[AG‐M37] znači da učitava sve dok se ulazni podaci slovo 'A', neko od slova
iz intervala od 'G' do 'M', ili cifre '3' ili '7'. Moguće je specificirati opseg karaktera
koje je potrebno ignorisati pri učitavanju podataka, korišćenjem specifikatora:
%[^opseg]
Na primjer, %[^A‐C] znači da se učitavaju karakteri sve dok ne naiđe karakter koji nije
u opsegu slova od 'A' do 'C', dok %[^]%^B‐K+] znači da se učitavaju karakteri sve
dok ne naiđe ']', '%', '^', slova od 'B' do 'K' ili karakter '+'.
Specifikator opsega u funkciji scanf pruža mogućnost učitavanja stringova koji
sadrže bjeline, na prvom mjestu spejsove, što ova funkcija podrazumijevano ne
omogućava. Naime, string str učitan na sljedeći način:
80 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

scanf("[^\n]",str);
može da sadrži sve karaktere osim karaktera za prelazak u novi red (Enter), što odgovara
ponašanju funkcije gets.
Ilustrujmo sada jednu situaciju koja može izazivati glavobolje. Naime, kada želimo
da učitamo neki podatak (karakter, broj, string), a nakon toga karakter, na sljedeći način:
scanf("%s",str);
scanf("%c",&ch);
nakon unosa prvog podatka (ovdje stringa) i pritiska Enter, u promjenljivu ch će
automatski biti smješten karakter za novi red, tj. korisnik neće imati mogućnost da unese
karakter. Razlog tome je činjenica da funkcija scanf prilikom učitavanja karaktera ne
uklanja bjeline, jer i njih tretira kao karaktere koji se mogu učitati. Ovo je moguće
izbjeći na sljedeći način:
scanf("%[^\n]%*c", str);
scanf("%c", &ch);
Prvi scanf učitava sve do Enter-a, pa zanemaruje Enter (zbog specifikatora %*c), zatim
se karakter ch neometano učitava sa drugim scanf.
Drugi način da se to izbjegne je da se ubaci razmak ispred %c, tj.
scanf(" %c", &ch);
Na ovaj način se ignorišu bjeline ispred karaktera koji se učitava, tj. u našem slučaju se
ignoriše Enter nakon unijetog stringa.
Opišimo sada funkciju sprintf. Ova funkcija štampa tekst u string (prvi argument
funkcije) isto kao što printf štampa tekst na ekranu. Sintaksa je ista kao kod funkcije
printf, sa dodatkom prvog argumenta stringa. Na primjer, naredba:
sprintf(s,"%.2d:%.2d:%.2d",14,7,34);
će formirati string s koji može predstavljati tekuće vrijeme "14:07:34". Funkcija
sprintf omogućava vrlo elegantno formiranje stringa, uz mogućnost formatiranja
teksta kako smo navikli kod funkcije printf. Obratite pažnju na specifikator
konverzije %.2d kojim se cijeli broj ispisuje u dva polja, pri čemu se zbog tačke prazna
polja popunjavaju nulama, što je zgodno kod formiranja tekućeg vremena.
Nasuprot funkcije sprintf imamo funkciju sscanf, koja omogućava učitavanje
podataka iz stringa (prvog argumenta funkcije), a ne preko tastature kako smo navikli
kod scanf. Sintaksa je ista kao kod funkcije scanf, sa dodatkom prvog argumenta
stringa. Na primjer, naredbama:
char s[]="14:07:34";
sscanf(s,"%2d%*c%2d%*c%2d", &sat, &min, &sek)
se iz stringa s, koji predstavlja vrijeme, u cjelobrojne promjenljive sat, min i sek
učitavaju sati, minuti i sekunde, respektivno. Pomoću %*c zanemarujemo dvotačke.
86 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

biblioteka alloc.h, ali pošto to nije standardna C biblioteka (dostupna kod Borland
Turbo C i TIGCC okruženja), treba je izbjegavati radi prenosivosti kôda.
Posmatrajmo slučaj dinamičke alokacije memorije za niz karaktera. Prvo je
potrebno deklarisati pokazivač na char, npr. char *niz. Saznali smo da niz ima 100
karaktera, uključujući terminacioni karakter. Memorija za ovaj niz se može zauzeti sa:
niz=(char *)malloc(100*sizeof(char));
Pojasnimo ovu naredbu. malloc(n) zauzima memorijski blok od n bajta. Kako želimo
zauzeti 100 pozicija za char podatke, iskoristili smo operator sizeof, koji nam vraća
broj bajtova potreban za smještaj jednog podatka tipa char. Funkcija malloc vraća
pokazivač na neodređen podatak (void *), tako da treba izvršiti eksplicitnu konverziju
tih podataka u onaj tip koji će zapravo biti korišćen. U ovom slučaju, to je (char *).
Dakle, rezultat ove operacije je pokazivač na niz od 100 karaktera. U slučaju da malloc
ne zauzme traženu memoriju, vratiće NULL pokazivač kao rezultat. Dobra programerska
praksa podrazumjeva da se poslije svake naredbe za dinamičku alokaciju provjeri da li
je uspješno obavljena poređenjem vraćenog pokazivača sa NULL vrijednošću. U
zavisnosti od značaja datog podatka, treba preduzeti dalje mjere (npr. izaći iz programa).
Nakon uspješne alokacije memorije za podatke, može se pristupati elementima niza
na bilo koji uobičajeni način (niz[4], *(niz+4)) i sa njima vršiti sve dozvoljene
operacije.
Prednost dinamičke alokacije u odnosu na nizove promjenljive dužine je da se
dinamički zauzeta memorija može osloboditi. To se naziva dinamičkom dealokacijom,
i vrši se funkcijom free koja za argument ima pokazivač na dinamički alociranu
memoriju. U našem primjeru, to se radi sa:
free(niz);
Pored funkcije malloc, za dinamičko zauzimanje memorije može poslužiti
funkcija calloc u obliku:
calloc(n,t);
kojom se zauzima n lokacija u memoriji, veličine t, i u te se lokacije upisuje 0. I ova
funkcija vraća podatak tipa void *, koji je potrebno eksplicitino konvertovati u željeni
pokazivač. Takođe, rezultat je NULL u slučaju neuspješne alokacije memorije.
Funkcija realloc, koja se poziva na sljedeći način:
realloc(niz,n);
realocira (smanjuje ili povećava) zauzetu memorija za podatak niz. Argument n je
veličina memorije koja se želi zauzeti. Funkcija vraća rezultat tipa void *.
Stari programski jezici (FORTRAN, COBOL, BASIC) vrše kontrolu memorije
statički i nemaju opisane funkcije.
88 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

komponenta strukture ima svoj identifikator. Osnovna namjena struktura je


modelovanje složenih objekata (npr. Student, Radnik, Automobil) i rad sa njima.
Struktura rješava fundamentalni nedostatak nizova u programskom jeziku C, a to
je nemogućnost rada sa raznorodnim podacima. Svi elementi niza su istog tipa. Takođe,
funkcije u C-u ne mogu vratiti niz kao podatak (mogu vratiti pokazivač na niz, ali ne i
niz kao objekat). Sa druge strane, funkcija može vratiti strukturu kao podatak.
Svi noviji programski jezici posjeduju ovaj tip podatka. U objektno-orijentisanim
programskim jezicima, strukture su nadograđene pojmom klase (eng. class).

1.2.20.1 Deklaracija strukturnih promjenljivih. Operator .


Struktura se uvodi u program ključnom riječju struct:
struct naziv_strukture{
//deklaracija promjenljivih (komponenti strukture)
} promjenljive_strukturnog_tipa;
Naziv strukture može biti svaki regularan identifikator. Nakon promjenljivih
strukturnog tipa, obavezno dolazi tačka-zarez. Jedan primjer strukture može biti:
struct abc{
char k;
int x;
} s1,s2;
U ovom primjeru smo definisali strukturu abc, koja ima dvije komponente: karakter k
i cio broj x. Pored definicije strukture abc, deklarisali smo dvije promjenljive ovog
strukturnog tipa, s1 i s2. Obije promjenljive imaju za komponente karakter i cio broj,
pri čemu char komponente kod s1 i s2 imaju isto ime k, dok int komponente imaju
isto ime x. Ako komponente imaju isto ime, kako se pravi razlika u pristupanju
istoimenim podacima? Odgovor slijedi u nastavku.
Elementima strukture se pristupa preko operatora tačka ., sa čije se lijeve strane
nalazi ime strukturne promjenljive, a sa desne ime komponente kojoj se pristupa. Na
primjer, dodijela vrijednosti komponentama promjenljive s1 može biti:
s1.k='A';
s1.x=6;
dok učitavanje i štampanje komponenti promjenljive s2 može biti:
scanf("%c",&s2.k);
printf("%d",s2.x);
Razliku u pristupanju istoimenim komponentama upravo čini obavezno ime strukturne
promjenljive.
Komponente strukture su vidljive samo unutar strukture kojoj pripadaju.
Komponente mogu imati ista imena, ali u različitim strukturama. Što se tiče
memorijskog smiještanja komponenata strukturne promjenljive, one se uvijek
1.2 P ROGRAMSKI JEZIK C 89

smiještaju redoslijedom kako su navedene u definiciji strukture, jedna komponenta za


drugom, tako da zauzimaju susjedne memorijske lokacije.
Navođenje imena promjenljivih nakon definicije strukture je opciono, odnosno
promjenljive strukturnog tipa možemo deklarisati kasnije. U tom slučaju, prvo bi
definisali strukturu (obratite pažnju na tačku-zarez na kraju definicije):
struct abc{
char k;
int x;
};
a onda bilo gdje u programu mogu deklarisati promjenljive strukturnog tipa na sljedeći
način:
struct abc s1,s2;
Kao što vidimo, promjenljive strukturnog tipa se deklarišu kao sve ostale promjenljive,
pri čemu mi definišemo tip, u ovom slučaju struct abc.
Struktura se može definisati bilo gdje u programu, tj. unutar bilo koje funkcije ili
van svih funkcija. Logično, deklaraciju ćemo vršiti tako da je struktura vidljiva svim
funkcijama u kojima će se deklarisati promjenljive tog strukturnog tipa. Obično se
strukture definišu van svih funkcija u kôdu programa, kako bi im sve funkcije mogle
pristupiti. Vrlo je važno napomenuti da se definisanjem strukture ne zauzima prostor u
memoriji, već se prostor zauzima deklaracijom promjenljivih strukturnog tipa. Sama
definicija predstavlja šablon po kom se kreiraju promjenljive strukturnog tipa, tj. alocira
memorija za komponente deklarisane strukturne promjenljive.
U deklaraciji strukture, naziv strukture je opcion, tj. može se izostaviti. Ukoliko se
naziv izostavi, onda se nakon definicije strukture moraju deklarisati sve strukturne
promjenljive tog tipa koje planiramo koristiti u programu (kao u prvom primjeru).
Jedino mjesto deklaracije promjenljivih tipa bezimene strukture je nakon definicije te
strukture.
Promjenljive strukturnog tipa se prilikom deklaracije mogu i inicijalizovati:
struct abc{
char k;
int x;
}s1={'a',4},s2;
gdje je sada promjenljiva s1 inicijalizovana na vrijednost {'a',4}.
Pogledajmo sada jednu složeniju strukturu, koja za komponentu ima string:
struct Radnik{
char imePrezime[40];
int godineStaza;
} r1={"Mitar Pavlovic",14};
90 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Uočite način inicijalizacije stringa komponente strukture, koji je prirodan i odgovara


načinu inicijalizacije bilo kog stringa.
Ukoliko struktura za podatak ima numerički niz, onda se inicijalizacija vrši na
sljedeći način:
struct Student{
int brojPolIsp;
int ocjene[40];
} s1={5,{9,8,8,7,10}};
Nizovi se, dakle, inicijalizuju koristeći velike zagrade. Prva ocjena studenta s1 bi se
mogla odštampati sa:
printf("%d",s1.ocjene[0]);
Moguće je definisati niz podataka tipa struktura:
struct Radnik{
char imePrezime[40];
int godineStaza;
} nizRadnika[20];
gdje je nizRadnika niz elemenata koji su tipa struktura Radnik. Operacije sa
elementima niza su direktne, i nekoliko takvih dajemo u nastavku:
nizRadnika[0].godineStaza=6;
printf("%s",nizRadnika[5].imePrezime);
strcpy(nizRadnika[7].imePrezime,"Aco Tadic");
Postoji mogućnost da se naredbom typedef definiše novi tip podatka koji je
struktura:
typedef struct{
char imePrezime[40];
int godineStaza;
} Radnik;
Dalje se u programu promjenljive tipa struktura Radnik mogu deklarisati kao:
Radnik r1,proizvodnja[50];

1.2.20.2 Pokazivači na strukture. Operator ‐>


Kao i na svaki drugi tip podatka, može se kreirati pokazivač i na podatke
strukturnog tipa:
struct abc{
char k;
int x;
} s1,*p1;
gdje je p1 pokazivač na strukturu abc. Nakon što dodijelimo vrijednost pokazivaču,
recimo:
96 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

podacima fajla, fajl se mora otvoriti. To se vrši funkcijom fopen koja vraća pokazivač
na otvoreni fajl i pridružuje ga deklarisanom pokazivaču, na sljedeći način:
FILE *a;
a=fopen(fajl_koji_se_otvara, način_pristupa);
fajl_koji_se_otvara je naziv fajla sa kojim se želi raditi. Nazivi fajlova se navode
u formi stringa. Na primjer, "Prvi.txt" označava da se želi pristupiti fajlu Prvi.txt u
folderu koji je za C kompajler tekući. Ako se želi pristupiti fajlu koji nije u tekućem
folderu, potrebno je navesti kompletnu putanju do njega: "C:\\Temp\\Drugi.txt".
Uočite \\ unutar stringa. Znate li čemu služe? Način pristupa je takođe string, koji može
sadržati sljedeće karaktere:
 r otvaranje fajla za čitanje,
 w otvaranje fajla za upis,
 a dopisivanje na kraj datoteke,
 + omogućava i upis i čitanje,
 b otvaranje datoteke u binarnom modu,
 t otvaranje datoteke u tekstualnom modu (podrazumijevano).
U tabeli 7 su date moguće vrijednosti stringa koji predstavlja način pristupa, kao i
objašnjenje šta će se desiti ukoliko fajl već postoji ili ne postoji.

Način
Značenje Ako fajl postoji/ne postoji
pristupa
r Čitanje Ako fajl ne postoji, fopen vraća NULL.
rb Čitanje u binarnom modu Ako fajl ne postoji, fopen vraća NULL.
Ako fajl postoji, njegov sadržaj će biti prepisan.
w Upis
Ako fajl ne postoji, biće kreiran.
Ako fajl postoji, njegov sadržaj će biti prepisan.
wb Upis u binarnom modu
Ako fajl ne postoji, biće kreiran.
a Dopisivanje na kraj fajla Ako fajl ne postoji, biće kreiran.
Dopisivanje na kraj fajla u
ab Ako fajl ne postoji, biće kreiran.
binarnom modu
r+ Čitanje i upis Ako fajl ne postoji, fopen vraća NULL.
Čitanje i upis u binarnom
rb+ Ako fajl ne postoji, fopen vraća NULL.
modu
Ako fajl postoji, njegov sadržaj će biti prepisan.
w+ Čitanje i upis
Ako fajl ne postoji, biće kreiran.
Čitanje i upis u binarnom Ako fajl postoji, njegov sadržaj će biti prepisan.
wb+
modu Ako fajl ne postoji, biće kreiran.
a+ Čitanje i dopisivanje Ako fajl ne postoji, biće kreiran.
Čitanje i dopisivanje u
ab+ Ako fajl ne postoji, biće kreiran.
binarnom modu
Tabela 7. Načini pristupa fajlu i odgovarajuća značenja.
1.2 P ROGRAMSKI JEZIK C 97

Vidimo da u slučaju pokušaja otvaranja nepostojećeg fajla za čitanje, funkcija fopen


vraća NULL pokazivač. Da ponovimo, dobra programerska praksa nalaže provjeru da li
je vraćeni pokazivač NULL pokazivač.
Objasnimo ovdje razliku tekstualnog i binarnog režima pristupa fajlu. Kod
binarnog režima, podaci se u fajl smještaju u zavisnosti od binarne reprezentacije tipa
podatka, dok se u tekstualnom režimu svi podaci smještaju u formi ASCII koda (jedan
karakter-jedan bajt). Tako se, na primjer, broj 1234567 u binarnom režimu zapisuje sa
4 bajta (pod uslovom da int zauzima 4 bajta), dok u tekstualnom režimu taj broj
zauzima 7 bajtova, za svaki karakter po jedan ('1', '2', '3', '4', '5', '6' i '7').
Postoje još neke razlike u smještanju karaktera za prelazak u novi red i karaktera koji
označava kraj fajla (EOF), ali nam to ovdje nije bitno.

1.2.23.2 Upis i čitanje iz fajla. Kretanje kroz fajl. Zatvaranje fajla


Funkcije za upis i čitanje iz fajla su veoma slične onima za standardni ulaz/izlaz.
Obično počinju sa slovom f (od fajl) i imaju dodatni argument koji predstavlja
pokazivač na fajl. Tako se umjesto funkcije printf za upis može koristiti funkcija
fprintf u obliku:
fprintf(a, ostali_djelovi);
gdje je a pokazivač na (otvoreni) fajl, a ostali djelovi su kao kod printf. Upis jednog
karaktera (c) se obavlja funkcijom fputc na sljedeći način:
fputc(c,a);
dok se upis stringa s vrši funkcijom:
fputs(s,a);
Za učitavanje karaktera može poslužiti i funkcija putc u obliku:
putc(c,a);
Prilikom upisivanja u fajl, dolazi do pomjeranja pozicije na koju će biti vršeno
naredno upisivanje. To se odvija na približno isti način na koji se vrši pisanje na ekranu
funkcijama za upis.
Slično se obavlja i čitanje iz fajla. Tako funkcija:
fscanf(a, ostali_djelovi);
učitava tražene podatke iz fajla na koji pokazuje a. I ovdje ostali_djelovi
predstavljaju argumente koji su identični onima kod funkcije scanf. Za učitavanje
jednog karaktera sa tekuće pozicije iz fajla označenog pokazivačem a može poslužiti
funkcija fgetc u zapisu:
fgetc(a);
kao i verzija:
1.2 P ROGRAMSKI JEZIK C 99

različitu od nule (logičku istinu) ako se došlo do kraja fajla i nulu (logičku neistinu) ako
nije.
Kada se završi rad sa nekim fajlom potrebno ga je zatvoriti. To se obavlja funkcijom
fclose kojoj se prosljeđuje pokazivač na fajl, tj. naredbom:
fclose(a);
U nekim implementacijama, možete koristiti funkciju fcloseall() ili
_fcloseall() kojom se zatvaraju svi eventualno otvoreni fajlovi.

1.2.23.3 Brisanje i promjena imena fajla


Fajlovi se mogu obrisati funkcijom remove, kojoj se proslijeđuje ime željenog fajla
u formi stringa. Ukoliko se fajl ne nalazi u tekućem folderu, potrebno je navesti puno
ime fajla, koje uključuje i putanju do fajla. Funkcija vraća 0 ako je fajl uspješno obrisan
i vrijednost različitu od 0 u slučaju greške. Tako, na primjer, sljedeći dio koda:
if(remove("C:\\Temp\\Test.c")==0)
puts("Fajl uspjesno obrisan");
else
puts("Fajl nije obrisan");
pokušava da obriše fajl Test.c, koji se nalazi u folderu C:\Temp, i štampa odgovarajuću
poruku.
Promjena imena postojećeg fajla se vrši funkcijom rename, koja za argumente ima
postojeće i novo ime fajla. Funkcija vraća 0 ako je fajl uspješno preimenovan i
vrijednost različitu od 0 u slučaju greške. Na primjer, promjena imena fajla
C:\Temp\Test.txt u C:\Temp\Proba.txt bi se izvršila sa:
rename("C:\\Temp\\Test.txt","C:\\Temp\\Proba.txt");

1.2.23.4 Redirekcija
Pored opisanih operacija, za rad sa fajlovima mogu da posluže i funkcije
operativnog sistema. Jedna od njih je redirekcija (preusmjeravanje). Redirekcija
omogućava da podatke učitavamo u program iz tekstualnog fajla ili da podatke iz
programa štampamo u tekstualni fajl, ali pritom ne koristeći ranije opisane funkcije za
čitanje i upis u fajl, već automatski, korišćenjem ove funkcionalnosti operativnog
sistema. Objasnimo redirekciju koristeći komandnu liniju Windows operativnog
sistema (ranije MS-DOS operativni sistem). Kreirajmo sljedeći program:
#include<stdio.h>

int main()
{
int a,b,c;
scanf("%d%d",&a,&b);
c=a+b;
100 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

printf("%d = %d + %d",c,a,b);
}
Pod pretpostavkom da je program preveden na mašinski jezik i da je naziv izvršne
verzije test.exe, otkucajmo
test
u komandnoj liniji i unesimo dva cijela broja (recimo, 5 i 9). Dobijamo odgovarajući
rezultat (recimo, 14 = 5 + 9). Na ovaj način smo do sada radili, tj. program pauzira
izvršavanje (funkcija scanf) dok korisnik ne unese potrebne podatke i pritisne Enter.
Kreirajmo sad fajl ulaz.txt pomoću raspoloživog editora, npr. Notepad-a, u koji
unesemo dva cijela broja, razdvojena bjelinom. Zadavanjem naredbe u komandnoj liniji:
test < ulaz.txt
sadržaj fajla ulaz.txt proslijeđujemo programu test umjesto standardnog ulaza,
tako da se ne čeka korisnikov unos, već se nakon ove naredbe odmah štampa
odgovarajući izlaz.
Slično, efekat naredbe:
test > izlaz.txt
je štampanje rezultata programa (sad opet korisnik treba da unese dva cijela broja) u
automatski kreirani fajl izlaz.txt, a ne u komandnoj liniji.
Konačno, ove dvije naredbe se mogu kombinovati na sljedeći način:
test < ulaz.txt > izlaz.txt
čime se ulaz čita iz fajla ulaz.txt, a izlaz štampa u fajl izlaz.txt.
Još jednom napominjemo da je redirekcija funkcija operativnog sistema, a ne
programskog jezika C, ali da se može koristiti na prikladan način u programiranju.
I veoma stari programski jezici kakvi su FORTRAN i BASIC imali su
implementiranu mogućnost rada sa fajlovima (bazama podataka). Posebno je
programski jezik COBOL specijalizovan za ovu namjenu. Programski jezik C i drugi
srodni jezici su nadogradili ove mogućnosti starijih programskih jezika najčešće
sofisticiranijim funkcijama. Postoje posebni programski paketi namjenjeni prevashodno
radu sa fajlovima kao podacima u cilju obrade podataka. Poznati jezik za podršku radu
sa bazama podataka je SQL.

1.2.24 Još malo o standardima C99 i C11

Pobrojmo do sad obrađene novine koje je donio standard C99 u odnosu na


prethodno važeći ANSI C standard (poznat i kao C89 ili C90):
 deklaracija promjenljivih bilo gdje u kôdu, a ne samo na početku bloka;
 jednolinijski komentari //;
1.3 L INKOVANI TIPOVI PODATAKA 107

1.3 Linkovani tipovi podataka

1.3.1 Liste

Linkovana lista (eng. linked list), ili samo lista, je napredan tip podatka koji
predstavlja niz međusobno povezanih objekata. Lista nije standardan niz, gdje su
elementi smješteni jedan za drugim u memoriji, već kod liste svaki element, još se
naziva i čvor (eng. node), predstavlja zaseban objekat u memoriji, nezavisan od pozicije
ostalih elemenata. Povezivanje čvorova liste, radi kretanja kroz nju i vršenja operacija
kao što su ubacivanje novih i brisanje postojećih elemenata, se vrši pomoću pokazivača.
Lista, kao tip podatka, postoji implementirana u nekim objektno-orjentisanim
programskim jezicima, kakvi su Java i VB.NET. U programskom jeziku C, lista ne
postoji kao tip podatka, već se čvorovi liste implementiraju preko struktura, a povezuju
koristeći pokazivače.
Već smo rekli da struktura može da sadrži pokazivač na drugu strukturu istog tipa.
Ovo se može iskoristiti kao osnova za kreiranje listi, u smislu da svaki element liste
može da pamti sljedeći element u listi, što je vrlo korisno u mnogim aplikacijama.
Posmatrajmo jedan jednostavan primjer kao motivaciju za uvođenje listi. Pretpostavimo
da imamo niz od N brojeva, koji zauzima N susjednih pozicija u memoriji. Vršimo
njegovo sortiranje (bilo kojom metodom) i dobijemo sortirani niz brojeva. Šta se dešava
ako dobijemo za obradu novih K brojeva? Da li se mora ponovo implementirati
operacija sortiranja nad svim brojevima? Ako se ovaj niz predstavi preko liste, to se ne
mora dogoditi. U nastavku ćemo objasniti kako.
Objasnimo prvo kako bismo mogli riješiti problem sa sortiranjem niza u slučaju
ubacivanja novog elementa. Pretpostavimo da imamo niz brojeva 5 3 1 6 8 9, i pored
tog niza posmatrajmo još jedan niz, nazovimo ga pozicije, čiji elementi predstavljaju
poziciju sljedećeg člana u sortiranom nizu (posmatraćemo prirodne pozicije 1, 2, 3, ...,
a ne pozicije elemenata u C nizu 0, 1, 2, ...). Ova dva niza sada možemo predstaviti kao:
niz 5 3 1 6 8 9
pozicije 4 1 2 5 6 0
Najmanji element u nizu je 1, nakon njega dolazi element na poziciji 2 (to je 3), nakon
3 dolazi element na poziciji 1 (to je 5) itd. Dakle, počevši od najmanjeg elementa,
krećemo se kroz niz prateći odgovarajuće vrijednosti u nizu pozicije. Pošto nakon
najvećeg elementa više nema elemenata, odgovarajući element niza pozicije je 0. Ako
sada želimo da dodamo broj 4 u niz, dodaćemo ga na kraj niza, tj. na sedmu poziciju. U
sortiranom poretku, on bi trebao da se nađe između elemenata 3 i 5. Tako bi za element
niza 3 sljedeći element bio novododati na poziciji 7, dok odgovarajući element niza
pozicije za novododati element 4 treba da bude 1. Na ovaj način se raskida veza
108 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

između elemenata 3 i 5, i između njih ubacuje element 4, tako da 3 pokazuje na 4, a 4


na 5. Ništa drugo se ne bi mijenjalo u memorijskoj predstavi ostalih elemenata:
niz 5 3 1 6 8 9 4
pozicije 4 7 2 5 6 0 1
Naravno, u ovom primjeru mi koristimo dodatnu memoriju za niz pozicije, ali će
procentualno taj gubitak kod složenijih tipova podataka modelovanih listom biti znatno
manji. Prethodno opisani metod eliminiše problem sortiranja nizova prilikom dodavanja
novih elemenata, ali ne eliminiše drugi fundamentalni problem, a to je realokacija niza
za smještanje novog elementa. Sličan problem sa realokacijom postoji i kad se brišu
elementi liste. Sama realokacija memorije, koja se može vršiti funkcijom realloc,
može da traje izvjesno vrijeme i da ima neizvjesan ishod u slučaju da je predmetni niz
vrlo velike dužine i da se podaci znatno složeniji nego u prethodnom primjeru. Treba
izbjegavati česte realokacije velikih nizova.
Rješenje na probleme memorijske alokacije novih elemenata liste, brisanje
postojećih elemenata, i sortiranja liste u slučaju ubacivanja novih elemenata pruža lista
implementirana preko samoreferentnih struktura.

1.3.1.1 Kreiranje jednostruko povezane liste


Objasnimo prvo način kreiranja čvorova liste. Posmatrajmo sljedeću strukturu:
struct st{
int i;
struct st *sljed;
};
Radi jednostavnosti ilustracije, ova struktura za podatak ima cio broj i. Tu, generalno,
možemo staviti proizvoljne podatke. Obavezni dio strukture pomoću koje se
implementira lista je pokazivač na strukturu istog tipa, kod nas struct st *sljed.
Pošto će svaki čvor liste pokazivati na sljedeći čvor u listi, pokazivač smo nazvali
sljed. Lista kod koje čvorovi pokazuju samo na sljedeći čvor u listi se naziva
jednostruko povezana lista. Pokazivač posljednjeg čvora ove liste je NULL pokazivač.
U nastavku ćemo jednostruko povezanu listu jednostavno zvati listom, a ostale tipove
ćemo zvati punim imenom.
Nakon deklaracije pokazivača na strukturu, tj.
struct st *s;
prvi čvor liste možemo dinamički alocirati naredbom:
s=(struct st *)malloc(sizeof(struct st));
Ovu naredbu bi trebalo ispratiti odgovarajućom provjerom da li je memorija ispravno
alocirana (da li je vraćeni pokazivač jednak NULL), što ćemo preskočiti u ovoj priči.
Sljedeći korak je upis podataka u čvor liste:
s‐>i=4;
1.3 L INKOVANI TIPOVI PODATAKA 109

a nakon toga se pokazivač postavi na adresu sljedećeg člana u listi. Pošto je ovo jedini
član liste, tj. sljedeći element ne postoji, uradićemo sljedeće:
s‐>sljed=NULL;
Na ovaj način je kreirana lista od jednog člana.
Ako se dodaje novi čvor u listu, prvo treba alocirati memoriju za njega (opet, uz
odgovarajuću provjeru):
p=(struct st *)malloc(sizeof(struct st));
Postavi se vrijednost ovog čvora (npr. p‐>i=1), postavi se da prethodni čvor pokazuje
na ovaj:
s‐>sljed=p;
Na ovaj način se povezuju čvorovi liste. Pošto je novododati čvor istovremeno i zadnji
čvor u listi, potrebno je uraditi sljedeće:
p‐>sljed=NULL;
Posljednji čvor liste pokazuje na NULL.
Procedura se ponavlja za svaki naredni čvor liste i ilustrovana je na slici 7:

Slika 7. Dodavanje čvora na kraj jednostruko povezane liste.

Prvi čvor liste se još naziva i glava liste, dok se zadnji naziva rep liste.

1.3.1.2 Umetanje čvora u unutrašnjost liste


Neka je s pokazivač na neki čvor u unutrašnjosti liste (tj. nije ni glava ni rep liste).
Ukoliko želimo unijeti novi čvor u listu, nakon elementa na koji pokazuje s, to se mora
obaviti na sljedeći način. Pretpostavimo da je element koji se unosi dat pokazivačem p.
Postavi se da p pokazuje na element na koji je prethodno pokazivao s (korak (1) na
slici 8):
p‐>sljed=s‐>sljed;
a zatim se postavi da s pokazuje na p (korak (2) na slici 8):
s‐>sljed=p;
Ova situacija je prikazana na slici 8.
114 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

se može brisati glava ili bilo koji drugi element liste. Funkcije dodajCvor i
obrisiCvor vraćaju glavu na izmjenjenu listu, a ta glava se može razlikovati do
originalne glave. Posljednja operacija koju vršimo nad listom je brisanje čitave liste
funkcijom obrisiListu. Nakon svake operacije nad listom, vrši se štampanje liste
void funkcijom stampajListu.
#include<stdio.h>
#include<stdlib.h>

struct lista{
int i;
struct lista *sljed;
};

struct lista *dodajCvor(struct lista *,int);


struct lista *obrisiCvor(struct lista *,int);
void stampajListu(struct lista *);
struct lista *obrisiListu(struct lista *);

int main()
{
struct lista *glava,*q,*t;
int a[]={1,3,6,10,15},k=0,n=sizeof(a)/sizeof(int),x;
// Kreiramo glavu liste
glava=(struct lista *)malloc(sizeof(struct lista));
if(glava==NULL) exit(1);
glava‐>sljed=NULL;
glava‐>i=a[k++];
// Kreiramo ostatak liste
t=glava;
while(k<n)
{
q=(struct lista *)malloc(sizeof(struct lista));
if(q==NULL) exit(1);
q‐>sljed=NULL;
q‐>i=a[k++];
t‐>sljed=q;
t=q;
}
stampajListu(glava);

printf("Unijeti broj koji se dodaje u listu: ");


scanf("%d",&x);
glava=dodajCvor(glava,x);
stampajListu(glava);

printf("Unijeti broj koji se brise iz liste: ");


scanf("%d",&x);
glava=obrisiCvor(glava,x);
116 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

{
struct lista *pom=glava‐>sljed;
glava‐>sljed=glava‐>sljed‐>sljed;
free(pom);
}
return novaGlava;
}

void stampajListu(struct lista *glava)


{
if(glava==NULL)
printf("Lista je prazna\n");
else
{
printf("Lista je: ");
while(glava!=NULL)
{
printf("%d ",glava‐>i);
glava=glava‐>sljed;
}
printf("\n");
}
}

struct lista *obrisiListu(struct lista *glava)


{
struct lista *pom;
while(glava!=NULL)
{
pom=glava‐>sljed;
free(glava);
glava=pom;
}
return glava;
}
U nastavku su data tri izvršenja ovog programa, sa različitim kombinacijama
ulaznih vrijednosti.
Lista je: 1 3 6 10 15
Unijeti broj koji se dodaje u listu: 0
Lista je: 0 1 3 6 10 15
Unijeti broj koji se brise iz liste: 15
Lista je: 0 1 3 6 10
Sad brisemo listu
Lista je prazna

Lista je: 1 3 6 10 15
Unijeti broj koji se dodaje u listu: 8
Lista je: 1 3 6 8 10 15
1.3 L INKOVANI TIPOVI PODATAKA 121

cn[i][j]=min(cs[i][j],cs[i][k]+cs[k][j]);
d(i) d(j)
cs[i][j]=cn[i][j];
}
d(i){
d(j)
printf("%d",cn[i][j]);
printf("\n");
}
}
I pored značaja pojma grafa, mi se nećemo duže na njemu zadržavati. Preći ćemo
na razmatranje tipa podataka koji se naziva stablo.

1.3.3 Stabla

Stablo ili drvo (eng. tree) je specijalni tip grafa, koje svojim oblikom i osobinama
donekle podseća na porodično stablo. U stablu svaki čvor ima tačno jedan čvor koji na
njega ukazuje. Svaki čvor može imati potomke, tj. čvorove koji potiču od datog čvora.
Za čvor kažemo da je predak svim čvorovima koji potiču od njega. Izvorišni čvor je
onaj na koji ne ukazuje nijedan drugi čvor i naziva se korijenom (eng. root). Čvorovi
koji nemaju potomke nazivaju se listovima.
Dajmo sada preciznija objašnjenja pojmova vezanih za stablo.
Usmjereni graf u kome nema ciklusa zove se usmjereni aciklični graf. Stablo je
usmjereni aciklični graf koji zadovoljava sljedeća tri svojstva:
 Postoji tačno jedan čvor, korijen, koji nije kraj nijedne ivice (tj. korijen nema
pretke).
 Svakom čvoru, izuzev korijena, odgovara tačno jedna ivica čiji je kraj taj čvor (tj.
svaki čvor osim korijena ima tačno jedan čvor koji na njega ukazuje).
 Postoji put (lako se dokazuje da je jedinstven) od korijena ka svakom čvoru.
Neka je T=(V,E) stablo. Ako (v,w) pripada E, onda se kaže da je v otac čvora w i da
je w sin čvora v. Ako postoji put od v ka w, kaže se da je v predak čvora w i da je w
potomak čvora v. Ako je osim toga vw, onda je v pravi predak čvora w, a w je pravi
potomak čvora v. Čvorovi koji nemaju pravih potomaka nazivaju se listovima. Čvor v
zajedno sa svojim potomcima naziva se podstablo od T, a sam čvor v je korijen tog
podstabla.
Visina čvora v u stablu jeste dužina najdužeg puta od v ka nekom listu. Visina stabla
jeste visina korijena.
Binarno stablo je stablo u kome važi:
 Svaki sin nekog čvora je označen ili kao lijevi sin ili kao desni sin tog čvora.
122 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

 Nijedan čvor nema više od jednog lijevog sina, niti više od jednog desnog sina.
Na slici 18 je dat grafički prikaz jednog binarnog stabla.

Slika 18. Grafički prikaz jednog binarnog stabla visine 3.

Podstablo Tl (ako ono postoji) čiji korijen je lijevi sin čvora v zove se lijevo
podstablo od v. Slično, podstablo Tr (ako ono postoji) čiji je korijen desni sin od v zove
se desno podstablo od v. Za svaki čvor iz Tl kažemo da se nalazi lijevo, tj. prije bilo kog
čvora iz Tr. Obično se binarno stablo predstavlja pomoću dva podniza LEFTSON i
RIGHTSON. Neka su vrhovi binarnog stabla označeni brojevima od 1 do n. Tada je
LEFTSON[i]=j akko je j lijevi sin od i. Ako čvor i nema lijevog sina, tada je
LEFTSON[i]=0. Slično se definiše i RIGHTSON. Na slici 19 je prikazano jedno
binarno stablo i njegova reprezentacija pomoću ovih nizova.

Slika 19. Jedno binarno stablo i njegova reprezentacija pomoću nizova LEFTSON i
RIGHTSON.

Potpuno binarno stablo je ono kod kojeg svaki čvor, osim listova, ima oba
potomka. Svi listovi potpunog binarnog stabla se nalaze na istoj udaljenosti (visini) od
korjena stabla. Na slici 20 je prikazano jedno potpuno binarno stablo visine 3.
124 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Pored inorder redosljeda postoje i preorder i postorder obilasci. Kod preorder


obilaska se prvo posjeti korijen, a zatim lijevo i desno podstablo, dok se kod postorder
obilaska prvo posjeti lijevo podstablo, pa desno podstablo i na kraju korijen. Sljedeći
primjer ilustruje ove tri metode obilaska binarnog stabla.
Primjer: Popuniti čvorove binarnog stabla sa slike 21 stavljajući u svaki čvor po jedno
slovo riječi KOMPAJLER tako da redosljed obilaska ovog stabla daje riječ
KOMPAJLER, ukoliko se koristi:
 inorder obilazak,
 preorder obilazak i
 postorder obilazak.
Na slici 22 je prikazano rješenje ovog problema.

Slika 22. Inorder, preorder i postorder obilasci binarnog stabla koji daju riječ KOMPAJLER.

Primjer: Zadato je binarno stablo i treba mu izračunati visinu. Neka je stablo definisano
preko LEFTSON i RIGHTSON nizova. Učitava se n čvorova. Korijenu odgovara indeks
1. Za ovo postoji efikasan rekurzivni algoritam koji se zasniva na sljedećim pravilima:
 Ako nema ni lijevog ni desnog podstabla, tj. ako je r=0 i l=0, onda je visina 0.
 Ako postoji i lijevo i desno podstablo, tj. ako je r0 i l0, onda se odredi visina
lijevog i desnog podstabla, pa se uzima veći od ta dva broja uvećan za 1.
 Ako je r0 i l=0 onda se uzima visina desne strane uvećana za 1.
 Ako je r=0 i l0 onda se uzima visina lijeve strane uvećana za 1.
Programska realizacija je data narednim kodom:
#include<stdio.h>

int n,le[100],ri[100];
int max(int,int);

int visina(int i)
{
int l,r;
l=le[i];
r=ri[i];
128 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

2.1 Kontrola toka programa

Zadatak 1. Napisati program koji simulira rad jednostavnog kalkulatora, koji će za dva unijeta
broja i jedan od osnovnih aritmetičkih operatora dati vrijednost izvršene operacije.
#include<stdio.h>

int main()
{
char oper;
float a,b;
printf("Unesite operator i dva broja: ");
scanf("%c%f%f",&oper,&a,&b);
switch(oper)
{
case '+':
printf("%f\n",a+b);
break;
case '‐':
printf("%f\n",a‐b);
break;
case '*':
printf("%f\n",a*b);
break;
case '/':
if (b!=0)
printf("%f\n",a/b);
else
puts("Dijeljenje nulom!");
break;
default:
puts("Ne moze taj operator");
}
}

Promjenljive korišćene u programu:


oper operator matematičke operacije
a, b operandi
Zadatak je prilično jednostavan i, mi se nadamo, realizacija razumljiva. Ako je zadat
operator +, ‐ ili *, primjenjuju se odgovarajuće operacije nad operandima. Ako je zadat operator
/, vrši se provjera da li je imenilac jednak nuli i štampa odgovarajuće upozorenje ako se desi
ovaj izuzetak. Ako je kao oper unesen neki karakter koji ne odgovara znacima četiri osnovne
matematičke operacije, ispisuje se odgovarajuća poruka. Podsjetimo se da pojedinačne unose
2.1 K ONTROLA TOKA PROGRAMA 129

razdvajamo bjelinama, dok se nakon posljednjeg unosa (u našem slučaju, drugog operanda) mora
pritisnuti Enter.
Izvršenje programa:
Unesite operator i dva broja: * 2 3
6.000000
Unesite operator i dva broja: %
4.5
7.8
Ne moze taj operator

Zadatak 2. Učitavaju se realni brojevi a i b. Tabelarno prikazati funkciju y=asin(x)+bcos(x) za


vrijednosti x = 0, 0.1, 0.2, itd. Prikaz se obustavlja kada se dogodi da dvije susjedne
vrijednosti y imaju suprotne predznake.
#include<stdio.h>
#include<math.h>

int main()
{
float x=0.0,a,b,p,t;
printf("Unesite brojeve a i b\n");
scanf("%f%f",&a,&b);
t=b;
do
{
printf("%f \t %f\n",x,t);
p=t;
x+=0.1;
t=a*sin(x)+b*cos(x);
} while(t*p>0);
}

Promjenljive korišćene u programu su:


a, b vrijednosti koje zadaje korisnik
t, p tekuća i prethodna vrijednost funkcije
x nezavisno promjenljiva
Nakon učitavanja a i b, postavlja se tekuća vrijednost funkcije na b jer se funkcija
izračunava za x = 0, gdje je sin(x) jednako 0 i cos(x) jednako 1. Zatim se ulazi u do...while
petlju gdje se vrši štampanje rezultata i zatim se postavlja vrijednost p na t, inkrementira x i
sračuna nova vrijednost funkcije. Prilikom štampanja se prvo štampa broj u formatu realnog
broja, zatim tabulacija sa \t i na kraju realni broj t. Funkcija printf završava prelaskom u
novi red. Program se izvršava sve dok su t i p istog znaka (proizvod promjenljivih istog znaka
je veći od nule).
Izvršenje programa:
Unesite brojeve a i b
130 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

0.3 ‐4
0.000000 ‐4.000000
0.100000 ‐3.950067
0.200000 ‐3.860666
0.300000 ‐3.732690
0.400000 ‐3.567419
0.500000 ‐3.366503
0.600000 ‐3.131950
0.700000 ‐2.866103
0.800000 ‐2.571620
0.900000 ‐2.251441
1.000000 ‐1.908767
1.100000 ‐1.547022
1.200000 ‐1.169819
1.300000 ‐0.780927
1.400000 ‐0.384233

Zadatak 3. Napisati program koji za unijeti prirodni broj računa sumu i proizvod svih prirodnih
brojeva koji su manji od ili jednaki tom broju.
#include<stdio.h>

int main()
{
int a,i=1,sum=0;
long int prod=1;
puts("Unesi neki prirodni broj");
scanf("%d",&a);
while(i<=a)
{
sum+=i;
prod*=i;
i++;
}
printf("Suma prirodnih brojeva do broja %d je: %d\n",a,sum);
printf("Proizvod prirodnih brojeva do broja %d je: %ld\n",a,prod);
}

Promjenljive korišćene u programu:


a prirodni broj do kojega se traže suma i proizvod
i pomoćna brojačka promjenljiva koja broji od 1 do a
sum promjenljiva u kojoj se čuva suma elemenata niza
prod promjenljiva u kojoj se čuva proizvod niza brojeva
Program rješava jedan od elementarnih programerskih problema. Suma se računa tako što
se prolazeći kroz petlju dodaje broj po broj sumi (probajte da umjesto while petlje iskoristiti
for ili do...while). Slično se formira i proizvod. Mora se izvršiti inicijalizacija sume (na
vrijednost 0) i proizvoda (na vrijednost 1), jer nijesmo sigurni šta se nalazi na memorijskim
lokacijama koje su zauzele ove promjenljive prilikom deklaracije.
2.1 K ONTROLA TOKA PROGRAMA 131

Izvršavanje programa:
Unesi neki prirodni broj
6
Suma prirodnih brojeva do broja 6 je: 21
Proizvod prirodnih brojeva do broja 6 je: 720

Zadatak 4. Napisati program koji provjerava da li je učitani prirodan broj N Hamming-ov broj.
Prirodan broj je Hamming-ov ukoliko su mu jedini prosti činioci 2, 3 ili 5.
#include<stdio.h>

int main()
{
int N;
printf("Unijeti prirodan broj: ");
scanf("%d",&N);
while(N%2==0)
N/=2;
while(N%3==0)
N/=3;
while(N%5==0)
N/=5;
if(N==1)
printf("Broj je Hamming‐ov");
else
printf("Broj nije Hamming‐ov");
}

Promjenljive korišćene u programu su:


N prirodan broj koji se provjerava da li je Hamming-ov
Tri while petlje eliminišu proste činioce 2, 3 i 5 iz učitanog broja. Ukoliko N nije
Hamming-ov broj, nakon ove tri petlje će biti veći od 1, jer sadrži i drugih prostih činilaca.
Izvršenja programa:
Unijeti prirodan broj: 24
Broj je Hamming‐ov

Unijeti prirodan broj: 105


Broj nije Hamming‐ov

Zadatak 5. Napisati program kojim se učitavaju prirodan broj k (stepen polinoma) i realni
brojevi a[0], a[1],…, a[k] (koeficijenti polinoma), čime se zadaje polinom
p(x)=a[0]+a[1]x+...+a[k]xk. Program treba da izračuna i odštampa vrijednost
polinoma p(x), za unijetu vrijednost realnog argumenta x.
#include<stdio.h>
132 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

int main()
{
int k,i;
float p=0,a,x,proiz=1;
printf("Unesite red polinoma: ");
scanf("%d",&k);
printf("Unesite vrijednost za x: ");
scanf("%f",&x);
printf("Unesite koeficijente polinoma ");
for(i=0;i<=k;i++)
{
scanf("%f",&a);
p+=a*proiz;
proiz*=x;
}
printf("Vrijednost polinoma za dato x je %f\n",p);
}

Promjenljive korišćene u programu su:


k red polinoma
i pomoćna brojačka promjenljiva
p vrijednost polinoma
a tekući koeficijenat polinoma
proiz promjenljiva u kojoj se čuva vrijednost xk
x vrijednost argumenta za koju se računa vrijednost polinoma
Polazimo od vrijednosti p=0 i proiz=1. U svakom koraku vršimo uvećanje polinoma za
vrijednost tekućeg člana a*proiz, pri čemu tekući koeficijent a učitavamo u petlji, a vrijednost
xk dobijamo naredbom proiz*=x.
Čitaocima koji su upoznati sa nizovima preporučujemo da napišu program u kome će
koeficijente polinoma učitavati u niz realnih brojeva i kao takve koristiti u izračunavanju
vrijednosti polinoma.
Izvršenje programa:
Unesite red polinoma: 3
Unesite vrijednost za x: 2
Unesite koeficijente polinoma 1 2 3 4
Vrijednost polinoma za dato x je 49.000000

Zadatak 6. Sastaviti program za pseudo-crtanje kvadrata. Program od korisnika traži unos


dužine stranice kvadrata, izraženu brojem karaktera, kao i karakter sa kojim će taj
kvadrat biti iscrtan.
#include<stdio.h>

int main()
{
char a;
2.1 K ONTROLA TOKA PROGRAMA 133

int i,j,dim;
printf("Unesi karakter i dimenzije kvadrata: ");
scanf("%c%d",&a,&dim);
for(i=1;i<=dim;i++)
{
if(i==1||i==dim)
{
for(j=1;j<=dim;j++)
printf("%c",a);
}
else
{
printf("%c",a);
for(j=2;j<dim;j++)
printf(" ");
printf("%c",a);
}
printf("\n");
}
}

Promjenljive korišćene u programu su:


i, j pomoćne promjenljive korišćene kao brojači
dim dimenzije kvadrata
a karakter koji se zadaje
Kvadrat se sastoji od gornje i donje stranice u kojima se dim puta ponavlja karakter a i od
bočnih stranica (dim‐2 reda), gdje se predmetni karakter pojavljuje na počeku i na kraju reda, a
između imamo bjeline. Unutar programske petlje se ispituje da li je u pitanju prvi i posljednji red
kvadrata. Ako jeste, ispisuje se karakter a dim puta, a ako nije ispisuju se bočne stranice (karakter
na početku i kraju i dim‐2 bjelina između).
Pokušajte da napišete program sa dvije ugnježdene for petlje i jednom if naredbom, sa
uslovom koji ako je ispunjen treba upisati karakter a, a ako nije bjelinu. Pomoć: Uslov je prilično
jednostavan: if(i==1||i==dim||j==1||j==dim).
Izvršenje programa:
Unesi karakter i dimenzije kvadrata: +
7
+++++++
+ +
+ +
+ +
+ +
+ +
+++++++
136 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

6
Priblizna vrijednost korijena je 2.44949

Zadatak 9. Napisati program koji pronalazi nulu funkcije y = x - cos(x), u intervalu x[0,/2)
iterativnim postupkom, koji se može opisati na sljedeći način:
Data je jednačina f(x) = 0, gde je f:[a,b]R, i x = g(x) je njen ekvivalentni oblik.
Ukoliko g:[a,b]R i g(x) ima izvod u svakoj tački x[a,b] takav da je |g'(x)|<1,
tada f(x) = 0 ima jedinstveno rješenje na intervalu [a,b] koje se može odrediti
iterativnom procedurom: xk+1 = g(xk), k = 0, 1, 2, ..., sa proizvoljnim x0[a,b].

#include<stdio.h>
#include<math.h>

int main()
{
float x0=1,x1,x2;
x2=cos(x0);
do{
x1=x2;
x2=cos(x1);
} while(fabs(x1‐x2)>=0.0001);
printf("Trazena nula je: %f\n",x2);
}

Promjenljive korišćene u programu su:


x0 početno pretpostavljeno rješenje
x1 rješenje u prethodnoj iteraciji
x2 tekuće rješenje
Na prvi pogled riječ je o jednostavnom programu, ali se mora voditi računa o sljedećoj
činjenici. Postoje dvije moguće funkcije g(x). Jedna je očigledno cos(x), ali se jednačina može
riješiti i kao x = arccos(x). Prije nego što se pređe na pisanje programa treba provjeriti koja od
ovih funkcija zadovoljava uslov da je izvod u datom intervalu manji ili jednak po modulu od 1.
Izvod funkcije cos(x) je -sin(x) i on je zasigurno manji ili jednak od 1 u čitavom intervalu. Izvod
funkcije arccos(x) je 1/ 1 x2 i ova funkcija ne zadovoljava predmetni uslov. Dalja realizacija
je jednostavna i prati opisani algoritam. Kao tačnost je usvojena vrijednost 0.0001, a može se
izvršiti i zadavanje određene vrijednosti.

Zadatak 10. Napisati program koji računa najveći zajednički djelilac (NZD) brojeva a i b
pomoću Euklidovog algoritma:
 Ako je a=b, tada je NZD=a i to je kraj algoritma.
 U veći broj upišemo razliku većeg i manjeg broja i vraćamo se na prvi korak.

#include<stdio.h>

int main()
{
138 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

h=(b‐a)/N;
I2=I1;
I1=0;
for(i=0;i<=N;i++)
{
x=a+i*h;
fun=sin(2*x*x‐3);
I1+=((i==0)||(i==N))?fun:2*fun;
}
I1*=0.5*h;
} while(fabs(I2‐I1)>eps);
printf("Priblizna vrijednost integrala je: %f\n",I1);
}

Promjenljive korišćene u programu su:


I1, I2 tekuća i prethodna vrijednost integrala
h korak
a, b granice intervala
x, fun vrijednost argumenta i funkcije
eps tačnost
Prvo se računa približna vrijednost integrala za N=2 (linija I1=0.5*h*...). Zatim se ulazi
u do...while petlju u kojoj se prvo množi prethodni broj podintervala sa 2, sračunaju novi
korak h i nova vrijednost integrala I1 u for petlji i naredbi I1*=0.5*h nakon petlje. Naredba
I1+=((i==0)||(i==N))?fun:2*fun u for petlji dodaje sumi vrijednost fun za odbirke na
krajevima intervala, odnosno 2*fun za odbirke unutar intervala. Za dobijanje vrijednosti sin(x),
koristimo funkciju sin iz biblioteke math.h.
Za vježbu pokušajte da optimizujete ovaj kôd polazeći od činjenice da je za računanje
integrala u svakom novom koraku korišćeno N/2 odbiraka iz prethodnog koraka.
Izvršenje programa:
Priblizna vrijednost integrala je: ‐0.528985

Zadatak 12. Napisati program koji za unijeti prirodni broj računa i štampa zbir njegovih cifara.
#include<stdio.h>

int main()
{
int s=0,N;
printf("Unesi prirodan broj\n");
scanf("%d",&N);
while(N!=0)
{
s+=N%10;
N/=10;
}
printf("Suma cifara prirodnog broja je: %d\n",s>0?s:‐s);
2.1 K ONTROLA TOKA PROGRAMA 139

Promjenljive korišćene u programu:


s suma cifara
N prirodni broj za koji se traži suma cifara
Nakon učitavanja prirodnog broja za koji se želi izračunati suma cifara, vrši se određivanje
ostatka dijeljenja tog prirodnog broja sa 10. Ova vrijednost predstavlja posljednju cifru prirodnog
broja. Suma cifara se zatim uvećava za ovu vrijednost, pa se prirodan broj dijeli sa 10. Pošto je
dijeljenje sa 10 obavljeno sa cijelim brojevima, rezultat je cijeli broj bez posljednje cifre.
Procedura se ponavlja do trenutka kada se dođe do broja 0. Rezultat dijeljenja -23 sa 10 je –2,
pa je ostatak pri dijeljenju -3, jer je -2*10-3=-23. Dakle, za negativne brojeve treba izvršiti
promjenu znaka rezultata (što je učinjeno u okviru printf funkcije) ili koristiti predznak kao
indikaciju da je polazni broj negativan.
Izvršenja programa:
Unesi prirodan broj
234
Suma cifara prirodnog broja je: 9

Unesi prirodan broj


‐345
Suma cifara prirodnog broja je: 12

Zadatak 13. Učitavaju se brojevi a, b i c, koji definišu datum, a štampa se sutrašnji datum.
Recimo, ako je (a, b, c) = (31, 1, 1992), treba štampati 1.2.1992.
#include<stdio.h>

int main()
{
int a,b,c,an,bn,cn,prest;
scanf("%d%d%d",&a,&b,&c);
prest=!(c%4)&&(!(c%400)||c%100);
if((a==31)&&(b==12))
{
an=bn=1;
cn=c+1;
}
else if(a==31||(a==30&&(b==4||b==6||b==9||b==11))||(a==29&&b==2)||
(a==28&&b==2&&!prest))
{
an=1;
bn=b+1;
cn=c;
}
else
{
140 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

an=a+1;
bn=b;
cn=c;
}
printf("%d.%d.%d\n",an,bn,cn);
}

Promjenljive korišćene u programu:


a, b, c tekući datum (dan, mjesec, godina)
an, bn, cn naredni datum (dan, mjesec, godina)
prest indikator da li je u pitanju prestupna godina
Kod određivanja datuma postoji nekoliko problema. Prvi je prestupna godina. Naime, svake
četvrte godine postoji 29. februar. Kako rotacija Zemlje oko Sunca nije jednaka tačno 365 dana
i 6 časova uveden je korektivni faktor kojim se tri puta u 400 godina preskače prestupna godina.
Od svih godina koje su djeljive sa 4, nijesu prestupne one koje su djelive sa 100, a nijesu djeljive
sa 400 (npr. 1800, 1900 nijesu prestupne, dok je 2000 prestupna). Kontrola da li je godina
prestupna se obavlja pomoću promjenljive prest. Suštinski, kod određivanja tekućeg datuma
postoje tri varijante: 1) dan je na kraju godine - sljedeći datum je 1.1. i godinu treba uvećati; 2)
dan je na kraju mjeseca - naredni dan je 1. i treba uvećati mjesec za jedan i ostaviti istu godinu;
3) dan nije na kraju mjeseca - treba dan uvećati za 1 i ostaviti isti mjesec i godinu. Ovo je
realizovano u programu. Uočiti relativno komplikovan uslov koji ispituje da li je u pitanju
posljednji dan u mjesecu, koji je takav zbog nejednakog trajanja pojedinih mjeseci. Za vježbu
pokušajte da dodate naredbu (ili naredbe) koje bi ispitivale da li je unesen korektan datum.
Naime, program ovako napisan bi kao korektan datum prihvatio 35.14.
Izvršenja programa:
13 3 2004
14 3 2004

28 2 2000
29 2 2000

31 12 2003
1 1 2004

35 14 2003
36 14 2003
2.2 N IZOVI 141

2.2 Nizovi

Zadatak 1. Napisati program kojim se unosi niz cijelih brojeva, od maksimalno 20 elemenata,
i računa i štampa njegov maksimalni i minimalni element.
#include<stdio.h>

int main()
{
int a[20],min,max,i,N;
printf("Unesi broj clanova niza\n");
scanf("%d",&N);
printf("Unesi clanove niza\n");
for(i=0;i<N;i++)
scanf("%d",a+i);
max=min=a[0];
for(i=1;i<N;i++)
if(a[i]>max)
max=a[i];
else if(a[i]<min)
min=a[i];
printf("Minimum niza je: %d\nMaksimum niza je: %d ",min,max);
}

Promjenljive korišćene u programu su:


a niz od 20 elemenata
min promjenljiva koja čuva minimum niza
max promjenljiva koja čuva maksimum niza
N broj elemenata niza
i pomoćna brojačka promjenljiva
Ovo je jedan od klasičnih programerskih problema gdje se prolazeći kroz elemente niza
određuje neka statistika niza. Nakon učitavanja niza, prvi element se postavlja za tekući
minimum i maksimum. Zatim se u petlji provjeravaju ostali članovi niza. Ako je neki element
veći od tekućeg maksimuma postaje tekući maksimum niza a. Takođe, ako je elemenat manji od
tekućeg minimuma postaje novi tekući minimum. Obratiti pažnju na naredbu za unos elemenata
niza. Kao što je poznato, funkcija scanf zahtjeva adresu promjenljive čija se vrijednost unosi
(što se precizira operatorom &). Ovdje nije potrebno koristiti taj operator jer je ime niza adresa
početnog elementa, a svi elementi niza se smiještaju jedan iza drugog tako da se adresa svakog
sljedećeg dobija uvećanjem prethodne za 1. U zavisnosti od tipa promjenljive, kompajler će ovo
+1 zamjeniti skokom u vrijednosti broja bajtova koji zauzima predmetni tip elementa niza.
Izvršenje programa:
Unesi broj clanova niza
142 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

6
Unesi clanove niza
5 2 3 ‐1 11 9
Minimum niza je: ‐1
Maksimum niza je: 11

Zadatak 2. Napisati program koji računa srednju vrijednost niza cijelih brojeva.
#include<stdio.h>

int main()
{
int i,N,x[50];
float mean=0.0;
printf("Unesi broj clanova niza\n");
scanf("%d",&N);
printf("Unesi clanove niza\n");
for(i=0;i<N;i++)
scanf("%d",x+i);
for(i=0;i<N;i++)
mean+=(float)x[i]/N;
printf("Srednja vrijednost niza je: \t %f \n",mean);
}

Promjenljive korišćene u programu su:


N broj članova niza
x niz cijelih brojeva čija se srednja vrijednost traži
i pomoćna brojačka promjenljiva
mean srednja vrijednost niza
Nakon učitavanja veličine niza i članova niza vrši se uvećavanje promjenljive mean (koja
je na početku inicijalizovana na nulu) za x[i]/N. Kako su obije promjenljive cijeli brojevi i
rezultat bi bio cijeli broj, što bi dovelo do odsijecanja necjelobrojnog dijela. Umjesto toga,
izvršili smo konverziju jedne od promjenljivih koja učestvuje u operaciji u tip float pomoću
cast operatora i na taj način dobili korektan rezultat. Na ovaj zadatak ćemo se vratiti još dva
puta, prvi put u dijelu sa funkcijama i drugi put kod dinamičkog zauzimanja memorije.
Izvršenje programa:
Unesi broj clanova niza
6
Unesi clanove niza
3 4 2 1 5 6
Srednja vrijednost niza je: 3.500000

Zadatak 3. Napisati program kojim se učitavaju prirodan broj k (stepen polinoma) i niz realnih
brojeva a[0], a[1],…, a[k] (koeficijenti polinoma), čime se zadaje polinom
p(x)=a[0]+a[1]x+...+a[k]xk. Program treba da izračuna i odštampa vrijednost
polinoma p(x), za unijetu vrijednost realnog argumenta x.
144 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

for(i=0;i<=k;i++)
scanf("%f",a+i);
for(i=0;i<=l;i++)
scanf("%f",b+i);
for(i=0;i<=k+l;i++)
c[i]=0.0;
for(i=0;i<=k;i++)
for(j=0;j<=l;j++)
c[i+j]+=a[i]*b[j];
for(i=0;i<=k+l;i++)
printf("%f\n",c[i]);
}

Promjenljive korišćene u programu:


i, j pomoćne brojačke promjenljive
k, l red polinoma a i b, respektivno
a, b, c a i b su zadati polinomi, a c je polinom proizvod
Vrijednost m-tog koeficijenta polinoma c se dobija sabiranjem svih mogućih proizvoda i-
tog koeficijenta prvog polinoma i j-tog koeficijenta drugog polinoma, takvih da je m=i+j, tj.

c  m   a i  b  j  pričemu je m  i  j .
i, j

Predmetni program zapravo određuje datu sumu. Nakon učitavanja polinoma a i b, vrši se
inicijalizacija polinoma c (najveći red ovog polinoma je k+l). Zatim se prosto primjeni opisana
operacija. Na kraju se vrši štampanje koeficijenata dobijenog polinoma.
Izvršenje programa:
Unesi red polinoma
2 3
1 2 3
3 2 1 4
3.000000
8.000000
14.000000
12.000000
11.000000
12.000000

Zadatak 5. Napisati program koji formira podniz niza cijelih brojeva koji se sastoji od onih
elemenata koji su veći od zadatog cijelog broja.
#include<stdio.h>

#define MAX 20

int main()
{
int N,k=0,i,a[MAX],b[MAX],C;
2.2 N IZOVI 145

printf("Unesi broj clanova niza: ");


scanf("%d",&N);
printf("Unesi cijeli broj: ");
scanf("%d",&C);
printf("Unesi clanove niza: ");
for(i=0;i<N;i++)
{
scanf("%d",a+i);
if(a[i]>C)
b[k++]=a[i];
}
if(k>0)
{
printf("Elementi niza veci od datog cijelog broja su:\n");
for(i=0;i<k;i++)
printf("%d ",b[i]);
}
}

Promjenljive koje su korišćene u programu:


N ukupan broj članova polaznog niza
k broj članova podniza koji se izdvaja
i pomoćna brojačka promjenljiva
a, b polazni i odredišni niz, respektivno
C broj u odnosu na koju se formira podniz
MAX maksimalan broj članova niza (konstanta zadata pretprocesorskom
direktivom #define)
Na početku programa se učitavaju dužina niza N i broj C koji služi kao kriterijum formiranja
podniza. Da bi program bio korektan, trebalo bi obezbijediti da se prilikom unosa broja N većeg
od MAX javi upozorenje korisniku i/ili prekine izvršavanje programa. Zatim se učitava niz a uz
istovremenu provjeru da li su elementi ovog niza veći od C. Ukoliko jesu, ove elemente
upisujemo u niz b uz istovremeno uvećanje broja članova odredišnog niza k++. Na kraju se
elementi dobijenog podniza štampaju. Trebalo bi obezbjediti da u slučaju da je podniz prazan
bude odštampano odgovarajuće upozorenje. Za vježbu napišite modifikaciju ovog programa koja
bi uzela u obzir dva pomenuta nedostatka datog programa.
Izvršenje programa:
Unesi broj clanova niza: 5
Unesi cijeli broj: 8
Unesi clanove niza: 13 ‐6 21 8 11
Elementi niza veci od datog cijelog broja su:
13 21 11
150 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

 U nastavku napisati dio programa kojim se u zavisnosti od unosa korisnika


štampa neka od dijagonala matrice paralelna sa glavnom dijagonalom. Ako
korisnik unese 0 štampa se glavna dijagonala, ako unese 1 dijagonala iznad
glavne, a ako se unese -1 dijagonala ispod glavne. Na primjer, za matricu
prikazanu u postavci, vrijednost 0 bi dala rezultat 1, 12, 23, 34, vrijednost 1 daje
rezultat 2, 13, 24, dok vrijednost -2 treba da vrati 21, 32.
 Zatim presložiti unesenu matricu u niz tako da se prvo smjeste elementi nulte
vrste, zatim prve, itd. do posljednje vrste matrice. Odštampati rezultujući niz.
 Na kraju napisati dio kôda kojim se unesena matrica štampa prateći strelice
prikazane na slici. Na primjer, za matricu:

1 2 3 4
11 12 13 14 
A 
21 22 23 24
 
31 32 33 34
redosljed štampanja je 1, 11, 21, 31, 32, 22, 12, 2, 3, 13, 23, 33, 34, 24, 14, 4.

#include<stdio.h>

#define MAX 20

int main()
{
int A[MAX][MAX],vektor[MAX*MAX];
int i,j,N,d;
printf("Unijeti dimenzije kvadratne matrice N: ");
scanf("%d",&N);
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
{
printf("Unesi A[%d][%d]: ",i,j);
scanf("%d",&A[i][j]);
}
printf("\n");
}
printf("Elementi matrice na glavnoj dijagonali su:\n");
for(i=0;i<N;i++)
printf("%d\t",A[i][i]);
printf("\nUnijeti dijagonalu: 0 glavna, 1 prva sporedna gore,\n");
printf("1 prva sporedna ispod glavne itd.\n");
scanf("%d",&d);
if(d<=N‐1&&d>=1‐N)
{
if(d>=0)
for(i=0,j=d;i<N&&j<N;i++,j++)
printf("A[%d][%d]=%d ",i,j,A[i][j]);
else
2.3 M ATRICE 151

for(i=‐d,j=0;i<N&&j<N;i++,j++)
printf("A[%d][%d]=%d ",i,j,A[i][j]);
}
else
printf("Nema te dijagonale.");
printf("\n");
for(i=0;i<N;i++)
for(j=0;j<N;j++)
{
vektor[i*N+j]=A[i][j];
printf("vektor[%d]=%d \n",i*N+j,vektor[i*N+j]);
}
for(j=0;j<N;j++)
if(j%2==0)
for(i=0;i<N;i++)
printf("A[%d][%d]=%d\n",i,j,A[i][j]);
else
for(i=N‐1;i>=0;i‐‐)
printf("A[%d][%d]=%d\n",i,j,A[i][j]);
}

Promjenljive korišćene u programu:


A matrica od najviše MAX×MAX elemenata (MAX je zadato makrodefinicijom)
vektor niz u koji se vrši "preslaganje" elemenata matrice
i, j pomoćne brojačke promjenljive
N dimenzije matrice
d promjenljiva koja određuje dijagonalu matrice
Nakon učitavanja dimenzija matrice i same matrice prelazi se na štampanje glavne
dijagonale matrice. Iskorišćena je osobina da se svi elementi na glavnoj dijagonali mogu zapisati
kao A[i][i]. Zatim se od korisnika zahtjeva da odabere dijagonalu (broj d) koja je paralelna
glavnoj dijagonali. Elementi matrice na odabranoj dijagonali se mogu zapisati kao A[i][i+d].
Radi efikasnosti, uputno je štampati dijagonalu pomoću jednog ciklusa koji mora provjeriti uslov
da su indeksi i i i+d u intervalu od 0 do N‐1. Ovo je odvojeno za dijagonale iznad glavne i za
one ispod glavne dijagonale sa po jednom for petljom. Pokušajte da sami rastumačite način na
koji je to izvršeno. Treći dio programa "presipa" (uz istovremeno štampanje elemenata) matricu
red po red u vektor. Elementi prvog reda matrice A[0][0], A[0][1], ..., A[0][N‐1] postaju
elementi niza vektor[0], vektor[1], ..., vektor[N‐1]. Sa povećanjem i i prelaskom u novi
red elementi A[1][0], A[1][1], ..., A[1][N‐1] se "preslaže" u elemente niza kao
vektor[N], vektor[N+1], ..., vektor[2*N‐1]. Lako je uočiti da se element matrice
A[i][j] preslikava u niz na poziciji vektor[i*N+j]. Konačno, posljednji dio programa
realizuje štampanje matrice u obliku date "zmije". Prolazi se kroz petlju po kolonama od prve ka
zadnjoj. Parne kolone (one za koje je ostatak djeljenja sa 2 jednak 0) se štampaju odozgo ka
dolje, dok se neparne kolone (parnom kolonom nazivamo one za koje je indeks j koji označava
kolone paran) štampaju odozdo na gore. Zbog obima odštampanog teksta tokom izvršavanja
ovog programa prepuštamo vam da sami provjerite njegovo funkcionisanje.
156 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

MTP=‐TP[i][j];
}
for(i=0;i<N;i++)
for(j=0;j<N;j++)
TM[i][j]+=TP[i][j];
p=‐p;
n=n+1;
} while(MTP>0.01*MA);
printf("Matricni sinus stampan red po red je:\n");
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
printf("%f\t",TM[i][j]);
printf("\n");
}
}

Promjenljive korišćene u programu su:


N veličina matrice
i, j, k pomoćne brojačke promjenljive
n, p pomoćne promjenljive za računanje faktorijela i vrijednosti (-1)i
A zadata matrica (sve matrice su dimenzionisane sa MAX koje se zadaje
makrorazvojem)
TM matrični sinus
QA pomoćna matrica u kojoj se čuva kvadrat matrice A
TP matrični proizvod koji se dodaje sumi koja predstavlja matrični sinus
TC pomoćna matrica za čuvanje matričnog proizvoda
MA maksimum matrice A
MTP maksimum člana koji se dodaje matričnom sinusu
U pitanju je relativno komplikovana realizacija problema kojeg je relativno jednostavno
idejno riješiti. U svakom slučaju, kada studenti nauče rad sa funkcijama, veći dio ove procedure
će biti moguće pojednostaviti. Nakon učitavanja veličine matrice N, vrši se učitavanje matrice uz
istovremeno određivanje maksimuma matrice (MA). Namjerno nijesmo koristili funkcije iz
programskih biblioteka. Ako je tekući element matrice veći od MA (MA je pretpostavljeno
pozitivna vrijednost) taj se element postavlja na maksimalni. Takođe, ako je tekući element manji
od ‐MA to znači da je negativan, ali da je po apsolutnoj vrijednosti veći od trenutno maksimalnog
pa se njegova negirana vrijednost postavlja na tekući maksimum. Zatim se u sljedećem bloku
naredbi izvrši računanje kvadratne matrice QA. To je pomoćna, ali korisna matrica u ovom
slučaju. Mi ćemo tu matricu privremeno čuvati u matrici TC. U prvom koraku nam je matrica
jednaka A, u drugom treba dodati (oduzeti) matricu -A3/3!=-A×A2/(3×2), dok u trećem koraku
treba sabrati matricu A5/5!=A3/3!×A2/(5×4). Dakle, u svakom koraku se matrica koja je korišćena
u prethodnom koraku množi sa kvadratnom matricom i dijeli sa brojem (2n+1)(2n) uz promjenu
znaka. Zatim se odredi maksimum te matrice koju treba dodavati MTP. Na kraju do...while
petlje se dobijena matrica sabere sa tekućom matricom TM u kojoj čuvamo rezultat. U petlji se
ostaje sve dok je zadovoljen logički uslov da je MTP>eps*MA. Na kraju programa se vrši
štampanje rezultata.
Izvršenje programa:
158 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

2.4 Stringovi

Zadatak 1. Učitati string koji sadrži manje od 50 karaktera i sva mala slova u tom stringu
konvertovati u velika, a ostale karaktere ne mijenjati.
#include<stdio.h>

int main()
{
char niz[50];
int i=0;
puts("Unesi neku rijec");
gets(niz);
while(niz[i]!='\0')
{
if (niz[i]>='a'&&niz[i]<='z')
niz[i]+='A'‐'a';
i++;
}
printf("Izmijenjeni string je: %s",niz);
}

Promjenljive korišćene u programu su:


niz niz karaktera koji se učitava
i pomoćna promjenljiva
Nakon učitavanja stringa (funkcija gets učitava do znaka za novi red), prolazi se redom
kroz karaktere stringa do terminacionog karaktera. Slova su u ASCII kodu poređana u dva niza
od kojih prvi niz počinje karakterom 'a', a završava sa 'z', dok drugi niz počinje karakterom
'A' i završava karakterom 'Z'. Da bi konvertovali malo slovo u odgovarajuće veliko, potrebno
je malom slovu dodati razliku između ASCII vrijednosti slova 'A' i 'a'. Alternativno, za
konverziju malih slova u velika, može nam poslužiti funkcija toupper iz biblioteke ctype.h.
U tom slučaju, umjesto koraka
niz[i]+= 'A'‐'a';
bi imali korak
niz[i]=toupper(niz[i]);
Izvršenje programa:
Unesi neku rijec
neka mala slova, A IMA I VELIKIH
Izmijenjeni string je: NEKA MALA SLOVA, A IMA I VELIKIH
2.4 S TRINGOVI 159

Zadatak 2. Učitati evidenciju o nekoliko lica (ime, dužine manje od 15 karaktera, i ulog), a
zatim štampati imena svih onih koji imaju ulog veći od prosječnog.
#include<stdio.h>

int main()
{
char imena[20][15];
float ulog[20],prosjek=0.0;
int i,a;
printf("Ucitaj broj imena: ");
scanf("%d",&a);
printf("Ucitaj imena lica i njihov ulog: ");
for(i=0;i<a;i++)
{
scanf("%s%f",imena[i],&ulog[i]);
prosjek+=ulog[i];
}
prosjek/=a;
puts("Ulog veci od prosjeka imaju:");
for(i=0;i<a;i++)
if(ulog[i]>prosjek)
printf("%s\t\t%6.2f\n",imena[i],ulog[i]);
}

Promjenljive korišćene u programu su:


imena matrica čiji pojedini redovi predstavljaju imena lica čiji se ulozi ispituju
ulog vrijednost uloga
prosjek prosječna vrijednost uloga
i pomoćna brojačka promjenljiva
a broj lica.
Uočimo nekoliko detalja. Pojedinim imenima iz matrice imena se pristupa preko
pokazivača na prvi element u datom redu. Tokom učitavanja imena i podataka o ulozima vrši se
sabiranje vrijednosti uloga. Prosječni ulog se računa tako što se ukupni ulog svih lica podijeli sa
brojem lica. U for petlji se zatim ispituje da li lica imaju ulog veći od prosječnog i ako imaju
vrši štampanje njihovih imena i podataka o ulozima. Iskorišćen je format %6.2f koji označava
da je za vrijednost promjenljive predviđeno 6 mjesta (uključujući i decimalni zarez), a dva od tih
mjesta su ostavljena za decimale.
Izvršenje programa:
Ucitaj broj imena: 5
Ucitaj imena lica i njihov ulog: Marko 56.7
Ilija 87.3
Vesna 99.5
Ana 112.4
Robert 76.1
Ulog veci od prosjeka imaju:
Ilija 87.30
160 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Vesna 99.50
Ana 112.40

Zadatak 3. Učitava se nekoliko riječi, pri čemu je dužina riječi manja od 11 karaktera, a zatim
se štampa koliko ima tu različitih riječi.
#include<stdio.h>
#include<string.h>

int main()
{
char rijeci[20][11];
int i,j,brRijeci,razl=0,uslov;
printf("Koliko rijeci zelite da unesete?\n");
scanf("%d",&brRijeci);
for(i=0;i<brRijeci;i++)
scanf("%s",rijeci[i]);
for(i=0;i<brRijeci‐1;i++)
{
uslov=1;
for(j=i+1;j<brRijeci;j++)
if(strcmp(rijeci[i],rijeci[j])==0)
uslov=0;
razl+=uslov;
}
printf("Ima %d razlicitih rijeci",razl);
}

Promjenljive korišćene u programu su:


rijeci matrica karaktera (niz stringova) koja sadrži riječi koje se učitavaju
i, j pomoćne brojačke promjenljive
brRijeci ukupan broj riječi
razl ukupan broj različitih riječi
uslov pomoćna promjenljiva koja ispituje da li se tekuća riječ ponavlja u spisku
Nakon učitavanja riječi vrši se poređenje jedne riječi sa svim ostalima. To se vrši u dvije
petlje od kojih prva petlja prolazi kroz sve riječi (petlja po brojačkoj promjenljivoj i), dok druga
petlja poredi rijeci[i] sa svim riječima nakon nje (petlja po j startuje od i+1). Ako se
rijeci[i] poklapa sa nekom od rijeci[j] (poređenje se vrši na funkcijom strcmp), ne
uvećava se broj različitih riječi (uslov se postavi na 0). Na ovaj način je obezbijeđeno da se
svaka riječ broji jednom, jer se posljednje pojavljivanje neke riječi u spisku broji zato što nakon
nje nema iste riječi.
Izvršenje programa:
Koliko rijeci zelite da unesete?
6
Marko
Janko
Pero
2.4 S TRINGOVI 161

Boris
Jano
Marko
Ima 5 razlicitih rijeci

Zadatak 4. Učitava se nekoliko redova teksta i broji se i štampa koliko se puta pojavljuje koje
slovo. Ostali karakteri se ne broje. Unos teksta se prekida kad se unese prazan red.
Napisati odgovarajući program.
#include<stdio.h>

int main()
{
char redovi[50][80],maloSlovo;
int brojRed=0,i,j,slova[26]={0};
printf("Unesi redove\n");
gets(redovi[brojRed]);
while(strlen(redovi[brojRed])!=0)
gets(redovi[++brojRed]);
for(i=0;i<brojRed;i++)
{
j=0;
while(redovi[i][j]!='\0')
{
if(isalpha(redovi[i][j]))
{
maloSlovo=tolower(redovi[i][j]);
slova[maloSlovo‐'a']++;
}
j++;
}
}
for(i=0;i<26;i++)
if(slova[i]>0)
printf("slova %c ima %d\n", 'a'+i,slova[i]);
}

Promjenljive korišćene u programu:


redovi sadržaj redova sa karakterima koji se učitavaju;
slova niz u kojem se čuvaju podaci o broju pojavljivanja pojedinih slova
engleske abecede;
brojRed čuva podatak o broju redova sa tekstom
maloSlovo tekuće slovo. Ukoliko je veliko slovo, pretvara se u malo funkcijom
tolower
i, j pomoćne brojačke promjenljive
Karakteri koji se učitavaju su smješteni u riječi za koje je alocirana matrica od 50 riječi po
80 karaktera (ovo je neracionalno korišćenje prostora za smještaj karaktera, ali će o tome biti
162 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

više riječi kasnije). Napomenimo da ako se vrši alociranje prostora za jedan red teksta to se
obično vrši limitirano na 80 karaktera, kolika je dužina ekrana u komandnoj liniji. Kako redove
unosimo sve dok se ne unese prazan red, za to će nam poslužiti while petlja koja se izvršava
sve dok je dužina unijetog stringa veća od 0. Učitane stringove smještamo u matricu redovi.
Slova engleske abecede (mi nećemo praviti razliku između malih i velikih slova) ima 26, pa smo
za niz u kome čuvamo podatke o tim promjenljivim alocirali 26 mjesta i sve članove niza
inicijalizirali na nulu. Zatim se za svaki red teksta ponavlja procedura opisana drugom while
petljom. Prolazi se kroz karakter po karakter teksta i provjerava da li je dati karakter slovo
korišćenjem funkcije isalpha (biblioteka ctype). Ovu provjeru smo mogli izvršiti i direktno,
znajući činjenicu da su slova po ASCII kodu poređana jedna za drugim od malog a do malog z
i u odvojenom nizu od velikog A do velikog Z. Ovaj način ostavljamo vama za vježbu. Pošto
želimo da slova[0] odgovara karakteru a, slova[1] odgovara karakteru b itd. koristimo
kompaktan zapis slova[maloSlovo‐'a'], gde je maloSlovo tekuće slovo, prethodno
pretvoreno u malo slovo ako je bilo veliko. Pretvaranje velikih slova u mala je izvršeno
funkcijom tolower (biblioteka ctype). Uočimo da nijesmo provjeravali karaktere do kraja
reda, već do terminacionog karaktera, jer nijesmo vršili upisivanje u dio memorije nakon
terminacionog karaktera. Na kraju programa, štampamo samo one karaktere koji je pojavljuju
bar jednom.
Izvršenje programa:
Unesi redove
Tako nam i treba.
Dok nismo priznavali da ima problema, nismo ih morali rjesavati.
Vodite racuna o svom prezimenu.
Ono ce ziveti duze od vas i odgovarati za sve gluposti i greske.

slova a ima 17
slova b ima 2
slova c ima 2
slova d ima 6
slova e ima 12
slova g ima 3
slova h ima 1
slova i ima 17
slova j ima 1
slova k ima 3
slova l ima 4
slova m ima 8
slova n ima 7
slova o ima 15
slova p ima 4
slova r ima 9
slova s ima 8
slova t ima 7
slova u ima 4
slova v ima 8
slova z ima 5
168 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

pomnoženim sa tekuće učitanim operandom (promjenljiva niz[j]). Tekući proizvod za naredni


izraz se inicijalizuje na pom=1. Druga mogućnost je da je naredni karakter '*', kad se vrši
množenje tekućeg proizvoda u else if dijelu. Dodajte sami dio kôda koji provjerava da li je
naredni karakter nekorektan (nije cifra, '+', niti '*'). Na kraju osnovne while petlje, vrši se
inkrementiranje promjenljive j (tražimo naredni operand) i promjenljive i (posmatramo prvi
karakter u narednom operandu).
Izvršenje programa:
Unijeti izraz
12+3*4+4+5*7
Vrijednost izraza je 63

Zadatak 8. Sastaviti program koji učitava prirodan broj N i štampa ga u rimskom obliku.
Recimo, učita se 149, a štampa se CXLIX.
#include<stdio.h>
#include<stdlib.h>

int main()
{
int i=0,del,cif,N;
char pom1,pom2,pom3,rim[30];
puts("Unijeti prirodan broj");
scanf("%d",&N);
if(N<=0)
{
puts("Pogresan broj!");
exit(0);
}
while(N>=1000)
{
rim[i++]='M';
N‐=1000;
}
while(N!=0)
{
if(N>=100)
{
pom1='M'; pom2='D'; pom3='C';
del=100;
}
else if(N<100&&N>=10)
{
pom1='C'; pom2='L'; pom3='X';
del=10;
}
else
{
pom1='X'; pom2='V'; pom3='I';
2.4 S TRINGOVI 169

del=1;
}
cif=N/del;
if(cif==9)
{
rim[i++]=pom3;
rim[i++]=pom1;
}
else if(cif>=5)
{
rim[i++]=pom2;
while(cif>5)
{
rim[i++]=pom3;
cif‐‐;
}
}
else if(cif==4)
{
rim[i++]=pom3;
rim[i++]=pom2;
}
else if(cif>=1)
{
while(cif>0)
{
rim[i++]=pom3;
cif‐‐;
}
}
N=N%del;
}
rim[i]='\0';
printf("Rimski N je %s\n",rim);
}

Promjenljive korišćene u programu:


rim string u kome se čuvaju podaci o rimskom broju
pom1, pom2, pomoćne promjenljive
pom3
i promjenljiva koja ukazuje na tekuću poziciju u stringu rim
del pomoćna promjenljiva koja ukazuje na tekuću poziciju u cijelom broju
cif pomoćna promjenljiva koja opisuje cifru cijelog broja koja se konvertuje
N broj koji se konvertuje
Rimski brojevi predstavljaju ekvivalent prirodnim brojevima. Ako je broj veći od 1000
postavlja se slovo M onoliko puta koliko ima hiljada. Zatim se gleda cifra koja opisuje stotine.
Ako je to 9, postavlja se CM, ako je u pitanju cifra veća od 4, a manja od 9, postavlja se slovo
170 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

D, koji predstavlja broj 500, praćen slovima C, koja predstavljaju stotine do odgovarajuće
vrijednosti (npr. 700=DCC). Ako je u pitanju broj 400, postavlja se kombinacija CD, koja
označava stotinu do 500. Ako je cifra stotina od 1 do 3, postavlja se odgovarajući broj karaktera
C. Sličnu proceduru treba obaviti na ciframa koje opisuju desetice i jedinice. Na osnovu datih
objašnjenja sami pokušajte da realizujete program, a zatim uporedite vaše rješenje sa ovim.
Prilikom izrade smo pretpostavili da ukoliko je broj veći od 1000 onda u njegovom rimskom
zapisu imamo onoliko slova M koliko ima hiljada u našem broju. Ovakav zapis je korišćen u
početku, a kasnije je u cilju predstavljanja brojeva dosta većih od 1000 uveden zapis po kome se
iznad postojećeg karaktera dodaje horizontalna linija da bi označila množenje vrijednosti tog
karaktera sa 1000. Tako imamo da se broj 1000 može predstaviti kao Ī, broj 5000 se može
predstaviti kao V , 10000 kao X itd. Pored toga su uvedene i vertikalne linije sa obe strane
karaktera, slično zagradama za apsolutnu vrijednost, za predstavljanje broja koji je 100 puta veći
od vrijednosti tog karaktera. U kombinaciji sa hortizontalnom linijom sad možemo pisati
izuzetno velike brojeve, kao što su | V | za 500000, | X | za 1000000 itd. Zbog nemogućnosti
jednostavnog zapisa ovih brojeva u C-u, odlučili smo se za prvobitni zapis.
Izvršenje programa:
Unijeti prirodan broj
1345
Rimski broj je MCCCXLV

Zadatak 9. Učitava se nekoliko riječi, a štampa se koliko tu ima različitih riječi. Nema uslova
o maksimalnoj dužini riječi. Jedino ograničenje je raspolaganje sa 10000 bajtova
za učitavanje.
#include<stdio.h>
#include<string.h>

int main()
{
char s[10000],*p[2000],temp[10000];
int size=1,tp=0,i,j=0,razl=0,uslov,k;
p[0]=s;
printf("Unijeti rijeci: ");
while(size)
{
gets(temp);
size=strlen(temp);
for(i=0;i<size;i++)
s[tp+i]=temp[i];
s[tp+size]='\0';
tp+=size+1;
if(size)
p[++j]=s+tp;
}
for(i=0;i<j;i++)
{
uslov=1;
2.4 S TRINGOVI 171

for(k=i+1;k<j;k++)
if(strcmp(p[i],p[k])==0)
{
uslov=0;
break;
}
razl+=uslov;
}
printf("Broj razlicitih rijeci je %d",razl);
}

Promjenljive korišćene u programu su:


s string u kome se čuvaju podaci o svim stringovima
p niz pokazivača koji pamti početke pojedinih nizova
temp pomoćni string namjenjen učitavanju podataka
size dužina tekuće učitanog stringa
tp ukupna dužina svih učitanih stringova do tekućeg
i, k pomoćne brojačke promjenljive
j ukupan broj učitanih riječi
razl broj različitih riječi
uslov provjera da li postoji ista riječ koja je već učitana
Predmetni program ilustruje dva važna koncepta: upotrebu pokazivača i rad sa velikim
brojem stringova. U obradi teksta često učestvuju stranice i stranice teksta. Najčešće su
pojedinačne riječi dužine od nekoliko karaktera. Stoga bi memorisanje svake riječi zasebno, u
nizu dovoljne dužine za vrlo duge riječi, zahtijevalo neprihvatljivo mnogo memorije, jer bi veliki
procenat te memorije bio nepotrebno zauzet. To se prevazilazi korišćenjem jednog stringa koji
pamti sve podstringove (učitane riječi) i jednog niza pokazivača koji pamti početke ovih
podstringova. Za razdvajanje podstringova se koristi karakter '\0'.
Algoritam se sastoji iz sljedećih koraka. Pokazivač na prvi karakter stringa s se postavi kao
prvi element niza p. Učita se string i funkcijom strlen odredi njegova dužina. Taj se string
doda u niz s i nakon njega se postavi karakter '\0'. Zatim se ažurira pokazivač na sljedeći string
(naredba p[++j]=s+tp) i broj učitanih stringova se uveća za jedan. Ako nije unesen string
(temp je prazan string), izlazi se iz while petlje i prelazi se u dio koji ispituje da li se pojavljuju
različite riječi.
Izvršenje programa:
Unijeti rijeci: Janko
Marko
Petar
Janko
Ilija
Ana
Daliborka
Ana

Broj razlicitih rijeci je 6


172 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

2.5 Funkcije

Zadatak 1. Napisati program koji će za unesen prirodan broj štampati sve proste brojeve koji
su manji od zadatog broja. Program treba da sadrži funkciju koja određuje da li je
broj, koji joj se prosljeđuje kao argument, prost.
#include<stdio.h>

int prost(int);

int main()
{
int i,n;
printf("Unesi broj:\n");
scanf("%d",&n);
printf("Prosti brojevi su:\n");
for(i=1;i<n;i++)
if(prost(i))
printf("%d ",i);
printf("\n");
}

int prost(int n)
{
int i;
for(i=2;i*i<=n;i++)
if(n%i==0)
return 0;
return 1;
}

Promjenljive korišćene u programu:


n broj koji se zadaje
i pomoćna brojačka promjenljiva
Promjenljive korišćene u funkciji:
n broj koji se provjerava da li je prost (argument funkcije)
i pomoćna brojačka promjenljiva
U glavnom programu, nakon učitavanja broja, ispitujemo sve prirodne brojeve do tog broja
da li su prosti i ako jesu štampamo ih. Broj je prost ako je djeljiv samo sa samim sobom i sa 1.
U funkciji provjeravamo da li je broj prost tako što prolazimo kroz brojeve od 2 do korijena
datog broja (uslov i*i<=n) i u slučaju da je argument djeljiv sa nekim od njih funkcija vraća 0
(broj nije prost). Ako se for petlja izvršila do kraja, broj je prost i vraća se rezultat 1. Uslov
i*i<=n je iskorišćen radi optimizacije izvršavanja, a sami izvršite njegovo tumačenje.
2.5 F UNKCIJE 173

Izvršenje programa:
Unesi broj:
97
Prosti brojevi su:
1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89

Zadatak 2. Napisati program koji će sadržati funkciju za štampanje prvih N prostih brojeva,
pri čemu je N broj unijet sa tastature.
#include<stdio.h>

int prost(int);

int main()
{
int i=1,j=0,n;
printf("Unesi broj:\n");
scanf("%d",&n);
printf("Prvih %d prostih brojeva su:\n",n);
while(j<n)
if(prost(i++))
{
printf("%d ",i‐1);
j++;
}
printf("\n");
}

int prost(int n)
{
int i;
for(i=2;i*i<n;i++)
if(n%i==0)
return 0;
return 1;
}

Promjenljive korišćene u programu:


i broj koji se provjerava
j tekući broj prostih brojeva
n zadati broj prostih brojeva
Promjenljive korišćene u funkciji:
n broj koji se provjerava da li je prost (argument funkcije)
i pomoćna brojačka promjenljiva
Zadatak predstavlja modifikaciju prethodnog zadatka, pa sami protumačite modifikaciju.
174 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Izvršenje programa:
Unesi broj:
23
Prvih 23 prostih brojeva su:
1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79

Zadatak 3. Napisati program koji sadrži funkciju koja određuje i vraća srednju vrijednost niza
realnih brojeva. Niz (dužina i elementi) se zadaje po startovanju programa i
prosljeđuje se funkciji. Odštampati vrijednost koju funkcija vraća.
#include<stdio.h>

float sred(int,float *);

int main()
{
int N,i;
float a[20];
puts("Unijeti broj clanova niza");
scanf("%d",&N);
puts("Unijeti elemente niza");
for(i=0;i<N;i++)
scanf("%f",a+i);
printf("Srednja vrijednost ovog niza je: %.2f\n",sred(N,a));
}

float sred(int M,float *y)


{
int i;
float mean=0.0;
for(i=0;i<M;i++)
mean+=y[i];
return mean/M;
}

Promjenljive korišćene u programu:


N broj članova niza
i pomoćna brojačka promjenljiva
a niz brojeva koji se učitava
Promjenljive korišćene u funkciji:
M broj članova niza (argument funkcije)
y niz prosljeđen preko pokazivača (argument funkcije)
i pomoćna brojačka promjenljiva
mean promjenljiva u kojoj se čuva srednja vrijednost (rezultat funkcije)
Ovaj program demonstrira drugu čestu primjenu funkcija u programiranju, a to je rad sa
nizovima podataka. Funkcija ima dva argumenta: cijeli broj (broj članova niza) i pokazivač na
float tip podatka (pokazivač na početak niza), preko koga se pristupa članovima niza.
2.5 F UNKCIJE 181

Zadatak 9. Napisati funkciju ukloniKarakter za uklanjanje svake pojave zadatog karaktera


iz stringa. Pored pokazivača na string, argument funkcije je karakter kog treba
ukloniti. Funkcija vraća broj uklonjenih karaktera.
#include<stdio.h>

int ukloniKarakter(char *,char);

int main()
{
char kar,s[50];
int b;
puts("Unijeti karakter");
scanf("%c",&kar);
puts("Unesi string");
scanf("%s",s);
b=ukloniKarakter(s,kar);
printf("String je %s",s);
printf(", a broj uklonjenih karaktera %c je %d\n",kar,b);
}

int ukloniKarakter(char *str,char kar)


{
int elim=0,i=0,j=0;
while(str[i]!='\0')
{
if(str[i]!=kar)
str[j++]=str[i];
else
elim++;
i++;
}
str[j]='\0';
return elim;
}

Promjenljive korišćene u programu:


kar karakter koji se eliminiše
s string nad kojim se vrši operacija
b broj eliminisanih karaktera
Promjenljive korišćene u funkciji su:
str pokazivač na string iz koga se eliminišu karakteri
kar karakter koji se eliminiše (argument funkcije)
elim broj eliminisanih karaktera, vraća se kao rezultat funkcije
i pomoćna brojačka promjenljiva
j promjenljiva koja broji karaktere u modifikovanom stringu
182 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Objasnimo samo funkciju, i to na konkretnom primjeru. Neka je string "Power" i neka se


eliminiše karakter 'P'. U while petlji prolazimo kroz sve karaktere stringa. Pošto prvi karakter
stringa 'P' treba eliminisati, na tom mjestu samo uvećali brojač eliminisanih slova (else grana)
u prvom prolasku kroz while petlju. U drugom prolasku kroz petlju, naredni karakter stringa,
slovo 'o', treba da pređe na prvo mjesto, tj. mjesto karaktera koji se eliminiše, što se obavlja
tako što 'o' upisujemo u str[0], a zatim inkrementiramo j. Nadamo se da je sada jasna ideja
i da dalju realizaciju možete sami pratiti. Prije kraja funkcije se vrši dodavanje karaktera '\0' i
vraća se broj eliminisanih karaktera.
Izvršenje programa:
Unijeti karakter
i
Unesi string
Kikiriki
String je Kkrk, a broj uklonjenih karaktera i je 4

Zadatak 10. Napisati funkciju uzastopni za uklanjanje određenog broja uzastopnih karaktera
iz stringa s. Pored pokazivača na string, argumenti funkcije su početna pozicija u
stringu od koje počinje uklanjanje i broj uzastopnih karaktera koje treba ukloniti.
#include <stdio.h>

void uzastopni(char*,int,int);

int main()
{
char s[]={"algorXYZitam"};
uzastopni(s,5,3);
printf("Novi string je s=\"%s\"",s);
}

void uzastopni(char *s,int poc,int n)


{
int i=0,j=0;
while(s[i]!='\0')
{
if(i<poc||i>=poc+n)
s[j++]=s[i];
i++;
}
s[j]='\0';
}

Promjenljive korišćene u programu:


s string iz kojeg se izbacuju uzastopni karakteri
Promjenljive korišćene u funkciji:
s pokazivač na string iz koga se izbacuju uzastopni karakteri
2.5 F UNKCIJE 183

poc pozicija u stringu odakle počinje izbacivanje


n broj karaktera koji se izbacuju
i pomoćna promjenljiva koja označava poziciju u originalnom stringu
j pomoćna promjenljiva koja označava poziciju u "prepravljenom" stringu
U okviru funkcije se prepisuju karakteri koji se zadržavaju uz ažuriranje pozicije određene
promjenljivom j. Ostali se karakteri prosto preskaču bez ažuriranja vrijednosti promjenljive j.
Procedura se ponavlja u okviru while petlje dok se ne naiđe na kraj stringa.
Izvršenje programa:
Novi string je s="algoritam"

Zadatak 11. Napisati funkciju koja broji koliko se početnih karaktera dva stringa, koji su joj
argumenti, poklapa. Napisati i glavni program koji testira ovu funkciju.
#include<stdio.h>

int poklapanje(char *,char *);

int main()
{
char prvi[30],drugi[20];
puts("Unesi stringove");
gets(prvi);
gets(drugi);
printf("Poklapa se %d karaktera",poklapanje(prvi,drugi));
}

int poklapanje(char *str1,char *str2)


{
int broj=0;
while(str1[broj]==str2[broj]&&str1[broj]&&str2[broj])
broj++;
return broj;
}

Promjenljive korišćene u glavnom programu:


prvi, drugi stringovi koji se porede
Promjenljive korišćene u funkciji:
str1, str2 pokazivači na stringove koji se porede
broj broj početnih karaktera u stringovima koji se poklapaju
Objasnimo funkciju, koja je prilično jednostavna. Poredimo dva stringa karakter po karakter
i dok su karakteri isti uvećavamo brojač. Kada nema poklapanja ili kada dođemo do kraja bilo
kojeg od stringova (terminacioni karakter ima vrijednost 0, što odgovara logičkoj neistini), izlazi
se iz while petlje i vraća rezultat.
Izvršenje programa:
184 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Unesi stringove
Rade
Radecki
Poklapa se 4 karaktera

Zadatak 12. Napisati funkciju koja vraća broj uzastopnih karaktera sa početka prvog stringa koji
se ne pojavljuju u drugom stringu. Napisati i glavni program koji testira funkciju.
#include<stdio.h>

int nepojavljivanje(char *,char *);

int main()
{
char s1[]="Petar",s2[]="Marko";
printf("%d karaktera se ne pojavljuje",nepojavljivanje(s1,s2));
}

int nepojavljivanje(char *st1,char *st2)


{
int i=0,j;
while(st1[i]!='\0')
{
j=0;
while(st2[j]!='\0')
{
if(st2[j]==st1[i])
return i;
j++;
}
i++;
}
return i;
}

Promjenljive korišćene u programu:


s1, s2 stringovi koji se porede
Promjenljive korišćene u funkciji:
st1, st2 pokazivači na stringove koji se porede
i, j pomoćne brojačke promjenljive. i je istovremeno rezultat funkcije
Objasnimo funkciju. Prolazimo kroz string st1 karakter po karakter, koji se porede sa svim
karakterima stringa st2 (unutrašnja while petlja). Ako se nađe poklapanje, vraća se rezultat, a
to je pozicija dokle se stiglo u stringu st1. Ako se unutrašnja petlja ne prekine naredbom
return, znači da se posmatrani karakter stringa st1 ne pojavljuje u stringu st2. Konačno, kad
prođemo kroz cio string st1, vraćamo vrijednost i koja predstavlja dužinu tog stringa.
Izvršenje programa:
2.5 F UNKCIJE 185

3 karaktera se ne pojavljuje

Zadatak 13. Napisati program koji sadrži funkciju koja konvertuje cijeli broj iz dekadnog
brojnog sistema u binarni brojni sistem, koristeći zapis sa dvojnim komplementom.
Štampati binarni zapis broja.
#include<stdio.h>
#include<string.h>

void dec2bin(int,char *);


void inicijalizacija(char *);

const int brojBita=16;

int main()
{
int broj;
char s[brojBita+1];
inicijalizacija(s);
printf("Unesi prirodni broj: ");
scanf("%d",&broj);
dec2bin(broj,s);
printf("Konverzijom se dobija: %s\n",s);
}

void inicijalizacija(char *bin)


{
int i;
for(i=0;i<brojBita;i++)
bin[i]='0';
bin[i]='\0';
}

void dec2bin(int N,char *bin)


{
int i=brojBita‐1,sign=0;
if(N<0)
{
sign=1;
N=‐N;
}
while(N>0)
{
bin[i‐‐]='0'+N%2;
N/=2;
}
if(sign==1)
{
for(i=0;i<brojBita;i++)
186 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

bin[i]=(bin[i]=='0')?'1':'0';
i=brojBita‐1;
while(i>0&&bin[i]=='1')
{
bin[i]='0';
i‐‐;
}
bin[i]='1';
}
}

Promjenljive korišćene u glavnom programu:


broj cijeli broj koji se pretvara u binarni oblik u formi stringa
s string koji predstavlja binarni zapis broja
Promjenljive korišćene u funkciji inicijalizacija:
bin string koji predstavlja binarni zapis broja N (argument funkcije)
i pomoćna brojačka promjenljiva
Promjenljive korišćene u funkciji dec2bin:
N cijeli broj koji se pretvara u binarni (argument funkcije)
bin pokazivač na string koji predstavlja binarni zapis broja N (argument funkcije)
i pomoćna brojačka promjenljiva
sign pomoćna promjenljiva koja ukazuje da li je broj pozitivan ili negativan
Funkcija inicijalizacija služi da inicijalizuje string argument na sve binarne nule, tj.
karakter '0'. Dužina stringa je određena globalnom promjenljivom brojBita, pri čemu je
string argument deklarisan u funkciji main. U programu smo se opredjelili za fiksan broj bita,
koji je umnožak broja 8, što je slučaj kod standardnih cjelobrojnih tipova podataka. Funkcija
dec2bin vrši konverziju cijelog broja u odgovarajući binarni oblik. Prvo se provjerava da li je
u pitanju pozitivan ili negativan broj i u zavisnosti od toga postavlja vrijednost sign na 0 ili 1,
respektivno. Zatim se sprovodi procedura za konverziju cijelog broja u string: broj se dijeli sa
dva i ostatak pri dijeljenju je cifra binarnog broja. Cifre binarnog broja se upisuju sa desna na
lijevo, počev od cifre najmanje težine ka cifri najveće težine. U slučaju da je broj negativan, traži
se njegov dvojni komplement (obrni binarne jedinice i nule i dodaj 1 na poziciju najmanje
težine). U tijelu if(sign==1) uslova, for petlja binarne jedinice mijenja nulama i obratno,
dok while petlja dodaje 1 na poziciju najmanje težine, što odgovara promjeni svih uzastopnih
jedinica sa kraja stringa binarnom nulom i prve nule na koju se naiđe sa desna na lijevo binarnom
jedinicom.
Izvršavanja programa (dva sa brojBita=16 i dva sa brojBita=32):
Unesi prirodni broj: 127
Konverzijom se dobija: 0000000001111111

Unesi prirodni broj: ‐1


Konverzijom se dobija: 1111111111111111

Unesi prirodni broj: 76823424


2.5 F UNKCIJE 187

Konverzijom se dobija: 00000100100101000011101110000000

Unesi prirodni broj: ‐2


Konverzijom se dobija: 11111111111111111111111111111110

Zadatak 14. Napisati funkciju int2str za konvertovanje cijelog broja u string. Napisati i
glavni program koji testira funkciju.
#include<stdio.h>

void int2str(int,char *);

int main()
{
int n;
char str[20];
printf("Unesite broj: ");
scanf("%d",&n);
int2str(n,str);
printf("Dobijeni string je %s",str);
}

void int2str(int n,char *s)


{
char p;
int i,sign=0,j=0;
if(n<0)
{
s[j++]='‐';
sign=1;
n=‐n;
}
else if(n==0)
s[j++]='0';
while(n>0)
{
s[j++]='0'+n%10;
n/=10;
}
for(i=sign;i<j/2+sign;i++)
{
p=s[i];
s[i]=s[j‐i‐1+sign];
s[j‐i‐1+sign]=p;
}
s[j]='\0';
}

Promjenljive korišćene u programu su:


188 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

n broj koji se pretvara u string


str dobijeni string
Promjenljive korišćene u funkciji su:
n broj koji se pretvara u string (argument funkcije)
s pokazivač na string koji predstavlja broj (argument funkcije)
p pomoćna promjenljiva koja služi kod "okretanja stringa"
i pomoćna brojačka promjenljiva
sign određuje predznak broja
j broj karaktera stringa
Cifre broja se razdvajaju određivanjem ostatka djeljenja sa 10. Međutim, kada ih upisujemo
u string, to radimo u obrnutom redosljedu (cifra desetica skroz lijevo, pa onda cifra jedinica itd.).
Dakle, nakon određivanja karaktera koji predstavljaju cifre broja, karaktere stringa treba
okrenuti. Da situacija bude komplikovanija tu se može umiješati i predznak. Algoritam radi
sljedeće: 1) provjera je li broj negativan i ako jeste postavlja predznak na početak stringa, a
pomoćnu promjenljivu sign na 1, u suprotnom ostaje 0; 2) odrede se cifre broja i smjeste u
string s (ovo se obavlja u while petlje); 3) u okviru for petlje vrši se okretanje stringa. Počinje
se od prvog karaktera koji nije predznak (za to je iskorišćena promjenljiva sign) i mijenjaju se
pozicije karaktera prve polovine stringa sa karakterima iz druge polovine (prva cifra sa zadnjom,
druga sa predzadnjom itd.). Zamjena karaktera stringa se vrši na uobičajen način, uvođenjem
pomoćne promjenljive.
Izvršavanja programa:
Unesite broj: 7854
Dobijeni string je 7854

Unesite broj: ‐85412


Dobijeni string je ‐85412

Unesite broj: 0
Dobijeni string je 0

Zadatak 15. Kreirati funkciju koja ima zaglavlje:


int max(int *a,int n,int *p)
Ova funkcija daje vrijednost maksimalnog elementa niza, dužine n, na koji
pokazuje a. Preko pokazivača p se dobija pozicija maksimalnog elementa u nizu.
Ako ima više najvećih elemenata funkcija treba da vrati poziciju prvog takvog
elementa.
Kreirati glavni program koji učitava cijele brojeve N i M, a zatim i matricu cijelih
brojeva dimenzija N×M. Program treba da odštampa maksimalni element matrice
kao i poziciju tog elementa (u kojoj se koloni i vrsti nalazi). U realizaciji programa
treba koristiti funkciju max.

#include<stdio.h>

int max(int *,int,int *);


2.5 F UNKCIJE 189

int main()
{
int matr[30][30],vektMax[30],pozMax[30],M,N,i,j;
int maxMatr,vektPozMax[2];
printf("Unesite dimenzije matrice: ");
scanf("%d%d",&M,&N);
for(i=0;i<M;i++)
for(j=0;j<N;j++)
scanf("%d",&matr[i][j]);
for(i=0;i<M;i++)
vektMax[i]=max(matr[i],N,&pozMax[i]);
maxMatr=max(vektMax,M,&vektPozMax[1]);
vektPozMax[0]=pozMax[vektPozMax[1]];
printf("Maksimum matrice je %d i nalazi se ",maxMatr);
printf("u koloni %d i redu %d",vektPozMax[0],vektPozMax[1]);
}

int max(int *a,int n,int *pmx)


{
int i,mx=a[0];
*pmx=0;
for(i=1;i<n;i++)
if(a[i]>mx)
{
mx=a[i];
*pmx=i;
}
return mx;
}

Promjenljive korišćene u programu:


i, j pomoćne brojačke promjenljive
M, N dimenzije matrice
matr matrica čiji se maksimum traži
vektMax niz maksimuma po redovima
pozMax niz pozicija maksimuma po redovima
maxMatr maksimum matrice
vektPozMax niz pozicija maksimuma u matrici
Promjenljive korišćene u funkciji max:
a pokazivač na prvi član niza čiji se maksimum traži (argument funkcije)
n dužina niza (argument funkcije)
pmx pokazivač na poziciju maksimuma niza (argument funkcije)
i pomoćna brojačka promjenljiva
mx tekuća vrijednost maksimuma niza (vraća se kao rezultat funkcije)
190 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Prvo objasnimo funkciju max. Na početku funkcije se postavlja da je tekući maksimum prvi
element u nizu i da je pozicija tog maksimuma nulta. Zatim se u for petlji prolazi kroz ostale
članove niza i kada se otkrije element veći od tekućeg maksimuma taj se element postavi za
tekući maksimum i ažurira se pozicija tekućeg maksimuma. Ako ima više elemenata koji su
jednaki maksimumu funkcija određuje kao poziciju maksimuma onu vrijednost na koju se prvo
naiđe u nizu. Šta treba promijeniti u funkciji da bi se pamtila posljednja pozicija? U glavnom
programu se učitaju dimenzije i vrijednosti elemenata matrice. Za svaki red matrice se poziva
funkcija max. Maksimumi pojedinih redova se memorišu u nizu vektMax, dok se pozicije
maksimuma u redovima memorišu u pozMax. U sljedećem koraku se maksimum matrice
određuje kao maksimum niza vektMax, a pozicija maksimuma u ovom nizu odgovara redu gdje
je detektovan maksimum matrice. Pozicija maksimuma niza pozMax odgovara koloni
maksimuma matrice. Protumačite zapis pozMax[vektPozMax[1]].
Izvršenje programa:
Unesite dimenzije matrice: 4
4
1 2 3 4
2 3 4 5
1 7 2 3
4 5 9 1
Maksimum matrice je 9 i nalazi se u koloni 2 i redu 3
2.6 R EKURZIVNE FUNKCIJE 191

2.6 Rekurzivne funkcije

Zadatak 1. Poznato je pravilo: najveći zajednički djelilac (NZD) brojeva a i b, u oznaci


NZD(a,b), pri čemu je a>b, je jednak NZD(b,a%b). Sastaviti program koji sadrži
rekurzivnu funkciju za računanje NZD dva broja zasnovan na ovom pravilu.
Rekurzija se prekida kada ostatak dijeljenja broja a sa brojem b postane 0. Štampati
dobijeni NZD.
#include<stdio.h>

int nzd(int,int);

int main()
{
int m,n;
printf("Unesite brojeve: ");
scanf("%d%d",&m,&n);
printf("GCD=%d\n",nzd(m,n));
}

int nzd(int a,int b)


{
if(b==0)
return a;
else if(b>a)
return nzd(b,a);
else
return nzd(b,a%b);
}

Promjenljive korišćene u programu:


m, n brojevi čiji se najveći zajednički djelilac traži
Promjenljive korišćene u funkciji nzd:
a, b brojevi čiji se najveći zajednički djelilac traži
Realizacija funkcije prati postavku problema u potpunosti, pa je nećemo posebno
objašnjavati.
Izvršenje programa:
Unesite brojeve: 132 128
GCD=4
192 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Zadatak 2. Napisati rekurzivnu funkciju čije je zaglavlje int nzd(int m,int n), koja
računa NZD dva pozitivna cijela broja na osnovu relacije:

 m, mn

nzd(m, n)   nzd(m  n, n), m  n
nzd(m, n  m), inače.

#include<stdio.h>

int nzd(int,int);

int main()
{
int m,n;
printf("Unesite brojeve: ");
scanf("%d%d",&m,&n);
printf("GCD=%d\n", nzd(m,n));
}

int nzd(int m,int n)


{
if(m==n)
return m;
else if(m>n)
return nzd(m‐n,n);
else
return nzd(m,n‐m);
}

Promjenljive korišćene u programu:


m, n brojevi čiji se najveći zajednički djelilac traži
Promjenljive korišćene u funkciji nzd:
m, n brojevi čiji se najveći zajednički djelilac traži
Realizacija funkcije prati postavku problema u potpunosti.
Izvršavanja programa:
Unesite brojeve: 132 128
GCD=4

Unesite brojeve: 128 128


GCD=128

Zadatak 3. Napisati rekurzivnu realizaciju funkcije čije je zaglavlje:


float cheby(float x,int n)
2.6 R EKURZIVNE FUNKCIJE 195

int main()
{
int n;
printf("Unesite cijeli broj: ");
scanf("%d",&n);
dek2bin(n,0);
printf("Binarna verzija broja %d je %s\n",n,s);
}

void dek2bin(int n,int i)


{
if(n==0)
{
s[i]='\0';
obrniString(0,i‐1);
}
else
{
s[i++]=n%2+'0';
dek2bin(n/2,i);
}
}

void obrniString(int i,int j)


{
if(i>=j) return;
char temp=s[i];
s[i]=s[j];
s[j]=temp;
obrniString(i+1,j‐1);
}

Globalne promjenljive:
s string koji sadrži binarni ekvivalent prirodnog broja
Promjenljive korišćene u glavnom programu:
n prirodni broj čiji se binarni oblik određuje
Promjenljive korišćene u funkciji dek2bin
n prirodni broj čiji se binarni ekvivalent traži
i pozicija u stringu s do koje se stiglo sa upisom
Promjenljive korišćene u funkciji obrniString
i, j indeksi elemenata stringa koji mijenjaju pozicije
Konverzija prirodnog broja n u njegov binarni ekvivalent je izvšena pomoću funkcija
dek2bin i obrniString. Funkcija dek2bin vrši konverziju, ali je dobijeni redosljed binarnih
2.7 S ORTIRANJE 197

2.7 Sortiranje

Zadatak 1. Sortirati elemente unijetog niza prirodnih brojeva u neopadajući poredak uz pomoć
insertion sort algoritma. U ovom algoritmu, niz se dijeli na dva podniza, sortirani
i nesortirani. Na početku, samo prvi član niza pripada sortiranom podnizu, a svi
ostali nesortiranom. Sortiranje se obavlja dodavanjem jednog po jednog elementa
nesortiranog u sortirani podniz, tj. ako imamo podniz od prvih k sortiranih
elemenata, uzima se prvi element nesortiranog podniza i umeće se gdje mu je
mjesto u sortiranom podnizu. Poslije tog umetanja imamo podniz od k+1 sortiranih
elemenata. Proceduru vršiti dok ima elemenata u nesortiranom podnizu.
#include<stdio.h>

void insertionSort(int a[],int);

int main()
{
int i=0,duz,niz[100];
puts("Unijeti duzinu niza");
scanf("%d",&duz);
puts("Elementi");
for(i=0;i<duz;i++)
scanf("%d",&niz[i]);
insertionSort(niz,duz);
printf("Sortirani niz je:\n");
for(i=0;i<duz;i++)
printf("%d ",niz[i]);
}

void insertionSort(int a[],int n)


{
int i,j,temp;
for(i=1;i<n;i++)
for(j=i;j>0&&a[j]<a[j‐1];j‐‐)
{
temp=a[j];
a[j]=a[j‐1];
a[j‐1]=temp;
}
}

Promjenljive korišćene u programu:


niz niz koji se sortira
duz dužina niza koji se sortira
198 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

i pomoćna brojačka promjenljiva


Promjenljive korišćene u funkciji:
a niz koji se sortira, predat preko pokazivača
n broj članova niza
i, j pomoćne brojačke promjenljive
temp pomoćna promjenljiva preko koje se obavlja zamjena elemenata niza
Insertion sort (sortiranje sa umetanjem) se obavlja na sljedeći način. Izvrši se sortiranje prva
dva elementa niza tako da se postave u ispravan redosljed. Treći element se dodaje tako što ga
poredimo prvo sa drugim elementom. U slučaju da treći nije manji od drugog, nema potrebe da
se vrši njihova zamjena, jer se treći već nalazi na dobroj poziciji. Sa druge strane, ako je treći
manji od drugog, zamjene im se mjesta, pa se zatim porede promijenjeni drugi element sa prvim
i zamjene im se mjesta ako je prvi veći. Proceduru nastavljamo sa četvrtim elementom kojeg po
istom principu pomijeramo ulijevo sve dok je element sa kojim se on poredi veći od njega. Na
ovaj način, prvi element iz nesortiranog podniza pronalazi svoje mjesto u sortiranom podnizu,
tj. pomijeramo ga ulijevo sve dok ne naiđemo na element niza koji nije veći od njega. Sami
protumačite programsku realizaciju.
Izvršenje programa:
Unijeti duzinu niza
7
Elementi
9 1 2 8 7 5 4
Sortirani niz je:
1 2 4 5 7 8 9

Zadatak 2. Sortirati elemente unijetog niza prirodnih brojeva u neopadajući poredak uz pomoć
quick sort algoritma.
#include<stdio.h>

void quickSort(int *,int,int);


int pod(int *,int,int);
void zam(int *,int,int);

int main()
{
int i=0,duz,niz[100];
puts("Unijeti duzinu niza");
scanf("%d",&duz);
puts("Elementi");
for(i=0;i<duz;i++)
scanf("%d",&niz[i]);
quickSort(niz,0,duz‐1);
printf("Sortirani niz je:\n");
for(i=0;i<duz;i++)
printf("%d ",niz[i]);
}
2.7 S ORTIRANJE 199

void quickSort(int *niz1,int a1,int a2)


{
int i;
if(a1>=a2)
return;
if(a1+1==a2)
{
if (niz1[a1]>niz1[a2])
zam(niz1,a1,a2);
return;
}
i=pod(niz1,a1,a2);
quickSort(niz1,a1,i‐1);
quickSort(niz1,i+1,a2);
}

int pod(int *niz2,int b1,int b2)


{
int i, j;
for(j=b1,i=b1+1;i<=b2;i++)
if(niz2[i]<=niz2[b1])
zam(niz2,++j,i);
zam(niz2,b1,j);
return j;
}

void zam(int *niz3,int i,int j)


{
int temp=niz3[i];
niz3[i]=niz3[j];
niz3[j]=temp;
}

Promjenljive korišćene u programu:


niz niz koji se sortira
duz dužina datog niza
i pomoćna brojačka promjenljiva
Promjenljive korišćene u funkciji quickSort:
niz1 niz koji se sortira, predat preko pokazivača
a1, a2 indeksi elemenata niza između kojih se obavlja sortiranje
i pozicija gdje se zadati niz razdvaja na podnizove
Promjenljive korišćene u funkciji pod:
niz2 niz koji se dijeli, predat preko pokazivača
b1, b2 indeksi elemenata niza između kojih se vrši dijeljenje
i, j pomoćne brojačke promjenljive
200 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Promjenljive korišćene u funkciji zam:


niz3 niz u kojem se vrši zamjena pozicija elemenata, predat preko pokazivača
i, j indeksi elemenata koji se mijenjaju
temp pomoćna promjenljiva u procesu zamjene
Quick sort algoritam pripada grupi algoritama podijeli-pa-vladaj (eng. divide&conquer).
Niz se dijeli u dva podniza, od kojih se jedan sastoji od svih elemenata niza koji su manji ili
jednaki posmatranom elementu niza, a drugi se sastoji od elemenata koji su veći od posmatranog
elementa niza. Posmatrani element niza se naziva pivotom i obično se, kao u našem slučaju,
usvaja da je to prvi element. To se obavlja u funkciji pod koja se u prvom koraku praktično
poziva za čitav niz od indeksa 0 do duz‐1. Ako naredni element koji se obrađuje pripada grupi
manjih ili jednakih prvom elementu, dolazi do zamjene pozicije ovog elementa niza sa prvim
elementom iz grupe većih elemenata na koji ukazuje j. Ujedno se pozicija j uvećava za jedan.
Ako se naiđe na veći element, grupa većih elemenata se uveća za jedan, odnosno ne vrši se
zamjena elemenata. Nakon prvog prolaska kroz niz, pivot zamjeni poziciju sa posljednjim
elementom podniza elemenata koji su manji ili jednaki njemu, čime on zauzima pravu poziciju
u sortiranom nizu, jer su svi elementi prije njega manji ili jednaki njemu, a nakon njega dolaze
veći elementi. Procedura pod vraća tekuću poziciju pivota sačuvanu u promjenljivoj j. Dakle,
nakon procedure pod niz je podijeljen na dva podniza, od kojih jedan čine članovi koji su
zasigurno manji ili jednaki pivotu i koji se nalaze na početku niza i drugog podniza koji čine
elementi veći od pivota. Procedura se obavlja rekurzivno i sada se primjenjuje na podniz
elemenata do pozicije pivota i na podniz od pivota ka kraju niza. U slučaju da dobijeni podniz
nema nijedan ili ima jedan element (uslov if(a1>=a2)) procedura se završava, a u slučaju da
ima dva elementa vrši se poređenje ova dva elementa i završava operacija nad podnizom.
Funkcija zam je iskorišćena za zamjenu elemenata niza i ona se obavlja na uobičajeni način.
Posmatrajmo ovaj algoritam na primjeru niza: 7 4 9 10 1 2. Pivot je prvi element, u našem
slučaju 7. Poziva se funkcija quick sa argumentima 0 i 5 (od prvog do posljednjeg elementa
niza). Ova funkcija poziva funkciju pod sa istim argumentima i nad istim nizom. Postavlja se
j=0 i i=1. Element niza na poziciji 1 je manji od elementa poziciji 0, pa dolazi do uvećanja j
na 1 i dolazi do zamjene elementa sa samim sobom, jer je i=j=1. Sada 4 predstavlja prvi od
elemenata u podnizu elemenata koji su manji ili jednaki pivotu. Zatim se uvećava brojač i na 2
i poredi se treći element niza sa pivotom. Kako je 9 veće od 7, ne vršimo dalje operacije (9 je
prvi u podnizu većih elemenata). i se uvećava na 3 i poredi se četvrti element sa pivotom. Kako
je 10>7, ne vrši se zamjena elemenata i 10 postaje drugi u podnizu većih elemenata. Zatim se i
uvećava na 4 i poredi se 1 sa 7. Sada se j uvećava na 2 i mijenjaju se elementi na pozicijima i i
j (tj. 1 i 9 mijenjaju mjesta) pa se dobija niz: 7 4 1 10 9 2. Znači, uvijek kada se naiđe na element
koji je manji ili jednak pivotu dolazi do promjene pozicije tog elementa sa prvim elementom iz
tekućeg podniza većih elemenata i tako se pridružuje elementu odgovarajućeg podniza. U
posljednjem prolazu se poredi element na poziciji i=5 sa pivotom i kako je on manji od pivota,
j se povećava na 3 i mijenjaju elementi na pozicijama i i j, pa se dobija niz: 7 4 1 2 9 10. Na
kraju se zamjeni pivot sa elementom na poziciji j=3 i dobija se niz 2 4 1 7 9 10. Kao rezultat se
vraća pozicija pivota j=3. Sada se poziva funkcija quickSort za elemente niza do pivota
(pozicije 0 do 2) i za elemente niza od pivota (pozicije 4 do 5). Procedura za drugi dio niza poredi
samo ova dva člana i kako su ispravno poređani ne vrši dalje operacije. Prođite kroz proceduru
za podniz manjih elemenata sami.
Izvršenje programa:
2.7 S ORTIRANJE 201

Unijeti duzinu niza


7
Elementi
6 4 9 7 2 1 3
Sortirani niz je:
1 2 3 4 6 7 9

Zadatak 3. Napisati program kojim se učitava niz prirodnih brojeva, sa najviše 30 članova, a
zatim se vrši preuređivanje elemenata tako da prvo dolaze parni, pa neparni brojevi.
Parni i neparni brojevi ne moraju biti uređeni ni po kom pravilu. Preuređivanje
odraditi “u mjestu” tj. bez korišćenja pomoćnih nizova. Štampati elemente tako
dobijenog niza.
#include<stdio.h>

int main()
{
int i,j,N,temp,a[30];
puts("Unijeti duzinu niza");
scanf("%d",&N);
puts("Unijeti elemente");
for(i=0;i<N;i++)
scanf("%d",a+i);
for(i=0;i<N‐1;i++)
for(j=i+1;j<N;j++)
if(a[i]%2==1&&a[j]%2==0)
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
for(i=0;i<N;i++)
printf("%d ",a[i]);
}

Promjenljive korišćene u programu:


i, j pomoćne brojačke promjenljive
N broj elemenata niza
temp pomoćna promjenljiva koja se koristi u zamjeni pozicija elemenata niza
a predmetni niz
Program je baziran na sortiranju sa ponovljenim minimumom. Do zamjene elemenata niza
dolazi ako je element a[i] neparan i a[j] je paran. Ovo je kompaktna, ali relativno neefikasna
procedura, jer zahtjeva (N-1)2 poređenja. Mnogo efikasniji način je jedan prolazak kroz niz i
provjeravanje parnosti i prebacivanje parnih članova na početak, a neparnih na kraj niza. Takva
procedura bi zahtjevala samo N poređenja. Napišite sami ovu proceduru.
204 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Promjenljive korišćene u programu:


rijeci riječi koje se sortiraju (zadate kao matrica, tj. niz stringova)
pomocna pomoćni string u procesu zamjene
broj broj riječi koji se zadaje
i, j pomoćne brojačke promjenljive
Ponovili smo proceduru algoritma sa ponovljenim minimumom. Osnovna razlika je ta što
se poređenje riječi obavlja preko funkcije strcmp, dok se zamjena pojedinih riječi obavlja
pomoću funkcije strcpy. Sami tumačite ovaj program na osnovu algoritma za ponovljeni
minimum.
Izvršenje programa:
Koliko rijeci zelite da unesete?
6
Unesite rijeci: Marko Janko Petar Mitar Ana Vojo
Sortirane rijeci su:
Ana
Janko
Marko
Mitar
Petar
Vojo

Zadatak 6. Napisati program koji će računati broj mandata koji dobijaju kandidatske liste po
izvršenim izborima na osnovu Dontovog (D’Hondt) izbornog sistema sa cenzusom
od p procenata. Postupak određivanja broja dobijenih mandata po Dontovom
sistemu se sastoji od sljedećeg.
U izbornoj jedinici u kojoj se bira N poslanika sa M kandidatskih lista nakon
prebrojavanja glasova:
 eliminišu se sve liste sa brojem glasova ispod cenzusa;
 vrši se dijeljenje broja glasova svake preostale liste sa 1, 2, 3,..., N;
 sa skupa svih lista se izdvoji N najvećih prethodno dobijenih količnika;
 najmanji tako dobijeni količnik se uzima za zajednički djelitelj;
 svaka lista ima pravo na onoliko mandata koliko se u njenom broju glasova
sadrži zajednički djelitelj.

#include<stdio.h>
#include<math.h>

int main()
{
float lista[10],cenzus,suma,kolicnik[300],pom;
int i,j,N,M,prosloCenz=0;
printf("Unijeti broj poslanika i poslanickih lista \n");
2.7 S ORTIRANJE 205

scanf("%d%d",&N,&M);
printf("Unijeti broj glasova pojedinih poslanickih lista \n");
suma=0.0;
for(i=0;i<M;i++)
{
scanf("%f",lista+i);
suma+=lista[i];
}
printf("Unijeti cenzus (u procentima)\n");
scanf("%f",&cenzus);
for(i=0;i<M;i++)
if(lista[i]>cenzus*suma/100)
{
prosloCenz++;
for(j=0;j<N;j++)
kolicnik[(prosloCenz‐1)*N+j]=lista[i]/(j+1);
}
for(i=0;i<prosloCenz*N‐1;i++)
for(j=1;j<prosloCenz*N;j++)
if(kolicnik[i]<kolicnik[j])
{
pom=kolicnik[i];
kolicnik[i]=kolicnik[j];
kolicnik[j]=pom;
}
for(i=0;i<M;i++)
if(lista[i]>cenzus*suma/100)
printf("Lista %d je osvojila %d mandata\n",
i,(int)(lista[i]/kolicnik[N‐1]));
}

Promjenljive korišćene u programu su:


lista niz u kome je upisan broj glasova koje su pojedine poslaničke liste dobile
cenzus vrijednost cenzusa u procentima
suma ukupan broj "važećih glasova"
kolicnik niz u kome se čuvaju podaci o količnicima
pom pomoćna promjenljiva korišćena u sortiranju količnika
i, j pomoćne brojačke promjenljive
N broj poslanika koji se biraju
M broj poslaničkih lista
prosloCenz broj lista koje su prešle cenzus
Ovo je uvijek aktuelna problematika kod nas. Realizacija se odvija u nekoliko koraka: 1)
Učitavanje podataka (broj kandidata koji se biraju i broj poslaničkih lista), te dobijenih glasova
po pojedinim listama (tokom upisivanja broja dobijenih glasova po listama sumira se ukupan
broj glasova koje su dobile poslaničke liste u promjenljivoj suma); 2) Provjerava se da li je lista
prešla preko cenzusa i ako jeste za tu se listu računaju količnici (broj glasova koji je lista dobila
se dijeli sa j+1 jer petlja ide od 0 do N-1). Svi dobijeni količnici se "slažu" u jedan niz. Obratite
214 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

2.9 Fajlovi

Zadatak 1. Napisati program za kreiranje fajla koji sadrži sve prirodne brojeve manje od
zadatog prirodnog broja, njihove kvadrate i kubove.
#include<stdio.h>

int main()
{
int broj,i;
FILE *a;
a=fopen("Fajl.txt","w");
if(a==NULL)
exit(1);
printf("Zadajte neki prirodan broj: ");
scanf("%d",&broj);
for(i=1;i<broj;i++)
fprintf(a,"%d\t%d\t%d\n",i,i*i,i*i*i);
fclose(a);
}

Promjenljive korišćene u programu:


a pokazivač na fajl Fajl.txt
broj prirodni broj koji se zadaje
i pomoćna brojačka promjenljiva
Nakon otvaranja i odgovarajuće provjere učitava se cijeli broj. U for petlji se vrši
upisivanje brojeva, njihovih kvadrata i kubova u odredišni fajl, koji se nakon završetka for
petlje zatvara funkcijom fclose.
Izvršenje programa:
Zadajte neki prirodan broj: 12

Dobijeni rezultujući fajl Fajl.txt:


1 1 1
2 4 8
3 9 27
4 16 64
5 25 125
6 36 216
7 49 343
8 64 512
9 81 729
10 100 1000
11 121 1331
2.9 FAJLOVI 215

Zadatak 2. Svaki red fajla sadrži po jedan cio broj. Sastaviti program koji štampa najmanji i
najveći broj u tom fajlu.
#include<stdio.h>
#include<string.h>

int main()
{
FILE *a;
int min,max,i;
if((a=fopen("CioBroj.txt","r"))==NULL)
{
puts("Nije otvoren fajl");
exit(1);
}
if(fscanf(a,"%d",&i)==EOF)
{
puts("Fajl je prazan");
exit(1);
}
min=max=i;
while(fscanf(a,"%d",&i)!=EOF)
if(i>max)
max=i;
else if(i<min)
min=i;
printf("Minimum i maksimum su: %d i %d\n",min,max);
fclose(a);
}

Promjenljive korišćene u programu:


a pokazivač na fajl
i broj koji se učitava
min minimalna vrijednost
max maksimalna vrijednost
Prvo se otvara fajl i ako otvaranje nije obavljeno uspješno izlazi se iz programa uz
odgovarajuće obavještenje. Takođe, iz programa se izlazi ako nema nijednog broja u fajlu. U
suprotnom se prvi broj postavlja za tekući i minimum i maksimum i zatim se u while petlji
prolazi kroz sve ostale brojeve. Ako se naiđe na broj koji je veći od maksimuma on se postavlja
za tekući maksimum, a takođe i minimum se ažurira ako se naiđe na broj koji je manji od
minimuma.
Sadržaj fajla CioBroj.txt koji je korišćen za testiranje programa:
6
5
11
3
2
216 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

18
21
3
0
6
17
Izvršenje programa:
Minimum i maksimum: 0 i 21

Zadatak 3. Svaki red fajla sadrži po jedan cio broj. Formirati novi fajl koji se razlikuje od
starog po tome što su prvi, treći, peti itd. broj udvostručeni.
#include<stdio.h>

int main()
{
int broj,i;
FILE *a,*b;
if((a=fopen("CioBroj.txt","r"))==NULL)
{
puts("Greska pri otvaranju fajla");
exit(1);
}
if((b=fopen("NoviCio.txt","w"))==NULL)
{
puts("Greska pri otvaranju fajla");
exit(1);
}
i=1;
while(fscanf(a,"%d",&broj)!=EOF)
{
fprintf(b,"%d\n",(i%2==1)?2*broj:broj);
i++;
}
fclose(a);
fclose(b);
}

Promjenljive korišćene u programu:


a, b pokazivači na izvorišni i odredišni fajl
i pomoćna brojačka promjenljiva
broj broj koji se učitava
Program je jednostavan i vrši operacije postavljene u zadatku. Štampanje brojeva u
odredišni fajl je obavljeno korišćenjem ternarnog operatora, koji duplira vrijednosti u neparnim
redovima, a parne redove prepisuje nepromijenjene.
Korišćenjem fajla CioBroj.txt iz prethodnog zadatka, sadržaj fajla NoviCio.txt je:
2.9 FAJLOVI 217

12
5
22
3
4
18
42
3
0
6
34

Zadatak 4. Napisati program koji u novootvoreni fajl upisuje sve trocifrene prirodne brojeve
čija je suma cifara paran broj i koji su istovremeno prosti.
#include<stdio.h>
#include<stdlib.h>

int prost(int);
int sumaCif(int);

int main()
{
int i;
FILE *a;
a=fopen("prost.txt","w");
if(a==NULL)
exit(1);
for(i=100;i<1000;i++)
if(prost(i)&&sumaCif(i)%2==0)
fprintf(a,"%d ",i);
fclose(a);
}

int prost(int n)
{
int i;
for(i=2;i*i<=n;i++)
if(n%i==0)
return 0;
return 1;
}

int sumaCif(int n)
{
if(n>0)
return n%10+sumaCif(n/10);
return 0;
222 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Sadržaj izvornog fajla Citaj.txt:


U
svakom
redu
fajla
upisana
je
jedna
rijec
Sadržaj odredišnog fajla Pisi.txt:
U 1
svakom 6
redu 4
fajla 5
upisana 7
je 2
jedna 5
rijec 5

Zadatak 9. Koliko se puta u određenom fajlu pojavljuje karakter ''novi red''? Kolika je najveća
dužina reda? Napisati program koji otvara tekstualni fajl i odgovara na ova pitanja.
#include<stdio.h>

int main()
{
int red=0,duzina=0,pom=0;
FILE *a;
char ch;
a=fopen("Citaj.txt","r");
while((ch=fgetc(a))!=EOF)
{
pom++;
if(ch=='\n')
{
red++;
pom‐‐;
if(pom>duzina)
duzina=pom;
pom=0;
}
}
duzina=(duzina>pom)?duzina:pom;
printf("U fajlu se znak za novi red pojavljuje %d puta\n",red);
printf("Najveca duzina je %d\n",duzina);
}

Promjenljive korišćene u programu:


2.9 FAJLOVI 223

a pokazivač na odredišni fajl


red brojač karaktera '\n'
duzina dužina najduže riječi
pom pomoćna promjenljiva koja mjeri dužinu tekuće riječi
Najvažniji dio programa je smješten u okviru while petlje. U uslovu u petlji se učitava
karakter, a učitavanje se sprovodi sve dok se ne naiđe na karakter za kraj fajla (EOF). Ako smo
naišli na kraj reda, pomoćna promjenljiva se poredi tekućom maksimalnom dužinom reda
(duzina) i u slučaju da je veća od nje, dolazi do ažuriranja promjenljive duzina. U svakom
slučaju, nailaskom na '\n', pom se postavlja na 0, kako bi bila spremna za određivanje dužine
sljedećeg reda. Može da se dogodi da je najduži red u fajlu onaj koji se završava sa EOF. U tom
slučaju će promjenljiva pom biti veća od promjenljive duzina, pa se obavlja dodatna provjera
van while petlje pomoću uslovnog operatora.

Zadatak 10. Fajl Tekst.txt sadrži više redova teksta, pri čemu dužina reda ne prelazi 80
karaktera, a red je sastavljen od velikih slova i bjelina. Učitava se jedna riječ. Treba
štampati sve redove fajla u kojima se ta riječ bar jednom pojavljuje.
#include<stdio.h>
#include<string.h>

int main()
{
FILE *a;
char rijec1[81],rijec2[81];
printf("Unesite neku rijec: ");
scanf("%s",rijec2);
a=fopen("Tekst.txt", "r");
if(a==0)
exit(1);
while(fgets(rijec1,81,a)!=NULL)
if(strstr(rijec1,rijec2))
printf("%s\n",rijec1);
fclose(a);
}

Promjenljive korišćene u programu:


a pokazivač na fajl
rijec1 redovi fajla
rijec2 riječ koja se traži u fajlu
Učitava se red po red fajla funkcijom fgets, pa se funkcijom strstr provjerava da li se
u učitanom stringu pojavljuje string rijec2. Funkcija strstr vraća pokazivač na prvo
pojavljivanje drugog stringa u prvom. Ukoliko strstr vrati NULL pokazivač, drugi string nije
sadržan u prvom. Ova realizacija ne provjerava da li se drugi string pojavljuje kao riječ u prvom
stringu sa bjelinom ispred i iza, već samo da li se pojavljuje. Za vježbu napisati funkciju koja
provjerava da li se zadati string pojavljuje u drugom stringu sa bjelinom ispred i iza (početak i
kraj stringa tretirati na poseban način) i zatim tu funkciju povezati sa predmetnim programom.
224 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Sadržaj fajla Tekst.txt:


Danas imamo casove.
Cas pocinje u 17.15.
Sjutra nemamo casove
jer je praznik.
Izvršenje programa:
Unesite neku rijec: casove
Danas imamo casove.
Sjutra nemamo casove

Zadatak 11. U svakom redu fajla Word.txt je upisana jedna riječ, čija dužina ne prelazi 19
karaktera. Na osnovu ovog fajla treba kreirati fajl SortWord.txt u kojem će biti
upisane sortirane riječi iz fajla Word.txt. Nakon datih obrada, zatvorite predmetne
fajlove. Vodite računa da li su fajlovi korektno otvoreni. Maksimalan broj riječi u
polaznom fajlu ne prelazi 100.
#include<stdio.h>
#include<string.h>

int main()
{
FILE *a,*b;
char rijeci[100][20], temp[20];
int i,j,n;
n=0;
if((a=fopen("Word.txt","r"))==NULL)
{
puts("Nije otvoren fajl");
exit(1);
}
if((b=fopen("SortWord.txt","w"))==NULL)
{
puts("Nije otvoren fajl");
exit(1);
}
while(fscanf(a,"%s",rijeci[n])!=EOF)
n++;
for(i=0;i<n‐1;i++)
for(j=i+1;j<n;j++)
if(strcmp(rijeci[i],rijeci[j])>0)
{
strcpy(temp,rijeci[i]);
strcpy(rijeci[i],rijeci[j]);
strcpy(rijeci[j],temp);
}
for(i=0;i<n;i++)
fprintf(b,"%s\n",rijeci[i]);
fclose(a);
2.9 FAJLOVI 225

fclose(b);
}

Promjenljive korišćene u programu:


a, b pokazivači na polazni i odredišni fajl
rijeci učitane riječi iz polaznog fajla
temp pomoćni string koji se koristi u sortiranju
i, j pomoćne brojačke promjenljive
n broj učitanih riječi iz izvornog fajla
U while petlji se učitavaju riječi uz njihovo prebrojavanje. Dvostruka for petlja koja
slijedi implementira algoritam za sortiranje riječi metodom uzastopnih minimuma, dok se u petlji
na kraju programa vrši štampanje riječi u odredišni fajl.
Primjer fajla Word.txt:
Marko
Ana
Anabela
Sirena
Danica
Janko
Dobijeni fajl SortWord.txt:
Ana
Anabela
Danica
Janko
Marko
Sirena

Zadatak 12. U svakom redu fajla Studenti.txt upisani su podaci za studente: Prezime (string do
20 karaktera), Ime (string do 20 karaktera), Dan rođenja, Mjesec rođenja (cijeli
broj) i Godina rođenja. Treba sortirati studente u fajl Sortirani.txt tako što će prvo
biti prikazani najstariji studenti, pa mlađi. U slučaju da dva studenta imaju isti
datum rođenja sortirati ih po prezimenu, pa po imenu (leksikografski). Voditi
računa da li su fajlovi pravilno otvoreni. Maksimalan broj studenata čiji su podaci
zapisani u fajlu Studenti.txt ne može da pređe 50.
#include<stdio.h>
#include<string.h>

int main()
{
FILE *a,*b;
char ime1[50][20],ime2[20],prez1[50][20],prez2[20];
int i,j=0,dan1[50],dan2,mjes1[50],mjes2,god1[50],god2;
if((a=fopen("Studenti.txt","r"))==NULL)
{
2.9 FAJLOVI 229

return 0;
if(b==2&&!pres&&a==29)
return 0;
return 1;
}

Promjenljive korišćene u programu:


ime ime studenta
prez prezime studenta
dan datum rođenja
mjes mjesec rođenja
god godina rođenja
ispit broj položenih ispita
pros prosječna ocjena
ocjena ocjena na ispitu
Promjenljive korišćene u funkciji:
a, b, c dan, mjesec i godina rođenja
pres indikator da li je u pitanju prestupna godina (1 jeste, 0 nije)
Program je model jedne funkcije u vođenju studentske službe. Dizajniran je da uputi
studente u mogućnosti programerskog vođenja različitih tipova evidencije. U while petlji
učitavaju se podaci o studentima iz fajla Studenti.txt do kraja fajla. Za svakog studenta se učitava
i ocjena iz fajla Ispit.txt. Selekcija if bira sve studente sa neispravnim podacima i prepisuje
njihove podatke u fajl Azuriran.txt uz obavještenje da treba provjeriti podatke. Ako su podaci
korektni i student nije položio ispit (ocjena 5), prepisuju se podaci iz fajla Student.txt, dok u
slučaju da je student položio ispit vrši se inkrementiranje broja položenih ispita i proračun
prosječne ocjene studenta. Funkcija provjeriDatum provjerava da li je u pitanju ispravno
zapisan datum i ako nije vraća 0, a u suprotnom 1.
Sadržaj fajla Studenti.txt:
Ivan Jovanovic 23 8 1979 13 8.45
Marko Jacimovic 17 9 1982 21 6.35
Sanja Markovic 14 4 1981 43 8.34
Janko Perovic 32 7 1982 23 7.34
Dario Tomic 11 6 1978 22 6.94
Sadržaj fajla Ispit.txt:
6
5
5
8
11
Rezultat obrade upisan u fajl Azuriran.txt:
Ivan Jovanovic 23 8 1979 14 8.27
Marko Jacimovic 17 9 1982 21 6.35
Sanja Markovic 14 4 1981 43 8.34 Provjeri podatke!
2.10 STRUKTURE I LISTE 231

2.10 Strukture i liste

Zadatak 1. Formirati strukturu koja predstavlja kompleksni broj, kao i osnovne funkcije sa
njom (sabiranje, oduzimanje, konjugovanje, realni i imaginarni dio, množenje i
dijeljenje).
#include<stdio.h>

struct complex add(struct complex,struct complex);


struct complex sub(struct complex,struct complex);
struct complex prod(struct complex,struct complex);
struct complex div(struct complex,struct complex);
float re(struct complex);
float im(struct complex);
struct complex konj(struct complex);

struct complex{
float real,imag;
};

int main()
{
struct complex a,b,c,d,e,f;
a.real=1.23;
a.imag=3.24;
b.real=1.75;
b.imag=‐2.86;
c=add(a,b);
d=sub(a,b);
e=prod(a,b);
f=div(a,b);
a=konj(a);
printf("Zbir kompleksnih brojeva je: %f %f\n",c.real,c.imag);
printf("Razlika kompleksnih brojeva je: %f %f\n",d.real,d.imag);
printf("Proizvod kompleksnih brojeva je: %f %f\n",e.real,e.imag);
printf("Kolicnik kompleksnih brojeva je: %f %f\n",f.real,f.imag);
printf("Konjugovani kompleksni broj je: %f %f\n",re(a),im(a));
}

struct complex add(struct complex x,struct complex y)


{
struct complex z;
z.real=x.real+y.real;
z.imag=x.imag+y.imag;
return z;
232 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

struct complex sub(struct complex x,struct complex y)


{
struct complex z;
z.real=x.real‐y.real;
z.imag=x.imag‐y.imag;
return z;
}

struct complex prod(struct complex x,struct complex y)


{
struct complex z;
z.real=x.real*y.real‐x.imag*y.imag;
z.imag=x.imag*y.real+x.real*y.imag;
return z;
}

struct complex div(struct complex x,struct complex y)


{
struct complex z;
float t=y.real*y.real+y.imag*y.imag;
z.real=(x.real*y.real+x.imag*y.imag)/t;
z.imag=(x.imag*y.real‐x.real*y.imag)/t;
return z;
}

float re(struct complex x)


{
return x.real;
}

float im(struct complex x)


{
return x.imag;
}

struct complex konj(struct complex x)


{
x.imag*=‐1;
return x;
}

Promjenljive članice strukture complex:


real, imag realni i imaginarni dio kompleksnog broja, respektivno
Promjenljive korišćene u glavnom programu:
a, b kompleksni brojevi, zadati preko strukture, nad kojima se vrše operacije
c, d, e, f rezultati pojedinih operacija nad kompleksnim brojevima
2.10 STRUKTURE I LISTE 233

Promjenljive korišćene u funkcijama add, sub, prod i div:


x, y kompleksni brojevi nad kojima se operacije vrše
z kompleksni broj, rezultat funkcije
Promjenljive korišćene u funkcijama re, im i konj:
x kompleksni broj čiji se realni dio, imaginarni dio, odnosno konjugovana
vrijednost traži
Ovaj zadatak demonstrira kako se u programskom jeziku C radi sa strukturama kao
argumentima funkcije. Strukture se, kao i ostali tipovi podataka u C-u, prosljeđuju po vrijednosti.
Čitaocu prepuštamo tumačenje funkcija.
Zadatak predstavlja jedan način rada sa kompleksnim brojevima. Drugi način bi bio
korišćenjem complex tip podatka uvedenog u standardu jezika C99, kako je opisano u poglavlju
1.2.24.1.
Izvršenje programa:
Zbir kompleksnih brojeva je: 2.980000 0.380000
Razlika kompleksnih brojeva je: ‐0.520000 6.100000
Proizvod kompleksnih brojeva je: 11.418900 2.152200
Kolicnik kompleksnih brojeva je: ‐0.632791 0.817267
Konjugovani kompleksni broj je: 1.230000 ‐3.240000

Zadatak 2. Napisati program koji učitava podatke za nekoliko lica, pri čemu su ti podaci:
1. pol lica (muški ili ženski), 2. ime lica i 3. ime supruge/supruga. Štampati koliko
tu ima bračnih parova.
#include<stdio.h>
#include<string.h>

typedef struct{
char pol[10];
char ime[10];
char suprug[10];
} LICE;

int main()
{
LICE osobe[20];
int n,brak=0,i,j;
puts("Unesi broj lica:");
scanf("%d",&n);
puts("Unesi podatke za lica:");
for(i=0;i<n;i++)
scanf("%s%s%s",osobe[i].pol,osobe[i].ime,osobe[i].suprug);
for(i=0;i<n‐1;i++)
for(j=i+1;j<n;j++)
{
234 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

if((strcmp(osobe[i].pol,osobe[j].pol)!=0)&&
(strcmp(osobe[i].ime,osobe[j].suprug)==0)&&
(strcmp(osobe[i].suprug,osobe[j].ime)==0))
{
brak++;
break;
}
}
printf("Ima ukupno %d brakova.\n",brak);
}

Promjenljive članice strukture LICE:


pol string koji predstavlja pol osobe
ime string koji predstavlja ime osobe
suprug string koji predstavlja ime bračnog druga osobe
Promjenljive korišćene u glavnom programu:
osobe niz podataka strukturnog tipa za koji se vrši provjera
n ukupan broj lica
i, j pomoćne brojačke promjenljive
brak broj identifikovanih brakova u grupi osoba
Nakon učitavanja podataka za lica for petljom, vrši se provjera za svako lice da li se u
ostatku spiska (nakon datog lica) nalazi osoba koja joj je bračni drug. To je moguće ako su im
polovi različiti i ako imena međusobno odgovaraju sa imenom supružnika (provjera se obavlja
u if naredbi). U slučaju da se naiđe na odgovaranje, nema potrebe da se za datu osobu (petlja
po i) vrše dalje provjere, pa se petlja po j prekida naredbom break. Na kraju se štampa broj
pronađenih bračnih parova.
Zbog čega unutrašnja petlja po j u dijelu pronalaženja bračnih parova počinje od j=i+1?
Izvršenje programa:
Unesi broj lica:
7
Unesi podatke za lica:
m Janko Mara
m Pero Vera
z Mara Janko
z Vera Savo
z Vera Pero
m David Milica
m Savo Jaca
Ima ukupno 2 brakova.

Zadatak 3. Napisati program koji formira jednostruku povezanu listu. Element liste je
struktura koja sadrži cio broj. Kriterijum za prekid unosa je unos broja većeg od
30, koji se postavlja na rep liste.
#include<stdio.h>
2.10 STRUKTURE I LISTE 239

{
q=(struct lista *)malloc(sizeof(struct lista));
if(q==NULL)
exit(1);
q‐>i=n;
q‐>next=glava;
return q;
}
else
{
q=glava;
while(q‐>next!=NULL&&q‐>next‐>i<n)
q=q‐>next;
r=(struct lista *)malloc(sizeof(struct lista));
r‐>i=n;
r‐>next=q‐>next;
q‐>next=r;
return glava;
}
}

Promjenljive korišćene u funkciji umetanjeSort:


glava pokazivač na glavu liste (argument funkcije)
n broj koji se umeće u listu (argument funkcije)
q, r pomoćne promjenljive koje pokazuju na tekuće elemente liste
Pretpostavka je da je lista sortirana u rastući redosljed. Ako je element koji se učitava manji
od onog na kojeg pokazuje glava liste taj element se postavlja za glavu liste. Pokazivač na
novododati element je ujedno nova glava liste, koja se vraća kao rezultat. U suprotnom se prolazi
kroz jedan po jedan član liste, dok se ne stigne do repa ili do elementa koji je veći od zadatog
cijelog broja koji se umeće na dato mjesto u listi. Rezultat funkcije je pokazivač na
nepromijenjenu glavu liste.

Zadatak 9. Elementi liste su cijeli brojevi. Sastaviti funkciju koja iz date liste izbacuje sve
brojeve koji su manji od zadatog broja (argument funkcije).
struct lista *izbaciManje(struct lista *glava,int n)
{
struct lista *q,*r;
while(glava!=NULL&&glava‐>i<n)
{
q=glava;
glava=glava‐>next;
free(q);
}
if(glava==NULL)
return NULL;
q=glava;
240 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

while(q‐>next!=NULL)
{
if(q‐>next‐>i<n)
{
r=q‐>next;
q‐>next=q‐>next‐>next;
free(r);
}
else
q=q‐>next;
}
return glava;
}

Promjenljive korišćene u funkciji izbaciManje:


glava pokazivač na glavu liste (argument funkcije)
n cijeli broj od kojeg se svi manji brišu iz liste (argument funkcije)
q, r pomoćne promjenljive (pokazivači na elemente liste)
Funkcija vraća pokazivač na glavu nove liste iz koje su elimisani svi brojevi manji od
zadatog broja. U slučaju da je prvi element, ili nekoliko prvih elemenata, manji od zadatog broja,
brišemo elemente sa početka liste (prva while petlja) i tada se mijenja glava liste koju funkcija
vraća. Ukoliko su svi elementi liste manji od zadatog broja, vraća se NULL pokazivač (if uslov
nakon prve while petlje). Druga while petlja briše sve preostale elemente liste manje do
zadatog broja. Brisanje se vrši sa pozicije prethodnog elementa, na kog pokazuje promjenljiva
q.

Zadatak 10. Napisati funkciju koja na osnovu zadate jednostruko povezane liste prirodnih
brojeva formira dvije nove liste: listu neparnih i listu parnih brojeva. Napisati i
glavni program koji testira funkciju.
#include<stdio.h>
#include<stdlib.h>

struct lista{
int i;
struct lista *next;
} *glavaPar=NULL,*glavaNepar=NULL;

void parniNeparni(struct lista *);


void stampajListu(struct lista *);

int main()
{
struct lista *p,*q,*t;
int n;
printf("Unesi elemente liste. Broj >30 prekida unos. ");
p=(struct lista *)malloc(sizeof(struct lista));
p‐>next=NULL;
2.10 STRUKTURE I LISTE 241

scanf("%d",&n);
p‐>i=n;
t=p;
while(n<30)
{
q=(struct lista *)malloc(sizeof(struct lista));
scanf("%d",&n);
q‐>next=NULL;
q‐>i=n;
t‐>next=q;
t=q;
}
parniNeparni(p);
printf("Parni\n");
stampajListu(glavaPar);
printf("Neparni\n");
stampajListu(glavaNepar);
}

void parniNeparni(struct lista *glava)


{
struct lista *tek=glava,*tekPar=NULL,*tekNepar=NULL;
int parInd=0,neparInd=0;
while(tek!=NULL)
{
if(tek‐>i%2)
{
if(neparInd==0)
{
neparInd=1;
glavaNepar=tek;
tekNepar=glavaNepar;
}
else
{
tekNepar‐>next=tek;
tekNepar=tek;
}
}
else
{
if(parInd==0)
{
parInd=1;
glavaPar=tek;
tekPar=glavaPar;
}
else
{
244 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

struct lista{
int i;
struct lista *next;
};

struct lista *udruzi(struct lista *,struct lista *);

int main()
{
struct lista *p1,*q1,*t1,*p2,*q2,*t2,*qn;
int n,r;
printf("Unesi elemente prve liste. Broj >30 prekida unos. ");
p1=(struct lista *)malloc(sizeof(struct lista));
p1‐>next=NULL;
scanf("%d",&n);
p1‐>i=n;
t1=p1;
while(n<30)
{
q1=(struct lista *)malloc(sizeof(struct lista));
scanf("%d",&n);
q1‐>next=NULL;
q1‐>i=n;
t1‐>next=q1;
t1=q1;
}
printf("Unesi elemente druge liste. Broj >30 prekida unos. ");
p2=(struct lista *)malloc(sizeof(struct lista));
p2‐>next=NULL;
scanf("%d",&n);
p2‐>i=n;
t2=p2;
while(n<30)
{
q2=(struct lista *)malloc(sizeof(struct lista));
scanf("%d",&n);
q2‐>next=NULL;
q2‐>i=n;
t2‐>next=q2;
t2=q2;
}
qn=udruzi(p1,p2);
printf("Sjedinjena lista je: ");
stampajListu(qn);
}

struct lista *udruzi(struct lista *gl1,struct lista *gl2)


{
struct lista *glava,*p1,*p2,*tek,*q;
2.10 STRUKTURE I LISTE 245

p1=gl1;
p2=gl2;
glava=(struct lista *)malloc(sizeof(struct lista));
if(p1‐>i<p2‐>i)
{
glava‐>i=p1‐>i;
p1=p1‐>next;
}
else
{
glava‐>i=p2‐>i;
p2=p2‐>next;
}
glava‐>next=NULL;
tek=glava;
while(p1!=NULL||p2!=NULL)
{
q=(struct lista *)malloc(sizeof(struct lista));
tek‐>next=q;
tek=q;
q‐>next=NULL;
if(p1==NULL||(p1!=NULL&&p2!=NULL&&p2‐>i<p1‐>i))
{
q‐>i=p2‐>i;
p2=p2‐>next;
}
else
{
q‐>i=p1‐>i;
p1=p1‐>next;
}
}
return glava;
}

Promjenljive korišćene u glavnom programu:


p1, q1 pokazivači na glavu prve liste i na tekući element liste prilikom učitavanja
t1 pomoćna promjenljiva korišćena prilikom učitavanja prve liste
p2, q2 pokazivači na glavu druge liste i na tekući element liste prilikom učitavanja
t2 pomoćna promjenljiva korišćena prilikom učitavanja druge liste
qn pokazivač na novu listu
n cijeli broj koji se učitava u listu
Promjenljive korišćene u funkciji udruzi:
gl1, gl2 pokazivači na glave dvije liste koje se udružuju (argumenti funkcije)
glava glava udružene liste
p1, p2 pokazivači na tekuće elemente dvije liste
tek pokazivač na tekući element udružene liste
246 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

q pokazivač na element liste (pomoćna promjenljiva)


U programu se poziva i funkcija stampajListu definisana u prethodnim primjerima, pa
joj ovdje nismo davali realizaciju.
Funkcija udruzi za argumente dobija pokazivače na liste koje treba objediniti. U funkciji
pamtimo tekuće elemente polaznih lista preko pokazivača p1 i p2, i pokazivač na tekući element
ukupne liste tek. Glavom nove liste postaje glava one polazne lista u kojoj se čuva manji
element. Kada se sa jedne od dvije polazne liste element premjesti u novoformiranu listu pomjera
se i pokazivač na tekući element te liste. Tekući element jedne liste postaje element nove liste
ako je manji od tekućeg elementa druge liste ili ako u drugoj listi nema više elemenata.
Izvršenje programa:
Unesi elemente prve liste. Broj >30 prekida unos. 1
2
3
8
9
55
Unesi elemente druge liste. Broj >30 prekida unos. 4
5
11
12
66
Sjedinjena lista je: 1 2 3 4 5 8 9 11 12 55 66

Zadatak 12. Elementi jednostruko povezane liste su riječi. Sastaviti funkciju koja određuje da
li su te riječi sortirane u rastući poredak (podrazumjevati leksikografsko uređenje).
U glavnom programu kreirati listu sa proizvoljnim brojem elemenata i testirati
funkciju.
#include<stdio.h>
#include<string.h>

struct lista{
char rijec[10];
struct lista *next;
};

int jeLiSortirana(struct lista *);

int main()
{
struct lista *p,*q,*t;
int n=0,ind;
puts("Unijeti rijeci: ");
p=(struct lista *)malloc(sizeof(struct lista));
if(p==NULL)
exit(1);
p‐>next=NULL;
2.10 STRUKTURE I LISTE 247

scanf("%s",p‐>rijec);
t=p;
while(n++<6)
{
q=(struct lista *)malloc(sizeof(struct lista));
if(q==NULL)
exit(1);
scanf("%s",q‐>rijec);
q‐>next=NULL;
t‐>next=q;
t=q;
}
ind=jeLiSortirana(p);
if(ind)
puts("Rijeci su leksikografski poredjane!");
else
puts("Rijeci nijesu leksikografski poredjane!");
}

int jeLiSortirana(struct lista *glava)


{
while(glava‐>next!=NULL)
{
if(strcmp(glava‐>rijec,glava‐>next‐>rijec)>=0)
return 0;
glava=glava‐>next;
}
return 1;
}

Promjenljive korišćene u glavnom programu:


p, q, t pokazivači na elemente liste (p pokazivač na glavu, q pokazivač na tekući
element koji se učitava i t pomoćni pokazivač)
n redni broj elementa koji se učitava
ind indikator da li je lista sortirana
Promjenljive korišćene u funkciji jeLiSortirana:
glava pokazivač na glavu liste (argument funkcije)
Funkcija koja provjerava sortiranost liste je jeLiSortirana. U while petlji funkcije
poredimo susjedne riječi i čim naiđemo na situaciju da je prethodna riječ veća ili jednaka
narednoj, prekidamo izvršenje funkcije i vraćamo 0 (lista nije sortirana). Kad se izađe iz petlje,
vraćamo 1 (lista je sortirana). U glavnom programu se formira lista od sedam riječi (ovo je
proizvoljan broj), poziva funkcija i štampa odgovarajuće obavještenje.
Izvršavanja programa:
Unijeti rijeci:
Ana
248 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Bilja
Ceca
Dana
Jaca
Mara
Stevo
Rijeci su leksikografski poredjane!

Unijeti rijeci:
Vuko
Djuro
Niksa
Petar
Idzo
Dusko
Simo
Rijeci nijesu leksikografski poredjane!

Zadatak 13. Sastaviti funkciju za preokretanje redosljeda elemenata jednostruko povezane liste.
Funkcija vraća pokazivač na glavu preokrenute liste.
struct lista *preokreni(struct lista *glava)
{
struct lista *preth=NULL,*tek=glava,*sljed;
while(tek‐>next!=NULL)
{
sljed=tek‐>next;
tek‐>next=preth;
preth=tek;
tek=sljed;
}
tek‐>next=preth;
return tek;
}

Promjenljive korišćene u funkciji preokreni:


glava pokazivač na glavu liste (argument funkcije)
preth pokazivač na prethodni element liste u odnosu na onaj koji se ispituje
tek pokazivač na tekući element liste koji se ispituje
sljed pokazivač na naredni element liste u odnosu na onaj koji se ispituje
Mada zvuči jednostavno, ovaj problem nije takav. U funkciji koristimo tri pokazivača: tek
(pamti tekući element), preth (pamti prethodni element) i sljed (pamti naredni element).
Operacija obrtanja dva elementa liste se obavlja u četiri poteza: zapamtimo naredni element liste,
tekući element postavimo da ukazuje na prethodni, prethodni element postaje tekući, a tekući
postaje naredni. Operacija se obavlja dok se ne stigne na kraj liste. Rep polazne liste postaje
glava liste i vraća se kao rezultat funkcije.
2.10 STRUKTURE I LISTE 253

glava=glava‐>next;
}
printf("\n");
}
}

Promjenljive korišćene u strukturi lista2P:


i cijeli broj član liste
next pokazivač na naredni element liste
prev pokazivač na prethodni element liste
Promjenljive korišćene u glavnom programu:
glava pokazivač na glavu liste
p, q pomoćni pokazivači na podatak tipa lista
x niz čiji elementi postaju elementi liste
n dužina niza x
i pomoćna brojačka promjenljiva
Promjenljive korišćene u funkciji dodajElement2P:
glava glava liste (argument funkcije)
n broj koji se dodaje u listu (argument funkcije)
Promjenljive korišćene u funkciji stampajListu:
glava glava liste (argument funkcije)
Tumačenje programa prepuštamo čitaocu. Napomenimo samo da je čitava problematika već
obrađena kod jednostruko povezane liste. Ovdje treba uvažiti postojanje pokazivača na prethodni
element liste prilikom dodavanja novih elemenata liste. U glavnom programu smo kreirali listu,
odštampali je, a nakon toga dodali još tri elementa liste. Nakon svakog dodavanje, štampamo
listu.
Izvršenje programa:
Lista je: 1 4 5 6 7 8 13
Lista je: 0 1 4 5 6 7 8 13
Lista je: 0 1 3 4 5 6 7 8 13
Lista je: 0 1 3 4 5 6 7 8 13 28

Zadatak 16. Napisati funkciju za brisanje odgovarajućeg čvora iz dvostruko povezane liste, koji
sadrži broj n, proslijeđen kao argument. Struktura za čvor liste je ista kao u
prethodnom zadatku.
struct lista2P *brisiElement2P(struct lista2P *glava,int n)
{
struct lista2P *q,*r;
if(glava‐>i==n)
{
q=glava‐>next;
q‐>prev=NULL;
254 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

free(glava);
return q;
}
else
{
q=glava;
while(q‐>next!=NULL&&q‐>next‐>i!=n)
q=q‐>next;
if(q‐>next!=NULL&&q‐>next‐>i==n)
{
r=q‐>next;
if(q‐>next‐>next!=NULL)
q‐>next‐>next‐>prev=q;
q‐>next=q‐>next‐>next;
free(r);
}
return glava;
}
}

Tumačenje funkcije prepuštamo čitaocu. U sekciji 1.3.1.7 je opisan slučaj za brisanje


elementa iz unutrašnjosti dvostruko povezane liste. Ova funkcija je opštija u smislu da uzima u
obzir i situaciju da se može brisati i glava (prva if naredba) i rep liste (if naredba nakon while
petlje).

Zadatak 17. Sastaviti program koji zadate riječi upisuje u dvostruko povezanu listu. Zatim se
učitava jednu riječ i provjerava da li ta riječ postoji u listi.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct lista2P{
char rijec[20];
struct lista2P *next;
struct lista2P *prev;
};

int postojanje(struct lista2P *,char *);

int main()
{
struct lista2P *p,*q,*t;
char s[20];
p=(struct lista2P *)malloc(sizeof(struct lista2P));
p‐>next=NULL;
p‐>prev=NULL;
puts("Unesi rijeci u listu: ");
gets(s);
strcpy(p‐>rijec,s);
2.10 STRUKTURE I LISTE 255

t=p;
while(strcmp(s,"kraj")!=0)
{
q=(struct lista2P *)malloc(sizeof(struct lista2P));
gets(s);
q‐>next=NULL;
q‐>prev=t;
strcpy(q‐>rijec,s);
t‐>next=q;
t=q;
}
puts("Unesi rijec koja se trazi u listi:");
scanf("%s",s);
if(postojanje(p,s))
puts("Postoji!");
else
puts("Ne postoji!");
}

int postojanje(struct lista2P *glava,char *s)


{
struct lista2P *q=glava;
while(q!=NULL)
{
if(strcmp(q‐>rijec,s)==0)
return 1;
q=q‐>next;
}
return 0;
}

Promjenljive članice strukture lista2P:


rijec string koji sadrži riječ-element liste
next pokazivač na naredni element liste
prev pokazivač na prethodni element liste
Promjenljive korišćene u glavnom programu:
p, q, t pokazivači na glavu, tekući element i prethodni element liste u odnosu na
učitani
s string pomoću kojeg se učitavaju riječi u listu
Promjenljive korišćene u funkciji postojanje:
glava pokazivač na glavu liste (argument funkcije)
s string koji se traži u listi (argument funkcije)
q pokazivač na član liste koji se ispituje
Glavni program učitava riječi do unosa riječi "kraj" i organizuje ih u dvostruko povezanu
listu. Funkcija postojanje provjerava da li tražena riječ postoji u dvostruko povezanoj listi; u
256 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

slučaju da postoji funkcija vraća vrijednost 1, a u suprotnom 0. Pokušajte da napišete funkciju


koja provjerava da li postoji riječ u dvostruko povezanoj listi, ali tako da se kao argument predaje
pokazivač na proizvoljni element liste.
Izvršavanja programa:
Unesi rijeci u listu:
Elvis
ranac
biber
kraj
Unesi rijec koja se trazi u listi:
kraj
Postoji!

Unesi rijeci u listu:


Elvis
ranac
biber
kraj
Unesi rijec koja se trazi u listi:
Tesla
Ne postoji!
2.12 B INARNO STABLO 265

2.12 Binarno stablo

Napomene: Pretpostavili smo da su stabla implementirana koristeći samoreferentne strukture.


Riješite zadatke i za ostale tipove predstavljanja stabla.
U većini programa biće rađeno samo sa funkcijama gdje se pretpostavlja da je stablo već
formirano.

Zadatak 1. Napisati program koji kreira binarno stablo sa slike.

#include<stdio.h>
#include<stdlib.h>

struct stablo{
int i;
struct stablo *left;
struct stablo *right;
};

int main()
{
struct stablo *p=NULL,*q=NULL,*r=NULL,*t=NULL;
p=(struct stablo *)malloc(sizeof(struct stablo));
if(p==NULL)
exit(1);
p‐>i=1;
p‐>left=NULL;
p‐>right=NULL;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=2;
q‐>left=NULL;
q‐>right=NULL;
266 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

p‐>left=q;
r=(struct stablo *)malloc(sizeof(struct stablo));
if(r==NULL)
exit(1);
r‐>i=3;
r‐>left=NULL;
r‐>right=NULL;
p‐>right=r;
t=q;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=4;
q‐>left=NULL;
q‐>right=NULL;
t‐>left=q;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=5;
q‐>left=NULL;
q‐>right=NULL;
t‐>right=q;
t=q;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=6;
q‐>left=NULL;
q‐>right=NULL;
t‐>left=q;
q=(struct stablo *)malloc(sizeof(struct stablo));
if(q==NULL)
exit(1);
q‐>i=7;
q‐>left=NULL;
q‐>right=NULL;
r‐>right=q;
}

Promjenljive korišćene u strukturi stablo:


i cijeli broj smješten u strukturi koja predstavlja čvor stabla
left pokazivač na lijevi sin čvora
right pokazivač na desni sin čvora
Promjenljive korišćene u glavnom programu:
p pokazivač na korijeni čvor
q, r, t pokazivači na čvorove u stablu
2.12 B INARNO STABLO 267

Nakon alokacije memorije za čvor (praćene sa provjerom uspješnosti ove operacije) vrši se
alokacija lijevog i desnog sina (pokazivači q i r). Zatim preko odgovarajućih pokazivača korijen
stabla pokaže na ove čvorove. U ostatku programa, promjenljiva q se koristi za alokaciju
pojedinih podataka članova, dok se kao pomoćna promjenljiva koja pamti pojedine čvorove
koristi promjenljiva t. Ovaj program je dat kao potencijalni test primjer za algoritme koji će biti
rađeni kasnije.

Zadatak 2. Svakom čvoru pridružen je cijeli broj. Cijele brojeve koji pripadaju čvoru treba
štampati u skladu sa preorder obilaskom.
void stampaPreorder(struct stablo *root)
{
printf("%d\n",root‐>i);
if(root‐>left!=NULL)
stampaPreorder(root‐>left);
if(root‐>right!=NULL)
stampaPreorder(root‐>right);
}

Promjenljive korišćene u funkciji stampaPreorder:


root pokazivač na korijen stabla (argument funkcije)
Po pravilu preorder obilaska prvo se obilazi (u ovom slučaju štampa) element koji se nalazi
u korijenom čvoru, zatim se obilazi lijevo podstablo (ovdje se to obavlja rekurzivnim pozivom
funkcije za lijevog sina) i na kraju desno podstablo, što se obavlja na isti način kao i za lijevo.
Za binarno stablo iz prvog primjera bilo bi odštampano 1 2 4 5 6 3 7.

Zadatak 3. Napisati funkciju koja određuje težinu stabla (broj čvorova stabla).
int tezina(struct stablo *root)
{
if(root‐>left!=NULL&&root‐>right!=NULL)
return 1+tezina(root‐>left)+tezina(root‐>right);
else if(root‐>left!=NULL)
return 1+tezina(root‐>left);
else if(root‐>right!=NULL)
return 1+tezina(root‐>right);
else
return 1;
}

Promjenljive korišćene u funkciji tezina:


root pokazivač na korijen stabla (argument funkcije)
Realizacija je rekurzivna. Broj čvorova je jednak 1 plus broj čvorova u lijevom i desnom
podstablu. U slučaju lista, funkcija vraća 1.
Pokušajte da napišete kraću funkciju koristivši mogućnost da funkcija radi i u slučaju da joj
je argument NULL pokazivač. Na primjer, jedna od naredbi u ovoj funkciji bi mogla da glasi:
268 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

if(root!=NULL) return 1+tezina(root‐>left)+tezina(root‐>right). Za vježbu


realizovati i druge funkcije iz ovog poglavlja korišćenjem ove osobine.

Zadatak 4. Sastaviti funkciju koja određuje najveći cijeli broj upisan u čvorove binarnog
stabla.
int maksCvor(struct stablo *root)
{
if(root‐>left!=NULL&&root‐>right!=NULL)
return max(max(root‐>i,maksCvor(root‐>left)),maksCvor(root‐>right));
else if(root‐>left!=NULL)
return max(root‐>i,maksCvor(root‐>left));
else if(root‐>right!=NULL)
return max(root‐>i,maksCvor(root‐>right));
return root‐>i;
}

int max(int a,int b)


{
return a>b?a:b;
}

Promjenljive korišćene u funkciji maksCvor:


root pokazivač na korijen stabla (argument funkcije)
Promjenljive korišćene u funkciji max:
a, b brojevi čiji se maksimum traži (argumenti funkcije)
Maksimalni element u stablu je ili korijen ili maksimum lijevog ili maksimum desnog
podstabla. To je i smisao prvog if bloka. Situacija se nešto pojednostavljuje ako korijen nema
nekog od potomaka.

Zadatak 5. Svakom čvoru pridružen je jedan cijeli broj. Sastaviti funkciju koja provjerava da
li na svakom putu od korijena ka listovima brojevi opadaju.
int opada(struct stablo *root)
{
if(root‐>left!=NULL&&root‐>right!=NULL)
if(root‐>i>root‐>left‐>i && root‐>i>root‐>right‐>i)
return opada(root‐>left)*opada(root‐>right);
else
return 0;
if(root‐>left!=NULL)
if(root‐>i>root‐>left‐>i)
return opada(root‐>left);
else
return 0;
if(root‐>right!=NULL)
if(root‐>i>root‐>right‐>i)
2.12 B INARNO STABLO 269

return opada(root‐>right);
else return 0;
return 1;
}

Promjenljive korišćene u funkciji opada:


root pokazivač na korijeni čvor u stablu (argument funkcije)
Posao je podijeljen na lijevog i desnog sina, sa analizom specijalnih slučajeva ako
posmatrani čvor u stablu ima samo jednog sina. Ako čvor ima oba sina, funkcija vraća 0 u slučaju
da broj upisan u čvoru nije veći od brojeva upisanih u sinovima. U suprotnom, funkcija vraća
proizvod rekurzivno pozvane funkcije za lijevog i za desnog sina (ako su oba 1, i rezultat će biti
1, odnosno stablo će zadovoljavati traženi uslov, a u suprotnom će biti 0). Slična se procedura
ponavlja ako postoji samo jedan sin, dok u slučaju da je u pitanju list (čvor koji nema potomaka)
rezultat je 1, jer smo stigli do kraja stabla po tom pravcu (odnosno uvjerili smo se u zadovoljenje
postavljenog uslova).
Modifikujte funkciju tako da se utvrđuje da li postoji bar jedan put od korijena ka listovima
tako da brojevi opadaju.

Zadatak 6. Sastaviti funkciju koja vraća koliko puta se dešava da je ocu i sinu u stablu
pridružen isti broj.
int otacSinIsti(struct stablo *root)
{
if(root‐>left!=NULL&&root‐>right!=NULL)
return (root‐>i==root‐>left‐>i)+(root‐>i==root‐>right‐>i)+
otacSinIsti(root‐>right)+otacSinIsti(root‐>left);
else if(root‐>left!=NULL)
return (root‐>i==root‐>left‐>i)+otacSinIsti(root‐>left);
else if(root‐>right!=NULL)
return (root‐>i==root‐>right‐>i)+otacSinIsti(root‐>right);
else
return 0;
}

Promjenljive korišćene u funkciji otacSinIsti:


root pokazivač na korijen stabla (argument funkcije)
Ovdje koristimo činjenicu da logički izrazi vraćaju 1 ako je uslov ispunjen i 0 ako nije. U
našem slučaju, izraz (root‐>i==root‐>left‐>i)+(root‐>i==root‐>right‐>i) daje 0
ako čvor i njegovi sinovi imaju različit upisan broj, 1 u slučaju jednog ponavljanja, a 2 ako čvor
i oba sina imaju upisan isti broj. Dobijenom rezultatu se pridružuje rekurzivni rezultat dobijen
pozivom ove funkcije za sinove korijena.

Zadatak 7. Svakom čvoru pridružen je jedan cijeli broj. Da li postoji barem jedan put od
korijena ka bilo kom listu u kome su upisani samo parni brojevi? Sastaviti
odgovarajuću funkciju.
270 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

int parni(struct stablo *root)


{
if(root‐>i%2==1)
return 0;
if(root‐>left!=NULL&&root‐>right!=NULL)
return parni(root‐>left)||parni(root‐>right);
else if(root‐>left!=NULL)
return parni(root‐>left);
else if(root‐>right!=NULL)
return parni(root‐>right);
else
return 1;
}

Promjenljive korišćene u funkciji parni:


root pokazivač na korijen stabla (argument funkcije)
Ako je tekući čvor neparan, sigurno je da nema putanje unutar njegovog podstabla koja daje
sve parne cifre. Ako je tekuća cifra parna, idemo korak niže u stablu, prema sinovima, da
provjerimo jesu li oni parni. Kad dođemo do lista, pretraga se prekida i vraća se 1. Čim se dođe
do lista, to znači da su svi čvorovi prije lista na toj putanji parni.

Zadatak 8. Svakom čvoru pridružena je jedna riječ dužine manje od 11 karaktera. Sastaviti
funkciju koja određuje i vraća koliko puta se pojavljuje data riječ.
struct stablo{
char rijec[12];
struct stablo *left;
struct stablo *right;
};

int brojRijeci(struct stablo *root,char *s)


{
int ind=0;
if(strcmp(root‐>rijec,s)==0)
ind=1;
if(root‐>left!=NULL&&root‐>right!=NULL)
return ind+brojRijeci(root‐>right,s)+brojRijeci(root‐>left,s);
if(root‐>left!=NULL)
return ind+brojRijeci(root‐>left,s);
if(root‐>right!=NULL)
return ind+brojRijeci(root‐>right,s);
return ind;
}

Promjenljive korišćene u strukturi stablo:


rijec riječ koja je upisana u čvoru stabla
left pokazivač na lijevog sina
right pokazivač na desnog sina
2.12 B INARNO STABLO 271

Promjenljive korišćene u funkciji brojRijeci:


root korijen stabla (argument funkcije)
s pokazivač na riječ koja se traži (argument funkcije)
ind pomoćna promjenljiva (indicira da li je riječ upisana u tekućem čvoru)
Na početku funkcije, promjenljiva ind se postavlja na 1 ako je riječ upisana u tekućem
čvoru, a u suprotnom ostaje 0. Ukupan broj riječi u stablu se dobija sabiranjem rezultata funkcije
za lijevo i desno stablo sa vrijednošću promjenljive ind.

Zadatak 9. Napisati funkciju koji određuje širinu stabla. Širina stabla se definiše kao najveći
broj čvorova duž jedne horizontale, tj. na istom rastojanju čvorova od korijena
stabla.
int sirina(struct stablo *root)
{
int N=visina(root);
int k,m=1,broj;
for(k=1;k<=N;k++)
{
broj=brojNivo(root,k);
if(broj>m)
m=broj;
}
return m;
}

int visina(struct stablo *root)


{
if(root‐>left!=NULL&&root‐>right!=NULL)
return 1+max(visina(root‐>left),visina(root‐>right));
else if(root‐>left!=NULL)
return 1+visina(root‐>left);
else if(root‐>right!=NULL)
return 1+visina(root‐>right);
else
return 0;
}

int brojNivo(struct stablo *root,int k)


{
if(k==0)
return 1;
if(root‐>left!=NULL&&root‐>right!=NULL)
return brojNivo(root‐>left,k‐1)+brojNivo(root‐>right,k‐1);
else if(root‐>left!=NULL)
return brojNivo(root‐>left,k‐1);
else if(root‐>right!=NULL)
return brojNivo(root‐>right,k‐1);
2.14 IGRE 275

2.13 Igre

Zadatak 1. Položaj figure na šahovskoj tabli zadaje se parom brojeva (brojevi su od 0 do 7).
Na tabli su samo jedan lovac i jedan skakač. Koliko je ukupno polja napadnuto sa
ove dvije figure?
#include<stdio.h>

int main()
{
int h1,v1,h2,v2,i,j,polja=0,A,B,C,D,E;
printf("Unijeti poziciju lovca: ");
scanf("%d %d",&h1,&v1);
printf("Unijeti poziciju skakaca: ");
scanf("%d %d",&h2,&v2);
for(i=0;i<8;i++)
for(j=0;j<8;j++)
{
A=!((i==h1&&j==v1)||(i==h2&&j==v2));
B=(i‐h2)*(i‐h2)+(j‐v2)*(j‐v2)==5;
C=(i‐h1==j‐v1)||(i‐h1==v1‐j);
D=!((v1‐v2==h2‐h1)||(v1‐v2==h1‐h2));
E=!((h2‐h1)*(h2‐h1)+(v2‐v1)*(v2‐v1)<(h1‐i)*(h1‐i)+(v1‐j)*(v1‐j)
&& (h2‐i)*(h2‐i)+(v2‐j)*(v2‐j)<(h1‐i)*(h1‐i)+(v1‐j)*(v1‐j));
if(A&&(B||(C&&(D||E))))
polja++;
}
printf("Broj napadnutih polja je: %d",polja);
}

Promjenljive korišćene u programu:


h1, v1 pozicija lovca
h2, v2 pozicija konja
i, j tekuća pozicija na tabli
polja broj napadnutih polja (na početku inicijalizovan na 0)
A, B, C, D, E pomoćne promjenljive korišćene u formiranju logičkog uslova
Za one koji ne poznaju pravila šaha evo nekoliko osnovnih napomena. Lovac napada figure
dijagonalno u odnosu na svoju poziciju (po obije dijagonale). Skakač (konj) napada ona polja
koja su po jednoj koordinati udaljena za 2, a po drugoj koordinati udaljena za 1. Lovac ne može
da napadne ona polja koja ne vidi, odnosno napada samo polja do prve figure na datoj dijagonali.
Skakač može da napadne sva polja bez obzira na figure između. Ako su lovac i skakač u istoj
boji (to je pretpostavka) onda se međusobno ne napadaju. Figura ne napada polje na kojem se
trenutno nalazi. Ako je jedno polje napadnuto od dvije figure to se broji samo jednom. Brojanje
276 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

napadnutih polja smo realizovali prolaskom kroz sva polja šahovske table i preko 5 pomoćnih
logičkih promjenljivih A, B, C, D i E. Promjenljiva A ispituje da li je tekuće polje (i,j) različito
od pozicija dvije unijete figure. Ovaj logički uslov mora biti ispunjen. Promjenljiva B ispituje da
li poziciju napada skakač. Ako su skakač i polje razmaknuti za 2 po jednoj koordinati i 1 po
drugoj koordinati, odnosno ako je kvadrat distance između njih 5, onda je polje napadnuto.
Promjenljiva C ispituje da li bi polje napadao lovac da nema skakača na tabli. Lovac napada polja
po glavnoj dijagonali u odnosu na njega, a to su polja za koja važi i‐h1==j‐v1, kao i polja na
sporednoj dijagonali u odnosu na njega, a to su polja kod kojih važi i‐h1==v1‐j. Uslov D
provjerava da li je skakač na nekoj od dijagonala u odnosu na lovca. Ako jeste (D će biti jednako
0), skakač može da preklopi neko od polja koje bi inače lovac napadao. Posljednji uslov E
provjerava da li je skakač bliži tekućoj tački i lovcu nego što je međusobno rastojanje između
lovca i tačke. Dakle, ako je skakač na poziciji koju napada lovac i ako skakač pokriva polje koje
bi inače bilo napadnuto, onda je rastojanje od skakača do posmatranog polja i skakača do lovca
manje od međusobnog rastojanja između lovca i skakača. Da bi polje bilo napadnuto treba da je
ispunjen uslov A, a onda je dovoljno da je ispunjen uslov B, a može i sa kombinacijom da je
ispunjen uslov C i barem jedan od uslova D i E.
Izvršenje programa:
Unijeti poziciju lovca: 2 3
Unijeti poziciju skakaca: 4 1
Broj napadnutih polja je: 15
Na slici ispod je ilustrovana opisana pozicija (L označava poziciju lovca, S poziciju skakača,
dok su X napadnuta polja).

7 X
6 X
5 X X
4 X X
3 L X X
2 X X X X
1 X S
0 X X
0 1 2 3 4 5 6 7

Zadatak 2. Napisati program koji rekurzivno rješava problem Hanojske kule (eng. Tower of
Hanoi). Postavka problema je data u objašnjenju izrade zadatka.
#include<stdio.h>
2.14 IGRE 277

#include<dos.h>

int N,pocetni[11],krajnji[11],pomocni[11];
char znak='*';

void Hanoj(int [],int [],int [],int );


void prebaci(int [],int []);
void crtaj(void);
void crtajDisk(int [],int);

int main()
{
int i;
printf("Unijeti broj diskova (ne veci od 10)\n");
scanf("%d",&N);
while(N>10)
{
printf("Unijeti broj diskova (ne veci od 10)\n");
scanf("%d",&N);
}
for(i=0;i<N;i++)
pocetni[i]=N‐i;
puts("Prebacivanje izgleda:");
Hanoj(pocetni,krajnji,pomocni,N);
}

void Hanoj(int a[],int b[],int c[],int N)


{
if(N==1)
{
prebaci(a,b);
crtaj();
}
else
{
Hanoj(a,c,b,N‐1);
prebaci(a,b);
crtaj();
Hanoj(c,b,a,N‐1);
}
}

void prebaci(int a[],int b[])


{
int i=0,j=0;
while(a[i]!=0)
i++;
i‐‐;
while(b[j]!=0)
278 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

j++;
b[j]=a[i];
a[i]=0;
}

void crtaj(void)
{
int i=0;
for(i=0;i<N;i++)
{
crtajDisk(pocetni,i);
printf(" ");
crtajDisk(krajnji,i);
printf(" ");
crtajDisk(pomocni,i);
printf("\n");
}
printf("\n\n\n");
delay(1000);
}

void crtajDisk(int a[],int i)


{
int j;
if(a[N‐i‐1]==0)
for(j=0;j<N;j++)
printf(" ");
else
{
for(j=0;j<N‐a[N‐1‐i];j++)
printf(" ");
for(j=0;j<a[N‐1‐i];j++)
printf("%c%c",znak,znak);
for(j=0;j<N‐a[N‐1‐i];j++)
printf(" ");
}
}

Promjenljive korišćene u programu:


i pomoćna brojačka promjenljiva
Promjenljive korišćene u funkciji Hanoj:
a, b, c nizovi cijelih brojeva (štapovi), gdje brojevi predstavljaju veličinu diskova na
štapu
N broj diskova koje treba prebaciti sa štapa a na b, koristeći c kao pomoćni štap
Promjenljive korišćene u funkciji prebaci:
a, b nizovi cijelih brojeva (štapovi) između kojih se vrši prebacivanje. Disk sa
vrha štapa a se prebacuje na vrh štapa b
2.14 IGRE 279

Promjenljive korišćene u funkciji crtajDisk:


a niz cijelih brojeva (štap) čiji disk crtamo
i redni broj diska, na štapu a, koji crtamo
Problem Hanojske kule je klasičan problem u programiranju koji se često koristi za
ilustrovanje moći rekurzije. Postavka problema je sljedeća: Imamo štap A na kome se nalazi N
diskova i još dva prazna štapa, B i C. Potrebno je prebaciti sve diskove sa štapa A na B, koristeći
štap C kao pomoćni. Pri tome se moramo pridržavati dva pravila: 1) uvijek se premješta samo
po jedan disk i 2) nije dozvoljeno smještati veći disk na manji. Postavka problema za N = 5 je
ilustrovana na donjoj slici:

Ovaj problem se može riješiti iterativno i rekurzivno. Mi smo se odlučili za rekurzivni


pristup. Naime, da bi prebacili N diskova sa štapa A na štap B potrebno je prvo prebaciti N-1
najmanjih diskova na štap C, nakon toga prebaciti najveći disk na štap B, a zatim N-1 diskova sa
štapa C prebaciti na disk B. Ova tri koraka su, za slučaj N = 5, ilustrovana na sljedećoj slici:

Za prebacivanje najvećeg diska sa štapa A na štap B ne treba biti genijalac, ali kako prebaciti
N-1 diskova na štap C, pridržavajući se pomenutih pravila? Odgovor se bazira na već pomenutoj
logici. Naime, da bismo prebacili N-1 najmanjih diskova na štap C potrebno je prvo prebaciti N-
2 najmanjih diskova na štap B, nakon toga prebaciti drugi po veličini disk na štap C (disk 4 sa
gornjih slika), a zatim N-2 diskova sa štapa B prebaciti na disk C. Analogno, da bismo prebacili
N-2 najmanjih diskova na štap B, potrebno je prvo prebaciti N-3 najmanjih diskova na štap C,
zatim prebaciti treći po veličini disk na štap B (disk 3 sa gornjih slika), a zatim N-3 diskova sa
štapa C prebaciti na disk B. Ova rekurzija se nastavlja sve dok broj diskova koji treba prebaciti
na štap B ili C ne postane 1. Ovo ujedno predstavlja i najbrže moguće rješenje datog problema.
Srce programa predstavlja rekurzivna funkcija Hanoj. Njeni argumenti su nizovi cijelih
brojeva a, b i c. Niz a, zapravo, predstavlja štap sa kojeg prebacujemo diskove, niz b predstavlja
štap na koji prebacujemo diskove i niz c predstavlja pomoćni štap u proceduri prebacivanja.
Diskovi na štapovima su predstavljeni cijelim brojevima od 1 (najmanji disk) do N (najveći disk).
Posljednji argument funkcije Hanoj je N, broj diskova koji se prebacuje sa štapa a na štap b. U
prvom pozivu ove funkcije, u okviru funkcije main, njoj se prosljeđuje ukupan broj diskova na
početnom štapu. Ukoliko je taj broj veći od 1 onda ova funkcija rekurzivno poziva sebe dva puta:
Hanoj(a,c,b,N‐1) i Hanoj(c,b,a,N‐1). Prvi poziv prebacuje N-1 diskova sa štapa a na
štap c, koristeći štap b kao pomoćni, dok drugi prebacuje N-1 diskova sa štapa c na štap b,
koristeći štap a kao pomoćni. Vidimo da to upravo odgovara opisanoj proceduri. U okviru
funkcije Hanoj postoje još dvije funkcije: prebaci i crtaj. Prva služi za prebacivanje diska
280 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

sa štapa a na štap b, dok druga služi za crtanje diskova na sva tri štapa. Pošto štapove
predstavljamo nizovima cijelih brojeva, prebacivanje predstavlja uzimanje posljednjeg elementa
niza a (tj. uzimanje sa vrha štapa) i njegovo nadovezivanje na niz b (tj. smještanje na vrh štapa).
Nakon toga u posljednji element niza a upisujemo 0 (tj. brišemo ga sa vrha štapa). Funkcija
crtaj crta diskove smještene na štapovima koristeći funkciju crtajDisk, kojoj se pored
pokazivača na niz cijelih brojeva a prosljeđuje i redni broj elementa i, tj. disk koji crtamo.
Ukoliko je taj element jednak nuli (na datom štapu ne postoji disk na “visini” i, pri čemu i=1
odgovara disku na samom dnu štapa, i=2 disku koji se nalazi na tom disku na dnu, itd.) onda na
ekranu ispisuje blanko znake, a ako nije jednak nuli onda se dati disk crta karakterom znak, koji
je deklarisan kao globalna promjenljiva, i to sa duplo više znakova nego što je veličina diska. Na
primjer, disk veličine 4 se crta sa 8 karaktera znak. Pored karaktera znak i nizovi koji
predstavljaju štapove se deklarišu kao globalne promjenljive. Pošto nizovi nisu eksplicitno
inicijalizovani oni se, kao globalne promjenljive, inicijalizuju na 0. Primjetimo da je u funkciji
crtaj korišćena bibliotečka rutina delay, koja odlaže izvršavanje programa za cio broj
milisekundi, koji joj se prosljeđuje kao argument. Da bi koristili ovu rutinu potrebno je uključiti
zaglavlje dos.h. U glavnoj funkciji se, nakon unosa broja diskova N, for petljom vrši
smještanje tih diskova na početni štap tako da bude pocetni[0]=N, pocetni[1]=N‐1, ...,
pocetni[N‐1]=1. Primjetimo da smo while petljom ograničili broj diskova na maksimalno
10. Razlog tome je broj operacija, tj. premještanja, koji treba izvršiti, a koji eksponencijalno raste
sa brojem diskova. Naime, ukoliko je broj diskova koji treba prebaciti sa početnog na krajnji štap
jednak N, onda je, po našoj proceduri, potrebno izvršiti 2N-1 premještanja. Napominjemo još
jednom da je ovo optimalna procedura sa stanovišta broja izvršenih premještanja. Studentima se
za vježbu ostavlja iterativno rješenje problema Hanojske kule.
Tvorac problema Hanojske kule je francuski matematičar Edouard Lucas. Originalna
postavka ovog problema sadrži N=8 diskova. Lucas je zagonetku “začinio” zanimljivom
legendom o mnogo većoj Brahma kuli, koja ima 64 diska od čistog zlata, smještenih na tri
dijamantske igle. U trenutku stvaranja svijeta hindu bog Brahma je sve diskove smjestio na prvu
iglu, poređavši ih od najvećeg ka najmanjem, i naložio hindu svještenicima da sve diskove
prebace na drugu iglu, koristeći treću kao pomoćnu, u skladu sa opisanim pravilima
premještanja. Od tad svještenici neprekidno rade na tom zadatku. U trenutku završetka zadatka
kula će se srušiti i uništiti univerzum. Ukoliko pretpostavimo da je svještenicima u prosjeku
potrebna 1 sekunda za premještanja jednog diska, onda im je za rješavanje čitavog problema
potrebno 264-1 sekundi, što je približno 585 milijardi godina, što je više od procijenjenog trajanja
univerzuma. Dakle, bez brige.

Zadatak 3. Napisati program koji simulira poznatu igru iks-oks. Igra se na tabli 3×3 na kojoj
prvi igrač postavlja X, drugi O i tako naizmjenično. Pobjeđuje onaj igrač koji u bilo
kom redu, koloni ili na dijagonalama postavi tri ista karaktera. Ako je polje zauzeto
preko njega se ne može unijeti novi karakter. Igra se može završiti i ako su sva
polja popunjena i pri tom nema pobjednika (neriješeno).
Program treba da omogući igračima da zadaju pozicije karaktera. Ako je zadata
pozicija van opsega ili ako je zadato polje zauzeto daje se upozorenje i zahtjeva
novi unos. Nakon svakog poteza takmičara provjerava se da li je takmičar pobjedio
(ako se ovo dogodi štampa se prigodna poruka) i vrši prikaz tabele. Posljednji
(deveti) forsirani potez na jedino preostalo mjesto program sam postavlja. Ako se
igra završi bilo neriješeno, bilo pobjedom jednog od takmičara, program pita da li
se želi nova igra i u slučaju odrečnog odgovora prekida se izvršavanje.
2.14 IGRE 281

#include<stdio.h>

int main()
{
int i,j,k,x,y,p1,p2,unos=0,pobjeda=0,losUnos;
char iks_oks[3][3],p[3]="XO",nova;
do
{
if(unos==0)
{
for(i=0;i<3;i++)
for(j=0;j<3;j++)
iks_oks[i][j]=' ';
}
losUnos=1;
if(unos<8)
{
if(unos%2==0)
printf("Unijeti poziciju prvog igraca ‐ X\n");
else
printf("Unijeti poziciju drugog igraca ‐ O\n");
scanf("%d%d",&x,&y);
if(x<0||x>2||y<0||y>2||iks_oks[x][y]=='X'||iks_oks[x][y]=='O')
losUnos=0;
}
else if(unos==8)
{
for(i=0;i<3;i++)
for(j=0;j<3;j++)
if(iks_oks[i][j]==' ')
iks_oks[i][j]='X';
}
if(losUnos==1)
{
if(unos<8)
{
if(unos%2==0)
iks_oks[x][y]='X';
else
iks_oks[x][y]='O';
}
unos++;
for(k=0;k<7;k++)
{
if(k==0)
printf(" | 0 | 1 | 2 \n");
if(k!=0&&k%2!=0)
printf("‐‐‐ ‐‐‐ ‐‐‐ ‐‐‐\n");
else if(k!=0&&k%2==0)
282 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

{
printf(" %d ",k/2‐1);
for(i=0;i<3;i++)
printf("| %c ",iks_oks[k/2‐1][i]);
printf("\n");
}
}
printf("\n");
for(k=0;k<2;k++)
{
for(i=0;i<3;i++)
{
p1=p2=1;
for(j=0;j<3;j++)
{
p1*=(iks_oks[i][j]==p[k]);
p2*=(iks_oks[j][i]==p[k]);
}
pobjeda=pobjeda||(p1||p2);
}
p1=p2=1;
for(i=0;i<3;i++)
{
p1*=(iks_oks[i][i]==p[k]);
p2*=(iks_oks[i][2‐i]==p[k]);
}
pobjeda=pobjeda||(p1||p2);
if(pobjeda)
break;
}
}
if(pobjeda==1||unos==9)
{
if(pobjeda==1)
printf("Pobjedio je %s takmicar!",k==0?"prvi":"drugi");
else
printf("Nerijesena partija!\n");
printf("\n\nZelite li novu partiju?\n\td ‐ Da \n\tn ‐ Ne\n");
do
{
scanf("%c",&nova);
} while(nova!='d'&&nova!='n');
if(nova=='d')
unos=pobjeda=0;
else
break;
}
if(losUnos==0)
printf("Pogresan unos!\n");
} while(1);
2.14 IGRE 283

Promjenljive korišćene u programu su:


i, j, k pomoćne brojačke promjenljive
x, y cijeli brojevi koji predstavljaju poziciju tekućeg karaktera koji se unosi
p1, p2 promjenljive koje se koriste za utvrđivanje da li je neki od takmičara
pobjedio, tj. u redu, koloni ili na dijagonalama postavio tri ista karaktera
unos promjenljiva koja broji koliko je poteza odigrano, tj. koliko je popunjeno
polja na tabli. Početna vrijednost je 0
pobjeda promjenljiva koja signalizira pobjedu nekog od takmičara. Ako je
pobjeda=1, ustanovljena je pobjeda pri tekućem potezu
losUnos promjenljiva koja signalizira nedozvoljenu poziciju unesenog karaktera. Ako
je losUnos=0, neki od takmičara je unio poziciju van table ili poziciju već
zauzetog polja
iks_oks matrica sa karakterima ' ', 'X' ili 'O', koja predstavlja tablu
p pomoćni string sa dva karaktera 'X' i 'O', koji se koristi za ustanovljavanje
pobjede nekog od takmičara
nova karakter koji unosi korisnik u cilju prekida ili igranja nove igre. nova='d'
označava da korisnik želi da igra novu partiju, dok nova='n' označava da
korisnik želi da prekine igru
U pitanju je realizacija svima poznate igre iks-oks. Mi smo se odlučili za implementaciju
korišćenjem jedne do...while petlje, pri čemu se iz petlje izlazi samo onda kada korisnik
unese slovo 'n' za prekid igre. Na početku petlje se u slučaju prvog poteza, tj. kad je unos=0,
inicijalizuje matrica iks_oks karakterima ' '. Na ovu inicijalizaciju smo se odlučili samo iz
razloga prikaza matrice na ekranu. Bjelina predstavlja prazno (slobodno) polje tabele. Nakon
toga se, u slučaju da je redni broj unosa manji od 8, sa tastature učitava pozicija tekućeg
karaktera, u vidu dva cijela broja x i y, koji mogu imati vrijednosti 0, 1 ili 2. U slučaju da je
unijeta vrijednost različita od dozvoljenih, kao i da je tekuće polje već zauzeto, što se ispituje
uslovom (x<0||x>2||y<0||y>2||iks_oks[x][y]=='X'||iks_oks[x][y]=='O'),
losUnos se postavlja na 0, ide se na ispisivanje poruke o pogrešnom unosu i od korisnika se
traži ponovni unos pozicije karaktera. U ovom slučaju, nećemo izaći iz do...while petlje jer
je uslov petlje uvijek ispunjen. Tek kad se unese validna pozicija karaktera, nastavlja se sa
izvršavanjem programa. Slučaj unos=8 odgovara situaciji u kojoj su igrači igrali po četiri poteza
i niko nije pobjedio i u tom slučaju program sam unosi karakter 'X' na posljednje preostalo
mjesto. Pretpostavka je da je prilikom prvog poteza uneseno 'X', tako da i posljednji deveti
potez takođe mora biti 'X'. Nakon unosa karaktera program crta tabelu koristeći petlju
for(k=0;k<7;k++). Jedna moguća tabela je prikazana na slici ispod:
| 0 | 1 | 2
‐‐‐ ‐‐‐ ‐‐‐ ‐‐‐
0 | O | | O
‐‐‐ ‐‐‐ ‐‐‐ ‐‐‐
1 | X | |
‐‐‐ ‐‐‐ ‐‐‐ ‐‐‐
2 | | | X
284 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Nakon crtanja tabele, u petlji for(k=0;k<2;k++) se ispituje se da li je neki od takmičara


pobjedio. Prvo se vrši ispitivanje za prvog takmičara, tj. za k=0 i p[0]='X', a nakon toga i za
drugog takmičara, tj. k=1 i p[1]='O'. Za ovo nam služe pomoćne promjenljive p1 i p2, koje
se na početku inicijalizuju na 1, a nakon toga se for petljama prolazi kroz čitavu matricu
iks_oks, ispitujući da li se u nekoj vrsti, koloni ili dijagonali nalaze tri ista karaktera 'X' ili
'O'. Ako se ustanovi pobjeda nekog od takmičara ili ako su popunjena sva polja u tabeli bez
pobjednika, ide se na ispisivanje poruke o eventualnom pobjedniku ili neriješenom ishodu igre.
Pojasnimo naredbu printf("Pobjedio je %s takmicar!",k==0?"prvi":"drugi").
Ukoliko se ustanovi pobjeda prvog igrača onda se iz for petlje for(k=0;k<2;k++) izlazi
naredbom break i k ostaje 0, što znači da će se ispisati string "prvi" na ekranu. Sa druge
strane, ukoliko se ustanovi pobjeda drugog igrača, iz for petlje se izlazi naredbom break i k
ostaje 1, što znači da će se ispisati string "drugi" na ekranu. Nakon toga se korisnik pita da li
želi novu partiju. U slučaju da želi, unos i pobjeda se resetuju na 0, vraćamo se na početak
petlje, gdje vršimo inicijalizaciju matrice iks_oks na karakter ' ', itd. U slučaju da korisnik
želi da prekine igru on mora unijeti karakter 'n', čime se ide na prekid izvršavanja do...while
petlje naredbom break.

Zadatak 4. Napisati program koji simulira kockarsku igru craps, koja se igra na sljedeći način.
Bacaju se dvije kocke na kojima se sa jednakom vjerovatnoćom mogu pojaviti
brojevi od 1 do 6. Nakon prvog bacanja sabiraju se dobijeni brojevi na kockama.
Ako je suma 2, 3 ili 12 (tzv. craps) učesnik gubi, a ako je suma 7 ili 11 učesnik
dobija. Ako suma na kockama nije nijedan od ovih brojeva dobijeni broj predstavlja
cilj učesnika u narednim bacanjima. Učesnik nastavlja da baca sve dok ne ponovi
ciljnu sumu ili baci sumu 7. Ako baci ciljnu sumu pobjedio je, a ako baci 7 izgubio
je. Program napraviti tako da učesnik na početku raspolaže sa određenim ulogom.
Dio uloga može da uloži u svaku pojedinačnu igru. Ulog se povećava za uloženi
dio ako je igra uspješna i smanjuje ako nije. Učesnik nakon svake igre, bilo
uspješne ili ne, može da odustane. Igra se prekida i ako učesnik ostane bez novca.
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int bacaj(void);

int main()
{
int suma,cilj,status,pot,ulog;
char nova='d';
do
{
printf("Unijeti pocetni ulog kao pozitivan cijeli broj: ");
scanf("%d",&pot);
} while(pot<=0);
srand((unsigned)time(0));
while (nova=='d'&&pot>0) {
printf("\nNa raspolaganju imate %d eura.\n",pot);
do
{
2.14 IGRE 285

printf("Koliko novca zelite uloziti u narednu igru? ");


scanf("%d",&ulog);
} while(ulog>pot);
printf("\n");
suma=bacaj();
switch(suma)
{
case 7:
case 11:
status=1;
break;
case 2:
case 3:
case 12:
status=‐1;
break;
default:
status=0;
cilj=suma;
printf("Ciljna suma je %d\n",cilj);
break;
}
while(status==0)
{
suma=bacaj();
if(suma==cilj)
status=1;
else if(suma==7)
status=‐1;
}
printf("Igrac je %s!\n",status==1 ? "pobjedio" : "izgubio");
pot+=status*ulog;
if(pot>0)
{
printf("\nZelite li novu igru?\n\td ‐ Da \n\tn ‐ Ne\n");
do
{
scanf("%c",&nova);
} while(!(nova=='d'||nova=='n'));
}
else
printf("Ostali ste bez novca!");
}
}

int bacaj(void)
{
int kocka1,kocka2,suma;
kocka1=1+rand()%6;
286 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

kocka2=1+rand()%6;
suma=kocka1+kocka2;
printf("Igrac je bacio %d + %d = %d\n",kocka1,kocka2,suma);
return suma;
}

Promjenljive korišćene u programu su:


suma suma brojeva dobijenih prilikom bacanja kocki
cilj ciljna suma koju igrač treba da dobije nakon prvog bacanja da bi pobijedio
status pomoćna promjenljiva koja se koristi za utvrđivanje da li je igrač pobijedio.
Ako je status=1 igrač je pobijedio, ako je status=‐1 igrač je izgubio i
ako je status=0 igra se nastavlja
pot cio broj koji predstavlja sumu novca sa kojom igrač raspolaže
ulog cio broj koji predstavlja ulog igrača u svakoj novoj igri i ne može biti veći od
pota
nova karakter koji unosi korisnik u cilju prekida ili igranja nove igre. nova='d'
označava da korisnik želi da igra novu partiju, dok nova='n' označava da
korisnik želi da prekine igru
Promjenljive korišćene u funkciji bacaj:
kocka1, kocka2 slučajni cijeli brojevi od 1 do 6 koji predstavljaju brojeve dobijene
prilikom bacanja kocki
suma suma brojeva dobijenih prilikom bacanja kocki
U pitanju je poznata kazino igra, čija su pravila jednostavna i opisana u postavci zadatka.
Program koristi funkciju bacaj koja vraća zbir brojeva dobijenih prilikom bacanja kocki.
Bacanje kocki se simulira funkcijom rand() koja vraća pseudo-slučajni cijeli broj u opsegu
[0,32767]. Pošto nama trebaju brojevi iz opsega [1,6], potrebno je opseg brojeva koji vraća
funkcija rand() preskalirati na taj opseg. To se, u okviru funkcije bacaj, vrši izrazom
kocka1=1+rand()%6. Cjelobrojni ostatak dijeljenja brojeva od 0 do 32767 brojem 6 su brojevi
iz skupa [0, 5], pa dodavanjem 1 na taj opseg dobijamo željeni opseg [1, 6]. Podsjetimo se da
funkcija rand() ne vraća pravi slučajni broj, već pseudo-slučajni broj iz datog opsega. Naime,
sve funkcije koje vraćaju “slučajne” brojeve iz određenog opsega, ili koji podležu nekoj od
poznatih distribucija, zapravo vraćaju neki od brojeva iz determinističke sekvence. Štaviše,
sekvenca brojeva koju vraća funkcija rand() se ponavlja prilikom svakog startovanja programa,
što predstavlja nepovoljnu okolnost sa stanovišta slučajnosti procesa bacanja kocki. Drugim
riječima, funkcija rand() bi uvijek bacala istu kombinaciju brojeva na kockama prilikom
svakog startovanja programa. Zbog toga je vrlo poželjno da data funkcija generiše različitu
sekvencu pseudo-slučajnih brojeva. Ovo se može izvršiti korišćenjem funkcije srand, koja kao
argument prima neoznačeni cijeli broj. Ovaj broj, koji se još naziva seed, određuje sekvencu koja
će biti generisana funkcijom rand() i poželjno je da taj broj prilikom svakog startovanja
programa bude različit. Za ovo nam može poslužiti funkcija time koja vraća trenutno vrijeme,
mjereno u broju sekundi proteklih od prvog januara 1970. vremena po Griniču.
U glavnom programu se, nakon unošenja početnog uloga, igrač pita koliku sumu želi da
uloži za narednu igru. Ta suma, naravno, mora biti manja od pota igrača, tj. sume novca kojom
on raspolaže u svakom trenutku. Nakon unosa uloga bacaju se kocke pozivom funkcije bacaj i
dobijena suma se kasnije koristi kao kontrolni izraz u naredbi switch, gdje u zavisnosti od te
2.14 IGRE 287

sume promjenljiva status postaje jednaka 1 (pobjeda), -1 (poraz) ili 0 (nastavak igre). U slučaju
da je status=0, dobijena suma postaje ciljna suma i ide se na ponovno bacanje kocki, sve dok
suma ne postane jednaka ciljnoj sumi ili broju 7. Nakon toga se ispisuje poruka o ishodu igre,
pot se uvećava ili smanjuje u zavisnosti od tog ishoda i, u slučaju da još ima novca na potu,
igrač se pita da li želi da nastavi sa igrom. U slučaju da ne želi, kao i u slučaju da više nema
novca na potu, izlazi se iz while petlje i prekida izvršavanje programa.
Izvršenje programa:
Unijeti pocetni ulog kao pozitivan cijeli broj: 210

Na raspolaganju imate 210 eura.


Koliko novca zelite uloziti u narednu igru? 130

Igrac je bacio 1 + 1 = 2
Igrac je izgubio!

Zelite li novu igru?


d ‐ Da
n ‐ Ne
d

Na raspolaganju imate 80 eura.


Koliko novca zelite uloziti u narednu igru? 50

Igrac je bacio 6 + 1 = 7
Igrac je pobjedio!

Zelite li novu igru?


d ‐ Da
n ‐ Ne
d

Na raspolaganju imate 130 eura.


Koliko novca zelite uloziti u narednu igru? 130

Igrac je bacio 5 + 1 = 6
Ciljna suma je 6
Igrac je bacio 1 + 2 = 3
Igrac je bacio 1 + 2 = 3
Igrac je bacio 3 + 6 = 9
Igrac je bacio 2 + 5 = 7
Igrac je izgubio!
Ostali ste bez novca!

Zadatak 5. Napisati program kojim se simulira miješanje i dijeljenje špila karata. Miješanje i
dijeljenje obaviti pomoću zasebnih funkcija.
#include<stdio.h>
288 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

#include<stdlib.h>
#include<time.h>

void mijesaj(int [][13]);


void dijeli(const int [][13],const char *[],const char *[]);

int main()
{
const char *znak[4]={"Herc","Karo","Tref","Pik"};
const char *broj[13]={"As","Dvojka","Trojka","Cetvorka","Petica",
"Sestica","Sedmica","Osmica","Devetka",
"Desetka","Pub","Kraljica","Kralj"};
int spil[4][13]={0};
srand(time(0));
mijesaj(spil);
printf("\n");
dijeli(spil,broj,znak);
}

void mijesaj(int spil[][13])


{
int karta,vrsta,kolona;
for (karta=1;karta<=52;karta++)
{
do
{
vrsta=rand()%4;
kolona=rand()%13;
} while(spil[vrsta][kolona]!=0);
spil[vrsta][kolona]=karta;
}
}

void dijeli(const int spil[][13],const char *broj[],const char


*znak[])
{
int karta,vrsta,kolona;
char temp[20];
for (karta=1;karta<=52;karta++)
for (vrsta=0;vrsta<=3;vrsta++)
{
for (kolona=0;kolona<=12;kolona++)
if(spil[vrsta][kolona]==karta)
{
sprintf(temp,"%s %s",broj[kolona],znak[vrsta]);
printf("%‐15s",temp);
if(karta%2==0)
printf("\n");
else
printf("\t");
2.14 IGRE 289

break;
}
if(spil[vrsta][kolona]==karta&&kolona<=12)
break;
}
}

Promjenljive korišćene u programu su:


znak niz pokazivača na stringove koji označavaju znakove kod karata
broj niz pokazivača na stringove koji označavaju redni broj karata
spil matrica cijelih brojeva koja predstavlja špil karata za igranje
Promjenljive korišćene u funkciji mijesaj:
spil matrica cijelih brojeva koja predstavlja špil karata za igranje
Promjenljive korišćene u funkciji dijeli:
spil matrica cijelih brojeva koja predstavlja špil karata za igranje
broj niz pokazivača na stringove koji označavaju redni broj karata
znak niz pokazivača na stringove koji označavaju znakove kod karata
karta cio broj između 1 i 52, koji označava pojedinačnu kartu
vrsta, kolona pomoćne brojačke promjenljive
temp pomoćni string u koji se smješta puni naziv karte prije ispisa

Predstavljen je relativno jednostavan program za miješanje i dijeljenje karata. Ovaj program


se kasnije može iskoristiti za implementaciju drugih programa koji igraju određenu igru sa
kartama. Špil karata predstavljamo pomoću matrice spil, koja je matrica cijelih brojeva (vidi
narednu sliku). Vrste matrice predstavljaju znakove – prva vrsta predstavlja herc, druga karo,
treća tref i četvrta pik. Kolone predstavljaju redni broj karata – kolone od 0 do 9 predstavljaju
karte od asa do desetke, dok kolone od 10 do 12 predstavljaju puba, kraljicu i kralja. Na početku
se ova matrica inicijalizuje na 0. Miješanje karata se obavlja funkcijom mijesaj koja, zapravo,
cijele brojeve od 1 do 52 smješta u slučajno izabrane vrste i kolone. Ovo je realizovano pomoću
jedne for i jedne do...while petlje. Brojačka promjenljiva u for petlji predstavlja kartu. U
do...while petlji se pomoću naredbi vrsta=rand()%4 i kolona=rand()%13 na slučajan
način izabiraju vrsta i kolona u koju ćemo smjestiti promjenljivu karta.
290 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Naravno, može se desiti da ove dvije naredbe generišu isti broj vrste i kolone. To znači da
pokušavamo da smjestimo kartu na poziciju u špilu koja je već zauzeta, tj. u kojoj je upisan cijeli
broj veći od 0. U tom je slučaju while uslov ispunjen i ide se na novo izvršavanje do...while
petlje. Tek kad dobijemo poziciju u matrici u kojoj je upisana 0, tj. nije smještena nijedna karta,
možemo upisati tekuću kartu. Jedan mogući izgled matrice spil nakon izvršenja funkcije
mijesaj je dat na narednoj slici.

Funkcija dijeli vrši dijeljenje karata koristeći tri ugniježdene for petlje. U spoljašnjoj
petlji promjenljiva karta uzima vrijednosti od 1 do 52, dok dvije unutrašnje for petlje vrše
pretragu matrice spil tražeći element matrice u koji je upisana tekuća vrijednost promjenljive
karta. Kada se pronađe traženi element vrši se štampanje karte koja odgovara tom elementu,
tako što se odštampa element niza broj koji odgovara rednom broju kolone traženog elementa
matrice i element niza znak koji odgovara rednom broju vrste traženog elementa matrice. Na
primjer, ukoliko pretpostavimo da je karta=1 smještena u elementu spil[1][5], kao što je
prikazano na slici iznad, onda će biti odštampana karta šestica karo. Onoga trenutka kada se
pronađe željeni element matrice spil izlazi se iz for petlje koja se kreće po promjenljivoj
kolona naredbom break, a zatim i iz for petlje koja se kreće po promjenljivoj vrsta
naredbom break. Prekidanje izvršavanja unutrašnjih for petlji naredbom break se vrši iz
razloga optimizacije vremena pretraživanja matrice spil. Onog trenutka kada pronađemo
željeni element matrice nema potrebe da pretražujemo ostatak matrice, već možemo preći na
traženje sljedećeg elementa. Radi pogodnosti ispisa izabrali smo da se u svakom redu ispišu po
dvije karte. U tu svrhu smo nakon svake neparne karte odštampali tabulator (\t), dok smo nakon
svake parne karte odštampali sekvencu za prelaz u novi red (\n).
Izvršavanje programa:
Desetka Pik Sestica Herc
Kraljica Tref Cetvorka Tref
As Pik Pub Karo
Sedmica Pik Devetka Karo
Petica Karo Trojka Karo
Kralj Pik Kraljica Herc
Petica Tref Desetka Karo
As Tref Pub Pik
As Herc Osmica Karo
Devetka Pik Osmica Tref
Kralj Herc Trojka Pik
Pub Herc Pub Tref
Osmica Pik Trojka Herc
Kraljica Karo Sedmica Karo
Devetka Tref Osmica Herc
Cetvorka Pik Devetka Herc
2.14 IGRE 291

Trojka Tref Dvojka Herc


Cetvorka Herc Cetvorka Karo
Desetka Tref Kraljica Pik
Sestica Pik Kralj Karo
Kralj Tref Sedmica Herc
As Karo Petica Herc
Dvojka Karo Dvojka Pik
Sestica Karo Sedmica Tref
Sestica Tref Desetka Herc
Dvojka Tref Petica Pik

Zadatak 6. Napisati program kojim se simulira prolazak kroz lavirint. Lavirint je predstavljen
pomoću matrice čiji su elementi karakteri '#' i '.', pri čemu tarabe predstavljaju
zidove, a tačke hodnike lavirinta. Kretanje kroz lavirint se može vršiti samo duž
hodnika. Mi ćemo posmatrati lavirint dimenzija 8×8 karaktera prikazan u postavci.
Prilikom prolaska, pridržavati se algoritma koji garantuje izlaz iz lavirinta, ukoliko
izlaz postoji. Algoritam se može opisati na sljedeći način. Prvo, stavimo desnu ruku
na zid lavirinta sa naše desne strane i započnimo kretanje. Prilikom kretanja ne
sklanjati ruku sa zida. Pridržavajući se ovog algoritma sigurno ćemo naći izlaz iz
lavirinta. Program treba da sadrži rekurzivnu funkciju koja simulira prolazak kroz
lavirint pridržavajući se opisanog algoritma, kao i funkciju koja crta lavirint nakon
svakog pomjeraja duž hodnika.
############
# . . . #. . . . . .#
. .# . #. ####.#
### . #. . . . #.#
# . . . . ###. #. .
#### . #. #. #.#
# . .# . #. #. #.#
## .# . #. #. #.#
# . . . . . . . . #.#
###### . ###.#
# . . . . . . #. . .#
############

#include<stdio.h>

char lavirint[12][13]=
{"############",
"#...#......#",
"..#.#.####.#",
"###.#....#.#",
"#....###.#..",
"####.#.#.#.#",
"#..#.#.#.#.#",
"##.#.#.#.#.#",
"#........#.#",
"######.###.#",
292 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

"#......#...#",
"############"};
int xUl,yUl,xIz,yIz,smjer;

void obilazak(char [][13],int,int);


void stampajLavirint(char [][13],int,int);

int main()
{
stampajLavirint(lavirint,‐1,‐1);
do
{
printf("Unijeti koordinate tacke od koje se krece: ");
scanf("%d%d",&xUl,&yUl);
} while(lavirint[xUl][yUl]=='#');
do
{
printf("Unijeti koordinate izlaza iz lavirinta: ");
scanf("%d%d",&xIz,&yIz);
} while(lavirint[xIz][yIz]=='#');
do
{
printf("Pocetni smjer kretanja: 1‐desno,2‐lijevo,3‐gore,4‐dolje");
scanf("%d",&smjer);
} while(smjer>4||smjer<1);
obilazak(lavirint,xUl,yUl);
}

void stampajLavirint(char a[][13],int x,int y)


{
int i,j;
for(i=0;i<12;i++)
{
for(j=0;j<12;j++)
if(i==x&&j==y)
printf("%c ",'O');
else
printf("%c ",a[i][j]);
printf("\n");
}
}

void obilazak(char a[][13],int x,int y)


{
printf("\nSmjer je %s\n",
smjer<3?(smjer%2?"desno":"lijevo"):(smjer%2?"gore":"dolje"));
stampajLavirint(a,x,y);
if(x==xIz&&y==yIz)
printf("Pronadjen izlaz!\n");
else
2.14 IGRE 293

switch(smjer)
{
case 1:
if(a[x+1][y]=='.')
{
smjer=4;
obilazak(a,x+1,y);
}
else if(a[x][y+1]=='.')
obilazak(a,x,y+1);
else if(a[x‐1][y]=='.')
{
smjer=3;
obilazak(a,x‐1,y);
}
else
{
smjer=2;
obilazak(a,x,y‐1);
}
break;
case 2:
if(a[x‐1][y]=='.')
{
smjer=3;
obilazak(a,x‐1,y);
}
else if(a[x][y‐1]=='.')
obilazak(a,x,y‐1);
else if(a[x+1][y]=='.')
{
smjer=4;
obilazak(a,x+1,y);
}
else
{
smjer=1;
obilazak(a,x,y+1);
}
break;
case 3:
if(a[x][y+1]=='.')
{
smjer=1;
obilazak(a,x,y+1);
}
else if(a[x‐1][y]=='.')
obilazak(a,x‐1,y);
else if(a[x][y‐1]=='.')
294 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

{
smjer=2;
obilazak(a,x,y‐1);
}
else
{
smjer=4;
obilazak(a,x+1,y);
}
break;
case 4:
if(a[x][y‐1]=='.')
{
smjer=2;
obilazak(a,x,y‐1);
}
else if(a[x+1][y]=='.')
obilazak(a,x+1,y);
else if(a[x][y+1]=='.')
{
smjer=1;
obilazak(a,x,y+1);
}
else
{
smjer=3;
obilazak(a,x‐1,y);
}
break;
default:
break;
}
}

Globalne promjenljive:
lavirint matrica karaktera koja predstavlja lavirint
xUl, yUl koordinate početne tačke kretanja
xIz, yIz koordinate izlaza
smjer cjelobrojna konstanta koja predstavlja smjer kretanja
Promjenljive korišćene u funkciji stampajLavirint:
a matrica karaktera koja predstavlja lavirint
x, y cjelobrojne promjenljive koje predstavljaju koordinate tekuće pozicije
prilikom kretanja kroz lavirint
i, j pomoćne brojačke promjenljive
Promjenljive korišćene u funkciji obilazak:
a matrica karaktera koja predstavlja lavirint
2.14 IGRE 295

x, y cjelobrojne promjenljive koje predstavljaju koordinate tekuće pozicije


prilikom kretanja kroz lavirint
U programu je za predstavljanje lavirinta iskorišćena globalna promjenljiva lavirint,
koja je matrica karaktera. Matrica je inicijalizovana karakterima '#' i '.'. Prilikom kreiranja
te matrice, pretpostavićemo dvije opravdane stvari. Prva je da lavirint ima izlaz, a druga je da se
bočne ivice lavirinta sastoje samo od zidova, osim na poziciji ulaza i izlaza, tj. da se u lavirint ne
može ući spolja ni na jednom mjestu osim na ulazu i izlazu. To znači da će prva i posljednja
vrsta i prva i posljednja kolona matrice lavirint sadržati dva karaktera '.', koji predstavljaju
ulaz i izlaz i svi ostali su karakteri '#'. Algoritam kretanja kroz lavirint garantuje izlazak iz
lavirinta samo pod uslovom da izlaz postoji. U slučaju da izlaz ne postoji algoritam nas vraća na
početak lavirinta.
Funkcija za crtanje lavirinta stampajLavirint ima za argumente dva cijela broja, x i y,
koji predstavljaju našu tekuću poziciju prilikom kretanja kroz lavirint. Na mjestu određenom
koordinatama x i y ispisujemo karakter 'O', kako bi vidjeli gdje se trenutno nalazimo. U glavnoj
funkciji se odmah na početku poziva funkcija stampajLavirint, pa korisnik na osnovu
odštampanog lavirinta unosi koordinate tačke od koje započinje kretanje, xUl i yUl, i koordinate
izlaza, xIz i yIz. U ovom prvom pozivu funkcije stampajLavirint koordinate x i y imaju
vrijednost –1, što znači da karakter 'O' neće biti ispisan u tom pozivu. Pored koordinata tekuće
pozicije u obilasku lavirinta potrebno je voditi računa i o smjeru kretanja. Promjenljiva koja je
za ovo zadužena je globalna promjenljiva smjer, i ona može imati vrijednost 1 (smjer desno),
2 (smjer lijevo), 3 (smjer gore) i 4 (smjer dolje). Početni smjer se zadaje nakon unosa početnih i
ciljnih koordinata u okviru glavne funkcije. Obilazak lavirinta se obavlja rekurzivnom funkcijom
obilazak. Odmah na početku ove funkcije štampaju se obavještenje o tekućem smjeru i
lavirint. Nakon toga ispitujemo da li smo stigli do izlaza lavirinta poredeći tekuće koordinate i
koordinate izlaza i, ukoliko je to slučaj, ispisuje se poruka Pronadjen izlaz!, čime se
završava program. Ukoliko to nije slučaj ide se na izračunavanje koordinata naredne pozicije,
pri čemu te koordinate zavise kako od strukture lavirinta tako i od smjera kretanja.
Objasnićemo kako se izračunavaju koordinate naredne pozicije na osnovu opisanog
algoritma samo u slučaju tekućeg kretanja udesno, a vi sami proučite izračunavanje u slučaju
ostalih smjerova kretanja koja su analogna kretanju udesno. Razmotrimo moguće situacije u
slučaju trenutnog kretanja udesno. Na donjim slikama su prikazane te situacije, pri čemu karakter
'O' označava našu tekuću poziciju i duže strelice iznad slika označavaju trenutni smjer kretanja
udesno. Na slici a) je prikazana situacija u kojoj smo naišli na “raskrsnicu” i u kojoj, u skladu sa
našim algoritmom, treba da skrenemo desno. Da ne bi došlo do zabune, ovo skretanje desno na
“raskrsnici” predstavlja skretanje nadolje, kao što je označeno kraćom strelicom. Ovo je lako
vizuelizovati ukoliko zamislimo da se krećemo u pravom lavirintu i uvijek držimo desnu ruku
na zidu sa naše desne strane.

##.# ##.# ##.# ####


. .O. . .O. . .O# . .O#
##.# #### #### ####
a) b) c) d)
Dakle, ukoliko je naša tekuća pozicija određena koordinatama x i y i ukoliko je zadovoljen uslov
a[x+1][y]=='.' onda promjenljivoj smjer dodijeljujemo vrijednost 4 i skrećemo nadolje
296 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

rekurzivnim pozivom funkcije obilazak sa argumentima x+1 i y. Ukoliko nije zadovoljen


uslov a[x+1][y]=='.', gledamo da nastavimo kretanje u istom smjeru, tj. u smjeru duže
strelice, što možemo uraditi samo ako je zadovoljen uslov a[x][y+1]=='.'. Ova situacija je
prikazana na slici b). U ovom slučaju ne mijenjamo vrijednost promjenljive smjer i nastavljamo
kretanje rekurzivnim pozivom funkcije obilazak sa argumentima x i y+1. Ukoliko smo
onemogućeni da skrenemo nadolje (desno u lavirintu) i da nastavimo kretanje u istom smjeru,
onda provjeravamo da li možemo skrenuti nagore (lijevo u lavirintu), tj. da li je zadovoljen uslov
a[x‐1][y]=='.'. Ako uslov jeste zadovoljen, što je prikazano na slici c), onda promjenljivoj
smjer dodijeljujemo 3 i skrećemo nagore pozivom funkcije obilazak sa argumentima x‐1 i
y. Konačno, ako smo onemogućeni da skrenemo nadolje, da nastavimo kretanje u istom smjeru
i da skrenemo nagore, ne ostaje nam ništa drugo nego da se vratimo jedan korak nazad. Ova
situacija je prikazana na slici d). Tada promjenljivoj smjer dodijeljujemo vrijednost 2 i vraćamo
se korak nazad pozivom funkcije obilazak sa argumentima x i y‐1. Čitava opisana procedura
pokriva samo slučaj trenutnog kretanja udesno, tj. case 1: slučaj switch(smjer) naredbe u
okviru funkcije obilazak. Na čitaocu je da rastumači preostala tri slučaja.

Zadatak 7. Napisati program koji simulira igru pogađanja sekvence od 4 cijela broja, pri čemu
su brojevi iz skupa {1,2,3,4,5,6}. Naime, program prvo generiše slučajnu sekvencu
brojeva iz datog skupa, pri čemu su dozvoljena ponavljanja brojeva u okviru
sekvence. Sekvenca se generiše u formi stringa. Neki primjeri slučajnih sekvenci
su 1231, 2216, 5436, 3334 itd. Zatim korisnik (igrač) nagađa generisanu sekvencu
unoseći string dužine 4 karaktera. Nakon unosa sekvence, program poredi
generisanu i unesenu sekvencu brojeva, i ispisuje koliko je cifara pogođeno i koliko
od njih se nalazi na pravoj poziciji. Korisnik ima mogućnost da 10 puta nagađa
sekvencu. Nagađanje se završava ukoliko je korisnik pogodio generisanu sekvencu
ili ukoliko je bezuspješno iskoristio svih 10 mogućnosti, nakon čega se korisnik
pita da li želi da prekine ili nastavi igru nagađanjem nove sekvence.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main()
{
int i,j,pokusaj,p1,p2;
char genSek[5],nagSek[5],nast='d';
srand(time(0));
while(nast=='d')
{
for(i=0;i<4;i++)
genSek[i]=1+rand()%6+'0';
genSek[i]='\0';
pokusaj=1;
while(pokusaj<=10)
{
p1=p2=0;
printf("Unijeti sekvencu. Imate jos %d pokusaja ",10‐pokusaj+1);
scanf("%s",nagSek);
if(strcmp(nagSek,genSek)==0)
2.14 IGRE 297

{
printf("Pogodak!\n");
break;
}
for(i=0;i<4;i++)
if(nagSek[i]==genSek[i])
p1++;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
if(nagSek[j]==genSek[i])
{
p2++;
nagSek[j]='0';
break;
}
printf("Imate %d %s,od toga %d na pravoj poziciji\n",
p2,(p2>1?"pogotka":"pogodak"),p1);
pokusaj++;
}
if(pokusaj>10)
printf("Bezuspjesno ste iskoristili svih 10 pokusaja\n");
do
{
printf("Zelite li novu igru?\t n ‐ ne\t d ‐ da: ");
scanf("%*c%c",&nast);
} while(nast!='d'&&nast!='n');
}
}

Promjenljive korišćene u programu:


i, j pomoćne brojačke promjenljive
pokusaj cjelobrojna promjenljiva koja broji pokušaje igrača. Maksimalno može biti
10 pokušaja
p1 cjelobrojna promjenljiva koja broji koliko se pogođenih brojeva u nagađanoj
sekvenci nalazi na pravim pozicijama
p2 cjelobrojna promjenljiva koja broji koliko ima ukupno pogođenih brojeva u
nagađanoj sekvenci
genSek string koji predstavlja generisanu sekvencu brojeva
nagSek string koji predstavlja nagađanu sekvencu brojeva
nast karakter na osnovu kog se odlučuje da li se želi igrati nova igra. Ako se unese
'd' želi se nastavak igre, dok 'n' prekida igru
Opisana igra je jedna varijacija igre pod nazivom mastermind. U originalnoj igri postoje
dva igrača, od kojih jedan (codemaker) zamišlja uređenu sekvencu od 4 boje (c1,c2,c3,c4), pri
čemu su boje izabrane iz skupa od 6 mogućih boja, dok drugi (codebreaker) pokušava da pogodi
tu sekvencu nagađajući sekvencu boja (g1,g2,g3,g4). Nakon svakog pokušaja codemaker govori
codebreaker-u dva broja (a,b), pri čemu a predstavlja broj pogođenih boja na pravim pozicijama
u sekvenci, dok b predstavlja broj boja koje se nalaze u sekvenci, ali ne na pravoj poziciji.
298 P ROGRAMSKI JEZIK C SA ZBIRKOM URAĐENIH ZADATAKA

Čitava igra je realizovana u okviru while petlje koja se izvršava sve dok nast ima
vrijednost 'd'. Na početku te petlje se generiše sekvenca od 4 broja iz skupa {1,2,3,4,5,6} i to
u obliku stringa. Rastumačiti zapis genSek[i]=1+rand()%6+'0'. Nakon toga se ulazi u
while petlju koja se izvršava sve dok je redni broj pokušaja, tj. vrijednost promjenljive
pokusaj, manji ili jednak 10. Prvo se od korisnika traži unos sekvence, takođe u obliku stringa,
i vrši se poređenje generisane i nagađane sekvence funkcijom strcmp. Ukoliko su odgovarajući
stringovi identični, ispisuje se odgovarajuća poruka i izlazi se iz unutrašnje while petlje
naredbom break. Ukoliko to nije slučaj, ide se na određivanje koliko je brojeva pogođeno i
koliko od njih se nalazi na pravim pozicijama. Prva for petlja poredi stringove, karakter po
karakter, i svaki put kad se naiđe na poklapanje karaktera, tj. brojeva sekvence, inkrementira se
promjenljiva p1, čija je početna vrijednost 0 postavljena na početku spoljašnje while petlje.
Nakon završetka te for petlje, p1 sadrži podatak o broju pogodaka na pravoj poziciji. Ukupan
broj pogodaka, bez obzira da li se nalaze na pravoj poziciji, se određuje korišćenjem dvije
ugnježđene for petlje, pri čemu se spoljašnja kreće po elementima generisane sekvence, a
unutrašnja po elementima nagađane sekvence. Onoga trenutka kada ustanovimo da se određeni
broj iz nagađane sekvence nalazi u generisanoj sekvenci inkrementiramo p2, a zatim na poziciji
pronađenog karaktera u nagađanoj sekvenci upišemo karakter '0', kako taj pronađeni karakter
više ne bi uzimali u obzir prilikom pretrage. Nakon izvršenja ovih petlji idemo na ispisivanje
poruke o tome koliko ima pogodaka i koliko se nalazi na pravoj poziciji. Na kraju spoljašnje
while petlje korisnik se pita da li želi da nastavi igru pogađanjem nove sekvence, što postiže
unošenjem karaktera 'd', ili da prekine igre unošenjem karaktera 'n'.
Izvršenje programa:
Unijeti sekvencu. Imate jos 10 pokusaja 2233
Imate 0 pogodak,od toga 0 na pravoj poziciji
Unijeti sekvencu. Imate jos 9 pokusaja 1144
Imate 2 pogotka,od toga 1 na pravoj poziciji
Unijeti sekvencu. Imate jos 8 pokusaja 1455
Imate 2 pogotka,od toga 0 na pravoj poziciji
Unijeti sekvencu. Imate jos 7 pokusaja 6614
Pogodak!
Zelite li novu igru? n ‐ ne d ‐ da: n

You might also like