You are on page 1of 378

E

le
kt
ro
n
sk
o
iz
d
an
je
(2
01
5)
E
le
kt
ro
n
sk
o
iz
d
an
je
(2
01
5)
Filip Mari Predrag Janii

5)
01
(2
PROGRAMIRANJE 1
je
Osnove programiranja kroz programski jezik C
an
d
iz
o
sk
n
ro
kt
le
E

Beograd
2015.
Autori:
dr Filip Mari, docent na Matematikom fakultetu u Beogradu
dr Predrag Janii, redovni profesor na Matematikom fakultetu u Beogradu

PROGRAMIRANJE 1

Izdava: Matematiki fakultet Univerziteta u Beogradu


Studentski trg 16, 11000 Beograd

5)
Za izdavaa: prof. dr Zoran Raki, dekan

Recenzenti:

01
dr Gordana Pavlovi-Laeti, redovni profesor na Matematikom fakultetu u
Beogradu

(2
dr Miodrag ivkovi, redovni profesor na Matematikom fakultetu u Beogradu
dr Dragan Uroevi, nauni savetnik na Matematikom institutu SANU

Obrada teksta, crtei i korice: autori

je
an
d
iz
o
sk
n
ro
kt

ISBN 978-86-7589-100-0
le
E

c 2015. Filip Mari i Predrag Janii


Ovo delo zatieno je licencom Creative Commons CC BY-NC-ND 4.0 (Attribution-
NonCommercial-NoDerivatives 4.0 International License). Detalji licence mogu se
videti na veb-adresi http://creativecommons.org/licenses/by-nc-nd/4.0/. Do-
zvoljeno je umnoavanje, distribucija i javno saoptavanje dela, pod uslovom da se
navedu imena autora. Upotreba dela u komercijalne svrhe nije dozvoljena. Prerada,
preoblikovanje i upotreba dela u sklopu nekog drugog nije dozvoljena.
5)
Sadraj

01
(2
je
Sadraj 5
an
I Osnovni pojmovi raunarstva i programiranja 10
d

1 Raunarstvo i raunarski sistemi 11


iz

1.1 Rana istorija raunarskih sistema . . . . . . . . . . . . . . . . . 12


1.2 Raunari fon Nojmanove arhitekture . . . . . . . . . . . . . . . 16
1.3 Oblasti savremenog raunarstva . . . . . . . . . . . . . . . . . . 21
o

1.4 Hardver savremenih raunara . . . . . . . . . . . . . . . . . . . 22


sk

1.5 Softver savremenih raunara . . . . . . . . . . . . . . . . . . . . 26

2 Reprezentacija podataka u raunarima 38


n

2.1 Analogni i digitalni podaci i digitalni raunari . . . . . . . . . . 38


ro

2.2 Zapis brojeva . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40


2.3 Zapis teksta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
kt

2.4 Zapis multimedijalnih sadraja . . . . . . . . . . . . . . . . . . 55

3 Algoritmi i izraunljivost 61
le

3.1 Formalizacije pojma algoritma . . . . . . . . . . . . . . . . . . 61


E

3.2 er-Tjuringova teza . . . . . . . . . . . . . . . . . . . . . . . . 63


3.3 ur maine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
3.4 Enumeracija urm programa . . . . . . . . . . . . . . . . . . . . 69
3.5 Neizraunljivost i neodluivost . . . . . . . . . . . . . . . . . . 71
3.6 Vremenska i prostorna sloenost izraunavanja . . . . . . . . . 73

4 Vii programski jezici 78


4.1 Kratki pregled istorije programskih jezika . . . . . . . . . . . . 79
4.2 Klasifikacije programskih jezika . . . . . . . . . . . . . . . . . . 80
4.3 Leksika, sintaksa, semantika programskih jezika . . . . . . . . . 80

5
4.4 Pragmatika programskih jezika . . . . . . . . . . . . . . . . . . 83

II Jezik C 90
5 Osnovno o programskom jeziku C 91
5.1 Standardizacija jezika . . . . . . . . . . . . . . . . . . . . . . . 91
5.2 Prvi programi . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

5)
6 Predstavljanje podataka i operacije nad njima 101

01
6.1 Promenljive i deklaracije . . . . . . . . . . . . . . . . . . . . . . 101
6.2 Osnovni tipovi podataka . . . . . . . . . . . . . . . . . . . . . . 104
6.3 Konstante i konstantni izrazi . . . . . . . . . . . . . . . . . . . 109

(2
6.4 Operatori i izrazi . . . . . . . . . . . . . . . . . . . . . . . . . . 112
6.5 Konverzije tipova . . . . . . . . . . . . . . . . . . . . . . . . . . 126
6.6 Nizovi i niske . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132

je
6.7 Korisniki definisani tipovi . . . . . . . . . . . . . . . . . . . . . 140
an
7 Naredbe i kontrola toka 151
7.1 Naredba izraza . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
d

7.2 Sloene naredbe (blokovi) . . . . . . . . . . . . . . . . . . . . . 152


7.3 Naredbe grananja . . . . . . . . . . . . . . . . . . . . . . . . . . 152
iz

7.4 Petlje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

8 Funkcije 168
o
sk

8.1 Primeri definisanja i pozivanja funkcije . . . . . . . . . . . . . . 168


8.2 Deklaracija i definicija funkcije . . . . . . . . . . . . . . . . . . 170
8.3 Parametri funkcije . . . . . . . . . . . . . . . . . . . . . . . . . 171
n

8.4 Prenos argumenata . . . . . . . . . . . . . . . . . . . . . . . . . 172


ro

8.5 Konverzije tipova argumenata funkcije . . . . . . . . . . . . . . 174


8.6 Povratna vrednost funkcije . . . . . . . . . . . . . . . . . . . . 175
kt

8.7 Nizovi i funkcije . . . . . . . . . . . . . . . . . . . . . . . . . . . 175


8.8 Korisniki definisani tipovi i funkcije . . . . . . . . . . . . . . . 178
8.9 Rekurzija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
le

8.10 Funkcije sa promenljivim brojem argumenata . . . . . . . . . . 179


E

9 Organizacija izvornog i izvrnog programa 186


9.1 Od izvornog do izvrnog programa . . . . . . . . . . . . . . . . 187
9.2 Organizacija izvornog programa . . . . . . . . . . . . . . . . . . 193
9.3 Organizacija izvrnog programa . . . . . . . . . . . . . . . . . . 219

10 Pokazivai i dinamika alokacija memorije 234


10.1 Pokazivai i adrese . . . . . . . . . . . . . . . . . . . . . . . . . 234
10.2 Pokazivai i argumenti funkcija . . . . . . . . . . . . . . . . . . 239
10.3 Pokazivai i nizovi . . . . . . . . . . . . . . . . . . . . . . . . . 241
10.4 Pokazivaka aritmetika . . . . . . . . . . . . . . . . . . . . . . . 244
10.5 Pokazivai i niske . . . . . . . . . . . . . . . . . . . . . . . . . . 247
10.6 Nizovi pokazivaa i viedimenzioni nizovi . . . . . . . . . . . . . 251
10.7 Pokazivai i strukture . . . . . . . . . . . . . . . . . . . . . . . 254
10.8 Pokazivai na funkcije . . . . . . . . . . . . . . . . . . . . . . . 255
10.9 Dinamika alokacija memorije . . . . . . . . . . . . . . . . . . . 258

11 Pregled standardne biblioteke 269


11.1 Zaglavlje string.h . . . . . . . . . . . . . . . . . . . . . . . . . 269

5)
11.2 Zaglavlje stdlib.h . . . . . . . . . . . . . . . . . . . . . . . . . 272
11.3 Zaglavlje ctype.h . . . . . . . . . . . . . . . . . . . . . . . . . 274

01
11.4 Zaglavlje math.h . . . . . . . . . . . . . . . . . . . . . . . . . . 275
11.5 Zaglavlje assert.h . . . . . . . . . . . . . . . . . . . . . . . . . 276

(2
12 Ulaz i izlaz programa 277
12.1 Standardni tokovi . . . . . . . . . . . . . . . . . . . . . . . . . . 277

je
12.2 Ulaz iz niske i izlaz u nisku . . . . . . . . . . . . . . . . . . . . 285
12.3 Ulaz iz datoteka i izlaz u datoteke . . . . . . . . . . . . . . . . 286
an
12.4 Argumenti komandne linije programa . . . . . . . . . . . . . . 294

A Tabela prioriteta operatora 300


d

B Reenja zadataka 301


iz

Indeks 373
o
sk
n
ro
kt
le
E

7
5)
Predgovor

01
(2
Ova knjiga pisana je kao udbenik za predmet Programiranje 1 na smeru

je
Informatika Matematikog fakulteta u Beogradu. U ovom predmetu i u ovoj
knjizi, centralno mesto ima programski jezik C, ali predmet i knjiga nisu samo
an
kurs ovog jezika, ve pokuavaju da dju ire osnove programiranja, ilustrovane
kroz jedan konkretan jezik.
d

Knjiga je nastala na osnovu materijala za predavanja koja smo na ovom


predmetu drali od 2005. godine. Ipak, vremenom je materijal proiren i de-
iz

lovima koji se u okviru tog predmeta ne predaju ili se predaju u vrlo ogranie-
nom obimu. Zahvaljujui tome, ova knjiga i njen nastavak (Programiranje 2
o

Osnove programiranja kroz programski jezik C) mogu se koristiti kao udbenici


sk

za vie razliitih kurseva. Za predmet na studijama na kojima nema drugih


(ili nema mnogo drugih) raunarskih predmeta, predlaemo obraivanje i-
tave knjige, s tim to obraivanje glave 4 preporuujemo samo za studije sa
n

jakom matematikom orijentacijom. itaocima (studentima) koji poznaju os-


ro

nove raunarstva i programiranja, a ne znaju jezik C, preporuujemo itanje


samo drugog dela knjige (Jezik C). Za kurs Programiranje 1 na smeru Infor-
kt

matika preporuujemo samo ubrzano upoznavanje sa glavama 1 i 2 (jer se ti


sadraji izuavaju u okviru drugih predmeta na prvoj godini). Za ovaj kurs
le

preporuujemo i upoznavanje sa glavama 3 i 4, a upoznavanje sa glavama 10 i


12 preporuujemo za drugi semestar.
E

Na kraju veine poglavlja naveden je vei broj pitanja i zadataka koji mogu
da slue za proveru znanja. Meu ovim pitanjima su praktino sva pitanja
koja su zadata na testovima i ispitima iz predmeta Programiranje 1 u peri-
odu od pet godina. Odgovori na pitanja nisu eksplicitno navoeni, jer su ve
implicitno sadrani u osnovnom tekstu knjige. Na kraju knjige navedena su
reenja zadataka, te se moe smatrati da ova knjiga obuhvata i potpunu prateu
zbirku zadataka. Za zadatka tipa ta ispisuje naredni program? nisu navoeni
odgovori jer italac to moe da proveri na svom raunaru (i knjiga pokuava
da ohrabri itaoca da itanje knjige kombinuje sa radom na raunaru).
U pripremi knjige koristili smo mnoge izvore, pre svega sa interneta. Od

8
izvora o jeziku C, pomenimo ovde znamenitu knjigu Programski jezik C (The
C Programming Language, K&R) Brajana Kernigana i Denisa Riija koja je
dugo sluila kao nezvanini standard jezik, knjigu Knjiga o C-u (The C Book)
Majka Banahana, Deklana Brejdija i Marka Dorana, kao i ISO standarde jezika
C. Za pripremu glave Algoritmi i izraunljivost koristili smo knjigu Teorija al-
goritama, jezika i automata - zbirka zadataka, Irene Spasi i Predraga Janiia.
Na veoma paljivom itanju i brojnim korisnim savetima zahvaljujemo re-
cenzentima Gordani Pavlovi-Laeti, Miodragu ivkoviu i Draganu Uroe-

5)
viu. Na brojnim sugestijama i ispravkama zahvalni smo i nastavnicima Matem-
atikog fakulteta Mileni Vujoevi-Janii, Nenadu Mitiu i Mladenu Nikoliu,

01
kao i studentima Nikoli Premevskom, Mladenu Canoviu, Nemanji Mioviu,
Vojislavu Grujiu, Stefanu oreviu, Petru Vukmiroviu, Bobanu Piskuliu,

(2
Jani Proti, Ljubici Peleksi, Ivanu Baleviu, Danielu Doi, Milou Samardiji,
Stefanu Saviu, Petru Kovrliji i Tomislavu Milovanoviu.
Ova knjiga dostupna je (besplatno) u elektronskom obliku preko internet

je
strana autora. Sadraj tampanog i elektronskog izdanja je identian. Be-
splatna dostupnost elektronskog oblika knjige odraava stav autora o otvorenim
an
sadrajima kdu programa i sadraju knjiga.
d
iz

Autori
o
sk

Beograd, april 2015. godine


n
ro
kt
le
E

9
5)
01
(2
Deo I

Osnovni pojmovi raunarstva i


je
an
programiranja
d
iz
o
sk
n
ro
kt
le
E

10
Glava 1

5)
Raunarstvo i raunarski sistemi

01
(2
je
Raunarstvo i informatika predstavljaju jednu od najatraktivnijih i naj-
vanijih oblasti dananjice. ivot u savremenom drutvu ne moe se zamis-
an
liti bez korienja razliitih raunarskih sistema : stonih i prenosnih raunara,
tableta, pametnih telefona, ali i raunara integrisanih u razliite maine (au-
tomobile, avione, industrijske maine, itd). Definicija raunarskog sistema je
d

prilino iroka. Moe se rei da se danas pod digitalnim raunarskim sistemom


iz

(raunarom) podrazumeva maina koja moe da se programira da izvrava ra-


zliite zadatke svoenjem na elementarne operacije nad brojevima. Brojevi
se, u savremenim raunarima, zapisuju u binarnom sistemu, kao nizovi nula
o

i jedinica tj. binarnih cifara, tj. bitova (engl. bit, od binary digit ). Koristei
sk

bitova, moe se zapisati 2 razliitih vrednosti. Na primer, jedan bajt (B)


oznaava osam bitova i moe da reprezentuje 28 , tj. 256 razliitih vrednosti.
1
n

Raunarstvo se bavi izuavanjem raunara, ali i optije, izuavanjem teorije


ro

i prakse procesa raunanja i primene raunara u raznim oblastima nauke,


tehnike i svakodnevnog ivota .
2
Raunari u dananjem smislu nastali su polovinom XX veka, ali koreni
kt

raunarstva su mnogo stariji od prvih raunara. Vekovima su ljudi stvarali


mehanike i elektromehanike naprave koje su mogle da reavaju neke numer-
le

ike zadatke. Dananji raunari su programabilni, tj. mogu da se isprogrami-


raju da vre razliite zadatke. Stoga je oblast programiranja , kojom se ova
E

knjiga bavi, jedna od najznaajnijih oblasti raunarstva. Za funkcionisanje


modernih raunara neophodni su i hardver i softver. Hardver (tehniki sis-
tem raunara) ine opipljive, fizike komponente raunara: procesor, memo-

1 Koliina podataka i kapacitet memorijskih komponenti savremenih raunara obino se


iskazuje u bajtovima ili izvedenim jedinicama. Obino se smatra da je jedan kilobajt (KB)
jednak 1024 bajtova (mada neke organizacije podrazumevaju da je jedan KB jednak 1000
bajtova). Slino, jedan megabajt (MB) je jednak 1024 KB ili 10242 B, jedan gigabajt (GB)
je jednak 1024 MB, a jedan teraabajt (TB) je jednak 1024 GB.
2 esto se kae da se raunarstvo bavi raunarima isto onoliko koliko se astronomija bavi
teleskopima, a biologija mikroskopima. Raunari nisu sami po sebi svrha i samo su sredstvo
koje treba da pomogne u ostvarivanju razliitih zadataka.

11
12 1. Raunarstvo i raunarski sistemi

rija, matina ploa, hard disk, DVD ureaj, itd. Softver (programski sistem
raunara) ine raunarski programi i pratei podaci koji odreuju izrauna-
vanja koja vri raunar. Raunarstvo je danas veoma iroka i dobro utemeljena
nauna disciplina sa mnotvom podoblasti.

1.1 Rana istorija raunarskih sistema

5)
Programiranje u savremenom smislu postalo je praktino mogue tek kra-
jem Drugog svetskog rata, ali je njegova istorija znatno starija. Prvi pre-
cizni postupci i sprave za reavanje matematikih problema postojali su jo

01
u vreme antikih civilizacija. Na primer, kao pomo pri izvoenju osnovnih
matematikih operacija koriene su raunaljke zvane abakus . U IX veku per-
3 precizno je opisao postupke raunanja u indo-

(2
sijski matematiar Al Horezmi
arapskom dekadnom brojevnom sistemu (koji i danas predstavlja najkorieniji
brojevni sistem). U XIII veku Leonardo Fibonai
4 doneo je ovaj nain zapi-

je
sivanja brojeva iz Azije u Evropu i to je bio jedan od kljunih preduslova za
razvoj matematike i tehnikih disciplina tokom renesanse. Otkrie logaritma
an
omoguilo je svoenje mnoenja na sabiranje, dodatno olakano raznovrsnim
5
analognih spravama (npr. klizni lenjir iber ) . Prve mehanike sprave koje
su mogle da potpuno automatski izvode aritmetike operacije i pomau u rea-
d

vanju matematikih zadataka su napravljene u XVII veku. Blez Paskal


6 kon-
iz

struisao je 1642. godine mehanike sprave, kasnije nazvane Paskaline , koje su


sluile za sabiranje i oduzimanje celih brojeva. Gotfrid Lajbnic
7 konstruisao je
1672. godine mainu koja je mogla da izvrava sve etiri osnovne aritmetike
o

operacije (sabiranje, oduzimanje, mnoenje i deljenje) nad celim brojevima.


sk

Ova maina bila je zasnovana na dekadnom brojevnom sistemu, ali Lajbnic je


prvi predlagao i korienje binarnog brojevnog sistema u raunanju.
n
ro
kt
le
E

Slika 1.1: Abakus. iber. Paskalina

3 Muhammad ibn Musa al-Khwarizmi (780850), persijski matematiar.


4 Leonardo Pisano Fibonacci, (11701250), italijanski matematiar iz Pize.
5 Zanimljivo je da su klizni lenjiri noeni na pet Apolo misija, ukljuujui i onu na Mesec,
da bi astronautima pomagali u potrebnim izraunavanjima.
6 Blaise Pascal (16231662), francuski filozof, matematiar i fiziar. U njegovu ast jedan
programski jezik nosi ime PASCAL.
7 Gottfried Wilhelm Leibniz (16461716), nemaki filozof i matematiar.
13 1. Raunarstvo i raunarski sistemi

Mehanike maine. ozef Mari akard


8 konstruisao je 1801. godine prvu
programabilnu mainu mehaniki tkaki razboj koji je koristio buene kar-
tice kao svojevrsne programe za generisanje kompleksnih ara na tkanini. Svaka
rupa na kartici odreivala je jedan pokret maine, a svaki red na kartici odgo-
varao je jednom redu are.
U prvoj polovini XIX veka, arls Bebid
9 dizajnirao je, mada ne i real-
izovao, prve programabilne raunske maine. Godine 1822. zapoeo je rad
na diferencijskoj maini koja je trebalo da rauna vrednosti polinomijalnih

5)
funkcija (i eliminie este ljudske greke u tom poslu) u cilju izrade to pre-
ciznijih logaritamskih tablica. Ime je dobila zbog toga to je koristila tzv. metod

01
konanih razlika da bi bila eliminisana potreba za mnoenjem i deljenjem.
Maina je trebalo da ima oko 25000 delova i da se pokree runo, ali nije
10 . Ubrzo nakon to je rad na prvom projektu utihnuo bez

(2
nikada zavrena
rezultata, Bebid je zapoeo rad na novoj maini nazvanoj analitika maina .
Osnovna razlika u odnosu na sve prethodne maine, koje su imale svoje speci-

je
fine namene, bila je u tome to je analitika maina zamiljena kao raun-
ska maina opte namene koja moe da se programira (programima zapisanim
an
na buenim karticama, slinim akardovim karticama). Program zapisan na
karticama kontrolisao bi mehaniki raunar (pokretan parnom mainom) i
omoguavao sekvencijalno izvravanje naredbi, grananje i skokove, slino pro-
d

gramima za savremene raunare. Osnovni delovi raunara trebalo je da budu


iz

mlin (engl. mill) i skladite (engl. store), koji po svojoj funkcionalnosti sasvim
odgovaraju procesoru i memoriji dananjih raunara. Ada Bajron
11 zajedno
sa Bebidem napisala je prve programe za analitiku mainu i, da je maina
o

uspeno konstruisana, njeni programi bi mogli da raunaju odreene sloene ni-


sk

zove brojeva (takozvane Bernulijeve brojeve). Zbog ovoga se ona smatra prvim
programerom u istoriji (i njoj u ast jedan programski jezik nosi ime Ada ).
n

Ona je bila i prva koja je uvidela da se raunske maine mogu upotrebiti i za


ro

nematematike namene, ime je na neki nain anticipirala dananje namene


digitalnih raunara.
kt

Elektromehanike maine. Elektromehanike maine za raunanje koristili


su se od sredine XIX veka do vremena Drugog svetskog rata.
le

Jedna od prvih je maina za itanje buenih kartica koju je konstruisao


12 . Ova maina koriena je 1890. za obradu rezultata popisa
E

Herman Holerit
stanovnitva u SAD. Naime, obrada rezultata popisa iz 1880. godine trajala
je vie od 7 godina, a zbog naglog porasta broja stanovnika procenjeno je

8 Joseph Marie Jacquard (17521834), francuski trgovac.


9 Charles Babbage (17911871), engleski matematiar, filozof i pronalaza.
10 Dosledno sledei Bebidev dizajn, 1991. godine (u nauno-popularne svrhe) uspeno
je konstruisana diferencijska maina koja radi besprekorno. Neto kasnije, konstruisan je i
tampa koji je Bebid dizajnirao za diferencijsku mainu, tj. tamparska presa povezana
sa parnom mainom koja je tampala izraunate vrednosti.
11 Augusta Ada King (ro. Byron), Countess of Lovelace, (18151852), engleska matem-
atiarka. U njenu ast nazvan je programski jezik ADA.
12 Herman Hollerith (18601929), ameriki pronalaza.
14 1. Raunarstvo i raunarski sistemi

5)
01
(2
Slika 1.2: akardov razboj. Bebidova diferencijska maina.

je
da bi obrada rezultata iz 1890. godine trajala vie od 10 godina, to je bilo
an
neprihvatljivo mnogo. Holerit je sproveo ideju da se podaci prilikom popisa za-
pisuju na mainski itljivom medijumu (na buenim karticama), a da se kasnije
obrauju njegovom mainom. Koristei ovaj pristup obrada rezultata popisa
d

uspeno je zavrena za godinu dana. Od Holeritove male kompanije kasnije je


iz

nastala uvena kompanija IBM.


Godine 1941, Konrad Cuze
13 konstruisao je 22-bitnu mainu raunanje Z3
koji je imao izvesne mogunosti programiranja (podrane su bile petlje, ali
o

ne i uslovni skokovi), te se esto smatra i prvim realizovanim programabilnim


sk

raunarom
14 . Cuzeove maine tokom Drugog svetskog rata naile su samo na
ograniene primene. Cuzeova kompanija proizvela je oko 250 razliitih tipova
n

raunara do kraja ezdesetih godina, kada je postala deo kompanije Simens


(nem. Siemens).
ro

U okviru saradnje kompanije IBM i univerziteta Harvard, tim Hauarda


Aikena
15 zavrio je 1944. godine mainu Harvard Mark I . Ova maina itala je
kt

instrukcije sa buene papirne trake, imala je preko 760000 delova, duinu 17m,
visinu 2.4m i masu 4.5t. Mark I mogao je da pohrani u memoriji (korienjem
le

elektromehanikih prekidaa) 72 broja od po 23 dekadne cifre. Sabiranje i


oduzimanje dva broja trajalo je treinu, mnoenje est, a deljenje petnaest
E

sekundi.

Elektronski raunari. Elektronski raunari koriste se od kraja 1930-ih do


danas.
Jedan od prvih elektronskih raunara ABC (specijalne namene reavanje

13 Konrad Zuse (19101995), nemaki inenjer.


14 Maini Z3 prethodile su jednostavnije maine Z1 i Z2, izgraeni 1938. i 1940. godine.
15 Howard Hathaway Aiken (19001973).
15 1. Raunarstvo i raunarski sistemi

5)
01
(2
je
Slika 1.3: Holeritova maina. Harvard Mark I. ENIAC (proces reprogrami-
ranja).
an
16 i Beri 17 .
d

sistema linearnih jednaina) napravili su 1939. godine Atanasov


Maina je prva koristila binarni brojevni sistem i elektrine kondenzatore (engl.
iz

capacitor) za skladitenje bitova sistem koji se u svojim savremenim vari-


jantama koristi i danas u okviru tzv. DRAM memorije. Maina nije bila pro-
o

gramabilna.
sk

Krajem Drugog svetskog rada, u Engleskoj, u Bleli parku (engl. Bletch-


ley Park) u kojem je radio i Alan Tjuring
18 , konstruisan je raunar Kolos
(engl. Colossus ) namenjen deifrovanju nemakih poruka. Raunar je omoguio
n

razbijanje nemake ifre zasnovane na maini Enigma , zahvaljujui emu su


ro

saveznici bili u stanju da prate komunikaciju nemake podmornike flote, to


je znaajno uticalo na ishod Drugog svetskog rata.
kt

U periodu izmeu 1943. i 1946. godine od strane amerike vojske i tima


univerziteta u Pensilvaniji koji su predvodili Don Mokli
19 i Dej Ekert 20 kon-
le

struisan je prvi elektronski raunar opte namene ENIAC (Electronic Nu-


merical Integrator and Calculator) . Imao je 1700 vakuumskih cevi, duinu
E

30m i masu 30t. Raunske operacije izvravao je hiljadu puta bre od elek-
tromehanikih maina. Osnovna svrha bila mu je jedna specijalna namena
raunanje trajektorije projektila. Bilo je mogue da se maina preprogramira i
za druge zadatke ali to je zahtevalo intervencije na preklopnicima i kablovima
koje su mogle da traju danima.

16 John Vincent Atanasoff (19031995).


17 Clifford Edward Berry (19181963).
18 Alan Turing (19121954), britanski matematiar.
19 John William Mauchly (19071980).
20 J. Presper Eckert (19191995).
16 1. Raunarstvo i raunarski sistemi

1.2 Raunari fon Nojmanove arhitekture


Rane maine za raunanje nisu bile programabilne ve su radile po un-
apred fiksiranom programu, odreenom samom konstrukcijom maine. Takva
arhitektura se i danas koristi kod nekih jednostavnih maina, na primer, kod
kalkulatora (digitrona). Da bi izvravali nove zadatke, rani elektronski rau-
nari nisu programirani u dananjem smislu te rei, ve su sutinski redizajni-
rani. Tako su, na primer, operaterima bile potrebne nedelje da bi prespojili

5)
kablove u okviru kompleksnog sistema ENIAC i tako ga instruisali da izvrava
novi zadatak.

01
Potpuna konceptualna promena dola je kasnih 1940-ih, sa pojavom rauna-
ra koji programe na osnovu kojih rade uvaju u memoriji zajedno sa podacima

(2
raunara sa skladitenim programima (engl. stored program computers) . U
okviru ovih raunara, postoji jasna podela na hardver i softver. Iako ideje
za ovaj koncept datiraju jo od arlsa Bebida i njegove analitike maine i

je
nastavljaju se kroz radove Tjuringa, Cuzea, Ekerta, Moklija, za rodonaelnika
ovakve arhitekture raunara smatra se Don fon Nojman
21 . Fon Nojman se u
an
ulozi konsultanta prikljuio timu Ekerta i Moulija i 1945. godine je u svom
izvetaju EDVAC (Electronic Discrete Variable Automatic Computer) opisao
arhitekturu koja se i danas koristi u najveem broju savremenih raunara i u
d

kojoj se programi mogu uitavati isto kao i podaci koji se obrauju. Rau-
iz

nar EDVAC, naslednik raunara ENIAC, koristio je binarni zapis brojeva, u


memoriju je mogao da upie hiljadu 44-bitnih podataka i bio je jedan od prvih
raunara koji su mogli da uitaju programe u memoriju. Iako je dizajn rau-
o

nara EDVAC bio prvi opis fon Nojmanove arhitekture, pre 1951. godine - kada
sk

je EDVAC puten u rad, ve je nekoliko raunara sline arhitekture bilo kon-


struisano i funkcionalno njega (Mark 1 i EDSAC - 1949. godine i MESM u
n

tadanjem SSSR, 1950. godine).


Osnovni elementi fon Nojmanove arhitekture raunara su procesor (koji
ro

ine aritmetiko-logika jedinica, kontrolna jedinica i registri) i glavna memo-


rija , koji su meusobno povezani. Ostale komponente raunara (npr. ulazno-
kt

izlazne jedinice, spoljanje memorije, . . . ) smatraju se pomonim i povezuju


se na centralni deo raunara koji ine procesor i glavna memorija. Sva obrada
le

podataka vri se u procesoru. U memoriju se skladite podaci koji se obrauju,


ali i programi, predstavljeni nizom elementarnih instrukcija (kojima se proce-
E

soru zadaje koju akciju ili operaciju da izvri). I podaci i programi se zapisuju
obino kao binarni sadraj i nema nikakve sutinske razlike izmeu zapisa pro-
grama i zapisa podataka. Tokom rada, podaci i programi se prenose izmeu
procesora i memorije. S obzirom na to da i skoro svi dananji raunari imaju
fon Nojmanovu arhitekturu, nain funkcionisanja ovakvih raunara bie opisan
detaljnije u poglavlju o savremenim raunarskim sistemima.
Moderni programabilni raunari se, po pitanju tehnologije koju su koristili,
mogu grupisati u etiri generacije, sve zasnovane na fon Nojmanovoj arhitek-

21 John Von Neumann (19031957), ameriki matematiar.


17 1. Raunarstvo i raunarski sistemi

turi.

5)
Slika 1.4: Osnovni gradivni elementi korieni u etiri generacije raunara:

01
vakuumska cev, tranzistor, integrisano kolo i mikroprocesor

(2
I generacija raunara (od kraja 1930-ih do kraja 1950-ih) koristila je vaku-
umske cevi kao logika kola i magnetne doboe (a delom i magnetne trake) za

je
memoriju. Za programiranje su korieni mainski jezik i asembler a glavne
primene su bile vojne i naune. Raunari su uglavnom bili unikatni (tj. za
an
veinu nije postojala serijska proizvodnja). Prvi realizovani raunari fon No-
jmanove arhitekture bili su Manesterska Beba (engl. Manchester Baby)
eksperimentalna maina, razvijena 1949. na Univerzitetu u Manesteru, na
d

kojoj je testirana tehnologija vakuumskih cevi i njen naslednik Manesterski


iz

Mark 1 (engl. Manchester Mark 1) , EDSAC razvijen 1949. na Univerzitetu


u Kembridu, MESM razvijen 1950. na Kijevskom elektrotehnikom institutu
i EDVAC koji je prvi dizajniran, ali napravljen tek 1951. na Univerzitetu u
o

Pensilvaniji. Tvorci raunara EDVAC, poeli su 1951. godine proizvodnju pr-


sk

vog komercijalnog raunara UNIVAC UNIVersal Automatic Computer koji


je prodat u, za to doba neverovatnih, 46 primeraka.
n

II generacija raunara
ro

(od kraja 1950-ih do polovine 1960-ih) koristila je


tranzistore umesto vakuumskih cevi. Iako je tranzistor otkriven jo 1947. go-
kt

dine, tek sredinom pedesetih poinje da se koristi umesto vakuumskih cevi kao
osnovna elektronska komponenta u okviru raunara. Tranzistori su izgraeni
od tzv. poluprovodnikih elemenata (obino silicijuma ili germanijuma). U
le

poreenju sa vakuumskih cevima, tranzistori su manji, zahtevaju manje en-


E

ergije te se manje i greju. Tranzistori su unapredili ne samo procesore i mem-


oriju ve i spoljanje ureaje. Poeli su da se iroko koriste magnetni diskovi
i trake, zapoelo je umreavanja raunara i ak korienje raunara u zabavne
svrhe (implementirana je prva raunarska igra Spacewar za raunar PDP-1 ).
U ovo vreme razvijeni su i prvi jezici vieg nivoa (FORTRAN, LISP, ALGOL,
COBOL). U to vreme kompanija IBM dominirala je tritem samo raunar
IBM 1401, prodat u vie od deset hiljada primeraka, pokrivao je oko treinu
tada postojeeg trita.

III generacija raunara (od polovine 1960-ih do sredine 1970-ih) bila je za-
snovana na integrisanim kolima smetenim na silicijumskim (mikro)ipovima .
18 1. Raunarstvo i raunarski sistemi

Prvi raunar koji je koristio ovu tehnologiju bio je IBM 360, napravljen 1964. go-
dine.

5)
01
Slika 1.5: Integrisana kola dovela su do minijaturizacije i kompleksni iani
spojevi su mogli biti realizovani na izuzetno maloj povrini.

(2
Nova tehnologija omoguila je poslovnu primenu raunara u mnogim oblas-

je
tima. U ovoj eri dominirali su mejnfrejm (engl. mainframe)mejnfrejm raunari
raunari koji su bili izrazito moni za to doba, ija se brzina merila milionima
an
instrukcija u sekundi (engl. MIPS; na primer, neki podmodeli raunara IBM
360 imali su brzinu od skoro 1 MIPS) i koji su imali mogunost skladitenja i
d

obrade velike koliine podataka te su korieni od strane vlada i velikih korpo-


racija za popise, statistike obrade i slino. Kod raunara ove generacije uve-
iz

den je sistem deljenja vremena (engl. timesharing) koji dragoceno procesorsko


vreme raspodeljuje i daje na uslugu razliitim korisnicima koji istovremeno
o

rade na raunaru i komuniciraju sa njim putem specijalizovanih terminala . U


sk

ovo vreme uvedeni su prvi standardi za jezike vieg nivoa (npr. ANSI FOR-
TRAN). Korieni su razliiti operativni sistemi, uglavnom razvijeni u okviru
kompanije IBM. Sa udelom od 90%, kompanija IBM je imala apsolutnu domi-
n

naciju na tritu ovih raunara.


ro

Pored mejnfrejm raunara, u ovom periodu iroko su korieni i mini rau-


nari (engl. minicomputers) koji se mogu smatrati prvim oblikom linih (person-
kt

alnih) raunara. Procesor je, uglavnom, bio na raspolaganju iskljuivo jednom


korisniku. Obino su bili veliine ormana i retko su ih posedovali pojedinci
(te se ne smatraju kunim raunarima). Tritem ovih raunara dominirala je
le

kompanija DEC Digital Equipment Corporation sa svojim serijama raunara


E

poput PDP-8 i VAX. Za ove raunare, obino se vezuje operativni sistem Unix
i programski jezik C razvijeni u Belovim laboratorijama (engl. Bell Laborato-
ries), a esto i hakerska
22 kultura nastala na univerzitetu MIT (engl. Mas-
sachusetts Institute of Technology).

IV generacija raunara (od ranih 1970-ih) zasnovana je na visoko inte-


grisanim kolima kod kojih je na hiljade kola smeeno na jedan silikonski ip.

22 Termin haker se obino koristi za osobe koje neovlaeno pristupaju raunarskim sis-
temima, ali hakeraj kao programerska podkultura podrazumeva anti-autoritaran pristup
razvoju softvera, obino povezan sa pokretom za slobodan softver. U oba sluaja, hakeri
su pojedinici koji na inovativan nain modifikuju postojee hardverske i softverske sisteme.
19 1. Raunarstvo i raunarski sistemi

5)
Slika 1.6: Mejnfrejm raunar: IBM 7094. Mini raunar: DEC PDP 7

01
U kompaniji Intel 1971. godine napravljen je prvi mikroprocesor Intel 4004

(2
celokupna centralna procesorska jedinica bila je smetena na jednom ipu. Iako
prvobitno namenjena za ugradnju u kalkulatore, ova tehnologija omoguila je
razvoj brzih a malih raunara pogodnih za linu tj. kunu upotrebu.

je
asopis Popular electronics nudio je 1975. godine itaocima mogunost
naruivanja delova za sklapanje mikroraunara MITS Altair 8800 zasnovanog
an
na mikroprocesoru Intel 8080 (nasledniku mikroprocesora Intel 4004 ). In-
teresovanje meu onima koji su se elektronikom bavili iz hobija bio je izuzetno
pozitivan i samo u prvom mesecu prodato je nekoliko hiljada ovih uradi-sm
d

raunara. Smatra se da je Altair 8800 bio inicijalna kapisla za revoluciju


iz

mikroraunara koja je usledila narednih godina. Altair se vezuje i za nas-


tanak kompanije Microsoft danas jedne od dominantnih kompanija u oblasti
proizvodnje softvera. Naime, prvi proizvod kompanije Microsoft bio je inter-
o

pretator za programski jezik BASIC za Altair 8800.


sk

Nakon Altaira pojavljuje se jo nekoliko raunarskih kompleta na skla-


panje. Prvi mikroraunar koji je prodavan ve sklopljen bio je Apple, na ijim
n

temeljima je nastala istoimena kompanija, danas jedan od lidera na tritu


ro

raunarske opreme.
Kuni raunari koristili su se sve vie uglavnom od strane entuzijasta
za jednostavnije obrade podataka, uenje programiranja i igranje rau-
kt

narskih igara. Kompanija Commodore je 1977. godine predstavila svoj rau-


narom Commodore PET koji je zabeleio veliki uspeh. Commodore 64, jedan
le

od najuspenijih raunara za kunu upotrebu, pojavio se 1982. godine. Iz


iste kompanije je i serija Amiga raunara sa kraja 1980-ih i poetka 1990-ih.
E

Pored kompanije Commodore, znaajni proizvoai raunara toga doba bili su


i Sinclair (sa veoma popularnim modelom ZX Spectrum ), Atari, Amstrad, itd.
Kuni raunari ove ere bili su obino jeftini, imali su skromne karakteristike i
najee koristili kasetofone i televizijske ekrane kao ulazno-izlazne ureaje.
Najznaajnija raunarska kompanija toga doba IBM ukljuila se na
trite kunih raunara 1981. godine, modelom IBM PC 5150, poznatijem jed-
nostavno kao IBM PC ili PC (engl. Personal computer ). Zasnovan na In-
telovom mikroprocesoru Intel 8088, ovaj raunar veoma brzo je zauzeo trite
raunara za linu poslovnu upotrebu (obrada teksta, tabelarna izraunavanja,
. . . ). Pratei veliki uspeh IBM PC raunara, pojavio se odreen broj klonova
20 1. Raunarstvo i raunarski sistemi

5)
01
(2
je
Slika 1.7: Prvi mikroprocesor: Intel 4004. Naslovna strana asopisa Popular
electronics sa Altair 8800. Commodore 64. IBM PC 5150.
an
raunara koji nisu proizvedeni u okviru kompanije IBM, ali koji su kompat-
d

ibilni sa IBM PC raunarima. PC arhitektura vremenom je postala standard


iz

za kune raunare. Sredinom 1980-ih, pojavom naprednijih grafikih (VGA) i


zvunih (SoundBlaster) kartica, IBM PC i njegovi klonovi stekli su mogunost
naprednih multimedijalnih aplikacija i vremenom su sa trita istisli sve ostale
o

proizvoae. I naslednici originalnog IBM PC raunara (IBM PC/XT, IBM


sk

PC/AT, . . . ) bili su zasnovani na Intelovim mikroprocesorima, pre svega na


x86 seriji (Intel 80286, 80386, 80486) i zatim na seriji Intel Pentium. Opera-
n

tivni sistem koji se tradicionalno vezuju uz PC raunare dolaze iz kompanije


ro

Microsoft prvo MS DOS, a zatim MS Windows. PC arhitektura podrava i


korienje drugih operativnih sistema (na primer, GNU/Linux).
Jedini veliki konkurent IBM PC arhitekturi koji se sve vreme odrao na
kt

tritu (pre svega u SAD) je serija raunara Macintosh kompanije Apple. Mac-
intosh, koji se pojavio 1984., je prvi komercijalni kuni raunar sa grafikim
le

korisnikim interfejsom i miem. Operativni sistem koji se i danas koristi na


Apple raunarima je Mac OS.
E

Iako su prva povezivanja udaljenih raunara izvrena jo krajem 1960-ih


godina, pojavom interneta (engl. internet) i veba (engl. World Wide Web
WWW), veina raunara postaje meusobno povezana sredinom 1990-ih god-
ina. Danas se veliki obim poslovanja izvrava u internet okruenju, a domen
korienja raunara je veoma irok. Dolo je do svojevrsne informatike revolu-
cije koja je promenila savremeno drutvo i svakodnevni ivot. Na primer, tokom
prve decenije XXI veka dolo je do pojave drutvenih mrea (engl. social net-
works) koje postepeno preuzimaju ulogu osnovnog medijuma za komunikaciju.
Tritem dananjih raunara dominiraju raunari zasnovani na PC arhitek-
turi i Apple Mac raunari. Pored stonih (engl. desktop) raunara popularni
21 1. Raunarstvo i raunarski sistemi

5)
01
(2
je
Slika 1.8: Stoni raunar. Prenosni raunar: IBM ThinkPad. Tablet: Apple
Ipad 2. Pametni telefon: Samsung Galaxy S2.
an
su i prenosni (engl. notebook ili laptop) raunari. U najnovije vreme, javlja
d

se trend tehnoloke konvergencije koja podrazumeva stapanje razliitih ure-


iz

aja u jedinstvene celine, kao to su tableti (engl. tablet) i pametni telefoni


(engl. smartphone). Operativni sistemi koji se danas uglavnom koriste na ovim
ureajima su IOS kompanije Apple, kao i Android kompanije Google.
o

Pored linih raunara u IV generaciji se i dalje koriste mejnfrejm raunari


sk

(na primer, IBM Z serija) i superraunari (zasnovani na hiljadama procesora).


Na primer, kineski superraunar Tianhe-2 radi brzinom od preko 30 petaflopsa
n

(dok proseni lini raunar radi brzinom reda 10 gigaflopsa).


23
ro

1.3 Oblasti savremenog raunarstva


kt

Savremeno raunarstvo ima mnogo podoblasti, kako praktinih, tako i teori-


jskih. Zbog njihove isprepletenosti nije jednostavno sve te oblasti sistematizo-
le

vati i klasifikovati. U nastavku je dat spisak nekih od oblasti savremenog


E

raunarstva (u skladu sa klasifikacijom amerike asocijacije ACM Associa-


tion for Computing Machinery, jedne od najveih i najuticajnijih raunarskih
zajednica):

Algoritmika (procesi izraunavanja i njihova sloenost);

Strukture podataka (reprezentovanje i obrada podataka);

23 Flops je mera raunarskih performansi, posebno pogodna za izraunavanja nad broje-


vima u pokretnom zarezu (i pogodnija nego generika mera koja se odnosi na broj instrukcija
u sekundi). Broj flopsa govori koliko operacija nad brojevima u pokretnom zarezu moe da
izvri raunar u jednoj sekundi. Brzina dananjih raunara se obino izraava u gigaflopsima
(109 flopsa), teraflopsima (1012 flopsa) i petaflopsima (1015 flopsa).
22 1. Raunarstvo i raunarski sistemi

Programski jezici (dizajn i analiza svojstava formalnih jezika za opisivanje


algoritama);

Programiranje (proces zapisivanja algoritama u nekom programskom je-


ziku);

Softversko inenjerstvo (proces dizajniranja, razvoja i testiranja pro-


grama);

5)
Prevoenje programskih jezika (efikasno prevoenje viih programskih
jezika, obino na mainski jezik);

01
Operativni sistemi (sistemi za upravljanje raunarom i programima);

(2
Mreno raunarstvo (algoritmi i protokoli za komunikaciju izmeu rau-
nara);

je
Primene (dizajn i razvoj softvera za svakodnevnu upotrebu);
an
Istraivanje podataka (pronalaenje relevantnih informacija u velikim sku-
povima podataka);
d

Vetaka inteligencija (reavanje problema u kojima se javlja kombina-


torna eksplozija);
iz

Robotika (algoritmi za kontrolu ponaanja robota);


o

Raunarska grafika (analiza i sinteza slika i animacija);


sk

Kriptografija (algoritmi za zatitu privatnosti podataka);


n

Teorijsko raunarstvo (teorijske osnove izraunavanja, raunarska matem-


ro

atika, verifikacija softvera, itd).

1.4 Hardver savremenih raunara


kt

Hardver ine opipljive, fizike komponente raunara. Iako je u osnovi savre-


le

menih raunarskih sistema i dalje fon Nojmanova maina (procesor i memo-


E

rija), oni se danas ne mogu zamisliti bez niza hardverskih komponenti koje
olakavaju rad sa raunarom.
Iako na prvi pogled deluje da se jedan uobiajeni stoni raunar sastoji od
kuita, monitora, tastature i mia, ova podela je veoma povrna, podlona
promenama (ve kod prenosnih raunara, stvari izgledaju znatno drugaije) i
nikako ne ilustruje koncepte bitne za funkcionisanje raunara. Mnogo znaa-
jnija je podela na osnovu koje raunar ine:

procesor tj. centralna procesorska jedinica (engl. Central Processing Unit,


CPU), koja obrauje podatke;
23 1. Raunarstvo i raunarski sistemi

glavna memorija (engl. main memory), u kojoj se istovremeno uvaju i


podaci koji se obrauju i trenutno pokrenuti programi (takoe zapisani
binarno, u obliku podataka);

razliiti periferijski ureaji ili ulazno-izlazne jedinice (engl. peripherals,


input-output devices, IO devices), kao to su mievi, tastature, ekrani,
tampai, diskovi, a koje slue za komunikaciju korisnika sa sistemom i
za trajno skladitenje podataka i programa.

5)
Sve nabrojane komponente meusobno su povezane i podaci se tokom rada

01
raunara prenose od jedne do druge. Veza izmeu komponenti uspostavlja se
hardverskim sklopovima koji se nazivaju magistrale (engl. bus). Magistrala
obuhvata provodnike koji povezuju ureaje, ali i ipove koji kontroliu protok

(2
podataka. Svi periferijski ureaji se sa memorijom, procesorom i magistralama
povezuju hardverskim sklopovima koji se nazivaju kontroleri . Matina ploa
(engl. motherboard) je tampana ploa na koju se prikljuuju procesor, mem-

je
orijski ipovi i svi periferijski ureaji. Na njoj se nalaze ipovi magistrale, a
danas i mnogi kontroleri periferijskih ureaja. Osnovu hardvera savremenih
an
raunara, dakle, ine sledee komponente:
d

Procesori. Procesor je jedna od dve centralne komponente svakog rau-


iz

narskog sistema fon Nojmanove arhitekture. Svi delovi procesora su danas


objedinjeni u zasebnu jedinicu (CPU) realizovanu na pojedinanom ipu
mikroprocesoru. Procesor se sastoji od kontrolne jedinice (engl. Control Unit)
o

koja upravlja njegovim radom i aritmetiko-logike jedinice (engl. Arithmetic


sk

Logic Unit) koja je zaduena za izvoenje aritmetikih operacija (sabiranje,


oduzimanje, mnoenje, poreenje, . . . ) i logikih operacija (konjunkcija, ne-
gacija, . . . ) nad brojevima. Procesor sadri i odreeni, manji broj, registara
n

koji privremeno mogu da uvaju podatke. Registri su obino fiksirane irine


ro

(8 bitova, 16 bitova, 32 bita, 64 bita). Komunikacija sa memorijom se ranije


vrila iskljuivo preko specijalizovanog registra koji se nazivao akumulator. Ar-
kt

itmetiko logika jedinica sprovodi operacije nad podacima koji su smeteni u


registrima i rezultate ponovo smeta u registre. Kontrolna jedinica procesora
le

ita instrukciju po instrukciju programa zapisanog u memoriji i na osnovu njih


odreuje sledeu akciju sistema (na primer, izvri prenos podataka iz proce-
E

sora na odreenu memorijsku adresu, izvri odreenu aritmetiku operaciju


nad sadrajem u registrima procesora, uporedi sadraje dva registra i ukoliko
su jednaki izvri instrukciju koja se nalazi na zadatoj memorijskoj adresi i
slino). Brzina procesora meri se u milionima operacija u sekundi (engl. Mil-
lion Instructions Per Second, MIPS) tj. poto su operacije u pokretnom zarezu
najzahtevnije, u broju operacija u pokretnom zarezu u sekundi (engl. FLoat-
ing Point Operations per Second, FLOPS). Dananji standardni procesori rade
oko 10 GFLOPS (deset milijardi operacija u pokretnom zarezu po sekundi).
Dananji procesori mogu da imaju i nekoliko jezgara (engl. core) koja istovre-
meno izvravaju instrukcije i time omoguuju tzv. paralelno izvravanje.
24 1. Raunarstvo i raunarski sistemi

CPU

Kontrolna Aritmetiko

jedinica logika jedinica

registri

magistrala

5)
01
Ulazni Izlazni
Memorija
ureaji ureaji

(2
Slika 1.9: Shema raunara fon Nojmanove arhitekture

je
an
Vane karakteristike procesora danas su broj jezgara (obino 1, 2 ili 4),
irina rei (obino 32 bita ili 64 bita) i radni takt (obino nekoliko gigaherca
(GHz)) vei radni takt obino omoguava izvravanje veeg broja operacija
d

u jedinici vremena.
iz

Memorijska hijerarhija. Druga centralna komponenta fon Nojmanove arhi-


o

tekture je glavna memorija u koju se skladite podaci i programi. Memorija je


sk

linearno ureeni niz registara (najee bajtova), pri emu svaki registar ima
svoju adresu. Kako se kod ove memorije sadraju moe pristupati u sluajnom
redosledu (bez unapred fiksiranog redosleda), ova memorija se esto naziva
n

i memorija sa slobodnim pristupom (engl. random access memory, RAM).


ro

Osnovni parametri memorija su kapacitet (danas obino meren gigabajtima


(GB)), vreme pristupa koje izraava vreme potrebno da se memorija pripremi
kt

za itanje odnosno upis podataka (danas obino mereno u nanosekundama


(ns)), kao i protok koji izraava koliinu podataka koji se prenose po jedinici
merenja (danas obino mereno u GBps).
le

U savremenim raunarskim sistemima, uz glavnu memoriju uspostavlja se


E

itava hijerarhija memorija koje slue da unaprede funkcionisanje sistema.


Memorije neposredno vezane za procesor koje se koriste iskljuivo dok je rau-
nar ukljuen nazivaju se unutranje memorije, dok se memorije koje se ko-
riste za skladitenje podataka u trenucima kada raunar nije ukljuen nazivaju
spoljne memorije . Procesor obino nema naina da direktno koristi podatke
koji se nalaze u spoljnim memorijama (jer su one znatno sporije od unutran-
jih), ve se pre upotrebe svi podaci prebacuju iz spoljnih u unutranju memo-
riju.
Memorijska hijerarhija predstavlja se piramidom. Od njenog vrha ka dnu
opadaju kvalitet i brzina memorija, ali zato se smanjuje i cena, pa se kapacitet
poveava.
25 1. Raunarstvo i raunarski sistemi

manja veliina/vea brzina vea veliina/manja brzina


via cena/manji kapacitet
CPU

pod napajanjem
registri

Ke memorija

RAM

5)
ROM/BIOS

01
nia cena/vei kapacitet

bez napajanja
USB diskovi

(2
hard diskovi

je
CD, DVD, Blu-ray, magnetne trake
an
Slika 1.10: Memorijska hijerarhija
d
iz

Registri procesora predstavljaju najbru memoriju jer se sve aritmetike i


o

logike operacije izvode upravo nad podacima koji se nalaze u njima.


Ke (engl. cache) je mala koliina brze memorije (nekoliko hiljada puta
sk

manjeg kapaciteta od glavne memorije; obino nekoliko megabajta) koja se


postavlja izmeu procesora i glavne memorije u cilju ubrzanja rada raunara.
n

Ke se uvodi jer su savremeni procesori postali znatno bri od glavnih mem-


ro

orija. Pre pristupa glavnoj memoriji procesor uvek prvo pristupa keu. Ako
traeni podatak tamo postoji, u pitanju je tzv. pogodak kea (engl. cache hit) i
kt

podatak se dostavlja procesoru. Ako se podatak ne nalazi u keu, u pitanju je


tzv. promaaj kea (engl. cache miss) i podatake se iz glavne memorije prenosi
u ke zajedno sa odreenim brojem podataka koji za njim slede (glavni fak-
le

tor brzine glavne memorije je njeno kanjenje i praktino je svejedno da li se


E

prenosi jedan ili vie podataka jer je vreme prenosa malog broja bajtova mnogo
manje od vremena kanjenja). Motivacija ovog pristupa je u tome to programi
esto pravilno pristupaju podacima (obino redom kojim su podaci smeteni u
memoriji), pa je velika verovatnoa da e se naredni traeni podaci i instrukcije
nai u ke-memoriji.
Glavna memorija uva sve podatke i programe koje procesor izvrava. Mali
deo glavne memorije ini ROM (engl. read only memory) nepromenljiva mem-
orija koja sadri osnovne programe koji slue za kontrolu odreenih kompone-
nata raunara (na primer, osnovni ulazno-izlazni sistem BIOS). Znatno vei
deo glavne memorije ini RAM privremena promenljiva memorija sa slobod-
nim pristupom. Terminoloki, podela glavne memorije na ROM i RAM nije
26 1. Raunarstvo i raunarski sistemi

najpogodnija jer ove vrste memorije nisu sutinski razliite nepromenljivi


deo (ROM) je takoe memorija sa slobodnim pristupom (RAM). Da bi RAM
memorija bila to bra, izrauje se uvek od poluprovodnikih (elektronskih)
elemenata. Danas se uglavnom realizuje kao sinhrona dinamika memorija
(SDRAM). To znai da se prenos podataka izmeu procesora i memorije vri
u intervalima odreenim otkucajima sistemskog sata (esto se u jednom otku-
caju izvri nekoliko prenosa). Dinamika memorija je znatno jeftinija i jednos-
tavnija, ali zato sporija od statike memorije od koje se obino gradi ke.

5)
Spoljne memorije uvaju podatke trajno, i kada raunar ostane bez elek-
trinog napajanja. Kao centralna spoljna skladita podataka uglavnom se ko-

01
riste hard diskovi (engl. hard disk) koji uvaju podatke korienjem magnetne
tehnologije, a u novije vreme se sve vie koriste i SSD ureaji (engl. solid state

(2
drive) koji uvaju podatke korienjem elektronskih tzv. fle memorija (engl.
flash memory). Kao prenosne spoljne memorije koriste se uglavnom USB fle-
memorije (izraene u slinoj tehnologiji kao i SSD) i optiki diskovi (CD, DVD,

je
Blu-ray).
an
Ulazni ureaji. Osnovni ulazni ureaji dananjih raunara su tastature i
mievi. Prenosni raunari imaju ugraenu tastaturu, a umesto mia moe se
d

koristiti tzv. taped (engl. touchpad). Tastature i mievi se sa raunarom


povezuju ili kablom (preko PS/2 ili USB prikljuaka) ili beino (najee ko-
iz

rienjem BlueTooth veze). Ovo su uglavnom standardizovani ureaji i nema


velikih razlika meu njima. Skeneri sliku sa papira prenose u raunar. Princip
o

rada je slian digitalnom fotografisanju, ali prilagoen slikanju papira.


sk

Izlazni ureaji. Osnovni izlazni ureaji savremenih raunara su monitori.


Danas dominiraju monitori tankog i ravnog ekrana (engl. flat panel display),
n

zasnovani obino na tehnologiji tenih kristala (engl. liquid crystal display,


ro

LCD) koji su osvetljeni pozadinskim LED osvetljenjem. Ipak, jo uvek su


ponegde u upotrebi i monitori sa katodnom cevi (engl. cathode ray tube, CRT).
kt

Grafiki kontroleri koji slue za kontrolu slike koja se prikazuje na monitoru


ili projektoru danas su obino integrisani na matinoj ploi, a ponekad su i na
le

istom ipu sa samim procesorom (engl. Accelerated Processing Unit, APU).


to se tehnologije tampe tie, danas su najzastupljeniji laserski tampai
E

i inkdet tampai (engl. inkjet). Laserski tampai su ee crno-beli, dok su


ink-det tampai obino u boji. Sve su dostupniji i 3D tampai.

1.5 Softver savremenih raunara


Softver ine raunarski programi i pratei podaci koji odreuju izrauna-
vanja koje vri raunar. Na prvim raunarima moglo je da se programira
samo na mainski zavisnim programskim jezicima na jezicima specifinim
za konkretnu mainu na kojoj program treba da se izvrava. Polovinom 1950-
ih nastali su prvi jezici vieg nivoa i oni su drastino olakali programiranje.
27 1. Raunarstvo i raunarski sistemi

Danas se programi obino piu u viim programskim jezicima a zatim prevode


na mainski jezik jezik razumljiv raunaru. Bez obzira na to kako je nastao,
da bi mogao da se izvri na raunaru, program mora da budu smeten u mem-
oriju u obliku binarno zapisanih podataka (tj. u obliku niza nula i jedinica)
koji opisuju instrukcije koje su neposredno podrane arhitekturom raunara.
U ovom poglavlju bie prikazani osnovni principa rada raunara kroz nekoliko
jednostavnih primera programa.

5)
1.5.1 Primeri opisa izraunavanja

01
Program specifikuje koje operacije treba izvriti da bi se reio neki zadatak.
Principi rada programa mogu se ilustrovati na primeru nekoliko jednostavnih

(2
izraunavanja i instrukcija koje ih opisuju. Ovi opisi izraunavanja dati su u
vidu prirodno-jezikog opisa ali direktno odgovaraju i programima na viim
programskim jezicima.

je
Kao prvi primer, razmotrimo izraunavanje vrednosti 2 + 3 za datu vred-
nost . U programiranju (slino kao i u matematici) podaci se predstavljaju
an
promenljivama. Meutim, promenljive u programiranju (za razliku od matem-
atike) vremenom mogu da menjaju svoju vrednost (tada kaemo da im se
dodeljuje nova vrednost). U programiranju, svakoj promenljivoj pridrueno je
d

(jedno, fiksirano) mesto u memoriji i tokom izvravanja programa promenljiva


iz

moe da menja svoju vrednost, tj. sadraj dodeljenog memorijskog prostora.


Ako je promenljiva ija je vrednost ulazni parametar oznaena sa , a promen-
ljiva ija je vrednost rezultat izraunavanja oznaena sa , onda se pomenuto
o

izraunavanje moe opisati sledeim jednostavnim opisom.


sk

y := 2*x + 3
n

Simbol * oznaava mnoenje, + sabiranje, a := oznaava da se promenljivoj


ro

sa njene leve strane dodeljuje vrednost izraza sa desne strane.


Kao naredni primer, razmotrimo odreivanje veeg od dva data broja. Ra-
kt

unari (tj. njihovi procesori) obino imaju instrukcije za poreenje brojeva, ali
odreivanje vrednosti veeg broja zahteva nekoliko koraka. Pretpostavimo da
le

promenljive i sadre dve brojevne vrednosti, a da promenljiva treba da


dobije vrednost vee od njih. Ovo izraunavanje moe da se izrazi sledeim
E

opisom.

ako je x >= y onda


m := x
inae
m := y

Kao malo komplikovaniji primer razmotrimo stepenovanje. Procesori skoro


uvek podravaju instrukcije kojima se izraunava zbir i proizvod dva cela broja,
ali stepenovanje obino nije podrano kao elementarna operacija. Sloenije
28 1. Raunarstvo i raunarski sistemi

operacije se mogu ostvariti korienjem jednostavnijih. Na primer, -ti ste-


pen broja (tj. vrednost ) mogue je izraunati uzastopnom primenom
mnoenja: ako se krene od broja 1 i puta sa pomnoi brojem , rezultat
e biti . Da bi moglo da se osigura da e mnoenje biti izvreno tano
puta, koristi se brojaka promenljiva koja na poetku dobija vrednost 0, a
zatim se, prilikom svakog mnoenja, uveava sve dok ne dostigne vrednost .
Ovaj postupak moemo predstaviti sledeim opisom.

5)
s := 1, i := 0
dok je i < n radi sledee:

01
s := sx, i := i+1

Kada se ovaj postupak primeni na vrednosti = 3 i = 2, izvodi se naredni

(2
niz koraka.
s := 1, i := 0, poto je i(=0) manje od
n(=2), vre se dalje ope-

je
racije
s := sx = 13 = 3, i := i+1 = 0+1 = 1, poto je i(=1) manje od
an
n(=2), vre se dalje ope-
racije
s := sx = 33 = 9, i := i+1 = 1+1 = 2, poto i(=2) nije manje od
d

n(=2), ne vre se dalje


iz

operacije.
o

1.5.2 Mainski programi


sk

Mainski programi su neposredno vezani za procesor raunara na kojem se


koriste procesor je konstruisan tako da moe da izvrava odreene elemen-
n

tarne naredbe. Ipak, razvoj najveeg broja procesora usmeren je tako da se


ro

isti mainski programi mogu koristiti na itavim familijama procesora.


Primitivne instrukcije koje podrava procesor su veoma malobrojne i jed-
kt

nostavne (na primer, postoje samo instrukcije za sabiranje dva broja, kon-
junkcija bitova, instrukcija skoka i slino) i nije lako kompleksne i apstraktne
le

algoritme izraziti korienjem tog uskog skupa elementarnih instrukcija. Ipak,


svi zadaci koje raunari izvravaju svode se na ove primitivne instrukcije.
E

Asemblerski jezici. Asemblerski (ili simboliki) jezici su jezici koji su veoma


bliski mainskom jeziku raunara, ali se, umesto korienja binarnog sadraja
za zapisivanje instrukcija koriste (mnemotehnike, lako pamtljive) simbolike
oznake instrukcija (tj. programi se unose kao tekst). Ovim se, tehniki, olakava
unos programa i programiranje (programer ne mora da direktno manipulie bi-
narnim sadrajem), pri emu su sve mane mainski zavisnog programiranja i
dalje prisutne. Kako bi ovako napisan program mogao da se izvrava, neop-
hodno je izvriti njegovo prevoenje na mainski jezik (tj. zapisati instrukcije
binarnom azbukom) i uneti na odgovarajue mesto u memoriji. Ovo prevoenje
29 1. Raunarstvo i raunarski sistemi

je jednostavno i jednoznano i vre ga jeziki procesori koji se nazivaju asem-


bleri .
Sva izraunavanja u primerima iz poglavlja 1.5.1 su opisana neformalno,
kao uputstva oveku a ne raunaru. Da bi se ovako opisana izraunavanja
mogla sprovesti na nekom raunaru fon Nojmanove arhitekture neophodno je
opisati ih preciznije. Svaka elementarna operacija koju procesor moe da izvri
u okviru programa zadaje se procesorskom instrukcijom svaka instrukcija
instruie procesor da izvri odreenu operaciju. Svaki procesor podrava un-

5)
apred fiksiran, konaan skup instrukcija (engl. instruction set). Svaki program
raunara predstavljen je nizom instrukcija i skladiti se u memoriji raunara.

01
Naravno, raunari se razlikuju (na primer, po tome koliko registara u proce-
soru imaju, koje instrukcije moe da izvri njihova aritmetiko-logika jedinica,

(2
koliko memorije postoji na raunaru, itd). Meutim, da bi se objasnili osnovni
principi rada raunara nije neophodno razmatrati neki konkretan raunar, ve
se moe razmatrati neki hipotetiki raunar. Pretpostavimo da procesor sadri

je
tri registra oznaena sa ax, bx i cx i jo nekoliko izdvojenih bitova (tzv. zas-
tavica). Dalje, pretpostavimo da procesor moe da izvrava naredne aritmetike
an
instrukcije (zapisane ovde u asemblerskom obliku):

Instrukcija add ax, bx oznaava operaciju sabiranja vrednosti brojeva


d

koji se nalaze u registrima ax i bx, pri emu se rezultat sabiranja smeta


iz

u registar ax. Operacija add moe se primeniti na bilo koja dva registra.
Instrukcija mul ax, bx oznaava operaciju mnoenja vrednosti brojeva
o

koji se nalaze u registrima ax i bx, pri emu se rezultat mnoenja smeta


sk

u registar ax. Operacija mul moe se primeniti na bilo koja dva registra.
Instrukcija cmp ax, bx oznaava operaciju poreenja vrednosti brojeva
n

koji se nalaze u registrima ax i bx i rezultat pamti postavljanjem zastavice


ro

u procesoru. Operacija cmp se moe primeniti na bilo koja dva registra.


kt

Program raunara je niz instrukcija koje se obino izvravaju redom, jedna


za drugom. Meutim, poto se javlja potreba da se neke instrukcije ponove vei
broj puta ili da se odreene instrukcije preskoe, uvode se instrukcije skoka.
le

Da bi se moglo specifikovati na koju instrukciju se vri skok, uvode se labele


E

oznaena mesta u programu. Pretpostavimo da na procesor moe da izvrava


sledee dve vrste skokova (bezuslovne i uslovne):

Instrukcija jmp label, gde je label neka labela u programu, oznaava


bezuslovni skok koji uzrokuje nastavak izvravanja programa od mesta u
programu oznaenog navedenom labelom.

Uslovni skokovi prouzrokuju nastavak izvravanja programa od instruk-


cije oznaene navedenom labelom, ali samo ako je neki uslov ispunjen.
Ukoliko uslov nije ispunjen, izvrava se naredna instrukcija. U nas-
tavku e se razmatrati samo instrukcija jge label, koja uzrokuje uslovni
30 1. Raunarstvo i raunarski sistemi

skok na mesto oznaeno labelom label ukoliko je vrednost prethodnog


poreenja brojeva bila vee ili jednako.

Tokom izvravanja programa, podaci se nalaze u memoriji i u registrima


procesora. S obzirom na to da procesor sve operacije moe da izvri iskljuivo
nad podacima koji se nalaze u njegovim registrima, svaki procesor podrava
i instrukcije prenosa podataka izmeu memorije i registara procesora (kao i
izmeu samih registara). Pretpostavimo da na procesor podrava sledeu

5)
instrukciju ove vrste.

01
Instrukcija mov oznaava operaciju prenosa podataka i ima dva parame-
tra prvi odreuje gde se podaci prenose, a drugi koji odreuje koji
se podaci prenose. Parametar moe biti ime registra (to oznaava da

(2
se pristupa podacima u odreenom registru), broj u zagradama (to oz-
naava da se pristupa podacima u memoriji i to na adresi odreenoj
brojem u zagradama) ili samo broj (to oznaava da je podatak ba taj

je
navedeni broj). Na primer, instrukcija mov ax bx oznaava da se sadraj
bx prepisuje u registar ax, instrukcija mov ax, [10] oznaava da
an
registra
se sadraj iz memorije sa adrese 10 prepisuje u registar ax, instrukcija
mov ax, 1 oznaava da se u registar ax upisuje vrednost 1, dok instruk-
d

cija oznaava mov [10], ax da se sadraj registra ax upisuje u memoriju


na adresu 10.
iz

Sa ovakvim procesorom na raspolaganju, izraunavanje vrednosti 2 + 3


o

moe se ostvariti na sledei nain. Pretpostavimo da se ulazni podatak (broj


) nalazi u glavnoj memoriji i to na adresi 10, a da rezultat treba smestiti na
sk

adresu 11 (ovo su sasvim proizvoljno odabrane adrese). Izraunavanje se onda


moe opisati sledeim programom (nizom instrukcija).
n

mov ax, [10]


ro

mov bx, 2
mul ax, bx
kt

mov bx, 3
add ax, bx
le

mov [11], ax
E

mov ax, [10] prepisuje vrednost promenljive (iz memorije sa


Instrukcija
adrese 10) ax. Instrukcija mov bx, 2 upisuje vrednost 2 u registar
u registar
bx. Nakon instrukcije mul ax, bx vri se mnoenje i registar ax sadri vrednost
2. Instrukcija mov bx, 3 upisuje vrednost 3 u registar bx, nakon instrukcije
add ax, bx se vri sabiranje i u registru ax se nalazi traena vrednost 2 + 3.
Na kraju se ta vrednost instrukcijom mov [11], ax upisuje u memoriju na
adresu 11.
Odreivanje veeg od dva broja moe se ostvariti na sledei nain. Pret-
postavimo da se ulazni podaci nalaze u glavnoj memoriji i to broj na adresi
31 1. Raunarstvo i raunarski sistemi

10, broj na adresi 11, dok rezultat treba smestiti na adresu 12. Program
(niz instrukcija) kojima moe da se odredi maksimum je sledei:

mov ax, [10]


mov bx, [11]
cmp ax, bx
jge vecix
mov [12], bx

5)
jmp kraj
vecix:

01
mov[12], ax
kraj:

(2
Nakon prenosa vrednosti oba broja u registre procesora (instrukcijama mov
ax, [10] i mov bx, [11]), vri se njihovo poreenje (instrukcija cmp ax, bx).

je
Ukoliko je broj vei od ili jednak broju prelazi se na mesto oznaeno labe-
lom vecix (instrukcijom jge vecix) i na mesto rezultata upisuje se vrednost
promenljive (instrukcijom mov[12], ax). Ukoliko uslov skoka jge nije is-
an
punjen (ako nije vee ili jednako ), na mesto rezultata upisuje se vrednost
promenljive (instrukcijom mov [12], bx) i bezuslovno se skae na kraj pro-
d

grama (instrukcijom jmp kraj) (da bi se preskoilo izvravanje instrukcije koja


iz

na mesto rezultata upisuje vrednost promenljive ).


Izraunavanje stepena moe se ostvariti na sledei nain. Pretpostavimo da
se ulazni podaci nalaze u glavnoj memoriji i to broj na adresi 10, a broj
o

na adresi 11, i da konaan rezultat treba da bude smeten u memoriju i to na


sk

adresu 12. Pretpostavimo da e pomone promenljive i koje se koriste u


postupku biti smetene sve vreme u procesoru, i to promenljiva u registru
n

ax, a promenljiva u registru bx. Poto postoji jo samo jedan registar (cx), u
njega e naizmenino biti smetane vrednosti promenljivih i , kao i konstanta
ro

1 koja se sabira sa promenljivom . Niz instrukcija kojim opisani hipotetiki


raunar moe da izrauna stepen je sledei:
kt

mov ax, 1
mov bx, 0
le

petlja:
E

mov cx, [11]


cmp bx, cx
jge kraj
mov cx, [10]
mul ax, cx
mov cx, 1
add bx, cx
jmp petlja
kraj:
mov [12], ax
32 1. Raunarstvo i raunarski sistemi

Ilustrujmo izvravanje ovog programa na izraunavanju vrednosti 32 . Inici-


jalna konfiguracija je takva da se na adresi 10 u memoriji nalazi vrednost = 3,
na adresi 11 vrednost = 2. Poetna konfiguracija (tj. vrednosti memorijskih
lokacija i registara) moe da se predstavi na sledei nain:

10: 3 ax: ?
11: 2 bx: ?
12: ? cx: ?

5)
Nakon izvravanja prve dve instrukcije (mov ax, 1 i mov bx, 0), postavlja

01
se vrednost registara ax i bx i prelazi se u sledeu konfiguraciju:

10: 3 ax: 1

(2
11: 2 bx: 0
12: ? cx: ?

je
Sledea instrukcija (mov cx, [11]) kopira vrednost 2 sa adrese 11 u regis-
tar cx:
an
10: 3 ax: 1
11: 2 bx: 0
d

12: ? cx: 2
iz

bx (cmp bx, cx) i kako uslov skoka (jge kraj)


Vri se poreenje sa registrom
nije ispunjen (vrednost 0 u bx 2 u cx), nas-
nije vea ili jednaka od vrednosti
tavlja se dalje. Nakon kopiranja vrednosti 3 sa adrese 10 u registar cx (in-
o

strukcijom mov cx, [10]), vri se mnoenje vrednosti u registrima ax i cx


sk

(instrukcijom mul ax, cx) i dolazi se u sledeu konfiguraciju:


n

10: 3 ax: 3
11: 2 bx: 0
ro

12: ? cx: 3
kt

Nakon toga, u cx se upisuje 1 (instrukcijom mov cx, 1) i vri se sabiranje


vrednosti registara bx i cx (instrukcijom add bx, cx) ime se vrednost u reg-
le

istru bx uveava za 1.

10: 3 ax: 3
E

11: 2 bx: 1
12: ? cx: 1

Bezuslovni skok (jmp petlja) ponovo vraa kontrolu na poetak petlje,


nakon ega se u cx 2 sa adrese 11 (mov cx, [11]).
opet prepisuje vrednost
Vri se poreenje sa registrom bx (cmp bx, cx) i kako uslov skoka (jge kraj)
nije ispunjen (vrednost 1 u bx nije vea ili jednaka vrednosti 2 u cx), nastavlja
se dalje. Nakon jo jednog mnoenja i sabiranja dolazi se do konfiguracije:
33 1. Raunarstvo i raunarski sistemi

10: 3 ax: 9
11: 2 bx: 2
12: ? cx: 1

Bezuslovni skok ponovo vraa kontrolu na poetak petlje, nakon ega se


u cx opet prepisuje vrednost 2 sa adrese 11. Vri se poreenje sa registrom
bx, no, ovaj put je uslov skoka ispunjen (vrednost 2 u bx je vea ili jednaka
vrednosti 2 u cx) i skae se na mesto oznaeno labelom kraj, gde se posled-

5)
njom instrukcijom (mov [12], ax) konana vrednost iz registra ax kopira u
memoriju na dogovorenu adresu 12, ime se stie u zavrnu konfiguraciju:

01
10: 3 ax: 9
11: 2 bx: 2

(2
12: 9 cx: 1

je
Mainski jezik. Fon Nojmanova arhitektura podrazumeva da se i sam pro-
an
gram (niz instrukcija) nalazi u glavnoj memoriji prilikom njegovog izvravanja.
Potrebno je svaki program (poput tri navedena) predstaviti nizom nula i je-
dinica, na nain razumljiv procesoru na mainskim jeziku. Na primer,
d

mogue je da su binarni kdovi za instrukcije uvedeni na sledei nain:


mov 001
iz

add 010
mul 011
o

cmp 100
sk

jge 101
jmp 110
n

Takoe, poto neke instrukcije primaju podatke razliite vrste (neposredno


navedeni brojevi, registri, apsolutne memorijske adrese), uvedeni su posebni
ro

kdovi za svaki od razliitih vidova adresiranja. Na primer:


neposredno 00
kt

registarsko 01
apsolutno 10
ax 00, bx 01,
le

Pretpostavimo da registar ima oznaku registar ima oznaku a


registar cx oznaku 10. Pretpostavimo i da su sve adrese osmobitne. Pod nave-
E

denim pretpostavkama, instrukcija mov [10], ax se, u ovom hipotetikom


mainskom jeziku, moe kodirati kao 001 10 01 00010000 00. Kd 001 dolazi
od instrukcije mov, zatim slede 10 i 01 koji ukazuju da prvi argument pred-
stavlja memorijsku adresu, a drugi oznaku registra, za im sledi memorijska
adresa (10)16 binarno kodirana sa 00010000 i na kraju oznaka 00 registra ax.
Na slian nain, celokupan prikazani mainski kd navedenog asemblerskog
programa koji izraunava 2 + 3 je mogue binarno kodirati kao:

001 01 10 00 00010000 // mov ax, [10]


001 01 00 01 00000010 // mov bx, 2
011 00 01 // mul ax, bx
34 1. Raunarstvo i raunarski sistemi

001 01 00 01 00000011 // mov bx, 3


010 00 01 // add ax, bx
001 10 01 00010001 00 // mov [11], ax

Izmeu prikazanog asemblerskog i mainskog programa postoji veoma direk-


tna i jednoznana korespondencija (u oba smera) tj. na osnovu datog mainskog
kda mogue je jednoznano rekonstruisati asemblerski kd.
Specifini hardver koji ini kontrolnu jedinicu procesora dekodira jednu po

5)
jednu instrukciju i izvrava akciju zadatu tom instrukcijom. Kod realnih proce-
sora, broj instrukcija i naini adresiranja su mnogo bogatiji a prilikom pisanja

01
programa potrebno je uzeti u obzir mnoge aspekte na koje se u navedenim
jednostavnim primerima nije obraala panja. Ipak, mainske i asemblerske

(2
instrukcije stvarnih procesora veoma su sline navedenim hipotetikim instruk-
cijama.

je
1.5.3 Klasifikacija savremenog softvera
an
Raunarski programi veoma su sloeni. Hardver raunara sainjen je od
elektronskih kola koja mogu da izvre samo elementarne operacije i, da bi rau-
nar mogao da obavi i najjednostavniji zadatak zanimljiv korisniku, neophodno
d

je da se taj zadatak razloi na mnotvo elementarnih operacija. Napredak


iz

raunara ne bi bio mogu ako bi programeri morali svaki program da opisuju


i razlau do krajnjeg nivoa elementarnih instrukcija. Zato je poeljno da pro-
grameri naredbe raunaru mogu zadavati na to apstraktnijem nivou. Rau-
o

narski sistemi i softver se grade slojevito i svaki naredni sloj oslanja se na


sk

funkcionalnost koju mu nudi sloj ispod njega. U skladu sa tim, softver savre-
menih raunara se obino deli na aplikativni i sistemski . Osnovni zadatak
n

sistemskog softvera je da posreduje izmeu hardvera i aplikativnog softvera


ro

koji krajnji korisnici koriste. Granica izmeu sistemskog i aplikativnog soft-


vera nije kruta i postoje programi za koje se moe smatrati da pripadaju obema
grupama (na primer, editori teksta).
kt

Aplikativni softver. Aplikativni softver je softver koji krajnji korisnici rau-


le

nara direktno koriste u svojim svakodnevnim aktivnostima. To su pre svega


E

pregledai Veba, zatim klijenti elektronske pote, kancelarijski softver (pro-


grami za kucanje teksta, izradu slajd-prezentacija, tabelarna izraunavanja),
video igre, multimedijalni softver (programi za reprodukciju i obradu slika,
zvuka i video-sadraja) itd.

Sistemski softver. je softver ija je uloga da kontrolie hardver i prua


usluge aplikativnom softveru. Najznaajniji skup sistemskog softvera, danas
prisutan na skoro svim raunarima, ini operativni sistem (OS). Pored OS,
sistemski softver sainjavaju i razliiti usluni programi : editori teksta, alat za
programiranje (prevodioci, dibageri, profajleri, integrisana okruenja) i slino.
35 1. Raunarstvo i raunarski sistemi

Korisnici OS esto identifikuju sa izgledom ekrana tj. sa programom koji


koriste da bi pokrenuli svoje aplikacije i organizovali dokumente. Meutim,
ovaj deo sistema koji se naziva korisniki interfejs (engl. user interface UI)
ili koljka (engl. shell) samo je tanak sloj na vrhu operativnog sistema i OS je
mnogo vie od onoga to krajnji korisnici vide. Najvei i najznaajni deo OS
naziva se jezgro (engl. kernel). Osim to kontrolie i apstrahuje hardver, op-
erativni sistem tj. njegovo jezgro sinhronizuje rad vie programa, rasporeuje
procesorsko vreme i memoriju, brine o sistemu datoteka na spoljanjim mem-

5)
orijama itd. Najznaanjni operativni sistemi danas su Microsoft Windows,
sistemi zasnovani na Linux jezgru (na primer, Ubuntu, RedHat, Fedora, Suse)

01
i Mac OS X.
OS upravlja svim resursima raunara (procesorom, memorijom, periferi-

(2
jskim ureajima) i stavlja ih na raspolaganje aplikativnim programima. OS je
u veoma tesnoj vezi sa hardverom raunara i veliki deo zadataka se izrvrava uz
direktnu podrku specijalizovanog hardvera namenjenog iskljuivo izvravanju

je
OS. Nekada se hardver i operativni sistem smatraju jedinstvenom celinom i
umesto podele na hardver i softver razmatra se podela na sistem (hardver i
an
OS) i na aplikativni softver.
Programer ne bi trebalo da misli o konkretnim detaljima hardvera, tj. po-
eljno je da postoji odreena apstrakcija hardvera. Na primer, mnogo je pogod-
d

nije ako programer umesto da mora da kae Neka se zavrti ploa diska, neka
iz

se glava pozicionira na odreenu poziciju, neka se zatim tu upie odreeni bajt


itd. moe da kae Neka se u datu datoteku na disku upie odreeni tekst.
OS je taj koji se brine o svim detaljima, dok se programer (tanije, aplikacije
o

koje on isprogramira), kada god mu je potrebno obraa sistemu da mu tu


sk

uslugu prui. Konkretni detalji hardvera poznati su u okviru operativnog sis-


tema i komande koje programer zadaje izvravaju se uzimajui u obzir ove
n

specifinosti. Operativni sistem, dakle, programeru prua skup funkcija koje


ro

on moe da koristi da bi postigao eljenu funkcionalnost hardvera, sakrivajui


pritom konkretne hardverske detalje. Ovaj skup funkcija naziva se program-
ski interfejs za pisanje aplikacija
24 (engl. Aplication Programming Interface,
kt

API ). Funkcije se nazivaju i sistemski pozivi (jer se OS poziva da izvri odreeni


zadatak). Programer nema mogunost direktnog pristupa hardveru i jedini
le

nain da se pristupi hardveru je preko sistemskih poziva. Ovim se osigurava


odreena bezbednost celog sistema.
E

Postoji vie nivoa na kojima se moe realizovati neka funkcionalnost. Pro-


gramer aplikacije je na vrhu hijerarhije i on moe da koristi funkcionalnost
koju mu prua programski jezik koji koristi i biblioteke tog jezika. Izvrni
programi esto koriste funkcionalnost specijalne rantajm biblioteke (engl. run-
time library) koja koristi funkcionalnost operativnog sistema (preko sistemskih
poziva), a zatim operativni sistem koristi funkcionalnost samog hardvera.

24 Ovaj termin se ne koristi samo u okviru operativnih sistema, ve i u irem kontekstu,


da oznai skup funkcija kroz koji jedan programski sistem koristi drugi programski sistem.
36 1. Raunarstvo i raunarski sistemi

Pitanja za vebu
Pitanje 1.1. Nabrojati osnovne periode u razvoju raunara i navesti njihove
osnovne karakteristike i predstavnike.

Pitanje 1.2. Ko je i u kom veku konstruisao prvu mehaniku spravu na kojoj


je bilo mogue sabirati prirodne brojeve, a ko je i u kom veku konstruisao prvu
mehaniku spravu na kojoj je bilo mogue sabirati i mnoiti prirodne brojeve?

5)
Pitanje 1.3. Kojoj spravi koja se koristi u dananjem svetu najvie odgovaraju
Paskalove i Lajbnicove sprave?

01
Pitanje 1.4. Kakva je veza izmeu tkakih razboja i raunara s poetka XIX

(2
veka?

Pitanje 1.5. Koji je znaaj arlsa Bebida za razvoj raunarstva i programi-


ranja? U kom veku je on dizajnirao svoje raunske maine? Kako se one

je
zovu i koja od njih je trebalo da bude programabilna? Ko se smatra prvim
programerom?
an
Pitanje 1.6. Na koji nain je Herman Holerit doprineo izvravanju popisa
d

stanovnika u SAD 1890? Kako su bili uvani podaci sa tog popisa? Koja
uvena kompanija je nastala iz kompanije koju je Holerit osnovao?
iz

Pitanje 1.7. Kada su nastali prvi elektronski raunari? Nabrojati nekoliko


najznaajnijih.
o

Pitanje 1.8.
sk

Na koji nain je programiran raunar ENIAC, a na koji raunar


EDVAC?
n

Pitanje 1.9. Koje su osnovne komponente raunara fon Nojmanove arhitek-


ro

ture? ta se skladiti u memoriju raunara fon Nojmanove arhitekture? Gde se


vri obrada podataka u okviru raunara fon Nojmanove arhitekture? Od kada
kt

su raunari zasnovani na fon Nojmanovoj arhitekturi?

Pitanje 1.10. ta su to raunari sa skladitenim programom? ta je to hard-


le

ver a ta softver?
E

Pitanje 1.11. ta su procesorske instrukcije? Navesti nekoliko primera.

Pitanje 1.12. Koji su uobiajeni delovi procesora? Da li se u okviru samog


procesora nalazi odreena koliina memorije za smetanje podataka? Kako se
ona naziva?

Pitanje 1.13. Ukratko opisati osnovne elektronske komponente svake gen-


eracije raunara savremenih elektronskih raunara? ta su bile osnovne elek-
tronske komponente prve generacije elektronskih raunara? Od koje generacije
raunara se koriste mikroprocesori? Koji tipovi raunara se koriste u okviru
III generacije?
37 1. Raunarstvo i raunarski sistemi

Pitanje 1.14. U kojoj deceniji dolazi do pojave raunara za kunu upotrebu?


Koji je najprodavaniji model kompanije Commodore? Da li je IBM proizvo-
dio raunare za kunu upotrebu? Koji komercijalni kuni raunar prvi uvodi
grafiki korisniki interfejs i mia?

Pitanje 1.15. Koja serija Intelovih procesora je bila dominantna u PC rau-


narima 1980-ih i 1990-ih godina?

Pitanje 1.16.

5)
ta je to tehnoloka konvergencija? ta su to tableti, a ta
pametni telefoni?

01
Pitanje 1.17. Koje su osnovne komponente savremenog raunara? ta je
memorijska hijerarhija? Zato se uvodi ke-memorija? Koje su danas najko-

(2
rienije spoljne memorije?

Pitanje 1.18. U koju grupu jezika spadaju mainski jezici i asemblerski jezici?

Pitanje 1.19.

je
Da li je kd na nekom mainskom jeziku prenosiv sa jednog na
sve druge raunare? Da li asembler zavisi od maine na kojoj se koristi?
an
Pitanje 1.20. Ukoliko je raspoloiv asemblerski kd nekog programa, da li
je mogue jednoznano konstruisati odgovarajui mainski kd? Ukoliko je
d

raspoloiv mainski kd nekog programa, da li je mogue jednoznano konstru-


iz

isati odgovarajui asemblerski kd?

Zadatak 1.1. Na opisanom asemblerskom jeziku opisati izraunavanje vred-


x := x*y + y + 3.
o

nosti izraza Generisati i mainski kd za napisani pro-


gram.
sk

Zadatak 1.2. Na opisanom asemblerskom jeziku opisati izraunavanje:


n

ako je (x < 0)
ro

y := 3*x;
inace
kt

x := 3*y;
Zadatak 1.3. Na opisanom asemblerskom jeziku opisati izraunavanje:
le

dok je (x <= 0) radi


E

x := x + 2*y + 3;
Zadatak 1.4.
Na opisanom asemblerskom jeziku opisati izraunavanje kojim
se izraunava [ ], pri emu se nalazi na adresi 100, a rezultat smeta na

adresu 200.

Pitanje 1.21. Koji su osnovni razlozi slojevite organizacije softvera? ta je


sistemski, a ta aplikativni softver?

Pitanje 1.22. Koji su osnovni zadaci operativnog sistema? ta su sistemski


pozivi? Koji su operativni sistemi danas najkorieniji?
Glava 2

5)
Reprezentacija podataka u

01
raunarima

(2
je
an
Dananji raunari su digitalni . To znai da su svi podaci koji su u njima
zapisani, zapisani kao nizovi celih brojeva. Dekadni brojevni sistem koji ljudi
koriste u svakodnevnom ivotu nije pogodan za zapis brojeva u raunarima jer
d

zahteva azbuku od 10 razliitih simbola (cifara). Bilo da se radi o elektronskim,


iz

magnetnim ili optikim komponentama, tehnologija izrade raunara i medijuma


za zapis podataka koristi elemente koji imaju dva diskretna stanja, to za
zapis podataka daje azbuku od samo dva razliita simbola. Tako, na primer,
o

ukoliko izmeu dve take postoji napon vii od odreenog praga, onda se smatra
sk

da tom paru taaka odgovara vrednost 1, a inae mu odgovara vrednost 0.


Takoe, polje hard diska moe biti ili namagnetisano to odgovara vrednosti 1
n

ili razmagnetisano to odgovara vrednosti 0. Slino, laserski zrak na povrini


ro

kompakt diska bui rupice kojim je odreen zapis podataka pa polje koje
nije izbueno predstavlja vrednost 0, a ono koje jeste izbuesno predstavlja
vrednost 1. U nastavku e biti pokazano da je azbuka od samo dva simbola
kt

dovoljna za zapisivanje svih vrsta brojeva, pa samim tim i za zapisivanje svih


vrsta digitalnih podataka.
le

2.1 Analogni i digitalni podaci i digitalni raunari


E

Kontinualna priroda signala. Veina podataka koje raunari koriste nas-


taje zapisivanjem prirodnih signala. Najznaajniji primeri signala su zvuk i
slika, ali se pod signalima podrazumevaju i ultrazvuni signali, EKG signali,
zraenja razliite vrste itd.
Signali koji nas okruuju u prirodi u veini sluajeva se prirodno mogu pred-
staviti neprekidnim funkcijama. Na primer, zvuni signal predstavlja promenu
pritiska vazduha u zadatoj taki i to kao neprekidnu funkciju vremena. Slika se
moe opisati intenzitetom svetlosti odreene boje (tj. odreene talasne duine)
u datom vremenskom trenutku i to kao neprekidna funkcija prostora.

38
39 2. Reprezentacija podataka u raunarima

Analogni zapis. Osnovna tehnika koja se primenjuje kod analognog zapisa


signala je da se kontinualne promene signala koji se zapisuje opiu kontinualnim
promenama odreenog svojstva medijuma na kojem se signal zapisuje. Tako,
na primer, promene pritiska vazduha koji predstavlja zvuni signal direktno
odgovaraju promenama nivoa namagnetisanja na magnetnoj traci na kojoj se
zvuk analogno zapisuje. Koliina boje na papiru direktno odgovara intenzitetu
svetlosti u vremenskom trenutku kada je fotografija bila snimljena. Dakle,
analogni zapis uspostavlja analogiju izmeu signala koji je zapisan i odreenog

5)
svojstva medijuma na kome je signal zapisan.
Osnovna prednost analogne tehnologije je da je ona obino veoma jednos-

01
tavna ukoliko se zadovoljimo relativno niskim kvalitetom (jo su drevni narodi
mogli da naprave nekakav zapis zvuka uz pomo jednostavne igle prikaene na

(2
trepereu membranu).
Osnovni problem analogne tehnologije je to je izrazito teko na medijumu
napraviti veran zapis signala koji se zapisuje i izrazito je teko napraviti dva

je
identina zapisa istog signala. Takoe, problem predstavlja i inherentna nestal-
nost medijuma, njegova promenljivost tokom vremena i podlonost spoljanjim
an
uticajima. S obzirom na to da varijacije medijuma direktno dovode do vari-
jacije zapisanog signala, vremenom neizbeno dolazi do pada kvaliteta analogno
zapisanog signala. Obrada analogno zapisanih signala je obino veoma komp-
d

likovana i za svaku vrstu obrade signala, potrebno je da postoji ureaj koji je


iz

specijalizovan za tu vrste obrade.

Digitalni zapis.
o

Osnovna tehnika koja se koristi kod digitalnog zapisa po-


dataka je da se vrednost signala izmeri u odreenim vremenskim trenucima ili
sk

odreenim takama prostora i da se onda na medijumu zapiu izmerene vred-


nosti. Ovim je svaki digitalno zapisani signal predstavljen nizom brojeva koji
n

se nazivaju odbirci ili semplovi (engl. sample) . Svaki od brojeva predstavlja


ro

vrednost signala u jednoj taki diskretizovanog domena. S obzirom na to da


izmerene vrednosti takoe pripadaju kontinualnoj skali, neophodno je izvriti i
kt

diskretizaciju kodomena, odnosno dopustiti zapisivanje samo odreenog broja


nivoa razliitih vrednosti.
Digitalni zapis predstavlja diskretnu aproksimaciju polaznog signala. Vano
le

pitanje je koliko esto je potrebno vriti merenje da bi se polazni kontinu-


E

alni signal mogao verno rekonstruisati. Odgovor daje tvrenje o odabiranju


(tzv. Najkvist-enonova teorema), koje kae da je signal dovoljno meriti dva
puta ee od najvie frekvencije koja sa u njemu javlja. Na primer, poto
ovekovo uho uje frekvencije do 20KHz, dovoljno je da frekvencija odabiranja
(sempliranja) bude 40KHz. Dok je za analogne tehnologije za postizanje vi-
sokog kvaliteta zapisa potrebno imati medijume visokog kvaliteta, kvalitet re-
produkcije digitalnog zapisa ne zavisi od toga kakav je kvalitet medija na kome
su podaci zapisani, sve dok je medijum dovoljnog kvaliteta da se zapisani bro-
jevi mogu razaznati. Dodatno, kvarljivost koja je inherentna za sve medije
postaje nebitna. Na primer, papir vremenom uti to uzrokuje pad kvaliteta
analognih fotografija tokom vremena. Meutim, ukoliko bi papir sadrao za-
40 2. Reprezentacija podataka u raunarima

5)
01
0

(2
Slika 2.1: Digitalizacija zvunog signala

je
pis brojeva koji predstavljaju vrednosti boja u takama digitalno zapisane fo-
an
tografije, injenica da papir uti ne bi predstavljala problem dok god se brojevi
mogu razaznati.
Digitalni zapis omoguava kreiranje apsolutno identinih kopija to dalje
d

omoguava prenos podataka na daljinu. Na primer, ukoliko izvrimo fotokopi-


iz

ranje fotografije, napravljena fotokopija je daleko loijeg kvaliteta od originala.


Meutim, ukoliko umnoimo CD na kojem su zapisani brojevi koji ine za-
pis neke fotografije, kvalitet slike ostaje apsolutno isti. Ukoliko bi se dva CD-a
o

pregledala pod mikroskopom, oni bi izgledali delimino razliito, ali to ne pred-


sk

stavlja problem sve dok se brojevi koji su na njima zapisani mogu razaznati.
Obrada digitalno zapisanih podataka se svodi na matematiku manipulaciju
n

brojevima i ne zahteva (za razliku od analognih podataka) korienje specijal-


ro

izovanih maina.
Osnovni problem implementacije digitalnog zapisa predstavlja injenica da
je neophodno imati veoma razvijenu tehnologiju da bi se uopte stiglo do iole
kt

upotrebljivog zapisa. Na primer, izuzetno je komplikovano napraviti ureaj


koji je u stanju da 40 hiljada puta izvri merenje intenziteta zvuka. Jedna
le

sekunda zvuka se predstavlja sa 40 hiljada brojeva, za iji je zapis neophodna


gotovo cela jedna sveska. Ovo je osnovni razlog zbog ega se digitalni zapis
E

istorijski javio kasno. Kada se dolo do tehnolokog nivoa koji omoguava


digitalni zapis, on je doneo mnoge prednosti u odnosu na analogni.

2.2 Zapis brojeva


Proces digitalizacije je proces reprezentovanja (raznovrsnih) podataka bro-
jevima. Kako se svi podaci u raunarima reprezentuju na taj nain bro-
jevima, neophodno je precizno definisati zapisivanje razliitih vrsta brojeva.
Osnovu digitalnih raunara, u skladu sa njihovom tehnolokom osnovom, pred-
stavlja binarni brojevni sistem (sistem sa osnovom 2) . U raunarstvu se koriste
41 2. Reprezentacija podataka u raunarima

i heksadekadni brojevni sistem (sistem sa osnovom 16) a i, neto ree, oktalni


brojevni sistem (sistem sa osnovom 8), zbog toga to ovi sistemi omoguavaju
jednostavnu konverziju izmeu njih i binarnog sistema. Svi ovi brojevni sistemi,
kao i drugi o kojima e biti rei u nastavku teksta, su pozicioni. U pozicionom
brojnom sistemu, udeo cifre u celokupnoj vrednosti zapisanog broja zavisi od
njene pozicije.
Treba naglasiti da je zapis broja samo konvencija a da su brojevi koji se
zapisuju apsolutni i ne zavise od konkretnog zapisa. Tako, na primer, zbir dva

5)
prirodna broja je uvek jedan isti prirodni broj, bez obzira na to u kom sistemu
su ova tri broja zapisana.

01
S obzirom na to da je svaki zapis broja u raunaru ogranien, ne mogu biti
zapisani svi celi brojevi. Ipak, za cele brojeve zapisive u raunaru se obino gov-

(2
ori samo celi brojevi, dok su ispravnija imena oznaeni celi brojevi (engl. signed
integers) i neoznaeni celi brojevi (engl. unsigned integers), koji podrazumevaju
konaan zapis. Ni svi realni brojevi (sa potencijalno beskonanim decimalnim

je
zapisom) ne mogu biti zapisani u raunaru. Za zapis zapisivih realnih bro-
jeva (koji su uvek racionalni) obino se koristi konvencija zapisa u pokretnom
an
zarezu. Iako je jedino precizno ime za ove brojeve brojevi u pokretnom zarezu
(engl. floating point numbers), esto se koriste i imena realni ili racionalni bro-
jevi. Zbog ogranienog zapisa brojeva, rezultati matematikih operacija nad
d

njima sprovedenih u raunaru, nee uvek odgovarati rezultatima koji bi se do-


iz

bili bez tih ogranienja (zbog takozvanih prekoraenja ). Naglasimo jo i da,


za razliku od matematike gde se skup celih brojeva smatra podskupom skupa
realnih brojeva, u raunarstvu, zbog razliitog naina zapisa, izmeu zapisa
o

ovih vrsta brojeva ne postoji direktna veza.


sk
n

2.2.1 Neoznaeni brojevi


ro

Pod neoznaenim brojevima podrazumeva se neoznaeni zapis nenegativnih


celih brojeva i znak se izostavlja iz zapisa.
kt

Odreivanje broja na osnovu datog zapisa. Pretpostavimo da je dat


pozicioni brojevni sistem sa osnovom , gde je prirodan broj vei od 1. Niz
le

cifara ( 1 . . . 1 0 ) predstavlja zapis1 broja u osnovi , pri emu za svaku


E

cifru vai 0 < .


Vrednost broja zapisanog u osnovi definie se na sledei nain:



( 1 . . . 1 0 ) = = + 1 1 + . . . 1 + 0
=0

Na primer:
(101101)2 = 1 25 + 0 24 + 1 23 + 1 22 + 0 2 + 1 = 32 + 8 + 4 + 1 = 45,
(3245)8 = 383 +282 +48+5 = 3512+264+48+5 = 1536+128+32+5 =
1701.
1 Ako u zapisu broja nije navedena osnova, podrazumeva se da je osnova 10.
42 2. Reprezentacija podataka u raunarima

Navedena definicija daje i postupak za odreivanje vrednosti datog zapisa:

:= 0
za svako od 0 do
:= +
ili, malo modifikovano:

5)
:= 0

01
za svako od 1 do
:= +

(2
Za izraunavanje vrednosti nekog ( + 1)-tocifrenog zapisa drugim nave-
(+1)
denim postupkom potrebno je sabiranja i + ( 1) + . . . + 1 =
2

je
mnoenja. Zaista, da bi se izraunalo potrebno je mnoenja, da bi se
1
izraunalo 1 potrebno je 1 mnoenja, itd. Meutim, ovo izrauna-
an

vanje moe da se izvri i efikasnije. Ukoliko se za izraunavanje lana iskoristi
1
ve izraunata vrednost , broj mnoenja se moe svesti na 2. Ovaj nain
izraunavanja primenjen je u sledeem postupku:
d
iz

:= 0
:= 1
o

za svako od 1 do
:=
sk

:= +
n

Jo efikasniji postupak izraunavanja se moe dobiti korienjem Hornerove


ro

sheme:

( 1 . . . 1 0 ) = (. . . (( + 1 ) + 2 ) . . . + 1 ) + 0
kt

Korienjem ove sheme, dolazi se do sledeeg postupka za odreivanje vred-


le

nosti broja zapisanog u nekoj brojevnoj osnovi:


E

:= 0
za svako od unazad do 0
:= +

Naredni primer ilustruje primenu Hornerovog postupka na zapis (9876)10 .

3 2 1 0
9 8 7 6
0 0 10 + 9 = 9 9 10 + 8 = 98 98 10 + 7 = 987 987 10 + 6 = 9876
43 2. Reprezentacija podataka u raunarima

Meurezultati dobijeni u ovom raunu direktno odgovaraju prefiksima za-


pisa ija se vrednost odreuje, a rezultat u poslednjoj koloni je traeni broj.
Navedeni postupak moe se primeniti na proizvoljnu brojevnu osnovu. Sledei
primer ilustruje primenu postupka na zapis (3245)8 .

3 2 1 0
3 2 4 5

5)
0 08+3=3 3 8 + 2 = 26 26 8 + 4 = 212 212 8 + 5 = 1701

01
Navedena tabela moe se krae zapisati na sledei nain:

(2
3 2 4 5
0 3 26 212 1701

je
Hornerov postupak je efikasniji u odnosu na poetni postupak, jer je u
svakom koraku dovoljno izvriti samo jedno mnoenje i jedno sabiranje (ukupno
an
+1 sabiranja i +1 mnoenja).
d

Odreivanje zapisa datog broja. Za svaku cifru u


u zapisu broja
0 < .
iz

osnovi vai da je Dodatno, pri deljenju broja osnovom ,


ostatak je 0 a celobrojni kolinik je broj iji je zapis ( 1 . . . 1 ) . Dakle,
izraunavanjem celobrojnog kolinika i ostatka pri deljenju sa , odreena je
o

poslednja cifra broja i broj koji se dobija uklanjanjem poslednje cifre iz zapisa.
sk

Ukoliko se isti postupak primeni na dobijeni kolinik, dobija se postupak koji


omoguava da se odrede sve cifre u zapisu broja . Postupak se zaustavlja kada
n

tekui kolinik postane 0. Ako se izraunavanje ostatka pri deljenju oznai sa


mod, a celobrojnog kolinika sa div, postupak kojim se odreuje zapis broja
ro

u datoj osnovi se moe formulisati na sledei nain:


kt

:= 0
dok je razliito od 0
le

:= mod
:= div
E

:= + 1
Na primer, 1701 = (3245)8 jer je 1701 = 212 8 + 5 = (26 8 + 4) 8 + 5 =
((3 8 + 2) 8 + 4) 8 + 5 = (((0 8 + 3) 8 + 2) 8 + 4) 8 + 5. Ovaj postupak se
moe prikazati i tabelom:

0 1 2 3
1701 1701 div 8 = 212 212 div 8 = 26 26 div 8 = 3 3 div 8 = 0
1701 1701 mod 8 = 5 212 mod 8 = 4 26 mod 8 = 2 3 mod 8 = 3
44 2. Reprezentacija podataka u raunarima

Prethodna tabela moe se krae zapisati na sledei nain:

1701 212 26 3 0
5 4 2 3

Druga vrsta tabele sadri celobrojne kolinike, a trea ostatke pri deljenju
sa osnovom , tj. traene cifre. Zapis broja se formira tako to se dobijene cifre
itaju unatrag.

5)
Ovaj algoritam i Hornerov algoritam su meusobno simetrini u smislu da
se svi meurezultati poklapaju.

01
Direktno prevoenje izmeu heksadekadnog i binarnog sistema.

(2
Osnovni razlog korienja heksadekadnog sistema je mogunost jednostavnog
prevoenja brojeva izmeu binarnog i heksadekadnog sistema. Pri tome, hek-
sadekadni sistem omoguava da se binarni sadraj memorije zapie kompakt-

je
nije (uz korienje manjeg broja cifara). Prevoenje se moe izvriti tako to
se grupiu etiri po etiri binarne cifre, krenuvi unazad, i svaka etvorka se
an
zasebno prevede u odgovarajuu heksadekadnu cifru na osnovu sledee tabele:

heksa binarno heksa binarno heksa binarno heksa binarno


d

0 0000 4 0100 8 1000 C 1100


iz

1 0001 5 0101 9 1001 D 1101


2 0010 6 0110 A 1010 E 1110
3 0011 7 0111 B 1011 F 1111
o
sk

Na primer, proizvoljni 32-bitni sadraj moe se zapisati korienjem osam


heksadekadnih cifara:
n

(1011 0110 0111 1100 0010 1001 1111 0001)2 = (6729 1)16
ro

Zapisi fiksirane duine U raunarima se obino koristi fiksirani broj bina-


rnih cifara (sauvanih u pojedinanim bitovima) za zapis svakog broja. Takve
kt

zapise oznaavamo sa (. . .) , ako se koristi cifara. Ukoliko je broj cifara


potrebnih za zapis broja krai od zadate duine zapisa, onda se broj proiruje
le

vodeim nulama. Na primer, 55 = (0011 0111)82 . Ograniavanjem broja cifara


ograniava se i raspon brojeva koje je mogue zapisati (u binarnom sistemu)
E

i to na raspon od 0 do 2 1. U sledeoj tabeli su dati rasponi za najee


koriene duine zapisa:

broj bitova raspon


8 od 0 do 255
16 od 0 do 65535
32 od 0 do 4294967295
45 2. Reprezentacija podataka u raunarima

2.2.2 Oznaeni brojevi

Oznaeni brojevi su celi brojevi iji zapis ukljuuje i zapis znaka broja
(+ ili ). S obzirom na to da savremeni raunari koriste binarni brojevni
sistem, bie razmatrani samo zapisi oznaenih brojeva u binarnom brojevnom
sistemu. Postoji vie naina zapisivanja oznaenih brojeva od kojih su najee
u upotrebi oznaena apsolutna vrednost i potpuni komplement.

5)
Oznaena apsolutna vrednost. Zapis broja se formira tako to se na prvu
poziciju zapisa unapred fiksirane duine , upie znak broja, a na preostalih

01
1 pozicija upie zapis apsolutne vrednosti broja. Poto se za zapis koriste
samo dva simbola (0 i 1), konvencija je da se znak + zapisuje simbolom 0,
a znak simbolom 1. Ovim se postie da pozitivni brojevi imaju identian

(2
+100 = (0 1100100)82 ,
zapis kao da su u pitanju neoznaeni brojevi. Na primer,
8
100 = (1 1100100)2 .
Osnovni problem zapisa u obliku oznaene apsolutne vrednosti je injenica

je
da se osnovne aritmetike operacije teko izvode ukoliko su brojevi zapisani na
an
ovaj nain.

Potpuni komplement. Zapis u potpunom komplementu (engl. twos com-


d

plement) oznaenih brojeva zadovoljava sledee uslove:


iz

1. Nula i pozitivni brojevi se zapisuju na isti nain kao da su u pitanju


neoznaeni brojevi, pri emu u njihovom zapisu prva cifra mora da bude
o

0.
sk

2. Sabiranje se sprovodi na isti nain kao da su u pitanju neoznaeni brojevi,


pri emu se prenos sa poslednje pozicije zanemaruje.
n

Na primer, broj+100 se u potpunom komplementu zapisuje kao (0 1100100)82 .


ro

8 8
Nula se zapisuje kao (0 0000000)2 . Zapis broja 100 u obliku (. . .)2 se moe
odrediti na sledei nain. Zbir brojeva 100 i +100 mora da bude 0.
kt

binarno dekadno
???????? -100
le

+ 01100100 +100
1 00000000 0
E


Analizom traenog sabiranja cifru po cifru, poevi od poslednje, sledi
da se 100 mora zapisati kao (10011100)82 . Do ovoga je mogue doi i na
sledei nain. Ukoliko je poznat zapis broja, zapis njemu suprotnog broja
+1
je mogue odrediti iz uslova da je + () = (1 00 . . . 00)2 . Poto je
+1
(1 00 . . . 00)2 = (11 . . . 11)2 + 1, zapis broja () je mogue odrediti tako to

se izrauna (11 . . . 11)2 +1. Izraunavanje razlike (11 . . . 11)2 se svodi na
komplementiranje svake pojedinane cifre broja . Tako se odreivanje zapisa
broja 100 moe opisati na sledei nain:
46 2. Reprezentacija podataka u raunarima

01100100 +100
10011011 komplementiranje
+ 1
10011100
Kao to je traeno, zapisi svih pozitivnih brojeva i nule poinju cifrom 0
dok zapisi negativnih brojeva poinju sa 1.
Broj 21 je jedini izuzetak u optem postupku odreivanja zapisa u pot-
punom komplementu. Zapis broja (100 . . . 00)2 je sam sebi komplementaran, a

5)
poto poinje cifrom 1, uzima se da on predstavlja zapis najmanjeg zapisivog
negativnog broja, tj. broja 21 . Zahvaljujui ovoj konvenciji, u zapisu pot-

01
1 1
punog komplementa (. . .)2 mogue je zapisati brojeve od 2 do 2 1. U
sledeoj tabeli su dati rasponi za najee koriene duine zapise u potpunom

(2
komplementu:
broj bita raspon
8 128
od do +127

je
16 od 32768 do +32767
32 od 2147483648 do +2147483647
an
Kao to je reeno, za sabiranje brojeva zapisanih u potpunom komplementu
moe se koristiti opti postupak za sabiranje neoznaenih brojeva (pri emu se
podrazumeva da moe doi do prekoraenja, tj. da neke cifre rezultata ne mogu
d

biti upisane u raspoloiv broj mesta). To ne vai samo za sabiranje, ve i za


iz

oduzimanje i mnoenje. Ovo pogodno svojstvo vai i kada je jedan od argu-


menata broj 21 (koji se je jedini izuzetak u optem postupku odreivanja
zapisa). Sve ovo su vani razlozi za korienje potpunog komplementa u rau-
o

narima.
sk

2.2.3 Zapis realnih brojeva


n
ro

Pored celobrojnih dostupni u programskim jezicima su po pravilu podrani


i realni brojevni tipovi. Realne brojeve je u raunaru mnogo komplikovanije
predstaviti nego cele brojeve. Obino je mogue predstaviti samo odreeni
kt

podskup skupa realnih brojeva i to podskup skupa racionalnih brojeva. Interni


zapis celog broja i njemu odgovarajueg realnog se ne poklapaju (nule i jedinice
le

kojima se oni zapisuju nisu iste), ak i kada se isti broj bitova koristi za njihov
zapis. Fiksiranjem bitova koji e se odvojiti za zapis nekog realnog broja,
E

odreuje se i broj moguih razliitih brojeva koji se mogu zapisati. Svakoj


kombinaciji nula i jedinica pridruuje se neki realan broj. Kod celih brojeva
odluivano je da li e se takvim kombinacijama pridruivati samo pozitivni ili
i pozitivni i negativni brojevi i kada je to odlueno, u principu je jasno koji
skup celih brojeva moe biti zapisan (taj skup vrednosti je uvek neki povezan
raspon celih brojeva). U zapisu realnih brojeva stvari su komplikovanije jer
je potrebno napraviti kompromis izmeu irine raspona brojeva koji se mogu
zapisati (slino kao i kod celih brojeva), ali i izmeu preciznosti brojeva koji
se mogu zapisati. Dakle, kao i celobrojni tipovi, realni tipovi imaju odreen
raspon vrednosti koji se njima moe predstaviti, ali i odreenu preciznost zapisa
47 2. Reprezentacija podataka u raunarima

(nju obino doivljavamo kao broj decimala, mada to tumaenje, kao to emo
uskoro videti, nije uvek u potpunosti tano).
Osnovni naini zapisivanja realnih brojeva su zapis u fiksnom zarezu i zapis
u pokretnom zarezu . Zapis u fiksnom zarezu podrazumeva da se posebno za-
pisuje znak broja, zatim ceo deo broja i zatim njegov razlomljeni deo (njegove
decimale). Broj cifara za zapis celog dela i za zapis realnog dela je fiksiran i
jednak je za sve brojeve u okviru istog tipa koji koristi zapis u fiksnom zarezu.
Zapis u pokretnom zarezu podrazumeva oblik . Vrednost je osnova

5)
koja se podrazumeva (danas je to obino 2, mada se nekada koristila i vrednost
16, dok se u svakodnevnoj matematikoj praksi esto brojevi izraavaju na

01
ovaj nain uz korienje osnove 10), se naziva mantisa, a vrednost naziva
se eksponent. Broj cifara (obino binarnih) za zapis mantise i broj cifara za

(2
zapis eksponenta je fiksiran i jednak je za sve brojeve u okviru istog tipa koji
koristi zapis u pokretnom zarezu. Zapisivanje brojeva u pokretnom zarezu pro-
pisano je standardom IEEE 754 iz 1985. godine, meunarodne asocijacije In-

je
stitut inenjera elektrotehnike i elektronike, IEEE (engl. Institute of Electrical
and Electronics Engineers). Ovaj standard predvia da se odreene kombi-
an
nacije bitova odvoje za zapis tzv. specijalnih vrednosti (beskonanih vrednosti,
greaka u izraunavanju i slino).
Ilustrujmo osnovne principe zapisa realnih brojeva na dekadnom zapisu
d

nenegativnih realnih brojeva (kao to smo rekli, u raunaru se ovakav zapis in-
iz

terno ne koristi, ve se koristi binarni zapis). Zamislimo da imamo tri dekadne


cifre koje moemo iskoristiti za zapis. Dakle, imamo 1000 brojeva koji se mogu
zapisati. Prva mogunost je da se odreeni broj cifara upotrebi za zapis celog
o

dela, a odreeni broj cifara za zapis decimala i takav nain zapisa predstavlja
sk

zapis u fiksnom zarezu. Na primer, ako se jedna cifra upotrebi za decimale


moi emo zapisivati vrednosti 00,0, 00,1, ..., 99,8 i 99,9. Ako se se za deci-
n

male odvoje dva mesta, onda moemo zapisivati vrednosti 0,00, 0,01, ..., 9,98
ro

i 9,99. Ovim smo dobili bolju preciznost (svaki broj je zapisan sa dve, umesto
sa jednom decimalom), ali smo to platili mnogo uim rasponom brojeva koje
moemo zapisati (umesto irine oko 100, dobili smo raspono irine oko 10). U
kt

raunaru se fiksni zarez esto ostvaruje binarno (odreeni broj bitova kodira
ceo, a odreeni broj bitova kodira razlomljeni deo), pri emu se uvek jedan bit
le

ostavlja za predstavljanje znaka broja ime se omoguava zapis i pozitivnih i


negativnih vrednosti.
E

Umesto fiksnog zareza, u raunarima se mnogo ee koristi pokretni zarez.


U takvom zapisu, nae tri dekadne cifre podeliemo tako da dve odlaze za zapis
mantise, a jednu za zapis eksponenta (pa e zapis biti oblika 1 2 3 ). Ako su
prve dve cifre 1 i 2 , tumaiemo da je mantisa 0, 1 2 . Ako je trea cifra
u zapisu 3 , tumaiemo da eksponent = 3 4 (ovim postiemo da vrednosti
eksponenta mogu da budu izmeu 4 i 5 tj. da mogu da budu i pozitivne i
negativne). Pogledajmo neke zapise koje na taj nain dobijamo.
48 2. Reprezentacija podataka u raunarima

zapis vrednost zapis vrednost ... zapis vrednost zapis vrednost


010 0,01 104 = 0,000001 011 0,01 103 = 0,00001 ... 018 0,01 104 = 100,0 019 0,01 105 = 1 000,0
020 0,02 104 = 0,000002 021 0,02 103 = 0,00002 ... 028 0,02 104 = 200, 0 029 0,02 105 = 2 000,0
980 0,98 104 = 0,000098 981 0,98 103 = 0,00098 ... 988 0,98 104 = 9800,0 989 0,98 105 = 98 000,0
990 0,99 104 = 0,000099 991 0,99 103 = 0,00099 ... 998 0,99 104 = 9900,0 999 0,99 105 = 99 000,0

Raspon je prilino irok (od 0, 000001 do 99 000, 0 tj. od oko 106 do oko
5
10 ) i mnogo iri nego to je to bilo kod fiksnog zareza, ali gustina zapisa je
neravnomerno rasporeena, to nije bio sluaj kod fiksnog zareza. Kod malih
brojeva preciznost zapisa je mnogo vea nego kod velikih. Na primer, kod malih

5)
brojeva moemo zapisivati veliki broj decimala, ali nijedan broj izmeu 98 000
i 99 000 nije mogue zapisati (brojevi iz tog raspona bi se morali zaokruiti na

01
neki od ova dva broja). Ovo nije preveliki problem, jer nam obino kod velikih
brojeva preciznost nije toliko bitna koliko kod malih (matematiki gledano,

(2
relativna greka koja nastaje usled zaokruivanja se ne menja puno kroz ceo
raspon). Dve vane komponente koje karakteriu svaki zapis u pokretnom
zarezu su raspon brojeva koji se mogu zapisati (u naem primeru to je bilo od

je
oko 106 do oko 105 ) i on je skoro potpuno odreen irinom eksponentna, kao i
broj dekadnih znaajnih cifara (u naem primer, imamo dve dekadne znaajne
an
cifre) i on je skoro potpuno odreeno irinom mantise.

2.3 Zapis teksta


d
iz

Meunarodna organizacija za standardizaciju, ISO (engl. International Stan-


dard Organization) definie tekst (ili dokument) kao informaciju namenjenu
o

ljudskom sporazumevanju koja moe biti prikazana u dvodimenzionalnom ob-


liku. Tekst se sastoji od grafikih elemenata kao to su karakteri, geometrijski ili
sk

fotografski elementi ili njihove kombinacije, koji ine sadraj dokumenta. Iako
se tekst obino zamilja kao dvodimenzioni objekat, u raunarima se tekst pred-
n

stavlja kao jednodimenzioni (linearni) niz karaktera koji pripadaju odreenom


ro

unapred fiksiranom skupu karaktera. U zapisu teksta, koriste se specijalni


karakteri koji oznaavaju prelazak u novi red, tabulator, kraj teksta i slino.
kt

Osnovna ideja koja omoguava zapis teksta u raunarima je da se svakom


karakteru pridrui odreen (neoznaeni) ceo broj (a koji se interno u rau-
narima zapisuje binarno) i to na unapred dogovoreni nain. Ovi brojevi se
le

nazivaju kdovima karaktera (engl. character codes). Tehnika ogranienja


E

ranih raunara kao i neravnomeran razvoj raunarstva izmeu razliitih ze-


malja, doveli su do toga da postoji vie razliitih standardnih tabela koje do-
deljuju numerike kdove karakterima. U zavisnosti od broja bitova potrebnih
za kodiranje karaktera, razlikuju se 7-bitni kdovi, 8-bitni kdovi, 16-bitni k-
dovi, 32-bitni kdovi, kao i kodiranja promenljive duine koja razliitim karak-
terima dodeljuju kdove razliite duine. Tabele koje sadre karaktere i njima
pridruene kdove obino se nazivaju kdne strane (engl. code page).
Postoji veoma jasna razlika izmeu karaktera i njihove grafike reprezentacije.
Elementi pisanog teksta koji najee predstavljaju grafike reprezentacije po-
jedinih karaktera nazivaju se glifovi (engl. glyph), a skupovi glifova nazivaju
se fontovi (engl. font). Korespondencija izmeu karaktera i glifova ne mora
49 2. Reprezentacija podataka u raunarima

biti jednoznana. Naime, softver koji prikazuje tekst moe vie karaktera pred-
staviti jednim glifom (to su takozvane ligature, kao na primer glif za karaktere
f i i: fi), dok jedan isti karakter moe biti predstavljen razliitim glifovima
u zavisnosti od svoje pozicije u rei. Takoe, mogue je da odreeni fontovi ne
sadre glifove za odreene karaktere i u tom sluaju se tekst ne prikazuje na el-
jeni nain, bez obzira to je ispravno kodiran. Fontovi koji se obino instaliraju
uz operativni sistem sadre glifove za karaktere koji su popisani na takozvanoj
WGL4 listi (Windows Glyph List 4 ) koja sadri uglavnom karaktere koriene

5)
u evropskim jezicima, dok je za ispravan prikaz, na primer, kineskih karaktera,
potrebno instalirati dodatne fontove. Specijalnim karakterima se najee ne

01
pridruuju zasebni grafiki likovi.

(2
Englesko govorno podruje. Tokom razvoja raunarstva, broj karaktera
koje je bilo poeljno kodirati je postajao sve vei. Poto je raunarstvo u ranim
fazama bilo razvijano uglavnom u zemljama engleskog govornog podruja, bilo

je
je potrebno predstaviti sledee karaktere:
an
Mala slova engleskog alfabeta: a, b, ... , z
Velika slova engleskog alfabeta: A, B, ... , Z
d

Cifre 0, 1, ..., 9
iz

Interpunkcijske znake: , . : ; + * - _ ( ) [ ] { } ...

Specijalne znake: kraj reda, tabulator, . . .


o

Standardne tabele kdova ovih karaktera su se pojavile jo tokom 1960-ih


sk

godina. Najrasprostranjenije od njih su:

EBCDIC - IBM-ov standard, korien uglavnom na mainframe rau-


n

narima, pogodan za buene kartice.


ro

ASCII - standard iz koga se razvila veina danas korienih standarda za


kt

zapis karaktera.

ASCII.
le

ASCII (American Standard Code for Information Interchange) je


standard uspostavljen 1968. godine od strane Amerikog nacionalnog instituta
E

za standarde, (engl. American National Standard Institute) koji definie sed-


mobitan zapis kda svakog karaktera to daje mogunost zapisivanja ukupno
128 razliitih karaktera, pri emu nekoliko kdova ima dozvoljeno slobodno ko-
rienje. ISO takoe delimino definie ASCII tablicu kao deo svog standarda
ISO 646 (US).
Osnovne osobine ASCII standarda su:

Prva 32 karaktera od (00)16 do (1 )16 su specijalni kontrolni karakteri.


Ukupno 95 karaktera ima pridruene grafike likove (engl. printable char-
acters).
50 2. Reprezentacija podataka u raunarima

0 1 2 3 4 5 6 7 8 9 A B C D E F
0 NUL STX SOT ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI
1 DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US
2 ! " # $ % & ' ( ) * + , - . /
3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 @ A B C D E F G H I J K L M N O
5 P Q R S T U V W X Y Z [ \ ] ^ _
6 ` a b c d e f g h i j k l m n o
7 p q r s t u v w x y z { | } ~ DEL

5)
Slika 2.2: ASCII tablica

01
(2
Cifre 0-9 predstavljene su kdovima (30)16 do (39)16 , tako da se njihov
ASCII zapis jednostavno dobija dodavanjem prefiksa 011 na njihov bina-
rni zapis.

je
Kdovi velikih i malih slova se razlikuju u samo jednom bitu u binarnoj
reprezentaciji. Na primer, se kodira brojem (41)16 odnosno (100 0001)2 ,
an
dok se kodira brojem (61)16 odnosno (110 0001)2 . Ovo omoguava da
se konverzija veliine slova u oba smera moe vriti efikasno.
d

Slova su poreana u kolacionu sekvencu, u skladu sa engleskim alfabetom.


iz

Razliiti operativni sistemi predviaju razliito kodiranje oznake za prelazak


u novi red. Tako operativni sistem Windows podrazumeva da se prelazak u
o

novi red kodira sa dva kontrolna karaktera i to (carriage return) pred-


sk

stavljen kdom (0)16 i (line feed) predstavljen kodom (0)16 , operativni


sistem Unix i njegovi derivati (pre svega Linux) podrazumevaju da se koristi
n

samo karakter , dok MacOS podrazumeva korienje samo karaktera .


ro

Nacionalne varijante ASCII tablice i ISO 646. Tokom 1980-ih, Ju-


goslovenski zavod za standarde definisao je standard YU-ASCII (YUSCII, JUS
kt

I.B1.002, JUS I.B1.003) kao deo standarda ISO 646, tako to su kdovi koji
imaju slobodno korienje (a koji u ASCII tabeli uobiajeno kodiraju zagrade
le

i odreene interpunkcijske znakove) dodeljeni naim dijakriticima:


YUSCII ASCII kd YUSCII ASCII kd
E

@ (40)16 (60)16
[ (5)16 { (7)16
\ (5)16 | (7)16
] (5)16 } (7)16
^ (5)16 ~ (7)16
Osnovne mane YUSCII kodiranja su to to ne potuje abecedni poredak,
kao i to da su neke zagrade i vani interpunkcijski znaci izostavljeni.

8-bitna proirenja ASCII tabele. Podaci se u raunaru obino zapisuju


bajt po bajt. S obzirom na to da je ASCII sedmobitni standard, ASCII karak-
51 2. Reprezentacija podataka u raunarima

teri se zapisuju tako to se njihov sedmobitni kd proiri vodeom nulom. Ovo


znai da jednobajtni zapisi u kojima je vodea cifra 1, tj. raspon od (80)16
do ( )16 nisu iskorieni. Meutim, ni ovih dodatnih 128 kdova nije do-
voljno da se kodiraju svi karakteri koji su potrebni za zapis tekstova na svim
jezicima (ne samo na engleskom). Zbog toga je, umesto jedinstvene tabele koja
bi proirivala ASCII na 256 karaktera, standardizovano nekoliko takvih tabela,
pri emu svaka od tabela sadri karaktere potrebne za zapis odreenog jezika
odnosno odreene grupe jezika. Praktian problem je to postoji dvostruka

5)
standardizacija ovako kreiranih kdnih strana i to od strane ISO (International
Standard Organization) i od strane znaajnih korporacija, pre svega kompanije

01
Microsoft.
ISO je definisao familiju 8-bitnih kdnih strana koje nose zajedniku oznaku

(2
ISO/IEC 8859 (kdovi od (00)16 do (1 )16 , (7 )16 i od (80)16 do (9 )16 ostali
su nedefinisani ovim standardom, iako se esto u praksi popunjavaju odreenim
kontrolnim karakterima):

je
ISO-8859-1 Latin 1 veina zapadno-evropskih jezika
ISO-8859-2 Latin 2 centralno i istono-evropski jezici
an
ISO-8859-3 Latin 3 juno-evropski jezici
ISO-8859-4 Latin 4 severno-evropski jezici
ISO-8859-5 Latin/Cyrillic irilica veine slovenskih jezika
d

ISO-8859-6 Latin/Arabic najee korieni arapski


iz

ISO-8859-7 Latin/Greek moderni grki alfabet


ISO-8859-8 Latin/Hebrew moderni hebrejski alfabet
Kompanija Microsoft definisala je familju 8-bitnih strana koje se oznaavaju
o

kao Windows-125x (ove strane se nekada nazivaju i ANSI ). Za srpski jezik,


sk

znaajne su kdne strane:


Windows-1250 centralno i istono-evropski jezici
n

Windows-1251 irilica veine slovenskih jezika


ro

Windows-1252 (esto se neispravno naziva i ANSI) zapadno-


evropski jezici
kt

0 1 2 3 4 5 6 7 8 9 A B C D E F
8
le

9
A NBSP SHY
E

B
C
D
E
F

Slika 2.3: ISO-8859-1 tablica

Unicode. Iako navedene kdne strane omoguavaju kodiranje tekstova koji


nisu na engleskom jeziku, nije mogue, na primer, u istom tekstu koristiti i
52 2. Reprezentacija podataka u raunarima

0 1 2 3 4 5 6 7 8 9 A B C D E F
8
9
A NBSP SHY
B
C
D
E
F

5)
Slika 2.4: ISO-8859-2 tablica

01
0 1 2 3 4 5 6 7 8 9 A B C D E F

(2
8
9
A
B

je
C
D
an
E
F
d
iz

Slika 2.5: Windows-1250 tablica

0 1 2 3 4 5 6 7 8 9 A B C D E F
o

8
sk

9
A NBSP SHY
B
n

C
D
ro

E
F
kt

Slika 2.6: ISO-8859-5 tablica


le
E

irilicu i latinicu. Takoe, za azijske jezike nije dovoljno 256 mesta za zapis
svih karaktera. Poto je kapacitet raunara vremenom rastao, postepeno se
krenulo sa standardizacijom skupova karaktera koji karaktere kodiraju sa vie
od jednog bajta. Kasnih 1980-ih, dve velike organizacije zapoele su standard-
izaciju tzv. univerzalnog skupa karaktera (engl. Universal Character Set
UCS). To su bili ISO, kroz standard 10646 i projekat Unicode , organizovan i
finansiran uglavnom od strane amerikih kompanija koje su se bavile proizvod-
njom viejezikog softvera (Xerox Parc, Apple, Sun Microsystems, Microsoft,
. . . ).
ISO 10646 zamiljen je kao etvorobajtni standard. Prvih 65536 karak-
53 2. Reprezentacija podataka u raunarima

0 1 2 3 4 5 6 7 8 9 A B C D E F
8
9
A
B
C
D
E
F

5)
01
Slika 2.7: Windows-1251 tablica

(2
tera koristi se kao osnovni viejezini skup karaktera, dok je preostali prostor
ostavljen kao proirenje za drevne jezike, naunu notaciju i slino.
Unicode je za cilj imao da bude:


je
Univerzalan (UNIversal) sadri sve savremene jezike sa pismom;
an
jedinstven (UNIque) bez dupliranja karaktera - kodiraju se pisma, a
ne jezici;
d

uniforman (UNIform) svaki karakter sa istim brojem bitova.


iz

Poetna verzija Unicode standarda svakom karakteru dodeljuje dvobajtni


kd (tako da kd svakog karaktera sadri tano 4 heksadekadne cifre). Dakle,
o

mogue je dodeliti kdove za ukupno 216 = 65536 razliitih karaktera. S vre-


sk

menom se shvatilo da dva bajta nee biti dovoljno za zapis svih karaktera
koji se koriste na planeti, pa je odlueno da se skup karaktera proiri i Uni-
n

code danas dodeljuje kdove od (000000)16 do (10 )16 podeljenih u 16


tzv. ravni, pri emu svaka ravan sadri 65536 karaktera. U najeoj upotrebi
ro

je osnovna viejezina ravan (engl. basic multilingual plane) koja sadri veinu
danas korienih karaktera (ukljuujui i CJK Chinese, Japanese, Korean
kt

karaktere koji se najee koriste) iji su kdovi izmeu (0000)16 i ( )16 .


Vremenom su se pomenuta dva projekta UCS i Unicode zdruila i danas
le

postoji izuzetno preklapanje izmeu ova dva standarda.


U sledeoj tabeli je naveden raspored odreenih grupa karaktera u osnovnoj
E

viejezikoj ravni:
54 2. Reprezentacija podataka u raunarima

0020-007E ASCII printable


00A0-00FF Latin-1
0100-017F Latin extended A (osnovno proirenje latinice,
sadri sve nae dijakritike)
0180-027F Latin extended B
...
0370-03FF grki alfabet
0400-04FF irilica

5)
...
2000-2FFF specijalni karakteri

01
3000-3FFF CJK (Chinese-Japanese-Korean) simboli
...

(2
Unicode standard u sutini predstavlja veliku tabelu koja svakom karakteru
dodeljuje broj. Standardi koji opisuju kako se niske karaktera prevode u nizove
bajtova se definiu dodatno.

UCS-2.
je
ISO definie UCS-2 standard koji svaki Unicode karakter osnovne
an
viejezike ravni jednostavno zapisuje sa odgovarajua dva bajta.

UTF-8.
d

Latinini tekstovi kodirani korienjem UCS-2 standarda sadre ve-


liki broj nula. Ne samo to te nule zauzimaju dosta prostora, ve zbog njih
iz

softver koji je razvijen za rad sa dokumentima u ASCII formatu ne moe da


radi bez izmena nad dokumentima kodiranim korienjem UCS-2 standarda.
o

Unicode Transformation Format (UTF-8) je algoritam koji svakom dvobajt-


nom Unicode karakteru dodeljuje odreeni niz bajtova ija duina varira od 1
sk

do najvie 3. UTF je ASCII kompatibilan, to znai da se ASCII karakteri za-


pisuju pomou jednog bajta, na standardni nain. Konverzija se vri na osnovu
n

sledeih pravila:
ro
kt

raspon binarno zapisan Unicode kd binarno zapisan UTF-8 kd


0000-007F 00000000 0xxxxxxx 0xxxxxxx
0080-07FF 00000yyy yyxxxxxx 110yyyyy 10xxxxxx
le

0800-FFFF zzzzyyyy yyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx


E

Na primer, karakter A koji se nalazi u ASCII tabeli ima Unicode kd


(0041)16 = (0000 0000 0100 0001)2 , pa se na osnovu prvog reda prethodne
tabele u UTF-8 kodiranju zapisuje kao (01000001)2 = (41)16 . Karakter ima
Unicode kd (0160)16 = (0000 0001 0110 0000)2 . Na njega se primenjuje drugi
red prethodne tabele i dobija se da je njegov UTF-8 zapis (1100 0101 1010 0000)2
tj. (50)16 . Opisani konverzioni algoritam omoguava da se itanje samo
poetka jednog bajta odredi da li je u pitanju karakter zapisan korienjem
jednog, dva ili tri bajta.
55 2. Reprezentacija podataka u raunarima

2.4 Zapis multimedijalnih sadraja


Raunari imaju sve veu ulogu u veini oblasti svakodnevnog ivota. Od
maina koje su pre svega sluile za izvoenje vojnih i naunih izraunavanja,
raunari su postali i sredstvo za kunu zabavu (gledanje filmova, sluanje
muzike), izvor informacija (internet) i nezaobilazno sredstvo u komunikaciji
(elektronska pota (engl. e-mail), askanje (engl. chat, instant messaging), video
konferencije, telefoniranje korienjem interneta (VoIP), . . . ). Nagli razvoj i

5)
proirivanje osnovne namene raunara je posledica iroke raspoloivosti velike
koliine multimedijalnih informacija (slika, zvuka, filmova, . . . ) koje su za-

01
pisane u digitalnom formatu. Sve ovo je posledica tehnolokog napretka koji
je omoguio jednostavno i jeftino digitalizovanje signala, skladitenje velike

(2
koliine digitalno zapisanih informacija, kao i njihov brz prenos i obradu.

2.4.1 Zapis slika

je
Slike se u raunaru zapisuju koristei vektorski zapis, rasterski zapis ili kom-
an
binovani zapis..
U vektorskom obliku, zapis slike sastoji se od konanog broja geometri-
jskih figura (taaka, linija, krivih, poligona), pri emu se svaka figura pred-
d

stavlja koncizno svojim koordinatama ili jednainom u Dekartovoj ravni. Slike


iz

koje raunari generiu esto koriste vektorsku grafiku. Vektorski zapisane slike
esto zauzimaju manje prostora, dozvoljavaju uveavanje (engl. zooming) bez
gubitaka na kvalitetu prikaza i mogu se lake preureivati, s obzirom na to
o

da se objekti mogu nezavisno jedan od drugoga pomerati, menjati, dodavati i


sk

uklanjati.
n
ro
kt
le
E

Slika 2.8: Primer slike u rasterskoj (levo) i vektorskoj (desno) grafici

U rasterskom zapisu, slika je predstavljena pravougaonom matricom kom-


ponenti koje se nazivaju pikseli (engl. pixel - PICture ELement). Svaki piksel je
individualan i opisan jednom bojom. Raster nastaje kao rezultat digitalizacije
slike. Rasterska grafika se jo naziva i bitmapirana grafika. Ureaji za prikaz
(monitori, projektori), kao i ureaji za digitalno snimanje slika (fotoaparati,
skeneri) koriste rasterski zapis.
56 2. Reprezentacija podataka u raunarima

Modeli boja. Za predstavljanje crno-belih slika, dovoljno je boju predstaviti


intenzitetom svetlosti. Razliite koliine svetlosti se diskretizuju u konaan broj
nivoa osvetljenja ime se dobija odreeni broj nijansi sive boje. Ovakav model
boja se naziva grayscale. Ukoliko se za zapis informacije o koliini svetlosti
koristi 1 bajt, ukupan broj nijansi sive boje je 256. U sluaju da se slika
predstavlja sa ukupno dve boje (na primer, crna i bela, kao kod skeniranog
teksta nekog dokumenta) koristi se model pod nazivom Duotone i boja se tada
predstavlja jednim bitom.

5)
U RGB modelu boja, kombinovanjem crvene (R), zelene (G) i plave (B)
komponente svetlosti reprezentuju se ostale boje. Tako se, na primer, kombi-

01
novanjem crvene i zelene boje reprezentuje uta boja. Bela boja se reprezentuje
maksimalnim vrednosti sve tri osnovne komponente, dok se crna boja reprezen-

(2
tuje minimalnim vrednostima osnovnih komponenti. U ovom modelu, zapis
boje ini informacija o intenzitetu crvene, plave i zelene komponente. RGB
model boja se koristi kod ureaja koji boje prikazuju kombinovanjem svetlosti

je
(monitori, projektori, . . . ). Ukoliko se za informaciju o svakoj komponenti ko-
risti po jedan bajt, ukupan broj bajtova za zapis informacije o boji je 3, te je
an
mogue koristiti 224 = 16777216 razliitih boja. Ovaj model se esto naziva i
TrueColor model boja.
Za razliku od aditivnog RGB modela boja, kod kojeg se bela boja dobija
d

kombinovanjem svetlosti tri osnovne komponente, u tampi se koristi subtrak-


iz

tivni CMY (Cyan-Magenta-Yellow) model boje kod kojeg se boje dobijaju kom-
binovanjem obojenih pigmenata na belom papiru. Kako se potpuno crna teko
dobija meanjem drugih pigmenata, a i kako je njena upotreba obino daleko
o

najea, obino se prilikom tampanja uz CMY pigmente koristi i crni pigment


sk

ime se dobija model CMYK.


Za obradu slika pogodni su HSL ili HSV (poznat i kao HSB) model boja.
n

U ovom modelu, svaka boja se reprezentuje Hue (H) komponentom (koja pred-
ro

stavlja ton boje), Saturation (S) komponentom (koja predstavlja zasienost


boje odnosno njenu jarkost) i Lightness (L), Value (V) ili Brightness (B)
komponentom (koja predstavlja osvetljenost).
kt

Formati zapisa rasterskih slika. Rasterske slike su reprezentovane ma-


le

tricom piksela, pri emu se za svaki piksel uva informacija o njegovoj boji.
E

Dimenzije ove matrice predstavljaju tzv. apsolutnu rezoluciju slike. Apsolutna


rezolucija i model boja koji se koristi odreuju broj bajtova potrebnih za za-
pis slike. Na primer, ukoliko je apsolutna rezolucija slike 800x600 piksela, pri
emu se koristi RGB model boje sa 3 bajta po pikselu, potrebno je ukupno
800 600 3 = 1440000 1.373 za zapis slike. Da bi se smanjila koliina
informacija potrebnih za zapis slike, koriste se tehnike kompresije i to (i) kom-
presije bez gubitka (engl. lossless), i (ii) kompresije sa gubitkom (engl. lossy).
Najee korieni formati u kojima se koristi tehnike kompresije bez gubitka
danas su GIF i PNG (koji se koriste za zapis dijagrama i slinih raunarski
generisanih slika), dok je najee korieni format sa gubitkom JPEG (koji se
obino koristi za fotografije).
57 2. Reprezentacija podataka u raunarima

2.4.2 Zapis zvuka

Zvuni talas predstavlja oscilaciju pritiska koja se prenosi kroz vazduh ili
neki drugi medijum (tenost, vrsto telo). Digitalizacija zvuka se vri meren-
jem i zapisivanjem vazdunog pritiska u kratkim vremenskim intervalima. Os-
novni parametri koji opisuju zvuni signal su njegova amplituda (koja odgovara
glasnoi) i frekvencija (koja odgovara visini). Poto ljudsko uho obino uje
raspon frekvencija od 20Hz do 20KHz, dovoljno je koristiti frekvenciju odabi-

5)
ranja 40KHz, tj. dovoljno je izvriti odabiranje oko 40 000 puta u sekundi.
Na primer, AudioCD standard koji se koristi prilikom snimanja obinih audio

01
kompakt diskova, propisuje frekvenciju odabiranja 44.1KHz. Za postizanje jo
veeg kvaliteta, neki standardi (miniDV, DVD, digital TV) propisuju odabi-
ranje na frekvenciji 48KHz. Ukoliko se snima ili prenosi samo ljudski govor

(2
(na primer, u mobilnoj telefoniji), frekvencije odabiranja mogu biti i znatno
manje. Drugi vaan parametar digitalizacije je broj bita kojim se zapisuje svaki
pojedinani odbirak. Najee se koristi 2 bajta po odbirku (16 bita), ime se

je
dobija mogunost zapisa 216 = 65536 razliitih nivoa amplitude.
Da bi se dobio prostorni oseaj zvuka, primenjuje se tehnika viekanalnog
an
snimanja zvuka. U ovom sluaju, svaki kanal se nezavisno snima posebnim
mikrofonom i reprodukuje na posebnom zvuniku. Stereo zvuk podrazumeva
d

snimanje zvuka sa dva kanala. Surround sistemi podrazumevaju snimanje sa


vie od dva kanala (od 3 pa ak i do 10), pri emu se esto jedan poseban kanal
iz

izdvaja za specijalno snimanje niskofrekvencijskih komponenti zvuka (tzv. bas).


Najpoznatiji takvi sistemi su 5+1 gde se koristi 5 regularnih i jedan bas kanal.
o

Kao i slika, nekomprimovan zvuk zauzima puno prostora. Na primer, jedan


2 44100
sk

minut stereo zvuka snimljenog u AudioCD formatu zauzima




60 2 = 10584000 10.1 . Zbog toga se koriste tehnike kom-
presije, od kojeg je danas najkorienija tehnika kompresije sa gubitkom MP3
n

(MPEG-1 Audio-Layer 3). MP3 kompresija se zasniva na tzv. psiho-akustici


ro

koja prouava koje je komponente mogue ukloniti iz zvunog signala, a da


pritom ljudsko uho ne oseti gubitak kvaliteta signala.
kt

Pitanja i zadaci
le

Pitanje 2.1. Kako su u digitalnom raunaru zapisani svi podaci? Koliko se


E

cifara obino koristi za njihov zapis?

Pitanje 2.2. Koje su osnovne prednosti digitalnog zapisa podataka u odnosu


na analogni? Koji su osnovni problemi u korienju digitalnog zapisa podataka?

Pitanje 2.3. ta je Hornerova shema? Opisati Hornerova shemu pseudo-


kdom.

Pitanje 2.4. Koje su prednosti zapisa u potpunom komplementu u odnosu na


zapis u obliku oznaene apsolutne vrednosti?

Pitanje 2.5. ta je to IEEE 754?


58 2. Reprezentacija podataka u raunarima

Pitanje 2.6. ta je to glif, a ta font? Da li je jednoznana veza izmeu


karaktera i glifova? Navesti primere.

Pitanje 2.7. Koliko bitova koristi ASCII standard? ta ini prva 32 karaktera
ASCII tabele? Kako se odreuje binarni zapis karaktera koji predstavljaju cifre?

Pitanje 2.8. Navesti barem dve jednobajtne kdne strane koje sadre iriline
karaktere.

5)
Pitanje 2.9. Nabrojati bar tri kdne sheme u kojima moe da se zapie re
raunarstvo.

01
Pitanje 2.10. Koliko bitova koristi ASCII tabela karaktera, koliko YUSCII
tabela, koliko ISO-8859-1, a koliko osnovna UNICODE ravan? Koliko razliitih

(2
karaktera ima u ovim tabelama?

Pitanje 2.11. Koja kodiranja teksta je mogue koristiti ukoliko se u okviru is-

je
tog dokumenta eli zapisivanje teksta koji sadri jedan pasus na srpskom (pisan
latinicom), jedan pasus na nemakom i jedan pasus na ruskom (pisan irili-
an
com)?

Pitanje 2.12. U emu je razlika izmeu Unicode i UTF-8 kodiranja?


d

Pitanje 2.13. Prilikom prikazivanja filma, neki program prikazuje titlove tipa
iz

"raunari e biti...". Objasniti u emu je problem.

Pitanje 2.14. ta je to piksel? ta je to sempl?


o

Zadatak 2.1.
sk

Prevesti naredne brojeve u dekadni brojevni sistem:


(a) (10111001)2 (b) (34)16 (c) (734)8
Zadatak uraditi klasinim postupkom, a zatim i korienjem Hornerove sheme.
n


ro


Zadatak 2.2. Zapisati dekadni broj 254 u osnovama 2, 8 i 16.
kt

Zadatak 2.3. (a)


Registar ima sadraj
le

1010101101001000111101010101011001101011101010101110001010010011.
Zapisati ovaj broj u heksadekadnom sistemu.
E

(b) Registar ima sadraj 3 46189237. Zapisati ovaj sadraj u bi-


narnom sistemu.

Zadatak 2.4. Na Vebu se boje obino predstavljaju estocifrenim heksadekad-


nim kdovima u RGB sistemu: prve dve cifre odgovaraju koliini crvene boje,
naredne dve koliini zelene i poslednje dve koliini plave. Koja je koliina RGB
komponenti (na skali od 0-255) za boju #35A7DC? Kojim se kdom predstavlja
ista uta boja ako se zna da se dobija meanjem crvene i zelene? Kako iz-

gledaju kdovi za nijanse sive boje?
59 2. Reprezentacija podataka u raunarima

Zadatak 2.5. U registru se zapisuju neoznaeni brojevi. Koji raspon brojeva


moe da se zapie ukoliko je irina registra:

(a) 4 bita (b) 8 bita (c) 16 bita (d) 24 bita (e) 32 bita

Zadatak 2.6. Ukoliko se koristi binarni zapis neoznaenih brojeva irine 8


bita, zapisati brojeve:

(a) 12 (b) 123 (c) 255 (d) 300

Zadatak 2.7.

5)
U registru se zapisuju brojevi u potpunom komplementu. Koji
raspon brojeva moe da se zapie ukoliko je irina registra:

01
(a) 4 bita (b) 8 bita (c) 16 bita (d) 24 bita (e) 32 bita

Zadatak 2.8. Odrediti zapis narednih brojeva u binarnom potpunom komple-

(2
mentu irine 8 bita:

(a) 12 (b) 123 (c) 0 (d) 18 (e) 128 (f ) 200

Zadatak 2.9. Odrediti zapis brojeva -5 i 5 u potpunom komplementu duine

je
6 bita. Odrediti, takoe u potpunom komplementu duine 6 bita, zapis zbira i

an
proizvoda ova dva broja.

Zadatak 2.10. Ukoliko se zna da je korien binarni potpuni komplement


d

irine 8 bita, koji su brojevi zapisani?


(a) 11011010 (b) 01010011 (c) 10000000 (d) 11111111
iz

(e) 01111111 (f ) 00000000


ta predstavljaju dati zapisi ukoliko se zna da je korien zapis neoznaenih

o

brojeva?
sk

Zadatak 2.11. Odrediti broj bitova neophodan za kodiranje 30 razliitih karak-



tera.
n

Zadatak 2.12. Znajui da je dekadni kd za karakter A 65, navesti kodi-


ro

rani zapis rei FAKULTET ASCII kdovima u heksadekadnom zapisu. Dekodirati


sledeu re zapisanu u ASCII kdu heksadekadno: 44 49 53 4B 52 45 54 4E
kt


45.

Zadatak 2.13.
le

Korienjem ASCII tablice odrediti kdove kojima se zapisuje


tekst: "Programiranje 1". Kdove zapisati heksadekadno, oktalno, dekadno i

E

binarno. ta je sa kodiranjem teksta Matematiki fakultet?


Zadatak 2.14. Za rei raunarstvo, informatika, navesti da li ih je mogue
kodirati narednim metodima i, ako jeste, koliko bajtova zauzimaju:
(a) ASCII (b) Windows-1250 (c) ISO-8859-5
(d) ISO-8859-2 (e) Unicode (UCS-2) (f ) UTF-8

Zadatak 2.15. Odrediti (heksadekadno predstavljene) kdove kojima se za-


pisuje tekst krui u UCS-2 i UTF-8 kodiranjima. Rezultat proveriti korien-

jem HEX editora.
60 2. Reprezentacija podataka u raunarima

Zadatak 2.16. HEX editori su programi koji omoguavaju direktno pregledanje,


kreiranje i auriranje bajtova koji sainjavaju sadraja datoteka. Korienjem
HEX editora pregledati sadraj nekoliko datoteka razliite vrste (tekstualnih,
izvrnih programa, slika, zvunih zapisa, video zapisa, . . . ).

Zadatak 2.17. Uz pomo omiljenog editora teksta (ili nekog naprednijeg, uko-
liko editor nema traene mogunosti) kreirati datoteku koja sadri listu imena
10 vaih najomiljenijih filmova (pisano latinicom uz ispravno korienje dijakri-

5)
tika). Datoteka treba da bude kodirana kodiranjem:
(a) Windows-1250 (b) ISO-8859-2 (c) Unicode (UCS-2) (d) UTF-8

01
Otvoriti zatim datoteku iz nekog pregledaa Veba i prouiti ta se deava kada
se menja kodiranje koje pregleda koristi prilikom tumaenja datoteke (obino
meni View->Character encoding). Objasniti i unapred pokuati predvideti

(2
ishod (uz pomo odgovarajuih tabela koje prikazuju kdne rasporede).

Zadatak 2.18. Za datu datoteku kodiranu UTF-8 kodiranjem, korienjem

je
editora teksta ili nekog od specijalizovanih alata (na primer, iconv) rekodirati
ovu datoteku u ISO-8859-2. Eksperimentisati i sa drugim kodiranjima.
an
Zadatak 2.19. Korienjem nekog naprednijeg grafikog programa (na primer,
GIMP ili Adobe Photoshop) videti kako se boja #B58A34 predstavlja u CMY i
d

HSB modelima.
iz
o
sk
n
ro
kt
le
E
Glava 3

5)
Algoritmi i izraunljivost

01
(2
je
Neformalno govorei, algoritam
1 je precizan opis postupka za reavanje
nekog problema u konanom broju koraka. Algoritmi postoje u svim ljud-
an
skim delatnostima. U matematici, na primer, postoje algoritmi za sabiranje
i za mnoenje prirodnih brojeva. U raunarstvu, postoje algoritmi za odrei-
d

vanje najmanjeg elementa niza, ureivanje elemenata po veliini i slino. Da bi


moglo da se diskutuje o tome ta se moe a ta ne moe izraunati algoritamski,
iz

potrebno je najpre precizno definisati pojam algoritma.

3.1 Formalizacije pojma algoritma


o
sk

Precizni postupci za reavanje matematikih problema postojali su u vreme


starogrkih matematiara (na primer, Euklidov algoritam za odreivanje na-
n

jveeg zajednikog delioca dva broja), pa i pre toga. Ipak, sve do poetka
ro

dvadesetog veka nije se uviala potreba za preciznim definisanjem pojma al-


goritma. Tada je, u jeku reforme i novog utemeljivanja matematike, postavl-
kt

jeno pitanje da li postoji algoritam kojim se (pojednostavljeno reeno) mogu


2
dokazati sve matematike teoreme . Da bi se ovaj problem uopte razmatrao,
le

bilo je neophodno najpre definisati (matematiki precizno) ta je to algoritam.

1 Re algoritam ima koren u imenu persijskog astronoma i matematiara Al-Horezmija


E

(engl. Muhammad ibn Musa al-Khwarizmi). On je 825. godine napisao knjigu, u meuvre-
menu nesauvanu u originalu, verovatno pod naslovom O raunanju sa indijskim brojevima.
Ona je u dvanaestom veku prevedena na latinski, bez naslova, ali se na nju obino pozivalo
njenim poetnim reima Algoritmi de numero Indorum, to je trebalo da znai Al-Horezmi
o indijskim brojevima (pri emu je ime autora latinizovano u Algoritmi). Meutim, veina
italaca je re Algoritmi shvatala kao mnoinu od nove, nepoznate rei algoritam koja se
vremenom odomaila sa znaenjem metod za izraunavanje.
2 Ovaj problem, poznat pod imenom Entscheidungsproblem, postavio je David Hilbert
1928. godine. Poznato je da je jo Gotfrid Lajbnic u XVII veku, nakon to je napravio
mainu za raunanje, verovao da e biti mogue napraviti mainski postupak koji e, manip-
ulisanjem simbolima, biti u stanju da d odgovor na sva matematika pitanja. Ipak, 1930-ih,
rezultatima era, Tjuringa i Gedela pokazano je da ovakav postupak ne moe da postoji.

61
62 3. Algoritmi i izraunljivost

U tome, bilo je dovoljno razmatrati algoritme za izraunavanje funkcija iji su


i argumenti i rezultujue vrednosti prirodni brojevi (a drugi matematiki i ne-
matematiki algoritmi se, digitalizacijom, mogu svesti na taj sluaj). Formalno
zasnovan pojam algoritma omoguio je da kasnije budu identifikovani problemi
za koje postoje algoritmi koji ih reavaju, kao i problemi za koje ne postoje
algoritmi koji ih reavaju.
Pitanjem formalizacije pojma algoritma 1920-ih i 1930-ih godina nezavisno
se bavilo nekoliko istaknutih matematiara i uvedeno je nekoliko raznorodnih

5)
formalizama, tj. nekoliko raznorodnih sistema izraunavanja. Najznaajniji
meu njima su:

01
Tjuringove maine (Tjuring),
3 i Klini4 ),

(2
rekurzivne funkcije (Gedel

-raun 5
(er ),

Postove maine (Post ),


6

je
Markovljevi algoritmi (Markov ),
7
an
maine sa beskonano mnogo registara (engl. Unlimited Register Ma-
chines urm)8 .
d

Navedeni sistemi nastali su pre savremenih raunara i veina njih podrazu-


iz

meva odreene apstraktne maine. I u to vreme bilo je jasno da je opis postupka


dovoljno precizan ukoliko je mogue njime instruisati neku mainu koja e taj
postupak izvriti. U tom duhu, i savremeni programski jezici predstavljaju pre-
o

cizne opise algoritama i mogue ih je pridruiti gore navedenoj listi. Znaajna


sk

razlika je u tome to nabrojani formalizmi tee da budu to jednostavniji tj. da


koriste to manji broj operacija i to jednostavije modele maina, dok savre-
n

meni programski jezici tee da budu to udobniji za programiranje te ukljuuju


ro

veliki broj operacija (koje, sa teorijskog stanovita, nisu neophodne jer se mogu
definisati preko malog broja osnovnih operacija).
Blok dijagrami (kae se i algoritamske eme, dijagrami toka, tj. tokovnici )
kt

mogu se smatrati poluformalnim nainom opisa algoritama. Oni nee biti raz-
matrani u teorijskom kontekstu ali e biti korieni u reenjima nekih zadataka,
le

radi lakeg razumevanja.


Za sistem izraunavanja koji je dovoljno bogat da moe da simulira Tjuring-
E

ovu mainu i tako da izvri sva izraunavanja koja moe da izvri Tjuringova

3 Kurt Gdel (1906-1978), austrijsko-ameriki matematiar.


4 Stephen Kleene (1909-1994), ameriki matematiar.
5 Alonzo Church (1903-1995), ameriki matematiar.
6 Emil L. Post (1897-1954), ameriki matematiar.
7 Andrej Andrejevi Markov (1856-1922), ruski matematiar.
8 Postoji vie srodnih formalizama koji se nazivajuurm. Prvi opisi mogu da se nau u
radovima eperdsona i Sturdisa (engl. Sheperdson and Sturgis), dok je opis koji e biti dat
u nastavku preuzet od Katlanda (engl. Nigel Cutland).
63 3. Algoritmi i izraunljivost

maina kae se da je Tjuring potpun (engl. Turing complete). Za sistem izrau-


navanja koji izraunava identinu klasu funkcija kao Tjuringova maina kaemo
da je Tjuring ekvivalentan (engl. Turing equivalent).
Ako se neka funkcija moe izraunati u nekom od navednih formalizama,
onda kaemo da je ona izraunljiva u tom formalizmu. Iako su navedni for-
malizmi meusobno veoma razliiti, moe se dokazati da su klase izraunljivih
funkcija identine za sve njih, tj. svi oni formalizuju isti pojam algoritma i
izraunljivosti. Drugim reina, svi navedeni formalizmi su Tjuring ekvivalentni.

5)
Zbog toga se, umesto pojmova poput, na primer, Tjuring-izraunljiva ili urm-
izraunljiva funkcija (i slinih) moe koristiti samo termin izraunljiva funkcija.

01
Svi navedeni formalizmi za izraunavanje, podrazumevaju u nekom smislu,
pojednostavljeno reeno, beskonau raspoloivu memoriju. Zbog toga savre-

(2
meni raunari (koji raspolau konanom memorijom) opremljeni savremenim
programskim jezicima nisu Tjuring potpuni, u strogom smislu. S druge starne,
svako izraunavanje koje se moe opisati na nekom modernom programskom

je
jeziku, moe se opisati i kao program za Tjuringovu mainu ili neki drugi ek-
vivalentan formalizam.
an
3.2 er-Tjuringova teza
d

Veoma vano pitanje je koliko formalne definicije algoritama uspevaju da


iz

pokriju na intuitivni pojam algoritma, tj. da li je zaista mogue efektivno


izvriti sva izraunavanja definisana nekom od formalizacija izraunljivosti i,
o

obratno, da li sva izraunavanja koja intuitivno umemo da izvrimo zaista


mogu da budu opisana korienjem bilo kog od precizno definisanih formal-
sk

izama izraunavanja. Intuitivno je jasno da je odgovor na prvo pitanje potvr-


dan (jer ovek moe da simulira rad jednostavnih maina za izraunavanje),
n

ali oko drugog pitanja postoji doza rezerve. er-Tjuringova teza


9 tvrdi da je
ro

odgovor na oba navedena pitanja potvrdan.

er-Tjuringova teza:
kt

Klasa intuitivno izraunljivih funkcija identina


je sa klasom formalno izraunljivih funkcija.
le

Ovo tvrenje je hipoteza, a ne teorema i ne moe biti formalno dokazana.


E

Naime, ono govori o intuitivnom pojmu algoritma, ija svojstva ne mogu biti
formalno, matematiki ispitana. Ipak, u korist ove teze govori injenica da je
dokazano da su sve navedene formalne definicije algoritama meusobno ekvi-
valentne, kao i da do sada nije pronaen nijedan primer intuitivno, efektivno
izvodivog postupka koji nije mogue formalizovati u okviru nabrojanih formal-
nih sistema izraunavanja.

9 Ovu tezu, svaki za svoj formalizam, formulisali su nezavisno er i Tjuring.


64 3. Algoritmi i izraunljivost

3.3 ur maine
U daljem tekstu, pojam izraunljivosti bie uveden i izuavan na bazi ur
maina ( urm). Iako su po skupu izraunljivih funkcija nabrojani formalizacije
pojma algoritma jednake, oni se meusobno razlikuju po svom duhu, a ur
maina je verovatno najblia savremenom stilu programiranja: ur maina ima
ogranien (mali) skup instrukcija, programe i memoriju.
Kao i drugi formalizmi izraunavanja, ur maine su apstrakcije sine koje

5)
formalizuju pojam algoritma, predstavljajui matematiku idealizaciju rau-
nara. ur maine su dizajnirane tako da koriste samo mali broj operacija.

01
Oigledno je da nije neophodno da formalizam za izraunavanje kao osnovnu
operaciju ima, na primer, stepenovanje prirodnih brojeva, jer se ono moe

(2
svesti na mnoenje. Slino, nije neophodno da formalizam za izraunavanje
kao primitivnu operaciju ima mnoenje prirodnih brojeva, jer se ono moe
svesti na sabiranje. ak ni sabiranje nije neophodno, jer se moe svesti na

je
operaciju pronalaenja sledbenika (uveavanja za vrednost 1). Zaista, defini-
cija prirodnih brojeva podrazumeva postojanje nule i operacije sledbenika
10 i
an
sve operacije nad prirodnim brojevima se svode na te dve elementarne. Pored
njih, ur maine koriste jo samo dve instrukcije. Sva ta etiri tipa instrukcija
brojeve itaju i upisuju ih na polja ili registre beskonane trake koja slui kao
d

prostor za uvanje podataka, tj. kao memorija maine. Registri trake oznaeni
iz

su sa 1 , 2 , 3 , . . .. Svaki od njih u svakom trenutku sadri neki prirodan


broj. Stanje registara u nekom trenutku zovemo konfiguracija. Sadraj -tog
registra (registra ) oznaava se sa , kao to je to ilustrovano na sledeoj
o

slici:
sk

1 2 3 ...
1 2 3 ...
n

Spisak i kratak opis urm instrukcija (naredbi) dati su u tabeli 3.1.11 U


ro

tabeli, na primer, 0 oznaava da se vrednost 0 upisuje u registar ,


a na primer := + 1 oznaava da se sadraj registra (vrednost )
kt

uveava za jedan i upisuje u registar .


urm program je konaan numerisan niz urm instrukcija. Instrukcije se
le

izravaju redom (poevi od prve), osim u sluaju instrukcije skoka. Izvravanje


programa se zaustavlja onda kada ne postoji instrukcija koju treba izvriti
E

(kada se doe do kraja programa ili kada se naie na skok na instrukciju koja
ne postoji u numerisanom nizu instrukcija).
Poetnu konfiguraciju ini niz prirodnih brojeva 1 , 2 , . . . koji su upisani
u registre 1 , 2 , . . .. Ako je funkcija koju treba izraunati (1 , 2 , . . . , ),
onda se podrazumeva da su vrednosti 1 , 2 , . . . , redom smetene u prvih
10 Skup prirodnih brojeva se definie induktivno, kao najmanji skup koji sadri nulu i
zatvoren je u odnosu na operaciju pronalaenja sledbenika.
11 Oznake urm instrukcija potiu od naziva ovih instrukcija na engleskom jeziku (zero
instruction, succesor instruction, transfer instruction i jump instruction).
65 3. Algoritmi i izraunljivost

oznaka naziv efekat

() nulainstrukcija 0 (tj. := 0)
() instrukcija sledbenik + 1 (tj. := + 1)
(, ) instrukcija prenosa (tj. := )
(, , ) instrukcija skoka ako je = , idi na -tu;
inae idi na sledeu instrukciju

Tabela 3.1: Tabela urm instrukcija

5)
01
registara.
12 Podrazumeva se i da, na kraju rada programa, rezultat treba da

(2
bude smeten u prvi registar.
Ako urm program za poetnu konfiguraciju 1 , 2 , . . . , ne staje sa
radom, onda piemo (1 , 2 , . . . , ) . Inae, ako program staje sa radom i u
prvom registru je, kao rezultat, vrednost , onda piemo (1 , 2 , . . . , ) .

je
Kaemo da urm program izraunava funkciju : N N ako za svaku

an
-torku argumenata 1 , 2 , . . . , za koju je funkcija definisana i vai (1 ,
2 , . . ., ) = istovremeno vai i (1 , 2 , . . . , ) . Funkcija je urm-
izraunljiva ako postoji urm program koji je izraunava.
d

Primer 3.1. Neka je funkcija definisana na sledei nain: (, ) = + .


iz

Vrednost funkcije moe se izraunati za sve vrednosti argumenata i ur


mainom. Ideja algoritma za izraunavanje vrednosti + je da se vrednosti
o

doda vrednost 1, puta, jer vai:


sk

+ = + 1 + 1 + ... + 1

n

urm program podrazumeva sledeu poetnu konfiguraciju:


ro

Odgovarajui
1 2 3 . . .
kt

... ...
i sledeu konfiguraciju u toku rada programa:
le

1 2 3 ...
+ ...
E

gde je {0, 1, . . . , }.
Algoritam se moe zapisati u vidu dijagrama toka i u vidu urm programa
kao u nastavku:

12 Neki opisi ur maina podrazumevaju da iza vrednosti 1 ,2 , . . . , sledi niz nula, tj. da
su svi registri (sem poetnih koji sadre argumente programa) inicijalizovani na nulu. U ovom
tekstu se to nee podrazumevati, delom i zbog toga to promenljive u jeziku C nemaju nulu
kao podrazumevanu vrednost.
66 3. Algoritmi i izraunljivost

:= 0


=


:= + 1

5)
:= + 1

01
1. (3)

(2
2. (3, 2, 100)
3. (1)
4. (3)

je
5. (1, 1, 2)
13 .
an
Prekid rada programa je realizovan skokom na nepostojeu instrukciju 100
Bezuslovni skok je realizovan naredbom oblika (1, 1, . . .) poreenje registra
sa samim sobom uvek garantuje jednakost te se skok vri uvek.
d

Primer 3.2. Neka je funkcija definisana na sledei nain:


iz

{
0 , ako
(, ) =
1 , inae
o

urm program koji je rauna koristi sledeu konfiguraciju u toku rada:


sk

1 2 3 ...
n

...
ro

gde dobija redom vrednosti 0, 1, 2 . . . sve dok ne dostigne vrednost ili vrednost
. Prva dostignuta vrednost je broj ne vei od onog drugog. U skladu sa tim
kt

zakljukom i definicijom funkcije , izraunata vrednost je 0 ili 1.


13 Broj 100 je odabran proizvoljno kao broj sigurno vei od broja instrukcija u programu.
le

I u programima koji slede, uniformnosti radi, za prekid programa e se takoe koristiti skok
na instrukciju 100.
E
67 3. Algoritmi i izraunljivost

:= 0


=

> 0 1
=

5)

1 1
:= + 1

01

(2
1. (3) =0
2. (1, 3, 6) = ?
3. (2, 3, 8) = ?

je
4. (3) := + 1
5. (1, 1, 2)
an
6. (1) 0 1
7. (1, 1, 100) kraj
8. (1)
d

9. (1) 1 1
iz

Primer 3.3. Napisati urm program koji izraunava funkciju



o

() = [ ]
sk

Predloeni algoritam se zasniva na sledeoj osobini:



n

= [ ] 2 < ( + 1)2
ro

Odgovarajui urm program podrazumeva sledeu poetnu konfiguraciju:


kt

1 2 . . .
... ...
le

i sledeu konfiguraciju u toku svog rada:


1 2 3 4 5 ...
E

1 = 2 2 = ( + 1)2 ...
Osnovna ideja algoritma je uveavanje broja za 1 i odgovarajuih vrednosti
1 i 2 , sve dok se broj
ne nae izmeu njih. Nakon to se 1 i 2 postave
2 2
na vrednosti kvadrata dva uzastopna broja i ( + 1) , broj 1 se postepeno
uveava za 1 i proverava se da li je jednak broju . Ukoliko se ne naie na ,
a 1 dostigne vrednost 2 , onda se uveava za 1 i tada oba broja 1 i 2
2
imaju vrednost (naravno, za uveano ). Tada je potrebno 2 postaviti na
2 2 2
vrednost ( + 1) , tj. potrebno je uveati 2 za ( + 1) = 2 + 1. Ovo
se postie tako to se 2 najpre uvea za 1, a zatim puta uvea za 2. Nakon
toga se ceo postupak ponavlja.
68 3. Algoritmi i izraunljivost

:= 0

1 := 0

2 := 1

5)

= 1

01

1
1 := 1 + 1

(2

1 = 2

je
:= + 1
2 := 2 + 1
an
:= 0
d
iz

2 := 2 + 2
:= + 1
o


=
sk

1. (2) := 0
n

2. (3) 1 := 0
ro

3. (4)
4. (4) 2 := 1
kt

5. (1, 3, 17) = 1 ?
6. (3) 1 := 1 + 1
le

7. (3, 4, 9) 1 = 2 ?
E

8. (1, 1, 5)

9. (2) := + 1
10. (4) 2 := 2 + 1
11. (5) := 0
12. (4) 2 := 2 + 1
13. (4) 2 := 2 + 1
14. (5) := + 1
15. (5, 2, 5) = ?
16. (1, 1, 12)
17. (2, 1) 1
69 3. Algoritmi i izraunljivost

Primer 3.4. Neka je funkcija definisana na sledei nain:


{
0 , ako je =0
() =
, inae

Nedefinisanost funkcije se postie time to se napravi program koji se ne


zaustavlja, izuzev ako je vrednost prvog argumenta jednaka 0:
1. (1, 2, 100)

5)
2. (1, 1, 1)

01
3.4 Enumeracija urm programa
Kardinalnost. Za dva skupa kae se da imaju istu kardinalnost ako i samo

(2
ako je izmeu njih mogue uspostaviti bijektivno preslikavanje. Za skupove
koji imaju istu kardinalnost kao skup prirodnih brojeva kae se da su prebro-
jivi . Dakle, neki skup je prebrojiv ako i samo ako je njegove elemente mogue

je
poreati u niz (niz odgovara bijekciji sa skupom prirodnih brojeva). Za skupove
an
koji su ili konani ili prebrojivi, kae se da su najvie prebrojivi. Georg Kan-
tor
14 je prvi pokazao da skup realnih brojeva ima veu kardinalnost od skupa
prirodnih brojeva, tj. da skup realnih brojeva nije prebrojiv.
d

Primer 3.5. Skupovi parnih i neparnih brojeva imaju istu kardinalnost jer je
iz

izmeu njih mogue uspostaviti bijektivno preslikavanje () = + 1. Ono to


je na prvi pogled iznenaujue je da su oba ova skupa prebrojiva, odnosno imaju
o

istu kardinalnost kao i skup prirodnih brojeva, iako su njegovi pravi podskupovi
sk

(npr. () = 2 uspostavlja bijekciju izmeu skupa prirodnih i skupa parnih


brojeva).
n

Lema 3.1. Ureenih parova prirodnih brojeva ima prebrojivo mnogo.


ro

Dokaz: Razmotrimo beskonanu tabelu elemenata koja odgovara svim ure-


kt

enim parovima prirodnih brojeva. Mogue je izvriti cik-cak obilazak


ove tabele, polazei od gornjeg levog ugla kao to je to pokazano na slici
le

3.1.
E

Prilikom obilaska, svakom elementu je mogue dodeliti jedinstven redni


broj, ime je uspostavljena traena bijekcija sa skupom prirodnih brojeva.


Korienjem slinih ideja, mogu se dokazati i sledea tvrenja.

Lema 3.2. Dekartov proizvod konano mnogo prebrojivih skupova je prebrojiv.

Lema 3.3. Najvie prebrojiva unija prebrojivih skupova je prebrojiv skup.

14 Georg Cantor, 18451918, nemaki matematiar.


70 3. Algoritmi i izraunljivost

1 2 3 4 5 6 7
1 1 2 6 7 ...

2 3 5 8
3 4 9
4
5

5)
6
7

01
. .
. .
. .

Slika 3.1: Cik-cak nabrajanje

(2
Enumeracija urm programa.
je
Vano pitanje je koliko ima urm programa i
koliko ima razliitih urm programa (tj. onih koji izraunavaju razliite funkcije).
an
Oigledno je da ih ima beskonano, a naredna tvrenja govore da ih ima pre-
brojivo mnogo.
d

Lema 3.4. Postoji prebrojivo mnogo razliitih urm instrukcija.


iz

Dokaz: Instrukcija tipa , trivijalno, ima prebrojivo mnogo ( instrukcije


o

se mogu poreati u niz na sledei nain: (1), (2), . . .). Slino je i


sk

sa instrukcijama tipa . instrukcije bijektivno odgovaraju parovima


prirodnih brojeva, pa ih (na osnovu leme 3.2) ima prebrojivo mnogo.
n

Slino, instrukcije odgovaraju bijektivno trojkama prirodnih brojeva,


pa ih (na osnovu leme 3.2) ima prebrojivo mnogo. Skup svih instrukcija
ro

je unija ova etiri prebrojiva skupa, pa je na osnovu leme 3.3 i ovaj skup
prebrojiv. 
kt

urm programa ima prebrojivo mnogo


le

Teorema 3.1. Razliitih


E

Dokaz: Skup svih urm programa se moe predstaviti kao unija skupa pro-
grama koji imaju jednu instrukciju, skupa programa koji imaju dve in-
strukcije i tako dalje. Svaki od ovih skupova je prebrojiv (na osnovu leme
3.2) kao konaan Dekartov stepen prebrojivog skupa instrukcija. Dakle,
skup svih urm programa je prebrojiv (na osnovu leme 3.3) kao prebrojiva
unija prebrojivih skupova. 

Na osnovu teoreme 3.1, mogue je uspostaviti bijekciju izmeu skupa svih


urm programa i skupa prirodnih brojeva. Drugim reima, moe se definisati
71 3. Algoritmi i izraunljivost

pravilo koje svakom urm programu dodeljuje jedinstven prirodan broj i koje
svakom prirodnom broju dodeljuje jedinstven urm program. Zbog toga, za
fiksirano dodeljivanje brojeva programima, moemo da govorimo o prvom, dru-
gom, treem, . . . , stotom urm programu itd.
3.5 Neizraunljivost i neodluivost

5)
Razmotrimo narednih nekoliko problema.

1. Neka su data dva konana skupa rei. Pitanje je da li je mogue nadovezati

01
nekoliko rei prvog skupa i, nezavisno, nekoliko rei drugog skupa tako
da se dobije ista re. Na primer, za skupove {, , } i {, , },

(2
jedno reenje je = . Za skupove {, } i
{, } reenje ne postoji, jer se nadovezivanjem rei prvog skupa uvek
dobija re ija su poslednja dva slova razliita, dok se nadovezivanjem rei

je
drugog skupa uvek dobija re ija su poslednja dva slova ista. Zadatak je
utvrditi da li postoji opti algoritam koji za proizvoljna dva zadata skupa
an
rei odreuje da li traena nadovezivanja postoje.
15

2. Neka su date Diofantske jednaine (1 , . . . , ) = 0, gde je polinom


d

sa celobrojnim koeficijentima. Zadatak je utvrditi da li postoji opti


iz

algoritam kojim se odreuje da li proizvoljna zadata diofantska jednaina


ima racionalnih reenja.
16
o

3. Zadatak je utvrditi da li postoji opti algoritam koji proverava da li se


17
sk

proizvoljni zadati program zaustavlja za date ulazne parametre.

4. Zadatak je utvrditi da li postoji opti algoritam koji za proizvoljni za-


n

dati skup aksioma i zadato tvrenje proverava da li je tvrnje posledica


18
ro

aksioma.

Za sva etiri navedena problema pokazano je da su algoritamski nereivi ili


kt

neodluivi (tj. ne postoji izraunljiva funkcija koja ih reava). Ovo ne znai


da nije mogue reiti pojedine instance problema
19 , ve samo da ne postoji
le

jedinstven, opti postupak koji bi mogao da rei proizvoljnu instancu problema.


Pored nabrojanih, ima jo mnogo vanih neodluivih problema
E

15 Ovaj problem se naziva Posts correspondence problem, jer ga je postavio i reio Emil
Post 1946. godine.
16 Ovaj problem je 10. Hilbertov problem izloen 1900. godine kao deo skupa problema
koje matematiari XIX veka ostavljaju u amanet matematiarima XX veka. Problem je
reio Matijaevi 1970-ih godina.
17 Ovaj problem reio je Alan Tjuring 1936. godine.
18 Ovaj, ve pomenut, problem, formulisao je David Hilbert 1928. godine, a neto kasnije
reenje je proisteklo iz rezultata era, Tjuringa i Gedela.
19 Instanca ili primerak problema je jedan konkretan zadatak koji ima isti oblik kao i
opti problem. Na primer, za prvi u navedenom spisku problema, jedna instanca je zadatak
ispitivanja da li je mogue nadovezati nekoliko rei skupa {, } i, nezavisno, nekoliko rei
skupa {, } tako da se dobije ista re.
72 3. Algoritmi i izraunljivost

U nastavku e precizno, korienjem formalizma ur maina, biti opisan


trei od navedenih problema, tj. halting problem, izuzetno vaan za teorijsko
raunarstvo.

Problem ispitivanja zaustavljanja programa (halting problem). Pi-


tanje zaustavljanja raunarskih programa je jedno od najznaajnijih pitanja
raunarstva i programiranja. esto je veoma vano da li se neki program zaus-

5)
tavlja za neku ulaznu vrednost, da li se zaustavlja za ijednu ulaznu vrednost
i slino. Za mnoge konkretne programe i za mnoge konkretne ulazne vred-
nosti, na ovo pitanje moe se odgovoriti. No, nije oigledno da li postoji opti

01
postupak kojim se za proizvoljni dati program i proizvoljne vrednosti ulaznih
argumenata moe proveriti da li se program zaustavlja ako se pokrene sa tim

(2
argumentima.
Iako se problem ispitivanja zaustavljanja moe razmatrati i za programe
u savremenim programskim jezicima, u nastavku emo razmotriti formulaciju
urm programe:
je
halting problema za

Da li postoji urm program koji na ulazu dobija drugi urm program i neki
an
broj i ispituje da li se program zaustavlja za ulazni parametar ?
Problem prethodne formulacije je injenica da traeni urm program mora
d

na ulazu da prihvati kao svoj argument drugi urm program, to je naizgled


urm programi kao argumente mogu da imaju
iz

nemogue, s obzirom na to da
samo prirodne brojeve. Ipak, ovo se jednostavno razreava zahvaljujui tome
to je svakom urm programu mogue dodeliti jedinstveni prirodan broj
o

koji ga identifikuje, i obratno, svakom broju moe se dodeliti program


sk

(kao to je opisano u poglavlju 3.4). Imajui ovo u vidu, dolazi se do teoreme


o halting problemu za urm.
n

Teorema 3.2 (Neodluivost halting problema) . Neka je funkcija definisana


ro

na sledei nain:
{
1, ako se program zaustavlja za ulaz
kt

(, ) =
0, inae.
le

Ne postoji program koji izraunava funkciju , tj. ne postoji program koji za


zadate vrednosti i moe da proveri da li se program zaustavlja za ulazni
E

argument .

Dokaz: Pretpostavimo da postoji program koji izraunava funkciju . Onda


se jednostavno moe konstruisati i program sa jednim argumentom ,
koji vraa rezultat 1 (tj. upisuje ga u prvi registar) ako se program
zaustavlja za ulaz, a rezultat 0 ako se program ne zaustavlja za ulaz
. Dalje, postoji i program (dobijen kombinovanjem programa i
programa iz primera 3.4) koji za argument vraa rezultat 0 ako se
ne zaustavlja za (tj. ako je (, ) = 0), a izvrava beskonanu petlju
ako se zaustavlja za (tj. ako je (, ) = 1). Za program vai:
73 3. Algoritmi i izraunljivost

() 0 ako je ()
() ako je ()

Ako postoji takav program , onda se i on nalazi u nizu svih programa


tj. postoji redni broj koji ga jedinstveno identifikuje, pa vai:

5)
() 0 ako je ()
() ako je ()

01
No, ako je jednako upravo , pokazuje se da je definicija ponaanja
programa (tj. programa ) kontradiktorna: program (tj. program

(2
) za ulaznu vrednost vraa 0 ako se ne zaustavlja za , a izvrava
beskonanu petlju ako se zaustavlja za :

je
() 0 ako je ()
an
() ako je ()

Dakle, polazna pretpostavka je bila pogrena i ne postoji program ,


d

tj. funkcija nije izraunljiva. Poto funkcija , karakteristina funkcija


iz

halting problema, nije izraunljiva, halting problem nije odluiv. 


o

Funkcija koja odgovara halting problemu je jedna od najznaajnih funkcija


iz skupa prirodnih brojeva u skup prirodnih brojeva koje ne mogu biti izrau-
sk

nate, ali takvih, neizraunljivih funkcija, ima jo mnogo. Moe se dokazati da


funkcija jedne promenljive koje za ulazni prirodni broj vraaju iskljuivo 0 ili
n

1 (tj. funkcija iz u {0, 1}) ima neprebrojivo mnogo, dok programa ima samo
ro

prebrojivo mnogo. Iz toga direktno sledi da ima neprebrojivo mnogo funkcija


koje nisu izraunljive.
kt

3.6 Vremenska i prostorna sloenost izraunavanja


le

Prvo pitanje koje se postavlja kada je potrebno izraunati neku funkciju


E

(tj. napisati neki program) je da li je ta funkcija izraunljiva (tj. da li uopte pos-


toji neki program koji je izraunava). Ukoliko takav program postoji, sledee
pitanje je koliko izvravanje tog program zahteva vremena i prostora (memo-
rije). U kontekstu urm programa, pitanje je koliko za neki urm program treba
izvriti pojedinanih instrukcija (ukljuujui ponavljanja) i koliko registara se
koristi. U urm programu navedenom u primeru 3.1 postoje ukupno etiri in-
strukcije, ali se one mogu ponavljati (za neke ulazne vrednosti). Jednostavno
se moe izraunati da se prva instrukcija u nizu izvrava + 1 puta, a preostale
tri po puta. Dakle, ukupan broj izvrenih instrukcija za ulazne vrednosti i
je jednak 4 + 1. Ako se razmatra samo takozvani red algoritma, onda se zane-
maruju konstante kojima se mnoe i sabiraju vrednosti ulaznih argumenata, pa
74 3. Algoritmi i izraunljivost

je vremenska sloenost u ovom primeru linearna po drugom argumentu, argu-


mentu (i to se zapisuje ()). Bez obzira na vrednosti ulaznih argumenata,
program koristi tri registra, pa je njegova prostorna sloenost konstantna (i to
se zapisuje (1)). Najee se sloenost algoritma odreuje tako da ukazuje na
to koliko on moe utroiti vremena i prostora u najgorem sluaju. Ponekad je
mogue izraunati i prosenu sloenost algoritma prosenu za sve mogue
vrednosti argumenata. O prostornoj i vremenoskoj sloenosti algoritama bie
vie rei u drugom tomu ove knjige.

5)
Pitanja i zadaci za vebu

01
Pitanje 3.1. Po kome je termin algoritam dobio ime?

(2
Pitanje 3.2. ta je to algoritam (formalno i neformalno)? Navesti nekoliko
formalizma za opisivanje algoritama. Kakva je veza izmeu formalnog i nefor-
malnog pojma algoritma. ta tvrdi er-Tjuringova teza? Da li se ona moe

je
dokazati?
an
Pitanje 3.3. Da li postoji algoritam koji opisuje neku funkciju iz skupa prirod-
nih brojeva u skup prirodnih brojeva i koji moe da se isprogramira u program-
d

skom jeziku C i izvri na savremenom raunaru, a ne moe na Tjuringovoj


maini?
iz

Pitanje 3.4. urm izraunljiva funkcija intuitivno izraunljiva?


Da li je svaka
urm izraunljiva?
Da li je svaka intuitivno izraunljiva funkcija
o

Pitanje 3.5. U emu je kljuna razlika izmeu urm maine i bilo kog stvarnog
sk

raunara?
n

Pitanje 3.6. Opisati efekaturm naredbe (, , ).


ro

Pitanje 3.7. Da li se nekim urm programom moe izraunati hiljadita cifra


broja 21000 ?
kt


Pitanje 3.8. Da li postoji urm program koji izraunava broj
2? Da li postoji
urm program koji izraunava -tu decimalnu cifru broja
le

2, gde je zadati
prirodan broj?
E

Pitanje 3.9. Da li se nekim urm programom moe izraunati hiljadita deci-


malna cifra broja ?

Pitanje 3.10. Koliko ima racionalnih brojeva? Koliko ima kompleksnih bro-
jeva? Koliko ima razliitih programa za Tjuringovu mainu? Koliko ima ra-
zliitih programa u programskom jeziku C?

Pitanje 3.11. Koliko elemenata ima unija konano mnogo konanih skupova?
Koliko elemenata ima unija prebrojivo mnogo konanih skupova? Koliko ele-
menata ima unija konano mnogo prebrojivih skupova? Koliko elemenata ima
unija prebrojivo mnogo prebrojivih skupova?
75 3. Algoritmi i izraunljivost

Pitanje 3.12. Koliko ima razliitih urm programa? Kakva je kardinalnost


skupa urm programa u odnosu na kardinalnost skupa prirodnih brojeva? Kakva
je kardinalnost skupa urm programa u odnosu na kardinalnost skupa realnih
brojeva? Kakva je kardinalnost skupa urm programa u odnosu na kardinalnost
skupa programa na jeziku C?

Pitanje 3.13. Da li se svakom urm programu moe pridruiti jedinstven priro-


dan broj (razliit za svaki program)? Da li se svakom prirodnom broju moe
urm program (razliit za svaki broj)?

5)
pridruiti jedinstven

Pitanje 3.14. Da li se svakom urm programu moe pridruiti jedinstven realan

01
broj (razliit za svaki program)? Da li se svakom realnom broju moe pridruiti
jedinstven urm program (razliit za svaki broj)?

(2
Pitanje 3.15. Kako se naziva problem ispitivanja zaustavljanja programa?
Kako glasi halting problem? Da li je on odluiv ili nije? Ko je to dokazao?

je
Pitanje 3.16. 1. Da li postoji algoritam koji za drugi zadati urm program
an
utvruje da li se zaustavlja ili ne?

2. Da li postoji algoritam koji za drugi zadati urm utvruje da li se zaus-


d

tavlja posle 100 koraka?

urm program koji za drugi zadati urm program


iz

3. Da li je mogue napisati
proverava da li radi beskonano?

urm program koji za drugi zadati urm program


o

4. Da li je mogue napisati
sk

proverava da li vraa vrednost 1?

5. Da li je mogue napisati urm program kojim se ispituje da li data izraun-


n

ljiva funkcija (ona za koju postoji urm program) zadovoljava da je


ro

(0) = 0?
6. Da li je mogue napisati urm program koji za drugi zadati urm program
kt

ispituje da li izraunava vrednost 2012 i zato?

Pitanje 3.17.
le

Na primeru korena uporedite URM sa savremenim asemberl-


skim jezicima. Da li URM ima neke prednosti?
E

Zadatak
3.1. Napisati urm program koji izraunava funkciju (, ) = .

Zadatak 3.2. Napisati urm program koji izraunava funkciju () = 2 .


Zadatak 3.3. Napisati urm program koji izraunava funkciju (, ) = .
Zadatak 3.4. Napisati urm program koji izraunava funkciju:
{
1 , ako
(, ) =
0 , inae
76 3. Algoritmi i izraunljivost

Zadatak 3.5. Napisati urm program koji izraunava funkciju


{
, ako
(, ) =
0 , inae

Zadatak 3.6. Napisati urm program koji izraunava funkciju:

5)
{
/3 , ako 3|
() =
, inae

01
Zadatak 3.7. urm program koji izraunava funkciju () = !.
Napisati

(2
Zadatak 3.8. Napisati urm program koji izraunava funkciju () = 2
[ ]
3 .

Zadatak 3.9. Napisati urm program koji broj 1331 smeta u prvi registar.

je
Zadatak 3.10. Napisati urm program koji izraunava funkciju () = 1000.
an
Zadatak 3.11. Napisati urm program koji izraunava funkciju (, ) = 2 +
.
d

Zadatak 3.12. Napisati urm program koji izraunava funkciju (, ) =


iz

(, ), odnosno:
{
, ako
(, ) =
o

, inae

urm
sk

Zadatak 3.13. Napisati program koji izraunava funkciju (, ) =


2(+)
n

Zadatak 3.14. Napisati urm program koji izraunava funkciju


ro

{
1 , ako |
(, ) =
kt

0 , inae

Zadatak 3.15. Napisati urm program koji izraunava funkciju


le

{ [ ]
, ako = 0
E

(, ) =
, inae

Zadatak 3.16. Napisati urm program koji izraunavaju sledeu funkciju:


{
2 , <
(, ) =
,

Zadatak 3.17. Napisati urm program koji izraunava funkciju:


{
/3 , 3|
(, ) =
2 ,
77 3. Algoritmi i izraunljivost

Zadatak 3.18. Napisati urm program koji izracunava funkciju (, , ) =


++
Zadatak 3.19. Napisati urm program koji izracunava funkciju (, , ) =
(, , ).

Zadatak 3.20. Napisati urm program koji izraunava funkciju


{ [ ]

5)
3 , ako 2|
(, , ) =
+1 , inae

01
Zadatak 3.21. Napisati urm program koji izraunava funkciju
{
1, ako je + >

(2
(, , ) =
2, inae

je
an
d
iz
o
sk
n
ro
kt
le
E
Glava 4

5)
Vii programski jezici

01
(2
je
Razvoj programskih jezika, u bliskoj je vezi sa razvojem raunara tj. sa
razvojem hardvera. Programiranje u dananjem smislu nastalo je sa pojavom
an
raunara fon Nojmanovog tipa iji se rad kontrolie programima koji su smeteni
u memoriji, zajedno sa podacima nad kojim operiu. Na prvim raunarima
d

tog tipa moglo je da se programira samo na mainski zavisnim programskim


iz

jezicima, a od polovine 1950-ih nastali su jezici vieg nivoa koji su drastino


olakali programiranje.
Prvi programski jezici zahtevali su od programera da bude upoznat sa na-
o

jfinijim detaljima raunara koji se programira. Problemi ovakvog naina pro-


sk

gramiranja su viestruki. Naime, ukoliko je eleo da programira na novom rau-


naru, programer je morao da izui sve detalje njegove arhitekture (na primer,
n

skup instrukcija procesora, broj registara, organizaciju memorije). Programi


napisani za jedan raunar mogli su da se izvravaju iskljuivo na istim takvim
ro

raunarima i prenosivost programa nije bila mogua.


Vii programski jezici namenjeni su ljudima a ne mainama i sakrivaju de-
kt

talje konkretnih raunara od programera. Specijalizovani programi (tzv. jeziki


procesori, programski prevodioci, kompilatori ili interpretatori ) na osnovu speci-
le

fikacije zadate na viem (apstraktnijem) nivou mogu automatski da proizvedu


mainski kd za specifian raunar na kojem se programi izvravaju. Ovim se
E

omoguava prenosivost programa (pri emu, da bi se program mogao izvravati


na nekom konkretnom raunaru, potrebno je da postoji procesor vieg program-
skog jezika ba za taj raunar). Takoe, znatno se poveava nivo apstrakcije
prilikom procesa programiranja to u mnogome olakava ovaj proces.
Razvojni ciklus programa u veini savremenih viih programskih jezika tee
na sledei nain. Danas je, nakon faze planiranja, prva faza u razvoju pro-
grama njegovo pisanje tj. unoenje programa na viem programskom jeziku
(tzv. izvorni program ili izvorni kd engl. source code), to se radi pomou
nekog editora teksta. Naredna faza je njegovo prevoenje, kada se na osnovu
izvornog programa na viem programskom jeziku dobija prevedeni kd na asem-

78
79 4. Vii programski jezici

blerskom odnosno mainskom jeziku (tzv. objektni kd engl. object code), to


se radi pomou nekog programskog prevodioca. U fazi povezivanja vie ob-
jektnih programa povezuje se sa objektnim kdom iz standardne biblioteke u
jedinstvenu celinu (tzv. izvrni program engl. executable program). Povezi-
vanje vri specijalizovan program poveziva, tj. linker (engl. linker) ili ureiva
veza . Nakon povezivanja, kreiran je program u izvrnom obliku i on moe da
se izvrava . Nabrojane faze se obino ponavljaju, vri se dopuna programa,
ispravljanje greaka, itd.

5)
4.1 Kratki pregled istorije programskih jezika

01
Prvim viim programskim jezikom najee se smatra jezik FORTRAN,

(2
nastao u periodu 1953-1957 godine. Njegov glavni autor je Don Bakus , koji
1
je implementirao i prvi interpretator i prvi kompilator za ovaj jezik. Ime FOR-
TRAN dolazi od The IBM Mathematical FORmula TRANslating System, to

je
ukazuje na to da je osnovna motivacija bila da se u okviru nauno-tehnikih pri-
mena omogui unoenje matematikih formula, dok je sistem taj koji bi unete
an
matematike formule prevodio u niz instrukcija koje raunar moe da izvrava.
Neposredno nakon pojave Fortrana, Don Mekarti
2 je dizajnirao programski
jezik LISP (LISt Processing ), zasnovan na -raunu. LISP je uveo funkcionalno
d

programiranje i dugo bio najpopularniji jezik u oblasti vetake inteligencije.


iz

Krajem 1950-ih godina, nastao je i jezik COBOL (COmmon Business-Oriented


Language ), ije su osnovne primene u oblasti poslovanja. Zanimljivo je da puno
o

starih COBOL programa i danas uspeno radi u velikim poslovnim sistemima


kod nas i u svetu.
sk

Rani programski jezici, nastali pod uticajem asemblerskih jezika, intenzivno


koriste skokove u programima (tzv. GOTO naredbu) to dovodi do programa
n

koje je teko razumeti i odravati. Kao odgovor na softversku krizu 1970-


ro

ih godina (period kada zbog loe prakse programiranja softver nije mogao da
dostigne mogunosti hardvera), nastala je praksa strukturnog programiranja
kt

u kojoj se insistira na disciplinovanom pristupu programiranju, bez nekon-


trolisanih skokova i uz korienje samo malog broja naredbi za kontrolu toka
programa.
le

Krajem 1950-ih godina zapoet je razvoj programskog jezika ALGOL 60


E

koji je uneo mnoge koncepte prisutne skoro u svim dananjim programskim


jezicima. Tokom 1970-ih pojavio se jezik C koji i dalje predstavlja osnovni
jezik sistemskog programiranja. Tokom 1970-ih pojavio se i jezik Pascal koji je
vrlo elegantan jezik iji je cilj bio da ohrabri strukturno programiranje i koji se
zbog ovoga (u svojim unapreenim oblicima) i danas ponegde koristi u nastavi
programiranja.

1 John Backus (19242007), ameriki naunik iz oblasti raunarstva, dobitnik Tjuringove


nagrade.
2 John McCarthy (19272011), ameriki naunik iz oblasti raunarstva. Dobitnik
Tjuringove nagrade za svoj rad na polju vetake inteligencije.
80 4. Vii programski jezici

Kao odgovor na jo jednu softversku krizu prelazi se na korienje tzv. objekt-


no-orijentisanih jezika koji olakavaju izradu velikih programa i podelu posla
u velikim programerskim timovima. Tokom 1980-ih se pojavljuje jezik C++
koji nadograuje jezik C objektno-orijentisanim konceptima, a tokom 1990-ih,
pod uticajem interneta, i jezik Java, ija je jedna od osnovnih ideja prenosivost
izvrnog kda izmeu heterogenih raunarskih sistema. Microsoft, je krajem
1990-ih zapoeo razvoj jezika C# koji se danas esto koristi za programiranje
Windows aplikacija.

5)
Pojavom veba postaju znaajni skript jezici, kao to su PHP, JavaScript,
Python, Ruby itd.

01
4.2 Klasifikacije programskih jezika

(2
Po nainu programiranja, programski jezici se klasifikuju u programske
paradigme.

je
Najkorieniji programski jezici danas spadaju u grupu imperativnih pro-
gramskih jezika. Kako u ovu grupu spada i programski jezik C (ali i program-
an
ski jezik Pascal, Fortran, Basic itd.), u nastavku e biti najvie rei upravo o
ovakvim jezicima. U ovim jezicima stanje programa karakteriu promenljive
kojima se predstavljaju podaci i naredbe kojima se vre odreene transfor-
d

macije promenljivih. Pored imperativne, znaajne programske paradigme su i


iz

objektno-orijentisana (u nju spadaju C++, Java, C# itd.), funkcionalna (u nju


spadaju Lisp, Haskell, ML, itd.), logika (u nju spada, na primer, Prolog). U
o

savremenim jezicima meaju se karakteristike razliitih programskih paradigmi


tako da je podela sve manje striktna.
sk

Veina programskih jezika danas je proceduralna to znai da je zadatak


programera da opie nain (proceduru) kojim se dolazi do reenja problema.
n

Nasuprot njima, deklarativni programski jezici (na primer Prolog) od pro-


ro

gramera zahtevaju da precizno opie problem, dok se mehanizam programskog


jezika onda bavi pronalaenjem reenja problema. Iako uspeni u ogranienim
kt

domenima, deklarativni jezici jo nisu dovoljno efikasni da bi bili dominantno


korieni.
le

4.3 Leksika, sintaksa, semantika programskih jezika


E

Da bi bilo mogue pisanje i prevoenje programa u odgovarajue programe


na mainskom jeziku nekog konkretnog raunara, neophodno je precizno defin-
isati ta su ispravni programi nekog programskog jezika, kao i precizno defin-
isati koja izraunavanja odgovaraju naredbama programskog jezika. Pitanjima
ispravnosti programa bavi se sintaksa programskih jezika (i njena podoblast
leksika programskih jezika ). Leksika se bavi opisivanjem osnovnih gradivnih
elemenata jezika, a sintaksa nainima za kombinovanje tih osnovnih elemenata
u ispravne jezike konstrukcije. Pitanjem znaenja programa bavi se seman-
tika programskih jezika . Leksika, sintaksa i semantika se izuavaju ne samo za
programske jezike, ve i za druge vetake jezike, ali i za prirodne jezike.
81 4. Vii programski jezici

Leksika. Osnovni leksiki elementi prirodnih jezika su rei, pri emu se raz-
likuje nekoliko razliitih vrsta rei (imenice, glagoli, pridevi, . . . ) i rei imaju
razliite oblike (padei, vremena, . . . ). Zadatak leksike analize prirodnog
jezika je da identifikuje rei u reenici i svrsta ih u odgovarajue kategorije.
Slino vai i za programske jezike. Programi se raunaru zadaju predstavl-
jeni nizom karaktera. Pojedinani karakteri se grupiu u nedeljive celine koje
predstavljaju osnovne leksike elemente, koji bi bili analogni reima govornog
jezika. Na primer, leksika jezika ur maina razlikuje rezervisane rei ( , , ,

5)
) i brojevne konstante. Razmotrimo naredni fragment kda u jeziku C:

01
if (a < 3)
x1 = 3+4*a;

(2
U ovom kdu, razlikuju se sledee lekseme (rei) i njima pridrueni tokeni
(kategorije).
if

je
kljuna re
( zagrada
a
an
identifikator
< operator
3 celobrojni literal
d

) zagrada
x1
iz

identifikator
= operator
3 celobrojni literal
o

+ operator
sk

4 celobrojni literal
* operator
a
n

celobrojni literal
; interpunkcija
ro

Leksikom programa obino se bavi deo programskog prevodioca koji se


naziva leksiki analizator .
kt

Sintaksa. Sintaksa prirodnih jezika definie naine na koji pojedinane rei


le

mogu da kreiraju ispravne reenice jezika. Slino je i sa programskim jezicima,


gde se umesto ispravnih reenica razmatraju ispravni programi. Na primer,
E

sintaksa jezika ur maina definie ispravne programe kao nizove instrukcija


oblika: (broj), (broj), (broj, broj, broj) i (broj, broj). Sintaksa definie
formalne relacije izmeu elemenata jezika, time pruajui strukturne opise is-
pravnih niski jezika. Sintaksa se bavi samo formom i strukturom jezika bez
bilo kakvih razmatranja u vezi sa njihovim znaenjem. Sintaksika struktura
reenica ili programa se moe predstaviti u obliku stabla. Prikazani fragment
kda je u jeziku C sintaksiki ispravan i njegova sintaksika struktura se moe
predstaviti na sledei nain:
82 4. Vii programski jezici

< =

3 1 +

3 *

5)
4

01
Dakle, taj fragment kda predstavlja if-then naredbu (iskaz) kojoj je uslov
dat izrazom poreenja vrednosti promenljive a i konstante 3, a telo predstavlja
naredba dodele promenljivoj x vrednosti izraza koji predstavlja zbir konstante

(2
3 i proizvoda konstante 4 i vrednosti promenljive a.
Sintaksom programa obino se bavi deo programskog prevodioca koji se
naziva sintaksiki analizator.

Semantika. je
an
Semantika pridruuje znaenje sintaksiki ispravnim niskama
jezika. Za prirodne jezike, semantika pridruuje ispravnim reenicama neke
specifine objekte, misli i oseanja. Za programske jezike, semantika za dati
d

program opisuje koje je izraunavanje opisano tim programom.


iz

Tako se, na primer, naredbi if (a < 3) x1 = 3+4*a; jezika C moe pri-


druiti sledee znaenje: Ako je tekua vrednost promenljive a manja od 3,
tada promenljiva x1 treba da dobije vrednost zbira broja 3 i etvorostruke
o

tekue vrednosti promenljive a.


sk

Postoje sintaksiki ispravne reenice prirodnog jezika kojima nije mogue


dodeliti ispravno znaenje, tj. za njih nije definisana semantika, na primer:
n

Bezbojne zelene ideje besno spavaju ili Pera je oenjeni neenja. Slino je
i sa programskim jezicima. Neki aspekti semantike korektnosti programa se
ro

mogu proveriti tokom prevoenja programa (na primer, da su sve promenljive


koje se koriste u izrazima definisane i da su odgovarajueg tipa), dok se neki
kt

aspekti mogu proveriti tek u fazi izvravanja programa (na primer, da ne dolazi
do deljenja nulom). Na osnovu toga, razlikuje se statika i dinamika semantika.
le

Naredni C kd nema jasno definisano dinamiko znaenje, pa u fazi izvravanja


dolazi do greke (iako se prevoenje na mainski jezik uspeno izvrava).
E

int x = 0;
int y = 1/x;
Dok veina savremenih jezika ima precizno i formalno definisanu leksiku
i sintaksu, formalna definicija semantike postoji samo za neke programske
3
jezike . U ostalim sluajevima, semantika programskog jezika se opisuje ne-
formalno, opisima zadatim korienjem prirodnog jezika. est je sluaj da neki

3 Leksika se obino opisuje regularnim izrazima, sintaksa kontekstno slobodnim gra-


matikama, dok se semantika formalno zadaje ili aksiomatski (npr. u obliku Horove logike) ili
u vidu denotacione semantike ili u vidu operacione semantike.
83 4. Vii programski jezici

aspekti semantike ostaju nedefinisani standardom jezika i preputa se imple-


mentacijama kompilatora da samostalno odrede potpunu semantiku. Tako, na
primer, programski jezik C ne definie kojim se redom vri izraunavanje vred-
nosti izraza, to u nekim sluajevima moe da dovede do razliitih rezultata
istog programa prilikom prevoenja i izvravanja na razliitim sistemima (na
primer, nije definisano da li se za izraunavanje vrednosti izraza f() + g()
najpre poziva funkcija f ili funkcija g).

5)
4.4 Pragmatika programskih jezika

01
Pragmatika jezika govori o izraajnosti jezika i o odnosu razliitih naina za
iskazivanje istih stvari. Pragmatika prirodnih jezika se odnosi na psiholoke i

(2
socioloke aspekte kao to su korisnost, opseg primena i efekti na korisnike. Isti
prirodni jezik se koristi drugaije, na primer, u pisanju tehnikih uputstava, a
drugaije u pisanju pesama. Pragmatika programskih jezika ukljuuje pitanja

je
kao to su lakoa programiranja, efikasnost u primenama i metodologija pro-
gramiranja. Pragmatika je kljuni predmet interesovanja onih koji dizajniraju
an
i implementiraju programske jezike, ali i svih koji ih koriste. U pitanja prag-
matike moe da spada i izbor naina na koji napisati neki program, u zavisnosti
od toga da li je, u konkretnom sluaju, vano da je program kratak ili da je
d

jednostavan za razumevanje ili da je efikasan.


iz

Pragmatika jezika se, izmeu ostalog, bavi i sledeim pitanjima dizajna


programskih jezika.
o

Promenljive i tipovi podataka.


sk

U fon Nojmanovom modelu, podaci se


smetaju u memoriju raunara (najee predstavljeni nizom bitova). Meu-
tim, programski jezici uobiajeno, kroz koncept promenljivih, nude programer-
n

ima apstraktniji pogled na podatke. Promenljive omoguavaju programeru da


ro

imenuje podatke i da im pristupa na osnovu imena, a ne na osnovu memorijskih


adresa (kao to je to sluaj kod asemblerskih jezika). Svakoj promenljivoj dodel-
kt

jen je odreen broj bajtova u memoriji (ili, eventualno, u registrima procesora)


kojima se predstavljaju odgovarajui podaci. Pravila ivotnog veka (engl. life-
time) odreuju u kom delu faze izvravanja programa je promenljivoj dodeljen
le

memorijski prostor (promenljivu je mogue koristiti samo tada). Nekada je


E

mogue na razliitim mestima u programu koristiti razliite promenljive istog


imena i pravila dosega indentifikatora (engl. scope) odreuju deo programa u
kome se uvedeno ime moe koristiti za imenovanje odreenog objekta (najee
promenljive).
Organizovanje podataka u tipove omoguava da programer ne mora da
razmilja o podacima na nivou njihove interne (binarne) reprezentacije, ve
da o podacima moe razmiljati znatno apstraktnije, dok se detalji interne
reprezentacije preputaju jezikom procesoru. Najei tipovi podataka di-
rektno podrani programskim jezicima su celi brojevi (0, 1, 2, -1, -2, . . . ),
brojevi u pokretnom zarezu (1.0, 3.14, . . . ), karakteri (a, b, 0, ,, !, . . . ), niske
84 4. Vii programski jezici

("zdravo"), . . . Pored ovoga, programski jezici obino nude i mogunost ko-


rienja sloenih tipova (na primer, nizovi, strukture tj. slogovi koji mogu da
objedinjavaju nekoliko promenjivih istog ili razliitog tipa).
Svaki tip podataka karakterie:

vrsta podataka koje opisuje (na primer, celi brojevi),

skup operacija koje se mogu primeniti nad podacima tog tipa (na primer,

5)
sabiranje, oduzimanje, mnoenje, . . . ),

nain reprezentacije i detalji implementacije (na primer, zapis u obliku bi-

01
narnog potpunog komplementa irine 8 bita, odakle sledi opseg vrednosti
od -128 do 127).

(2
Naredni fragment C kda obezbeuje da su promenljive x, y i z celobrojnog
tipa (tipa int), a da z nakon izvravanja ovog fragmenta ima vrednost jednaku
promenljivih x i y.

je
zbiru

int x, y;
an
...
int z = x + y;
d
iz

Prilikom prevoenja i kreiranja mainskog kda, prevodilac, voen tipom pro-


menljivih x, y i z, dodeljuje odreeni broj bitova u memoriji ovim promenljivim
(tj. rezervie odreene memorijske lokacije iskljuivo za smetanje vrednosti
o

ovih promenljivih) i generie kd (procesorske instrukcije) kojim se tokom


sk

izvravanja programa sabiraju vrednosti smetene na dodeljenim memorijskim


lokacijama. Pri tom se vodi rauna o nainu reprezentacije koji se koristi za
n

zapis. S obzirom da je naglaeno da su promenljive x , y i z, celobrojnog tipa,


ro

prevodilac e najverovatnije podrazumevati zapis u potpunom komplementu i


operacija + e se prevesti u mainsku instrukciju kojom se sabiraju brojevi za-
pisani u potpunom komplementu (koja je obino direktno podrana u svakom
kt

procesoru). U zavisnosti od tipa operanada, ista operacija se ponekad prevodi


na razliite naine. Tako, da su promenljive bile tipa float, najverovatnije bi
le

se podrazumevao zapis u obliku pokretnog zareza i operacija + bi se prevela u


mainsku instrukciju kojom se sabiraju brojevi zapisani u pokretnom zarezu.
E

Time, tipovi podataka iz jezika vieg nivoa u odgovarajuem mainskom kdu


postoje samo implicitno, najee samo kroz mainske instrukcije nastale nakon
prevoenja.
Tokom prevoenja programa se razreavaju i pitanja konverzija tipova . Na
primer, u C kdu int x = 3.1; promenljiva x je deklarisana kao celobrojna,
pa se za nju odvaja odreeni broj bitova u memoriji i ta memorija se inicijalizuje
binarnim zapisom broja 3. Naime, realnu vrednost 3.1 nije mogue zapisati
kao celobrojnu promenljivu, pa se vri konverzija tipa i zaokruivanje vrednosti.
Statiki tipizirani jezici (ukljuujui C), zahtevaju da programer definie
tip svake promenljive koja se koristi u programu i da se taj tip ne menja tokom
85 4. Vii programski jezici

izvravanja programa. Meutim, neki statiki tipizirani programski jezici (na


primer Haskell ili ML) ne zahtevaju od programera definisanje tipova, ve se
tipovi automatski odreuju iz teksta programa. S druge strane, u dinamiki
tipiziranim jezicima ista promenljiva moe da sadri podatke razliitog tipa
tokom raznih faza izvravanja programa. U nekim sluajevima doputeno je
vriti operacije nad promenljivima razliitog tipa, pri emu se tip implicitno
konvertuje. Na primer, jezik JavaScript ne zahteva definisanje tipa promen-
jlivih i doputa kd poput a = 1; b = "2"; a = a + b;.

5)
Kontrola toka izvravanja. Osnovi gradivni element imperativnih pro-

01
grama su naredbe . Osnovna naredba je naredba dodele kojom se vrednost neke
promenljive postavlja na vrednost nekog izraza definisanog nad konstantama i

(2
definisanim promenljivim. Na primer, x1 = 3 + 4*a;.
Naredbe se u programu esto niu i izvravaju sekvencijalno, jedna nakon
druge. Meutim, da bi se postigla vea izraajnost, programski jezici uvode

je
specijalne vrste naredbi kojima se vri kontrola toka izvravanja programa
(tzv. kontrolne strukture). Ovim se postie da se u zavisnosti od tekuih
an
vrednosti promenljivih neke naredbe uopte ne izvravaju, neke izvravaju vie
puta i slino. Najee koriene kontrolne strukture su granajue naredbe
(if-then-else), petlje (for, while, do-while, repeat-until) i naredbe skoka
d

(goto). Za naredbu skoka (goto) je pokazano da nije neophodna, tj. svaki pro-
iz

gram se moe zameniti programom koji daje iste rezultate a pri tome koristi
samo sekvencijalno nizanje naredbi, naredbu izbora (if-then-else) i jednu
do-while).4
o

vrstu petlje (na primer, Ovaj vaan teorijski rezultat ima svoju
punu praktinu primenu i u dananjem, strukturnom, programiranju naredba
sk

skoka se gotovo uopte ne koristi jer dovodi do nerazumljivih (tzv. pageti)


programa.
n
ro

Potprogrami. Veina programskih jezika prua mogunost definisanja neke


vrste potprograma, ali nazivi potprograma se razlikuju (najee se koriste ter-
kt

mini funkcije, procedure, sabrutine ili metode ). Potprogrami izoluju odreena


izraunavanja koja se kasnije mogu pozivati tj. koristiti na vie razliitih mesta
le

u razliitim kontekstima. Tako je, na primer, u jeziku C mogue definisati


funkciju kojom se izraunava najvei zajedniki delilac (NZD) dva broja, a
E

kasnije tu funkciju iskoristiti na nekoliko mesta da bi se izraunao NZD razlii-


tih parova brojeva.

int nzd(int a, int b) { ... }


...
x = nzd(1234, 5678);
y = nzd(8765, 4321);
4 Ovo tvrenje je poznato kao teorema o strukturnom programiranju, Korado Bema
(nem. Corrado Bhm) i uzepea Jakopinija (it. Giuseppe Jacopini) iz 1966.
86 4. Vii programski jezici

Primetimo da su potprogrami obino parametrizovani tj. mogu da primaju


ulazne parametre (kae se i argumente ). Postoje razliiti naini prenosa param-
etara u potprograme. Na primer, u nekim sluajevima (tzv. prenos po vred-
nosti ) funkcija dobija svoju kopiju parametra navedenog u pozivu i sve vreme
barata kopijom, ostavljajui originalni parametar nepromenjen. U nekim slua-
jevima (tzv. prenos po adresi ), parametar se ne kopira ve se u funkciju prenosi
samo memorijska adresa na kojoj se parametar nalazi i funkcija sve vreme
barata originalnim parametrom.

5)
Potprogrami imaju i mogunost vraanja vrednosti izraunavanja pozi-
vaocu. Neki jezici (npr. Pascal) sutinski razlikuju funkcije koje izraunavaju

01
(i kae se vraaju ) neku vrednost i procedure iji je zadatak samo da proizvedu
odreeni sporedni efekat (npr. da ispiu neto na ekran ili da promene vrednost

(2
promenljive).

Modularnost. Modularnost podrazumeva razbijanje veeg programa na neza-

je
visne celine. Celine, koje sadre definicije srodnih podataka i funkcija, obino
se nazivaju biblioteke (engl. library) i smetaju se u posebne datoteke. Ovim
an
se postie lake odravanje kompleksnih sistema, kao i mogunost viestruke
upotrebe pojedinih modula u okviru razliitih programa. Celine se obino za-
d

sebno prevode i kasnije povezuju u jedinstven program.


Programski jezici obino imaju svoje standardne biblioteke (engl. standard
iz

library) koje sadre funkcije esto potrebne programeru (npr. funkciju za izrau-
navanje duine niske karaktera). esto se kd funkcija standardne biblioteke
o

statiki povezuju sa programom (tj. ukljuuje se u izvrni kd programa).


Osim standardne biblioteke, programske jezike esto karakterie i rantajm
sk

biblioteka (engl. runtime library) koja predstavlja sponu izmeu kompila-


tora (tj. izvrnih programa koje on generie) i operativnog sistema. Funkcije
n

rantajm biblioteke se ne ukljuuju u izvrni kd programa ve se dinamiki


ro

pozivaju tokom izvravanja programa (i zato, da bi program mogao da se izvri


na nekom operativnom sistemu, neophodno je da na njemu bude instalirana
kt

rantajm biblioteka za taj programski jezik).

Upravljanje memorijom.
le

Neki programski jezici (na primer C, C++) za-


htevaju od programera da eksplicitno rukuje memorijom tj. da od sistema za-
E

hteva memoriju u trenutku kada je ona potrebna i da tu memoriju eksplicitno


oslobaa, tj. vraa sistemu kada ona programu vie nije potrebna. Drugi pro-
gramski jezici (na primer, Java, C#, Haskell, ML) oslobaaju programera ove
dunosti time to koriste tzv. sakupljae otpada (engl. garbage collector) iji je
zadatak da detektuju memoriju koju program ne koristi i da je oslobaaju. Iako
je programiranje u jezicima sa sakljupljaima otpada jednostavnije, programi
su obino sporiji (jer odreeno vreme odlazi na rad sakupljaa otpada).
U cilju obezbeivanja pogodnog naina da se hardverom upravlja, neki
programski jezici doputaju programeru da pristupi proizvoljnoj memorijskoj
adresi (npr. u jeziku C, to se moe raditi korienjem pokazivaa), ime se dobija
87 4. Vii programski jezici

vea sloboda, ali i mnogo vea mogunost pravljenja greaka. Sa druge strane,
neki programski jezici imaju za cilj skrivanje svojstava hardvera od program-
era, tite memoriju od direktnog pristupa programera i doputaju korienje
podataka samo u okviru memorije zauzete promenljivim programa.

4.4.1 Jeziki procesori

5)
Jeziki procesori (ili programski prevodioci) su programi ija je uloga da
analiziraju leksiku, sintaksiku i (donekle) semantiku ispravnost programa
vieg programskog jezika i da na osnovu ispravnog ulaznog programa vieg

01
programskog jezika generiu kd na mainskom jeziku (koji odgovara polaznom
programu, tj. vri izraunavanje koje je opisano na viem programskom jeziku).

(2
Da bi konstrukcija jezikih procesora uopte bila mogua, potrebno je imati pre-
cizan opis leksike i sintakse, ali i to precizniji opis semantike vieg programskog
jezika.

je
U zavisnosti od toga da li se ceo program analizira i transformie u main-
ski kd pre nego to moe da se izvri, ili se analiza i izvravanje programa
an
obavljaju naizmenino deo po deo programa (na primer, naredba po naredba),
jeziki procesori se dele u dve grupe: kompilatore i interpretatore.
d

Kompilatori. Kompilatori (ili kompajleri) su programski prevodioci kod ko-


iz

jih su faza prevoenja i faza izvravanja programa potpuno razdvojene.


Nakon analize izvornog kda programa vieg programskog jezika, kompi-
latori generiu izvrni (mainski) kd i dodatno ga optimizuju, a zatim
o

uvaju u vidu izvrnih (binarnih) datoteka . Jednom sauvani mainski


sk

kd mogue je izvravati neogranien broj puta, bez potrebe za ponovnim


prevoenjem. Krajnjim korisnicima nije neophodno dostavljati izvorni
n

kd programa na viem programskom jeziku, ve je dovoljno distribuirati


5
ro

izvrni mainski kd . Jedan od problema u radu sa kompilatorima je


da se prevoenjem gubi svaka veza izmeu izvrnog i izvornog kda pro-
grama. Svaka (i najmanja) izmena u izvornom kdu programa zahteva
kt

ponovno prevoenje programa ili njegovih delova. S druge strane, kom-


pilirani programi su obino veoma efikasni.
le

Interpretatori. Interpretatori su programski prevodioci kod kojih su faza


E

prevoenja i faza izvravanja programa isprepletane. Interpretatori anal-


iziraju deo po deo (najee naredbu po naredbu) izvornog kda programa
i odmah nakon analize vre i njegovo izvravanje. Rezultat prevoenja
se ne smeta u izvrne datoteke, ve je prilikom svakog izvravanja ne-
ophodno iznova vriti analizu izvornog kda. Zbog ovoga, programi koji
se interpretiraju se obino izvravaju znatno sporije nego u sluaju kom-
pilacije. S druge strane, razvojni ciklus programa je eto krai ukoliko

5 Ipak, ne samo u akademskom okruenju, smatra se da je veoma poeljno da se uz


izvrni distribuira i izvorni kd programa (tzv. softver otvorenog kda, engl. open source) da
bi korisnici mogli da vre modifikacije i prilagoavanja programa za svoje potrebe.
88 4. Vii programski jezici

se koriste interpretatori. Naime, prilikom malih izmena programa nije


potrebno iznova vriti analizu celokupnog kda.

Za neke programske jezike postoje i interpretatori i kompilatori. Intepreta-


tor se tada koristi u fazi razvoja programa da bi omoguio interakciju korisnika
sa programom, dok se u fazi eksploatacije kompletno razvijenog i istestiranog
programa koristi kompilator koji proizvodi program sa efikasnim izvravanjem.
Danas se esto primenjuje i tehnika kombinovanja kompilatora i interpreta-

5)
tora. Naime, kd sa vieg programskog jezika se prvo kompilira u neki precizno
definisan meujezik niskog nivoa (ovo je obino jezik neke apstraktne virtuelne

01
maine), a zatim se vri interpretacija ovog meujezika i njegovo izvravanje
na konkretnom raunaru. Ovaj pristup primenjen je kod programskog jezika

(2
Java, kod .Net jezika (C#, VB), itd.

Pitanja i zadaci za vebu


Pitanje 4.1.
je
Ukoliko je raspoloiv kd nekog programa na nekom viem pro-
an
gramskom jeziku (na primer, na jeziku C), da li je mogue jednoznano kon-
struisati odgovarajui mainski kd? Ukoliko je raspoloiv mainski kd nekog
programa, da li je mogue jednoznano konstruisati odgovarajui kd na jeziku
d

C?
iz

Pitanje 4.2. Za koji programski jezik su izgraeni prvi interpretator i kompi-


lator i ko je bio njihov autor? Koji su najkorieniji programski jezici 1960-ih,
o

koji 1970-ih, koji 1980-ih i 1990-ih, a koji danas? ta je to strukturno, a ta


sk

objektno-orijentisano programiranje?

Pitanje 4.3. Koje su najznaajnije programske paradigme? U koju paradigmu


n

spada programski jezik C? ta su to proceduralni, a ta su to deklarativni jezici?


ro

Pitanje 4.4. ta je rezultat leksike analize programa? ta leksiki analizator


dobija kao svoj ulaz, a ta vraa kao rezultat svog rada? ta je zadatak sintak-
kt

sike analize programa? ta sintaksiki analizator dobija kao svoj ulaz, a ta


vraa kao rezultat svog rada?
le

Pitanje 4.5. Navesti primer leksiki ispravnog, ali sintaksiki neispravnog dela
E

programa. Navesti primer sintaksiki ispravnog, ali semantiki neispravnog dela


programa.

Pitanje 4.6. ta karakterie svaki tip podataka? Da li su tipovi prisutni i u


prevedenom, mainskom kdu? ta je to konverzija tipova? ta znai da je
programski jezik statiki, a ta znai da je dinamiki tipiziran?

Pitanje 4.7. Koje su osnovne naredbe u programskim jezicima? ta je to


GOTO naredba i po emu je ona specifina?
89 4. Vii programski jezici

Pitanje 4.8. emu slue potprogrami? Koji su najee vrste potprograma?


emu slue parametri potprograma, a emu slui povratna vrednost? Koji su
najei naini prenosa parametara?

Pitanje 4.9. ta podrazumeva modularnost programa? emu slue biblioteke?


ta je standardna, a ta rantajm biblioteka?

Pitanje 4.10. ta su prednosti, a ta mane mogunosti pristupa proizvoljnoj

5)
memoriji iz programa? ta su sakupljai otpada?

Pitanje 4.11. ta je kompilator, a ta interpretator? Koje su osnovne pred-

01
nosti, a koje su mane korienja kompilatora u odnosu na korienje interpre-
tatora?

(2
Pitanje 4.12. Ako se koristi kompilator, da li je za izvravanje programa ko-
risniku neophodno dostaviti izvorni kd programa? Ako se koristi interpretator,
da li je za izvravanje programa korisniku neophodno dostaviti izvorni kd pro-

je
grama?
an
d
iz
o
sk
n
ro
kt
le
E
E
le
kt
ro
n
sk
o

90
Deo II
iz
d
Jezik C
an
je
(2
01
5)
Glava 5

5)
Osnovno o programskom jeziku C

01
(2
je
Programski jezik C je programski jezik opte namene koji je 1972. godine
razvio Denis Rii
1 u Belovim telefonskim laboratorijama (engl. Bell Telephone
an
Laboratories) u SAD. Ime C dolazi od injenice da je jezik nastao kao naslednik
jezika B (a koji je bio pojednostavljena verzija jezika BCPL). C je jezik koji
d

je bio namenjen prevashodno pisanju sistemskog softvera i to u okviru opera-


tivnog sistema Unix. Meutim, vremenom je poeo da se koristi i za pisanje
iz

aplikativnog softvera na velikom broju drugih platformi. C je danas prisutan


na irokom spektru platformi od mikrokontrolera do superraunara. Jezik
o

C je uticao i na razvoj drugih programskih jezika (najznaajniji od njih je jezik


C++ koji se moe smatrati proirenjem jezika C).
sk

Jezik C spada u grupu imperativnih, proceduralnih programskih jezika.


Kako je izvorno bio namenjen za sistemsko programiranje, programerima nudi
n

prilino direktan pristup memoriji i konstrukcije jezika su tako osmiljene da


ro

se jednostavno prevode na mainski jezik. Zahvaljuju tome, u C-u se relativno


jednostavno kreiraju programi koji su ranije uglavnom pisani na asemblerskom
kt

jeziku. Jezik je kreiran u minimalistikom duhu ima mali broj kljunih


rei, a dodatna funkcionalnost programerima se nudi uglavnom kroz korienje
(standardizovanih) bibliotekih funkcija (na primer, ne postoje naredbe jezika
le

kojima bi se uitavali podaci sa tastature raunara ili ispisivali na ekran, ve


E

se ovi zadaci izvravaju pozivanjem funkcija iz standardne biblioteke).


U razvoju jezika C se od samog poetka insistiralo na standardizaciji i
prenosivosti kda (tzv. portabilnosti), zahvaljujui emu se isti programi na
C-u mogu koristiti (tj. prevoditi) na razliitim platformama.

5.1 Standardizacija jezika


Postoji nekoliko znaajnih neformalnih i formalnih standarda jezika C:

1 Dennis Ritchie (19412011), ameriki informatiar, dobitnik Tjuringove nagrade


1983. godine.

91
92 5. Osnovno o programskom jeziku C

K&R C. Brajan Kerningen


2 i Denis Rii objavili su 1978. godine prvo izdanje
knjige Programski jezik C (The C Programming Language). Ova knjiga,
meu programerima obino poznata kao K&R ili kao white book, godi-
nama je sluila kao neformalna specifikacija jezika. ak i posle pojave
novih standarda, K&R je dugo vremena sluio kao najmanji zajedniki
imenilac koji je korien kada je bilo potrebno postii visok stepen preno-
sivosti, jer su konstrukte opisane u prvoj verziji K&R knjige uglavnom
podravali svi C prevodioci.

5)
ANSI C i ISO C. Tokom 1980-ih godina, C se proirio na veliki broj het-

01
erogenih platformi i time se javila potreba za zvaninom standardizaci-
jom jezika. Godine 1989. ameriki nacionalni institut za standardizaciju
(ANSI) objavio je standard pod zvaninim nazivom ANSI X3.159-1989

(2
Programming Language C . Ova verzija jezika C se obino jednostavnije
naziva ANSI C ili C89. Godine 1990. Meunarodna organizacija za stan-

je
dardizaciju (ISO) usvojila je ovaj dokument (uz sitnije izmene) pod oz-
nakom ISO/IEC 9899:1990 ime je briga o standardizaciji jezika C prela
an
od amerikog pod meunarodno okrilje. Ova verzija jezika se ponekad
naziva i C90, pa C89 i C90 predstavljaju isti jezik. Ovaj jezik predstavlja
nadskup K&R jezika C i ukljuuje mnoge konstrukte do tada nezvanino
d

podrane od strane velikog broja prevodilaca.


iz

C99. Godine 1999. usvojen je novi standard jezika C ISO/IEC 9899:1999,


poznat pod kraim imenom C99. Ovaj standard uveo je sitne izmene
o

u odnosu na prethodni i uglavnom proirio jezik novim konstruktima, u


sk

velikoj meri inspirisanim modernim programskim jezicima kakav je C++.

C11 (nekada C1X). Godine 2007. zapoet je rad na novom standardu jezika
n

C koji je zavren 2011. godine i objavljen kao ISO/IEC 9899:2011. Ovaj,


ro

sada tekui standard, ozvaniio je neka svojstva ve podrana od strane


popularnih kompilatora i precizno opisao memorijski model radi jasnije i
bolje podrke tzv. vienitnim izraunavanjima (kada se nekoliko izrau-
kt

navanja u programu odvija paralelno).


le

U nastavku teksta uglavnom e biti razmatran ANSI C, pri emu e biti


uvedeni i neki konstrukti jezika C99 i C11 (uz jasnu naznaku da se radi o
E

dopunama).

Pitanja za vebu
Pitanje 5.1.1. Kada je nastao programski jezik C? Ko je njegov autor? Za
koji operativni sistem se vezuje nastanak programskog jezika C?

Pitanje 5.1.2. U koju grupu jezika spada C? Za ta se najvie koristi?

2 Brian Kerninghan (1942), kanadsko-ameriki informatiar.


93 5. Osnovno o programskom jeziku C

Pitanje 5.1.3. Koja je najpoznatija knjiga o jeziku C? Nabrojati sve zvanine


standarde jezika C.

5.2 Prvi programi


Jezik C deli mnoga svojstva sa drugim programskim jezicima, ali ima i svoje
specifinosti. U nastavku teksta jezik C bie prezentovan postupno, koncept

5)
po koncept, esto iz opte perspektive programiranja i programskih jezika. Na
poetku, bie navedeno i prodiskutovano nekoliko jednostavnijih programa koji
ilustruju osnovne pojmove programskog jezika C.

01
Program Zdravo!

(2
Prikaz jezika C bie zapoet programom koji na standardni izlaz ispisuje
poruku Zdravo!. Neki delovi programa e biti objanjeni samo povrno, a u

je
kasnijem tekstu e biti dat njihov detaljni opis.

Program 5.1.
an
#include <stdio.h>
d

int main() {
iz

printf("Zdravo!\n"); /* ispisuje tekst */


return 0;
}
o
sk

Navedeni program sastoji se iz definicije jedne funkcije i ona se zove main


(od engleskog main glavna, glavno). Program moe da sadri vie funkcija,
n

ali obavezno mora da sadri funkciju koja se zove main i izvravanje programa
ro

uvek poinje od te funkcije. Prazne zagrade iza njenog imena ukazuju na to da


se eventualni argumenti ove funkcije ne koriste. Re int pre imena funkcije oz-
kt

naava da ova funkcija, kao rezultat, vraa celobrojnu vrednost (engl. integer),
tj. vrednost tipa int.
Naredbe funkcije main su grupisane izmeu simbola {i} (koji oznaavaju
le

poetak i kraj tela funkcije). Obe naredbe funkcije zavravaju se simbolom ;.


E

Kao to je ranije pomenuto, programi obino imaju vie funkcija i funkcija


main poziva te druge funkcije za obavljanje raznovrsnih podzadataka. Funkcije
koje se pozivaju mogu da budu korisniki definisane (tj. napisane od strane is-
tog programera koji pie program) ili biblioteke (tj. napisane od strane nekog
drugog tima programera). Odreene funkcije ine takozvanu standardnu bib-
lioteku programskog jezika C i njihov kd se obino isporuuje uz sam kompi-
lator. Prva naredba u navedenoj funkciji main je poziv standardne biblioteke
funkcijeprintf koja ispisuje tekst na takozvani standardni izlaz (obino ekran).
Tekst koji treba ispisati se zadaje izmeu para znakova ", koji se ne ispisuju.
U ovom sluaju, tekst koji se ispisuje je Zdravo!. Ni znakovi \n se ne ispisuju,
nego obezbeuju prelazak u novi red. Da bi C prevodilac umeo da generie
94 5. Osnovno o programskom jeziku C

kd poziva funkcije printf, potrebno je da zna tip njene povratne vrednosti i


tipove njenih argumenata. Ovi podaci o funkciji printf, njen svojevrsni opis,
takozvana deklaracija, nalaze se u datoteci zaglavlja stdio.h (ova datoteka
ini deo standardne biblioteke zaduen za ulazno-izlaznu komunikaciju i deo je
instalacije prevodioca za C). Prva linija programa, kojom se prevodiocu saop-
tava da je neophodno da ukljui sadraj datoteke stdio.h je pretprocesorska
direktiva. To nije naredba C jezika, ve instrukcija C pretprocesoru koji pred-
stavlja nultu fazu kompilacije i koji pre kompilacije program priprema tako to

5)
umesto prve linije upisuje celokupan sadraj datoteke stdio.h.
Naredba return 0; prekida izvravanje funkcije main i, kao njen rezultat,

01
vraa vrednost 0. Vrednost funkcije vraa se u okruenje iz kojeg je ona poz-
vana, u ovom sluaju u okruenje iz kojeg je pozvan sm program. Uobiajena

(2
konvencija je da se iz funkcije main vraa vrednost 0 da ukae na to da je
izvravanje proteklo bez problema, a ne-nula vrednost da ukae na problem ili
greku koja se javila tokom izvravanja programa.

je
Tekst naveden izmeu simbola /* i simbola */ predstavlja komentare. Oni
su korisni samo programerima, doprinose itljivosti i razumljivosti samog pro-
an
grama. Njih C prevodilac ignorie i oni ne utiu na izvrnu verziju programa.
Primetimo da je svaka naredba u prikazanom programu pisana u zaseb-
nom redu, pri emu su neki redovi uvueni u odnosu na druge. Naglasimo da
d

sa stanovita C prevodioca ovo nije neophodno (u ekstremnom sluaju, dop-


iz

uteno bi bilo da je ceo kd osim prve linije naveden u istoj liniji). Ipak, smatra
se da je nazubljivanje (engl. indentation) kda u skladu sa njegovom sintak-
sikom strukturom nezaobilazna praksa. Naglasimo i da postoje razliiti stilovi
o

nazubljivanja (na primer, da li otvorena vitiasta zagrada poinje u istom ili


sk

sledeem redu). Meutim, obino se smatra da nije bitno koji se stil koristi
dok god se koristi na uniforman nain.
n

Ukoliko se, na primer, koristi GCC prevodilac (to je est sluaj pod opera-
ro

tivnim sistemom Linux), izvorni program , poput navedenog programa (unet u


nekom editoru teksta i sauvan u datoteci), moe se prevesti u izvrni program
tako to se u komandoj liniji navodi:
kt

gcc -o ime_izvrsnog_koda ime_izvornog_koda


Na primer, ako je izvorni kd sauvan u datoteci zdravo.c, a eljeno ime
le

izvrnog programa je zdravo, potrebno je uneti tekst:


gcc -o zdravo zdravo.c
E

U tom sluaju, izvrni program pokree se sa ./zdravo. Ako se ime izvrnog


programa izostavi, podrazumeva se ime a.out .
3
Nakon pokretanja programa dobija se sledei izlaz.

Zdravo!

Tokom prevoenja, prevodilac moe da detektuje greke (engl. error) u


izvornom programu. Tada prevodilac ne generie izvrni program nego izve-

3 Razlog za ovo je istorijski a.out je skraeno za assembler output jer je izvrni program
obino bio izlaz nakon faze asembliranja.
95 5. Osnovno o programskom jeziku C

tava programera o vrsti tih greaka i brojevima linija u kojima se nalaze. Pro-
gramer, na osnovu tog izvetaja, treba da ispravi greke u svom programu i
ponovi proces prevoenja. Pored greaka, prevodilac moe da prijavi i up-
ozorenja (engl. warning) za mesta u programu koja ukazuju na potencijalne
propuste u programu. Ako u prevoenju ne postoje greke ve samo upo-
zorenja, generie se izvrni program, ali on se moda nee ponaati na eljeni
nain, te treba biti oprezan sa njegovim korienjem. Vie rei o grekama i
upozorenjima bie u poglavlju 9.2.5.

5)
01
Program koji ispisuje kvadrat unetog celog broja

Prethodni program nije uzimao u obzir nikakve podatke koje bi uneo ko-

(2
risnik, ve je prilikom svakog pokretanja davao isti izlaz. Naredni program
oekuje od korisnika da unese jedan ceo broj i onda ispisuje kvadrat tog broja.

Program 5.2.
#include <stdio.h>
je
an
int main() {
int a;
d

printf("Unesite ceo broj: ");


iz

scanf("%i", &a);
printf("Kvadrat unetog broja je: %i", a*a);
return 0;
o

}
sk

main je takozvana deklaracija promenljive. Ovom dekla-


Prva linija funkcije
n

racijom se uvodi promenljiva a celobrojnog tipa tipa int. Naredna naredba


ro

(poziv funkcije printf) na standardni izlaz ispisuje tekst Unesite ceo broj:,
a naredba nakon nje (poziv funkcije scanf) omoguava uitavanje vrednosti
kt

neke promenljive sa standardnog ulaza (obino tastature). U okviru poziva


funkcije scanf zadaje se format u kojem e podatak biti proitan u ovom
sluaju treba proitati podatak tipa int i format se u tom sluaju zapisuje kao
le

%i. Nakon formata, zapisuje se ime promenljive u koju treba upisati proi-
& govori da e promenljiva a biti promenjena u funkciji
E

tanu vrednost. Simbol


scanf, tj. da e proitani broj biti smeten na adresu promenljive a. Korien-
jem funkcije printf, pored fiksnog teksta, moe se ispisati i vrednost neke
promenljive ili izraza. U formatu ispisa, na mestu u tekstu gde treba ispisati
vrednost izraza zapisuje se format tog ispisa u ovom sluaju %i4 , jer e biti
ispisana celobrojna vrednost. Nakon niske koja opisuje format, navodi se izraz
koji treba ispisati u ovom sluaju vrednost a*a, tj. kvadrat broja koji je

4 Umesto %i, potpuno ravnopravno mogue je koristiti i %d. %i dolazi od toga to je


podatak tipa int, a %d jer je u dekadnom brojevnom sistemu.
96 5. Osnovno o programskom jeziku C

korisnik uneo. Nakon prevoenja i pokretanja programa, korisnik unosi broj,


a zatim se ispisuje njegov kvadrat. Na primer,

Unesite ceo broj: 5


Kvadrat unetog broja je: 25

5)
Program koji izraunava rastojanje izmeu taaka

Sledei primer prikazuje program koji rauna rastojanje izmeu dve take

01
u dekartovskoj ravni. Osnovna razlika u odnosu na prethodne programe je to
se koriste razlomljeni brojevi, tj. brojevi u pokretnom zarezu. Dakle, u ovom
int double,

(2
programu, umesto promenljivih tipa koriste se promenljive tipa
dok se prilikom uitavanja i ispisa ovakvih vrednost za format koristi niska %lf
umesto niske %i. Dodatno, za raunanje kvadratnog korena koristi se funkcija
sqrt math.h.

je
deklarisana u zaglavlju

Program 5.3.
an
#include <stdio.h>
#include <math.h>
d
iz

int main() {
double x1, y1, x2, y2;
printf("Unesi koordinate prve tacke: ");
o

scanf("%lf%lf", &x1, &y1);


sk

printf("Unesi koordinate druge tacke: ");


scanf("%lf%lf", &x2, &y2);
n

printf("Rastojanje je: %lf\n",


sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)));
ro

return 0;
}
kt

Nakon unosa podataka, traeno rastojanje se rauna primenom Pitagorine


le


teoreme (2 1 )2 + (2 1 )2 . Poto u jeziku C ne postoji operator ste-
penovanja, kvadriranje se sprovodi mnoenjem.
E

Za prevoenje programa koji koriste matematike funkcije, prilikom ko-


rienja GCC prevodioca potrebno je navesti argument -lm (kao poslednji ar-
gument). Na primer,
gcc -o rastojanje rastojanje.c -lm
Nakon uspenog prevoenja i pokretanja programa korisnik unosi podatke
i ispisuje se rezultat. Na primer,

Unesi koordinate prve tacke: 0.0 0.0


Unesi koordinate druge tacke: 1.0 1.0
Rastojanje je: 1.414214
97 5. Osnovno o programskom jeziku C

Program koji ispituje da li je uneti broj paran

Naredni program ispituje da li je uneti broj paran.

Program 5.4.
#include <stdio.h>

int main() {

5)
int a;
printf("Unesi broj: ");

01
scanf("%d", &a);
if (a % 2 == 0)
printf("Broj %d je paran\n", a);

(2
else
printf("Broj %d je neparan\n", a);
return 0;

je
}
an
Ceo broj je paran ako i samo ako je ostatak pri deljenju sa dva jednak nuli.
Ostatak pri deljenju se izraunava operatorom %, dok se poreenje jednakosti
d

vri operatorom ==. Operator ispitanja jednakosti (==) i operator dodele (=) se
if
iz

sutinski razlikuju. Naredba je naredba grananja kojom se u zavisnosti od


toga da li je navedeni uslov (u ovom sluaju a % 2 == 0) ispunjen usmerava
tok programa. Uslov se uvek navodi u zagradama () iza kojih ne sledi simbol
o

;. U sluaju da je uslov ispunjen, izvrava se prva naredba navedena nakon


then grana), dok se u sluaju da uslov nije ispunjen izvrava,
sk

uslova (takozvana
ukoliko postoji, naredba navedena nakon rei else (takozvana else grana). I
then i else grana mogu da sadre samo jednu naredbu ili vie naredbi.
n

Ukoliko
sadre vie naredbi, onda te naredbe ine blok iji se poetak i kraj moraju
ro

oznaiti zagradama { i }. Grana else ne mora da postoji.


kt

Program koji ispisuje tablicu kvadrata i korena


le

Naredni program ispisuje kvadrate i korene svih celih brojeva od 1 do 100.

Program 5.5.
E

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

#define N 100

int main() {
int i;
for (i = 1; i <= N; i++)
printf("%3d %5d %7.4f\n", i, i*i, sqrt(i));
98 5. Osnovno o programskom jeziku C

return 0;
}
Novina u ovom programu, u odnosu na prethodne, je petlja for koja slui
da se odreene naredbe ponove vie puta (obino uz razliite vrednosti pro-
menljivih). Petlja se izvrava tako to se prvo izvri inicijalizacija promenljive
i na vrednost 1 (zahvaljujui kdu i=1), u svakom koraku petlje se vrednost
promenljive i uveava za 1 (zahvaljujui kdu i++) i sve to se ponavlja dok
i N i <= N). Pre pre-

5)
vrednost promenljive ne postane (zahvaljujui uslovu
voenja programa vrednost N menja se brojem 100 i to zahvaljujui tzv. pret-
#define N 100 koja se esto koristi za definisanje kon-

01
procesorskoj direktivi
stantnih parametara u programima (efekat je isti kao da je u petlji stajalo
for (i = 1; i <= 100; i++), meutim, gornja granica je jasno izdvojena

(2
na poetku programa i lake se moe uoiti i promeniti. O petljama e biti
vie rei u glavi 7, a o pretprocesoru e biti vie rei u poglavlju 9.2.1.
Za svaku vrednost promenljive i izmeu 1 i 100 izvrava se telo petlje

je
naredba printf. U okviru format niske se, umesto %d, koristi %3d, ime se
postie da se broj uvek ispisuje u polju irine 3 karaktera. Slino se, korienjem
an
%7.4f, postie da se vrednost korena ispisuje na 4 decimale u polju ukupne
irine 7 karaktera.
d
iz

Program koji ispituje zbir prvih prirodnih brojeva.

Naredni program ispisuje prvi prirodni broj za koji je zbir svih prirodnih
o

brojeva do tog broja vei od 100.


sk

Program 5.6.
#include <stdio.h>
n
ro

#define N 100
kt

int main() {
int i = 1;
int s = 1;
le

while(s <= N) {
E

i++;
s = s+i;
}
printf("%d\n", i);
return 0;
}

Novina u ovom programu, u odnosu na prethodne, je petlja while koja se


izvrava sve dok je ispunjen zadati uslov. Deklaracijaint i = 1; je deklaracija
sa inicijalizacijom njom se promenljvoj i dodeljuje inicijalna vrednost 1. Kao
99 5. Osnovno o programskom jeziku C

i u prethodnom programu, fleksibilnosti radi, umesto fiksne vrednost 100 u


uslovu petlje koristi se simboliko ime N uvedeno pretprocesorskom direktivom.

Program koji prevodi mala u velika slova.

Naredni program na standardni izlaz prepisuje reenicu koja se unosi na


standardni ulaz, zamenjujui pri tom sva mala slova velikim.

5)
Program 5.7.
#include <stdio.h>

01
#include <ctype.h>
int main() {

(2
int c;
printf("Otkucaj recenicu (zavrsi je znakom .): ");
do {

je
c = getchar();
putchar(toupper(c));
an
} while (c != .);
putchar(\n);
return 0;
d

}
iz
o

Kada se pokrene program, dobija se ovakav rezultat.


sk

Otkucaj recenicu (zavrsi je znakom .): Ovo je C program.


OVO JE C PROGRAM.
n

U programu se koristi funkcija getchar koja uitava karakter sa standard-


ro

nog ulaza (i smeta ga promenljivu c), funkcija toupper koja prevodi mala
u velika slova (a ne menja argument ako nije malo slovo) i funkcija putchar
kt

koja ispisuje karakter na standardni izlaz. Da bi se koristile funkcije getchar i


putchar potrebno je ukljuiti zaglavlje <stdio.h>, a da bi se koristila funkcija
le

toupper potrebno je ukljuiti zaglavlje <ctype.h>. U programu je koriena


petlja do-while ije se telo uvek izvrava bar jednom i u kojoj se provera uslova
E

petlje vri na kraju (za razliku od petlje while u kojoj se uslov proverava na
poetku petlje i telo petlja ne mora da se izvri ni jednom). Telo petlje se
izvrava sve dok je uitani i prepisani karakter c razliit od take. Pri tome,
unetni tekst (sa velikim slovima umesto malim) se ispisuje uvek kada se pritisne
taster za unos (enter ). Kada se unese i prepie taka, tada se petlja zavrava.

Pitanja i zadaci za vebu


Pitanje 5.2.1. Funkciju kojeg imena mora da sadri svaki C program?
100 5. Osnovno o programskom jeziku C

Pitanje 5.2.2. ta su to pretprocesorske direktive? Da li je #include<stdio.h>


pretprocesorska direktiva, naredba C-a ili poziv funkcije iz standardne biblioteke?

Pitanje 5.2.3. ta su to datoteke zaglavlja?

Pitanje 5.2.4. ta je standardna biblioteka programskog jezika?

Pitanje 5.2.5. Kojom naredbom se vraa rezultat funkcije u okruenje iz kojeg


je ona pozvana?

5)
Zadatak 5.2.1. Napisati program koji za uneti poluprenik izraunava obim

01
i povrinu kruga (za broj koristiti konstantu M_PI iz zaglavlja math.h).
Zadatak 5.2.2. Napisati program koji za unete koordinate temena trougla

(2
izraunava njegov obim i povrinu.

Zadatak 5.2.3. Napisati program koji za unete dve duine stranica trougla

je
i ( > 0, > 0) i ugla izmeu njih ( > 0 je zadat u stepenima) izraunava
duinu tree stranice i veliine dva preostala ugla i . Napomena: koristiti
an
sinusnu i/ili kosinusnu teoremu i funkcije sin() i cos() iz zaglavlja math.h
.
d

Zadatak 5.2.4. Napisati program koji za unetu brzinu u



ispisuje odgo-

iz


varajuu brzinu u .

Zadatak 5.2.5. Napisati program koji za unetu poetnu brzinu 0 , ubrzanje


o

i vreme izraunava trenutnu brzinu i preeni put tela koje se kree



sk

ravnomerno ubrzano.

Zadatak 5.2.6. Napisati program koji za unetih 9 brojeva 1 , 2 , . . . , 9 izrau-


n

nava determinantu:
1 2 3
ro


4 5 6

7 8 9
kt


le


Zadatak 5.2.7. Napisati program koji ispisuje maksimum tri uneta broja.
E

Zadatak 5.2.8. Napisati program koji uitava cele brojeve i i zatim ispisuje

sve kubove brojeva izmeu njih.
Glava 6

5)
Predstavljanje podataka i

01
operacije nad njima

(2
je
an
U ovoj glavi bie opisani naredni pojmovi:

Promenljive i konstante,
d

koje su osnovni oblici podataka kojima se operie


u programu.
iz

Tipovi promenljivih, koji odreuju vrstu podataka koje promenljive mogu


da sadre, nain reprezentacije i skup vrednosti koje mogu imati, kao i
o

skup operacija koje se sa njima mogu primeniti.


sk

Deklaracije, koje uvode spisak promenljivih koje e se koristiti, odreuju kog


n

su tipa i, eventualno, koje su im poetne vrednosti.

Operatori,
ro

odgovaraju operacijama koje su definisane nad podacima odreene


vrste.
kt

Izrazi, koji kombinuju promenljive i konstante (korienjem operatora), dajui


nove vrednosti.
le

6.1 Promenljive i deklaracije


E

Promenljive su osnovni objekti koji se koriste u programima. Svaka promen-


ljiva mora biti deklarisane pre korienja.

6.1.1 Promenljive i imena promenljivih

Promenljiva je objekat kojem je pridruen neki prostor u memoriji i u


svakom trenutku svog postojanja ima vrednost kojoj se moe pristupiti
koja se moe proitati i koristiti, ali i koja se (ukoliko nije traeno drugacije)
moe menjati.

101
102 6. Predstavljanje podataka i operacije nad njima

Imena promenljivih (ali i funkcija, struktura, itd.) odreena su identifika-


torima . U prethodnim programima koriene su promenljive ija su imena a,
i, x1, x2 itd. Generalno, identifikator moe da sadri slova i cifre, kao i simbol
_ (koji je pogodan za duga imena), ali identifikator ne moe poinjati cifrom.
Dodatno, kljune rei jezika C (na primer, if, for, while) ne mogu se koristiti
kao identifikatori.
U identifikatorima, velika i mala slova se razlikuju. Na primer, promenljive
sa imenima a i A se tretiraju kao dve razliite promenljive. esta praksa je

5)
da malim slovima poinju imena promenljivih i funkcija, a velikim imena sim-
bolikih konstanti (vidi poglavlja o nabrojivim tipovima 6.7.4 i pretprocesoru

01
9.2.1), vrednosti koje se ne menjaju u toku programa.
Imena promenljivih i funkcija, u principu, treba da oslikavaju njihovo zna-

(2
enje i ulogu u programu, ali za promenljive kao to su indeksi u petljama
se obino koriste kratka, jednoslovna imena (na primer i). Ako ime promen-
ljive sadri vie rei, onda se, radi bolje itljivosti, te rei razdvajaju sim-

je
bolom _ (na primer, broj_studenata) ili poetnim velikim slovima (na primer,
brojStudenata) ovo drugo je takozvana kamilja notacija (CamelCase). Pos-
an
toje razliite konvencije za imenovanje promenljivih. U nekim konvencijama,
kao to je maarska notacija, poetna slova imena promenljivih predstavljaju
kratku oznaka tipa te promenljive (na primer, iBrojStudenata).1
d

Iako je dozvoljeno, ne preporuuje se korienje identifikatora koji poinju


iz

simbolom _, jer se oni obino koriste za sistemske funkcije i promenljive.


ANSI C standard (C89) garantuje da se barem 31 poetnih znakova imena
promenljive smatra znaajnom, dok standard C99 poveava taj broj na 63.
o

Ukoliko dve promenljive imaju vie od 63 poetna znaka ista, onda se ne garan-
sk

tuje da e te dve promenljive biti razlikovane.


2
n

6.1.2 Deklaracije
ro

Sve promenljive moraju biti deklarisane pre korienja. Deklaracija


3 sadri
tip i listu od jedne ili vie promenljivih tog tipa, razdvojenih zarezima.
kt

int broj; /* deklaracija celog broja */


le

int a, b; /* deklaracija vise celih brojeva */


E

U optem sluaju nije propisano koju vrednost ima promenljiva neposredno


4
nakon to je deklarisana . Prilikom deklaracije moe se izvriti poetna inici-
jalizacija. Mogue je kombinovati deklaracije sa i bez inicijalizacije.

1 Postoje argumenti protiv korienja takve notacije u jezicima u kojima kompilatori vre
proveru korektnosti tipova.
2 Za takozvane spoljanje promenljive ove vrednosti su 6 (C89) odnosno 31 (C99).
3 Deklaracije promenljivih najee su ujedno i definicije. Odnos izmeu deklaracija i
definicija promenljivih u jeziku C veoma je suptilan i bie razmotren u glavi 9.
4 Samo u nekim specijalnim sluajevima (koji e biti diskutovani u daljem tekstu), po-
drazumevana vrednost je 0.
103 6. Predstavljanje podataka i operacije nad njima

int vrednost = 5; /* deklaracija sa inicijalizacijom */


int a = 3, b, c = 5; /* deklaracije sa inicijalizacijom */

Izraz kojim se promenljiva inicijalizuje zvaemo inicijalizator.


Kvalifikator const (dostupan u novijim standardima jezika C) moe biti
dodeljen deklaraciji promenljive da bi naznaio i obezbedio da se njena vrednost
nee menjati, na primer:

5)
/* ovu promenljivu nije moguce menjati */
const double GRAVITY = 9.81;

01
Kvalifikator const mogue je navesti i nakon imena tipa.

(2
double const GRAVITY = 9.81;

Izmeu ova dva naina navoenja kvalifikatora razlike postoje samo kod

je
sloenijih tipova (pre svega pokazivaa) i o tome e biti vie rei u narednim
an
glavama.
Vrednost tipa const T moe biti dodeljena promenljivoj tipa T, ali pro-
menljivoj tipa const T ne moe biti dodeljena vrednost (osim prilikom inici-
d

jalizacije) pokuaj menjanja vrednosti konstantne promenljive (kao i svakog


iz

drugog konstantog sadraja) dovodi do greke prilikom prevoenja programa.


Deklaracije promenljivih mogu se navoditi na razliitim mestima u pro-
gramu. Najee se navode na poetku funkcije (u dosada navedenim primer-
o

ima to je bila funkcija main). Ukoliko je promenljiva deklarisana u nekoj


sk

funkciji, onda kaemo da je ona lokalna za tu funkciju i druge funkcije ne


mogu da je koriste. Razliite funkcije mogu imati lokalne promenljive istog
n

imena. Promenljive deklarisane van svih funkcija su globalne i mogu se koris-


titi u vie funkcija. Vidljivost tj. oblast vaenja identifikatora (i njima uvedenih
ro

promenljivih) odreena je pravilima dosega identifikatora o emu e vie rei


biti u poglavlju 9.2.2.
kt

Pitanja i zadaci za vebu


le

Pitanje 6.1.1. Koji su osnovni oblici podataka kojima se operie u programu?


E

Pitanje 6.1.2. Da li se promenljivoj moe menjati tip u toku izvravanja pro-


grama?

Pitanje 6.1.3. Da li ime promenljive moe poinjati simbolom _? Da li se


ovaj simbol moe koristiti u okviru imena?

Pitanje 6.1.4. Da li ime promenljive moe poinjati cifrom? Da li se cifre


mogu koristiti u okviru imena?

Pitanje 6.1.5. Koliko poetnih karaktera u imenu promenljive u jeziku C se


garantovano smatra bitnim?
104 6. Predstavljanje podataka i operacije nad njima

Pitanje 6.1.6. ta je to inicijalizacija promenljive? U optem sluaju, ukoliko


celobrojna promenljiva nije inicijalizovana, koja je njena poetna vrednost?

Pitanje 6.1.7. ta je uloga kvalifikatora const?

6.2 Osnovni tipovi podataka


Kao i u veini drugih programskih jezika, u jeziku C podaci su organizovani

5)
u tipove. To omoguava da se u programima ne radi samo nad pojedinanim
bitovima i bajtovima (tj. nad nulama i jedinicama), ve i nad skupovima bitova

01
koji su, pogodnosti radi, organizovani u sloenije podatke (na primer, cele
brojeve ili brojeve u pokretnom zarezu). Jedan tip karakterie: vrsta podataka

(2
koje opisuje, nain reprezentacije, skup operacija koje se mogu primeniti nad
podacima tog tipa, kao i broj bitova koji se koriste za reprezentaciju (odakle
sledi opseg moguih vrednosti).

6.2.1 Tip int


je
an
U jeziku C, cele brojeve opisuje tip int (od engleskog integer, ceo broj ).
Podrazumeva se da su vrednosti ovog tipa oznaene i reprezentuju se najee
d

koristei potpuni komplement. Nad podacima ovog tipa mogu se koristiti arit-
iz

metike operacije (na primer, +, -, *, /, %), relacije (na primer,<, >=) i druge.
Standardom nije propisano koliko bitova koriste podaci tipa int, ali je pro-
pisano da se koristi najmanje esnaest bita (tj. dva bajta). Veliina tipa int
o

je obino prilagoena konkretnoj maini, tj. njenom procesoru. Na dananjim


sk

raunarima, podaci tipa int obino zauzimaju 32 ili 64 bita , tj. 4 ili 8 bajtova5 .
Tipu int mogu biti pridrueni kvalifikatori short, long i (od standarda
C99) long long.
n

Ovi kvalifikatori uvode cele brojeve potencijalno razliitih


duina u odnosu na int. Nije propisano koliko tipovi short int, long int i
ro

long long int zauzimaju bitova, ali propisano je da short zauzima barem dva
bajta, da int zauzima barem onoliko bajtova koliko i short int, da long int
kt

zauzima barem onoliko bajtova koliko int i da zauzima barem etiri bajta, a
da long long int zauzima barem onoliko koliko zauzima long int. Ime tipa
le

short int moe se krae zapisati short, ime tipa long int moe se krae
zapisati long, a ime tipa long long int moe se krae zapisati sa long long.
E

Bilo kojem od tipova short, int, long i long long mogu biti pridrueni
kvalifikatori signed ili unsigned. Kvalifikator unsigned oznaava da se broj

tretira kao neoznaen i da se sva izraunavanja izvravaju po modulu 2 , gde
je broj bitova koji tip koristi. Kvalifikator signed oznaava da se broj tretira
kao oznaen i za njegovu reprezentaciju se najee koristi zapis u potpunom
komplementu. Ukoliko uz tip short, int, long ili long long nije naveden ni
kvalifikator signed ni kvalifikator unsigned, podrazumeva se da je vrednost
oznaen broj.

5 Kaemo da veliina podataka zavisi od sistema, pri emu se pod sistemom podrazumeva
i hardver raunara i operativni sistem na kojem e se program izvravati.
105 6. Predstavljanje podataka i operacije nad njima

Podaci o opsegu ovih (i drugih tipova) za konkretan raunar i C prevodi-


lac sadrani su u standardnoj datoteci zaglavlja <limits.h>. Pregled danas
najeih vrednosti (u tridesetdvobitnom okruenju) dat je u tabeli 6.1.

oznaeni (signed) neoznaeni (unsigned)


karakteri 1B = 8b 1B = 8b
7
(char) [-2 , 27 -1] = [0, 28 -1] =
[-128, 127] [0, 255]

5)
kratki 2B = 16b 2B = 16b
(short int) [-32K, 32K-1] = [0, 64K-1] =

01
15
[-2 , 215 -1] = [0, 216 -1] =
[-32768, 32767] [0, 65535]

(2
dugi 4B = 32b 4B = 32b
(long int) [-2G, 2G-1] = [0, 4G-1] =
31
[-2 , 231 -1] = [0, 232 -1] =

je
[-2147483648,2147483647] [0, 4294967295]
veoma dugi 8B = 64b 8B = 64b
an
63
(long long int) [-2 263 -1] =
, [0, 264 -1] =
18 18 19
od C99 [-9.210 , 9.210 ] [0, 1.8410 ]
d
iz

Slika 6.1: Najei opseg tipova (vai i na x86 + gcc platformi).

Prilikom tampanja i uitavanja vrednosti celobrojnih tipova, u pozivu


o

printf ili scanf, potrebno je navesti i razliite formate


funkcije (npr. %u za
sk

unsigned, %l za long), o emu e vie rei biti u glavi 12.


Konaan opseg tipova treba uvek imati u vidu jer iz ovog razloga neke
n

matematike operacije nee dati oekivane vrednosti (kaemo da dolazi do


ro

prekoraenja . Na primer, na tridesetdvobitnom sistemu, naredni program


tampa negativan rezultat.
kt

Program 6.1.
#include <stdio.h>
le

int main() {
E

int a = 2000000000, b = 2000000000;


printf("Zbir brojeva %d i %d je: %d\n", a + b);
return 0;
}

Zbir brojeva 2000000000 i 2000000000 je: -294967296

Da su promenljive bile deklarisane kao promenljive tipa unsigned int i


tampane korienjem formata %u, rezultat bi bio 4000000000 i bio bi ispravan
106 6. Predstavljanje podataka i operacije nad njima

i u klasinom matematikom smislu (u smislu sabiranja neogranienih celih


brojeva).

Program 6.2.
#include <stdio.h>

int main() {
unsigned int a = 2000000000, b = 2000000000;

5)
printf("Zbir brojeva %u i %u je: %u\n", a, b, a + b);
return 0;

01
}

(2
Zbir brojeva 2000000000 i 2000000000 je: 4000000000

6.2.2 Tip char


je
an
Male cele brojeve opisuje tip char (od engleskog character karakter,
simbol, znak). I nad podacima ovog tipa mogu se primenjivati aritmetike
d

operacije i relacije. Podatak tipa char zauzima tano jedan bajt. Standard
char smatra oznaenim
iz

ne propisuje da li se podatak tipa ili neoznaenim


brojem (na nekom sistemu se moe smatrati oznaenim, a na nekom drugom
se moe smatrati neoznaenim). Na tip char mogu se primeniti kvalifikatori
o

unsigned i signed. Kvalifikator unsigned obezbeuje da se vrednost tretira


sk

kao neoznaen broj, skup njegovih moguih vrednosti je interval od 0 do 255 i


sva izraunavanja nad podacima ovog tipa se izvravaju po modulu 28 = 256.
signed
n

Kvalifikator obezbeuje da se vrednost tretira kao oznaen broj i za


njegovu reprezentaciju se najee koristi zapis u potpunom komplementu, a
ro

skup njegovih moguih vrednosti je interval od 128 do 127.


Iako je char opti brojevni tip podataka i moe da se koristi za predstavl-
kt

janje vrednosti malih celih brojeva, u jeziku C se ovaj tip obino koristi za
brojeve koji predstavljaju kdove karaktera (otuda i ime char). Standard ne
le

propisuje koje kodiranje se koristi, ali na savremenim PC raunarima i C im-


plementacijama, najee se koristi ASCII kodiranje karaktera. U tom sluaju,
E

karakteri su u potpunosti identifikovani svojim ASCII kdovima i obratno. Bez


obzira da li je u pitanju oznaeni ili neoznaeni karakter, opseg dozvoljava da
se sauva svaki ASCII kd (podsetimo e, ASCII kodovi su sedmobitni i imaju
vrednosti izmeu 0 i 127).
Brojevna vrednost promenljive c tipa char moe se ispisati sa printf("%d",
c), a znakovna sa printf("%c", c). Formati koji se koriste za ispisivanje i
uitavanje vrednosti ovog i drugih tipova navedeni su u glavi 12.
Standardna biblioteka sadri mnoge funkcije (i makroe) za rad sa karakter-
skim tipom i one su deklarisane u datoteci zaglavlja <ctype.h>. Najkorienije
su isalpha, isdigit i sline kojima se proverava da li je karakter slovo abecede
107 6. Predstavljanje podataka i operacije nad njima

ili je cifra, kao i toupper, tolower kojima se malo slovo prevodi u veliko i
obratno. Vie detalja o standardnoj biblioteci moe se nai u glavi 11.

6.2.3 Tipovi float, double i long double


Realne brojeve ili, preciznije, brojeve u pokretnom zarezu opisuju tipovi
float, double i (od standarda C99) long double . Tip float opisuje brojeve
u pokretnom zarezu osnovne tanosti, tip double opisuje brojeve u pokret-

5)
nom zarezu dvostruke tanosti, a tip long double brojeve u pokretnom zarezu
proirene tanosti. Nije propisano koliko ovi tipovi zauzimaju bitova, ali pro-

01
pisano je da double zauzima barem onoliko bajtova koliko i float, a da
long double zauzima barem onoliko bajtova koliko double. Podaci o opsegu

(2
i detaljima ovih (i drugih tipova) za konkretan raunar i C prevodilac sadrani
su u standardnoj datoteci zaglavlja <float.h>.
I nad podacima ovih tipova mogu se koristiti uobiajene aritmetike op-

je
eracije (osim operacije raunanja ostatka pri deljenju %) i relacije.
Brojevi u pokretnom zarezu u savremenim raunarima se najee zapisuju
an
u skladu sa IEEE754 standardom. Ovaj standard ukljuuje i mogunost zapisa
specijalnih vrednosti (na primer, +, , ). Na primer, vrednost izraza
1.0/0.0 je +, za razliku od celobrojnog izraza 1/0 ije izraunavanje dovodi do
d

greke prilikom izvravanja programa (engl. division by zero error). Standard


iz

ne definie ponaanje u sluaju celobrojnog deljenja nulom a obino se za takvo


deljenje u kdu prijavljuje samo upozorenje (ukoliko se do tog deljenja doe
u fazi izvravanja, prijavljuje se greka i prekida se izvravanje programa).
o

Vrednost izraza 0.0/0.0 je , tj. not-a-number i ta specijalna vrednost se


sk

koristi da oznai matematiki nedefinisane vrednosti (npr. ili koren


ili logaritam negativnog broja). Specijalne vrednosti dalje mogu da uestvuju
n

u izrazima. Npr. ako se na izraz ija je vrednost + doda neka konstantna


ro

vrednost, dobija se opet vrednost +, ali vrednost izraza 1.0/ + jednaka je


0.0. S druge strane, svi izrazi u kojima uestvuje vrednost ponovo imaju
vrednost . Ako se koristi printf, vrednost se tampa
kt

GCC i funkcija
kao inf, a kao nan.
Najvei broj funkcija iz C standardne biblioteke (pre svega matematike
le

<math.h>) koriste tip podataka double. Tip


funkcije definisane u zaglavlju
float se u programima koristi uglavnom zbog utede memorije ili vremena na
E

raunarima na kojima je izvoenje operacija u dvostrukoj tanosti veoma skupo


(u dananje vreme, meutim, veina raunara podrava efikasnu manipulaciju
brojevima zapisanim u dvostrukoj tanosti).
I prilikom tampanja i uitavanja vrednosti ovih tipova, u pozivu funkcije
printf ili scanf, potrebno je navesti razliite formate (npr. %f za float i
double6 ), o emu e vie rei biti u glavi 12.
6 to se tie uitavanja podataka tipa double od standarda C99 doputeno je i navoenje
formata %lf.
108 6. Predstavljanje podataka i operacije nad njima

6.2.4 Logiki tip podataka

Iako mnogi programski jezici imaju poseban tip za predstavljanje (logikih)


istinitosnih vrednosti, programski jezik C sve do standarda C99 nije imao
ovakav tip podataka ve je koriena konvencija da se logiki tip predstavlja
preko brojevnih (najee celobrojnih) vrednosti tako to se smatra da vrednost
0 ima istinitosnu vrednost netano, a sve vrednosti razliite od 0 imaju istini-
tosnu vrednost tano. Ipak, C99 standardizuje tip podataka bool i konstante

5)
true koja oznaava tano i false koja oznaava netano.

Pitanja i zadaci za vebu

01
Pitanje 6.2.1. ta sve karakterie jedan tip podataka?

(2
Pitanje 6.2.2. Navesti barem jedan tipa u jeziku C za koji je standardom
definisano koliko bajtova zauzima.

Pitanje 6.2.3. Koliko bajtova zauzima podatak tipa


je char? Ukoliko nije nave-
an
deno, da li se podrazumeva da je podatak ovog tipa oznaen ili neoznaen?

Pitanje 6.2.4. Koliko bajtova zauzima podatak tipa signed char? Koja je
d

najmanja a koja najvea vrednost koju moe da ima?


iz

Pitanje 6.2.5. Koliko bajtova zauzima podatak tipa unsigned char? Koja je
najmanja a koja najvea vrednost koju moe da ima?
o

Pitanje 6.2.6. Koja ogranienja vai za duine u bajtovima tipova short int,
sk

int, long int?


Pitanje 6.2.7. int oko etiri milijarde, koliko je sizeof(int)?
n

Ako je opseg tipa


ro

Pitanje 6.2.8. Ukoliko je tip promenljive int, da li se podrazumeva da je


njena vrednost oznaena ili neoznaena ili to standard ne propisuje?
kt

Pitanje 6.2.9. U kojoj datoteci zaglavlja se nalaze podaci o opsezima celobro-


jevnih tipova za konkretnu implementaciju?
le

Pitanje 6.2.10. a 32-bitnom sistemu, koliko bajtova zauzima podatak tipa:


E

(a) char (b) int (c) short int (d) unsigned long
Pitanje 6.2.11. Koja ogranienja vai za duine u bajtovima tipova float i
double?
Pitanje 6.2.12. U kojoj datoteci zaglavlja se nalaze podaci o opsezima tipova
broja u pokretnom zarezu za konkretnu implementaciju?
109 6. Predstavljanje podataka i operacije nad njima

6.3 Konstante i konstantni izrazi


Svaki izraz koji se pojavljuje u programu je ili promenljiva ili konstanta ili
poziv funkcije ili sloen izraz. Konstante su fiksne vrednosti kao, na primer,
0, 2, 2007, 3.5, 1.4e2 ili a. Ista vrednost se ponekad moe predstaviti
razliitim konstantama. Za sve konstante i za sve izraze, pravilima jezika jed-
noznano su odreeni njihovi tipovi. Poznavanje tipova konstanti i izraza je
vano jer od tih tipova moe zavisiti vrednost sloenog izraza u kojem figurie

5)
konstanta ili neki podizraz. Od tipova konstanti i izraza zavisi i koje operacije
je mogue primeniti nad njima. Tipovi konstanti i izraza su neophodni i da bi

01
se znalo koliko memorijskog prostora treba rezervisati za neke meurezultate
u fazi izvravanja.

(2
6.3.1 Celobrojne konstante

123 ili 45678

je
Celobrojne konstante navedene u tekstu programa kao to su
su tipa int. Velike celobrojne konstante koje ne mogu biti reprezentovane
an
tipom int, a mogu u tip long su tipa long. Ako ne mogu da budu reprezento-
vane ni tipom long, a mogu tipom unsigned long, onda su tipa unsigned long.
Dakle, taan tip dekadne celobrojne konstante ne moe da se odredi ako se ne
d

znaju detalji sistema. Na primer, na veini sistema poziv


iz

printf("%d %d", 2147483647, 2147483648);


o

ispisuje
sk

2147483647 -2147483648
n

jer se 2147483647 tumai kao konstanta tipa int, dok se 2147483648 tumai
ro

kao konstanta tipa unsigned long, to ne odgovara formatu %d.


Ukoliko se eli da se neka celobrojna konstanta tretira kao da ima tip
unsigned, u U.
kt

onda se na njenom kraju zapisuje slovo ili Tip takve kon-


stante bi unsigned long, ako ona moe da bude reprezentovana tim tipom,
a unsigned int inae. Ukoliko se eli eksplicitno naglasiti da se neka celobro-
le

jna konstanta tretira kao da je tipa long, onda se na njenom kraju zapisuje
l ili L. Na primer, 12345l long, 12345 je int, 12345ul
E

slovo je tipa tipa a je


unsigned long.
Osim u dekadnom, celobrojne konstante mogu biti zapisane i u oktalnom i
u heksadekadnom sistemu. Zapis konstante u oktalnom sistemu poinje cifrom
0, a zapis konstante u heksadekadnom sistemu poinje simbolima 0x ili 0X. Na
primer, broj 31 se moe u programu zapisati na sledee naine: 31 (dekadni
zapis), 037 (oktalni zapis), 0x1f (heksadekadni zapis). I oktalne i heksadekadne
konstante mogu da budu oznaene slovima U ili u tj. l i L na kraju svog zapisa.
Negativne konstante ne postoje, ali se efekat moe postii izrazima gde se
ispred konstante navodi unarni operator - (kada se u tekstu programa naie na
-123 vrednost predstavljena ovim izrazom jeste minus stodvadesettri, ali izraz
110 6. Predstavljanje podataka i operacije nad njima

nije konstanta ve je sainjen od unarnog operatora primenjenog na konstantu).


Slino, moe se navesti i operator plus, ali to nema efekta (npr. +123 je isto
kao i 123).

6.3.2 Konstante u pokretnom zarezu

Konstante realnih brojeva ili, preciznije, konstantni brojevi u pokretnom


123.4) ili eksponent (1e-2) ili i

5)
zarezu sadre decimalnu taku (na primer,
jedno i drugo. Vrednosti ispred i iza decimalne take mogu biti izostavljene
(ali ne istovremeno). Na primer, ispravne konstante su i .4 ili 5. (ali ne i .).

01
Brojevi su oznaeni i konstante mogu poinjati znakom - ili znakom + (koji
ne proizvodi nikakav efekat). Tip svih ovih konstanti je double, osim ako na

(2
kraju zapisa imaju slovo f ili F kada je njihov tip float (npr. 1.23f). Slova L
i l na kraju zapisa oznaavaju da je tip vrednosti long double.

je
6.3.3 Karakterske konstante
an
Iako se tip char koristi i za predstavljanje malih celih brojeva, on se pre-
vashodno koristi za predstavljanje kdova karaktera (najee ASCII kdova).
Direktno specifikovanje karaktera korienjem numerikih kdova nije prepo-
d

ruljivo. Umesto toga, preporuuje se korienje karakterskih konstanti. Karak-


iz

terske konstante u programskom jeziku C se navode izmeu navodnika.


Vrednost date konstante je numerika vrednost datog karaktera u korienoj
karakterskoj tabeli (na primer, ASCII). Na primer, u ASCII kodiranju, karak-
o

terska konstanta 0 predstavlja vrednost 48 (koja nema veze sa numerikom


sk

vrednou 0), A je karakterska konstanta ija je vrednost u ASCII kdu 65,


a je karakterska konstanta ija je vrednost u ASCII kdu 97, to je ilus-
n

trovano sledeim primerom.


ro

char c = a;
char c = 97; /* ekvivalentno prethodnom (na ASCII masinama),
kt

ali se ne preporucuje zbog toga sto smanjuje


citljivost i prenosivost programa */
le

Karakterske konstante su tipa int, ali se najee dodeljuju promenljivama


E

tipa char i tada se njihova vrednost konvertuje (vie o konverzijama tipova


prilikom dodele bie reeno u poglavlju 6.5). Standard doputa i navoenje
vie karaktera izmeu navodnika (npr. ab), ali vrednost ovakve konstante
nije precizno definisana standardom (stoga emo takve konstante izbegavati u
programima).
Specijalni karakteri se mogu navesti korienjem specijalnih sekvenci karak-
tera koje poinju karakterom \ (engl. escape sequences). Jezik C razlikuje
sledee specijalne sekvence:
111 6. Predstavljanje podataka i operacije nad njima

\a alert (bell) character


\b backspace
\f formfeed
\n newline
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash

5)
\? question mark
\ single quote

01
\" double quote
\ooo (npr. \012) octal number
\xhh \x12)

(2
(npr. hexadecimal number
Karakterska konstanta \0 predstavlja karakter ija je vrednost nula. Ovaj
karakter ima specijalnu ulogu u programskom jeziku C jer se koristi za ozna-

je
avanje kraja niske karaktera (o emu e vie biti rei u nastavku). Iako je
numerika vrednost ovog karaktera ba 0, esto se u programima pie \0
an
umesto 0 da bi se istakla karakterska priroda ovog izraza.
Poto se karakterske konstante identifikuju sa njihovim numerikim vred-
nostima, one predstavljaju podatke tipa int i mogu ravnopravno da uestvuju
d

u aritmetikim izrazima (o izrazima e vie biti reeno u nastavku ove glave).


iz

Na primer, na ASCII sistemima, izraz 0 <= c && c <= 9 proverava da li


karakterska promenljiva c sadri ASCII kd neke cifre, dok se izrazom c - 0
dobija numerika vrednost cifre iji je ASCII kd sadran u promenljivoj c.
o
sk

6.3.4 Konstantni izrazi


n

Konstantni izraz je izraz koji sadri samo konstante (na primer, 4 + 3*5).
ro

Takvi izrazi mogu se izraunati u fazi prevoenja i mogu se koristiti na svakom


mestu na kojem se moe pojaviti konstanta. Tip konstantnog izraza zavisi od
tipova operanada (vie o tome bie reeno u poglavlju 6.5.3)./
kt

Pitanja i zadaci za vebu


le

Pitanje 6.3.1. Deklarisati oznaenu karaktersku promenljivu pod imenom c i


E

inicijalizovati je tako da sadri kd karaktera Y.


Pitanje 6.3.2. Deklarisati neoznaenu karaktersku promenljivu x, tako da ima
inicijalnu vrednost jednaku kdu karaktera ;.
Pitanje 6.3.3. Deklarisati promenljivu koja je tipa neoznaeni dugi ceo broj i
inicijalizovati tako da sadri heksadekadnu vrednost FFFFFFFF.
Pitanje 6.3.4. Da li C omoguava direktan zapis binarnih konstanti? Koji je
najkrai nain da se celobrojna promenljiva inicijalizuje na binarni broj
101110101101010011100110?
112 6. Predstavljanje podataka i operacije nad njima

Pitanje 6.3.5. Koja je razlika izmeu konstanti 123 i 0123, a koja izmeu
konstanti 3.4 i 3.4f?
Pitanje 6.3.6. Kojeg su tipa i koje brojeve predstavljaju sledee konstante?
(a)1234 (b) . (c) 6423ul (d) 12.3e-2 (e) 3.74e+2f (f ) 0x47 (g) 0543
(h) 3u (i) 0
Pitanje 6.3.7. Kolika je vrednost konstantnog izraza 0x20 + 020 + 2 - 0
?

5)
Pitanje 6.3.8. Ako se na sistemu koristi ASCII tabela karaktera, ta ispisuje

01
sledei C kd:

char c1 = a; char c2 = 97;

(2
printf("%c %d %d %c", c1, c1, c2, c2);

6.4 Operatori i izrazi

je
Izrazi se u programskom jeziku C grade od konstanti i promenjivih pri-
an
menom irokog spektra operatora. Osim od konstanti i promenljivih, elemen-
tarni izrazi se mogu dobiti i kao rezultat poziva funkcija, operacija pristupa
d

elementima nizova, struktura i slino.


Operatorima su predstavljene osnovne operacije i relacije koje se mogu vriti
iz

nad podacima osnovnih tipova u jeziku C. Za celobrojne tipove, te operacije


ukljuuju aritmetike, relacijske i logike operacije. Podrane su i operacije
o

koje se primenjuju nad pojedinanim bitovima celobrojnih vrednosti.


Operatori se dele na osnovu svoje arnosti tj. broja operanada na koje se pri-
sk

menjuju. Unarni operatori deluju samo na jedan operand i mogu biti prefiksni
kada se navode pre operanda i postfiksni kada se navode nakon svog operanda.
n

Binarni operatori imaju dva operanda i obino su infiksni tj. navode se izmeu
ro

svojih operanda. U jeziku C postoji jedan ternarni operator koji se primenjuje


na tri operanda.
kt

Vrednost izraza odreena je vrednou elementarnih podizraza (konstanti,


promenljivih) i pravilima pridruenim operatorima. Neka pravila izraunavanja
vrednosti izraza nisu propisana standardom i ostavljena je sloboda implementa-
le

torima prevodilaca da u potpunosti preciziraju semantiku na nain koji dovodi


E

do efikasnije implementacije. Tako, na primer, standardi ne definiu kojim se


redom izraunavaju operandi operatora + pre njihovog sabiranja, pa nije pro-
pisano koja od funkcija f i g e biti prva pozvana prilikom izraunavanja izraza
f() + g().

6.4.1 Prioritet i asoscijativnost operatora

Izrazi mogu da obuhvataju vie operatora i zagrade ( i ) se koriste da bi


odredile kojim redosledom ih treba primenjivati. Ipak, slino kao i u matem-
atici, postoje konvencije koje omoguavaju izostavljanje zagrada. Jedna od os-
novnih takvih konvencija je prioritet operatora koji definie kojim redosledom
113 6. Predstavljanje podataka i operacije nad njima

e se dva razliita operatora primenjivati kada se nau u istom, nezagraenom


izrazu. Na primer, kako je i oekivano, vrednost konstantnog izraza 3 + 4 * 5
bie 23, jer operator * ima prioritet u odnosu na operator +. Semantika jezika
C uglavnom potuje prioritet koji vai u matematici, ali, s obzirom na mnogo
vei broj operatora uvodi i dodatna pravila. Prioritet operatora dat je u tabeli
navedenoj u dodatku A. Navedimo neke osnovne principe u definisanju prior-
iteta:

5)
1. Unarni operatori imaju vei prioritet u odnosu na binarne.

2. Postfiksni unarni operatori imaju vei prioritet u odnosu na prefiksne

01
unarne operatore.

3. Aritmetiki operatori imaju prioritet u odnosu na relacijske koji imaju

(2
prioritet u odnosu na logike operatore.

4. Operatori dodele imaju veoma nizak prioritet.

je
Druga vana konvencija je asocijativnost operatora koja definie kojim re-
dosledom e se izraunavati dva ista operatora ili operatora istog prioriteta
an
kada se nau uzastopno u istom, nezagraenom izrazu. Obino se razlikuju
leva asocijativnost, kada se izraz izraunava sleva na desno i desna asocija-
d

tivnost, kada se izraz izraunava zdesna na levo. Neki jezici razlikuju i neaso-
cijativnost, kada je zabranjeno da se isti operator dva puta uzastopno ponovi
iz

u istom izrazu. Matematiki, neki operatori su asocijativni, to znai da vred-


nost izraza ne zavisi od redosleda primene uzastopno ponovljenog operatora.
o

Na primer, u matematici uvek vai + ( + ) = ( + ) + . Zbog naina


+) nisu uvek
sk

reprezentacije podataka u raunaru mnogi operatori (ukljuujui i


asocijativni. Na primer, izraz (INT_MIN + INT_MAX) + 1 ima vrednost 0 bez
nastanka prekoraenja, dok izraz INT_MIN + (INT_MAX + 1) ima vrednost 0,
n

7
ali dolazi do prekoraenja . Ovakvo ponaanje je posledica injenice da je u
ro

jeziku C sabiranje celih brojeva zapravo sabiranje po modulu. Operator - ima


levu asocijativnost. Na primer, vrednost izraza 1 - 2 - 3 je -4 (a ne 2, to bi
kt

bio sluaj da ima desnu asocijativnost, iz ega je jasno da - nije asocijativan


operator). Veina operatora ima levu asocijativnost (najznaajniji izuzeci su
le

prefiksni unarni operatori i operatori dodele). Asocijativnost operatora data je


u tabeli u dodatku A.
E

6.4.2 Operator dodele

Operatorom dodele se neka vrednost pridruuje datoj promenljivoj. Oper-


ator dodele se zapisuje =. Na primer,

7 INT_MIN i INT_MAX su simbolika imena ije su vrednosti uvedene direktivom #define u


datoteci zaglavlja limits.h. Njima odgovaraju vrednosti najmanje i najvee vrednosti tipa
int na konkretnom sistemu.
114 6. Predstavljanje podataka i operacije nad njima

broj_studenata = 80;
broj_grupa = 2;

U dodeljivanju vrednosti, sa leve strane operatora dodele moe da se nalazi


promenljiva, a bie diskutovano kasnije, i element niza ili memorijska lokacija.
Ti objekti, objekti kojima moe biti dodeljena vrednost nazivaju se l-vrednosti
(od engleskog l-value ili left-value).

5)
Operator dodele se moe koristiti za bilo koji tip l-vrednosti, ali samo ako je
desna strana odgovarajueg tipa (istog ili takvog da se njegova vrednost moe

01
konvertovati u tip l-vrednosti o emu e vie rei biti u poglavlju 6.5).
Tip izraza dodele je tip leve strane, a vrednost izraza dodele je vrednost koja
e biti dodeljena levoj strani (to nije uvek vrednost koju ima desna strana).

(2
Promena vrednosti objekta na levoj strani je propratni (boni, sporedni) efekat
(engl. side effect) do kojeg dolazi prilikom izraunavanja vrednosti izraza. Na
primer, izvravanje naredbe broj_studenata = 80; svodi se na izraunavanje

je
izrazabroj_studenata = 80. Tip promenljive broj_studenata je istovre-
meno i tip ovog izraza, vrednost je jednaka 80, a prilikom ovog izraunavanja
an
menja se vrednost promenljive broj_studenata. Ovakvo ponaanje moe se
iskoristiti i za viestruko dodeljivanje. Na primer, nakon sledee naredbe, sve
x, y i z 0:
d

tri promenljive imae vrednost


iz

x = y = z = 0;
o
sk

6.4.3 Aritmetiki operatori

Nad operandima brojevnih tipova mogu se primeniti sledei aritmetiki


n

operatori:
ro

+ binarni operator sabiranja;

- binarni operator oduzimanja;


kt

* binarni operator mnoenja;

/
le

binarni operator (celobrojnog) deljenja;

% binarni operator ostatka pri deljenju;


E

- unarni operator promene znaka;

+ unarni operator.

Operator % mogue je primeniti iskljuivo nad operandima celobrojnog tipa.


Operator deljenja oznaava razliite operacije u zavisnosti od tipa svojih
operanada.
8 Kada se operator deljenja primenjuje na dve celobrojne vrednosti

8 Na hardveru su operacije nad celim brojevima i brojevima u pokretnom zarezu imple-


mentirane nezavisno i u izvrnom programu koristi se jedna od njih, izabrana u fazi pre-
voenja u zavisnosti od tipova operanada. Informacije o tipovima iz izvornog programa su
115 6. Predstavljanje podataka i operacije nad njima

primenjuje se celobrojno deljenje (tj. rezultat je celi deo kolinika). Na primer,


izraz 9/5 ima vrednost 1. U ostalim sluajevima primenjuje se deljenje realnih
brojeva (preciznije, deljenje brojeva u pokretnom zarezu). Na primer, izraz
9.0/5.0 ima vrednost 1.8 (jer se koristi deljenje brojeva u pokretnom zarezu).
U sluaju da je jedan od operanada ceo broj, a drugi broj u pokretnom zarezu,
vri se implicitna konverzija celobrojnog operanda u broj u pokretnom zarezu
i primenjuje se deljenje brojeva u pokretnom zarezu (vie rei o konverzijama
bie u poglavlju 6.5).

5)
Prefiksni unarni operatori + i - imaju desnu asocijativnost i vii prioritet
od svih binarnih operatora. Operatori *, / i % imaju isti prioritet, vii od

01
prioriteta binarnih operatora + i -. Svi navedeni binarni operatori imaju levu
asocijativnost.

(2
Inkrementiranje i dekrementiranje. Inkrementiranje generalno znai po-
stepeno uveavanje ili uveavanje za neku konkretnu vrednost. U programi-

je
ranju se pod inkrementiranjem obino podrazumeva uveavanje za 1, a pod
dekrementiranjem umanjivanje za 1. U jeziku C, operator inkrementiranja
an
(uveavanja za 1) zapisuje se sa ++, a operator dekrementiranja (umanjivanja
za 1) zapisuje se sa --:
d

++ (prefiksno i postfiksno) inkrementiranje;


iz

-- (prefiksno i postfiksno) dekrementiranje.


o

Oba operatora mogu se primeniti nad celim brojevima i brojevima u pokretnom


sk

zarezu. Inkrementiranje i dekrementiranje se mogu primenjivati samo nad l-


vrednostima (najee su to promenljive ili elementi nizova). Tako, na primer,
izraz 5++ nije ispravan. Oba operatora su unarna (imaju po jedan operand) i
n

mogu se upotrebiti u prefiksnom (na primer, ++n) ili postfiksnom obliku (na
ro

primer,n++). Razlika izmeu ova dva oblika je u tome to ++n uveava vrednost
promenljive n pre nego to je ona upotrebljena u irem izrazu, a n++ je uveava
kt

nakon to je upotrebljena. Preciznije, vrednost izraza n++ je stara vrednost


promenljive n, a vrednost izraza ++n je nova vrednost promenljive n, pri emu
le

se u oba sluaja, prilikom izraunavanja vrednosti izraza, kao propratni efekat,


uveava vrednost promenljive n. Na primer, ako promenljiva n ima vrednost
E

5, onda

x = n++;

dodeljuje promenljivoj x vrednost 5, a

x = ++n;

na ovaj, ali i na druge sline naine, upotrebljene tokom prevoenja i one se ne uvaju u
izvrnom programu.
116 6. Predstavljanje podataka i operacije nad njima

dodeljuje promenljivoj x vrednost 6. Promenljiva n u oba sluaja dobija vred-


nost 6. Slino, kd

int a = 3, b = 3, x = a++, y = ++b;


printf("a = %d, b = %d, x = %d, y = %d\n);

ispisuje

a = 4, b = 4, x = 3, y = 4

5)
01
Ukoliko ne postoji iri kontekst, tj. ako inkrementiranje ini itavu naredbu,
vrednost izraza se i ne koristi i onda nema razlike izmeu naredbe n++; i ++n;.
Na primer,

(2
int a = 3, b = 3;
a++; ++b;
printf("a = %d, b = %d\n", a, b);

je
an
prethodni kd ispisuje

a = 4, b = 4
d
iz

Sm trenutak u kojem se vri izvravanje propratnog efekta precizno je


definisan standardom i odreen tzv. sekvencionim takama (engl. sequence
point) jezika u kojima se garantuje da je efekat izvren. Tako je, na primer,
o

izmeu svake dve susedne promenljive koje se deklariu (mesto oznaenom sim-
sk

bolom ,) sekvenciona taka, kao i kraj naredbe i deklaracije (mesto oznaeno


sa ;), dok operator + to 9
esto nije . Efekat moe biti izvren i pre sekvencione
n

take, ali za to ne postoji garancija, tako neto zavisi od konkretne imple-


mentacije (od kompilatora) i na to se ne treba oslanjati. Na primer, naredni
ro

kd

int a = 3, x = a++, y = a++;


kt

int b = 3, z = b++ + b++;


printf("a = %d, x = %d, y = %d,\n", a, x, y);
le

printf("b = %d, z = %d\n", b, z);


E

ispisuje

a = 5, x = 3, y = 4,
b = 5, z = 6

Zaista, x prima originalnu vrednost promenljive a (vrednost 3). Poto je zarez


nakon inicijalizacije promenljive x oznaava mesto gde je sekvenciona taka,
prvo uveanje promenljive a vri se na tom mestu tako da y prima uveanu

9 Precizan spisak svih sekvencionih taaka moe se nai u standardu jezika.


117 6. Predstavljanje podataka i operacije nad njima

vrednost promenljive a (vrednost 4). Drugo uveanje promenljive a (na vred-


nost 5 vri se na kraju deklaracije sa inicijalizacijama, tj. na mestu oznaenom
sa ;). U drugom sluaju u nekoj implementaciji se moe desiti da promenljiva
z sabira dva puta originalnu vrednost promenljive b (vrednost 3), jer + ne oz-
naava sekvencionu taku i na tom mestu promenljiva jo ne mora biti uveana,
a promenljiva b se uveava i to dva puta na kraju deklaracije sa inicijalizacijama
(na mestu obeleenom sa ;, jer na tom mestu postoji sekvenciona taka).
Primetimo da semantika operatora inkrementiranja moe biti veoma komp-

5)
likovana i stoga se ne savetuje korienje sloenijih izraza sa ovim operatorima,
koji se oslanjaju na fine detalje semantike.

01
6.4.4 Relacijski i logiki operatori

(2
Relacijski operatori. Nad celim brojevima i brojevima u pokretnom zarezu
mogu se koristiti sledei binarni relacijski operatori:

je
== jednako;
an
!= razliito.

> vee;
d

>= vee ili jednako;


iz

< manje;

<= manje ili jednako;


o

<, <=, > i >= imaju isti prioritet i to vii od op-


sk

Relacijski operatori poretka


eratora jednakosti == != i svi imaju levu asocijativnost. Rezultat
i razliitosti
relacionog operatora primenjenog nad dva broja je vrednost 0 (koja odgovara
n

istinitosnoj vrednosti netano) ili vrednost 1 (koja odgovara istinitosnoj vred-


ro

nosti netano). Na primer, izraz 3 > 5 ima vrednost 0, a izraz 7 < 5 != 1


je isto to i (7 < 5) != 1 i ima vrednost 1 jer izraz 7 < 5 ima vrednost 0,
kt

to je razliito od 1. Ako promenljiva x ima vrednost 2, izraz 3 < x < 5 ima


vrednost 1 (tano) to je razliito od moda oekivane vrednosti 0 (netano) jer
2 nije izmeu 3 i 5. Naime, izraz se izraunava sleva na desno, podizraz 3 < x
le

ima vrednost 0, a zatim izraz 0 < 5 ima vrednost 1. Kako u ovakvim slua-
E

jevima kompilator ne prijavljuje greku, a u fazi izvravanja se dobija rezultat


neoekivan za poetnike, ovo je opasna greka programera koji su navikli na uo-
biajenu matematiku notaciju. Dakle, proveru da li je neka vrednost izmeu
dve zadate neophodno je vriti uz primenu logikog operatora &&.
Binarni relacijski operatori imaju nii prioritet od binarnih aritmetikih
operatora.
Operator == koji ispituje da li su neke dve vrednosti jednake i operator
dodele = razliiti su operatori i imaju potpuno drugaiju semantiku. Njihovo
nehotino meanje est je uzrok greaka u C programima.
118 6. Predstavljanje podataka i operacije nad njima

Logiki operatori. Logiki operatori primenjuju se nad brojevnim vrednos-


tima i imaju tip rezultata int. Brojevnim vrednostima pridruene su logike ili
istinitosne vrednosti na sledei nain: ukoliko je broj jednak 0, onda je njegova
logika vrednost 0 (netano), a inae je njegova logika vrednost 1 (tano). Iako
su sve vrednosti operanada koje su razliite od 0 doputene i tumae se kao
tano, rezultat izraunavanja tano nije proizvoljna vrednost razliita od 0, ve
iskljuivo vrednost 1.
Postoje sledei logiki operatori:

5)
! logika negacija ne;

01
&& logika konjunkcija i;

|| logika disjunkcija ili.

(2
Operator && ima vii prioritet u odnosu na operator ||, a oba su levo
asocijativna. Binarni logiki operatori imaju nii prioritet u odnosu na binarne

je
relacijske i logike operatore. Operator !, kao unarni operator, ima vii prioritet
u odnosu na bilo koji binarni operator i desno je asocijativan. Na primer,
an
vrednost izraza 5 && 4.3 jednaka je 1;
d

vrednost izraza 10.2 || 0 jednaka je 1;


iz

vrednost izraza 0 && 5 jednaka je 0;


vrednost izraza !1 jednaka je 0;
o

vrednost izraza !9.2 jednaka je 0;


sk

vrednost izraza !0 jednaka je 1;


vrednost izraza !(2>3) jednaka je 1;
n

irazom 3 < x && x < 5 proverava se da li je vrednost promenljive x


ro

izmeu 3 i 5;
izraz a > b || b > c && b > d ekvivalentan je izrazu
kt

(a>b) || ((b>c) && (b>d));


g % 4 == 0 && g % 100 != 0 || g % 400 == 0
le

izrazom proverava se
da li je godinag prestupna.
E

Lenjo izraunavanje. U izraunavanju vrednosti logikih izraza koristi se


strategija lenjog izraunavanja (engl. lazy evaluation). Osnovna karakteris-
tika ove strategije je izraunavanje samo onog to je neophodno. Na primer,
prilikom izraunavanja vrednosti izraza

2<1 && a++

bie izraunato da je vrednost podizraza (2<1) jednaka 0, pa je i vrednost


celog izraza (zbog svojstva logikog i) jednaka0. Zato nema potrebe izrauna-
vati vrednost podizraza a++, te e vrednost promenljive a ostati nepromenjena
119 6. Predstavljanje podataka i operacije nad njima

nakon izraunavanja vrednosti navedenog izraza. S druge strane, nakon izra-


unavanja vrednosti izraza

a++ && 2<1

vrednost promenljive a e biti promenjena (uveana za 1). U izrazima u ko-


jima se javlja logiko i, ukoliko je vrednost prvog operanda jednaka 1, onda se
izraunava i vrednost drugog operanda.

5)
U izrazu u kojem se javlja logiko ili, ukoliko je vrednost prvog operanda
jednaka 1, onda se ne izraunava vrednost drugog operanda. Ukoliko je vred-

01
nost prvog operanda jednaka 0, onda se izraunava i vrednost drugog operanda.
Na primer, nakon

(2
1<2 || a++_

se ne menja vrednost promenljive a, a nakon

je
2<1 || a++
an
se menja (uveava za 1) vrednost promenljive a.
d

6.4.5 Bitovski operatori


iz

C podrava naredne operatore za rad nad pojedinanim bitovima, koji se


mogu primenjivati samo na celobrojne argumente:
o


sk

bitovska negacija;

& bitovska konjunkcija;


n

| bitovska disjunkcija;
ro

^ bitovska eksluzivna disjunkcija;

pomeranje (iftovanje) bitova ulevo;


kt

pomeranje (iftovanje) bitova udesno.


le

Kao unarni operator, operator ~ ima najvei prioritet i desno je asocija-


E

tivan. Prioritet operatora pomeranja je najvei od svih binarnih bitovskih


operatora nalazi se izmeu prioriteta aritmetikih i relacijskih operatora.
Ostali bitovski operatori imaju prioritet izmeu relacijskih i logikih operatora
i to & ima vei prioritet od ^ koji ima vei prioritet od |. Ovi operatori imaju
levu asocijativnost.

& bitovsko i primenom ovog operatora vri se konjunkcija pojedinanih


bitova dva argumenta (-ti bit rezultata predstavlja konjunkciju -tih
bitova argumenata). Na primer, ukoliko su promenljive x1 i x2 tipa
unsigned char i ukoliko je vrednost promenljive x1 jednaka 74, a pro-
menljive x2 jednaka 87, vrednost izraza x1 & x2 jednaka je 66. Naime,
120 6. Predstavljanje podataka i operacije nad njima

broj 74 se binarno zapisuje kao 01001010, broj 87 kao 01010111, i kon-


junkcija njihovih pojedinanih bitova daje 01000010, to predstavlja za-
pis broja 66. S obzirom na to da se prevoenje u binarni sistem efikasnije
sprovodi iz heksadekadnog sistema nego iz dekadnog, prilikom korienja
bitovskih operatora konstante se obino zapisuju heksadekadno. Tako bi,
u prethodnom primeru, broj x1 imao vrednost zapisanu kao 0x4A, a broj
x2 bi imao vrednost zapisanu kao 0x57.

5)
| bitovsko ili primenom ovog operatora vri se (obina) disjunkcija po-
jedinanih bitova dva navedena argumenta. Za brojeve iz tekueg primera,

01
vrednost izraza x1 | x2 je 01011111, tj. 95, tj. 0x5F.
bitovsko ekskluzivno ili primenom ovog operatora vri se ekskluzivna

(2
disjunkcija pojedinanih bitova dva argumenta. Za brojeve iz tekueg
primera, vrednost izraza x1 ^ x2 je 00011101, tj. 29, tj. 0x1D.
jedinini komplement

je
primenom ovog operatora vri se komplemen-
tiranje (invertovanje) svakog bita argumenta. Na primer, vrednost izraza
an
~x1 u tekuem primeru je 10110101, tj. B5, tj. 181.
levo pomeranje (iftovanje) primenom ovog operatora bitovi prvog
d

argumenta se pomeraju u levo za broj pozicija naveden kao drugi argu-


iz

ment. Poetni bitovi prvog argumenta se zanemaruju, dok se na zavrna


mesta rezultata upisuju nule. Levo pomeranje broja za jednu poziciju
odgovara mnoenju sa dva. x ima tip
Na primer, ukoliko promenljiva
o

unsigned char i vrednost 0x95, tj. 10010101, vrednost izraza x << 1 je


sk

00101010, tj. 0x2A.


desno pomeranje (iftovanje) primenom ovog operatora bitovi pr-
n

vog argumenta se pomeraju u desno za broj pozicija naveden kao drugi


ro

argument. Krajnji (desni) bitovi prvog argumenta se zanemaruju, a to


se tie poetnih (levih) bitova rezultata, postoji mogunost da se oni
kt

popunjavaju uvek nulama (tzv. logiko pomeranje ) ili da se oni popune


vodeim bitom (koji predstavlja znak) prvog argumenta (tzv. aritmetiko
le

pomeranje ). Osnovna motivacija aritmetikog pomeranja je da desno


pomeranje broja za jednu poziciju odgovara celobrojnom deljenju sa dva.
E

Koje pomeranje e se vriti zavisi od tipa prvog argumenta. Ukoliko je


argument neoznaen, poetni bitovi rezultata e biti postavljeni na nulu,
bez obzira na vrednost vodeeg bita prvog argumenta. Ukoliko je ar-
gument oznaen, poetni bitovi rezultata e biti postavljeni na vrednost
vodeeg bita prvog argumenta. Na primer, ukoliko promenljiva x ima tip
signed char i vrednost 0x95, tj. 10010101, vrednost izraza x >> 1 je
11001010, tj. 0xCA. Ukoliko je tip promenljive x unsigned char, tada je
vrednost izraza x >> 1 broj 01001010, tj. 0x4A.

Definisani su i operatori sloenih bitovskih dodela (&=, |=, ^=, <<=, >>=).
Na primer, a &= 1 ima isto znaenje kao a = a & 1.
121 6. Predstavljanje podataka i operacije nad njima

Bitovske operatore ne treba meati sa logikim operatorima. Na primer,


vrednost izraza 1 && 2 je 1 (tano i tano je tano), dok je vrednost izraza
1 & 2 jednaka 0 (000...001 & 000..010 = 000...000).
Vie rei o upotrebi bitovskih operatora kao i primeri programa bie dati u
drugom tomu ove knjige.

6.4.6 Sloeni operatori dodele

5)
Dodela koja ukljuuje aritmetiki operator i = i + 2; moe se zapisati
krae i kao i += 2;. Slino, naredba x = x * (y+1); ima isto dejstvo kao i

01
x *= y+1;. Za veinu binarnih operatora postoje odgovarajui sloeni opera-
tori dodele:

(2
+=, -=, *=, /=, %=, &=, |=, =, =

Operatori dodele imaju nii prioritet od svih ostalih operatora i desnu aso-

je
cijativnost.
an
Izraunavanje vrednosti izraza

izraz1 op= izraz2


d

obino ima isto dejstvo kao i izraunavanje vrednosti izraza


iz

izraz1 = izraz1 op izraz2


o

gde je op jedan od nabrojanih operatora. Meutim, postoje sluajevi kada


sk

navedena dva izraza imaju razliite vrednosti


10 , te treba biti obazriv sa ko-
rienjem sloenih operatora dodele.
n

Krai zapis uz korienje sloenih operatora dodele obino daje i neto


ro

efikasniji kd (mada savremeni kompilatori obino umeju i sami da prepoz-


naju situacije u kojima se izraz moe prevesti isto kao da je zapisan u kraem
obliku).
kt

Naredni primer ilustruje primenu sloenog operatora dodele (obratiti panju


na rezultat operacija koji zavisi od tipa operanda).
le

Program 6.3.
E

#include <stdio.h>

int main() {
unsigned char c = 254;

10 Na primer, prilikom izraunavanja izraza a[i++] += 1 (a[i] i-ti element niza


oznaava
a), promenljiva a[i++] = a[i++] + 1
inkrementira se samo jednom, dok se u sluaju
promenljiva inkrementira dva puta. Slino, prilikom izraunavanja izraza a[f()] += 1,
funkcija f() se poziva samo jednom, dok se u sluaju a[f()] = a[f()] + 1 funkcija f()
poziva dva puta, to moe da proizvede razliiti efekat ukoliko funkcija f() u dva razliita
poziva vraa razliite vrednosti. Funkcije i nizovi su detaljnije opisani u narednim glavama.
122 6. Predstavljanje podataka i operacije nad njima

c += 1; printf("c = %d\n", c);


c += 1; printf("c = %d\n", c);
return 0;
}
Izlaz programa:

c = 255
c = 0

5)
01
6.4.7 Operator uslova

?:

(2
Ternarni operator uslova se koristi u sledeem optem obliku:

izraz1 ? izraz2 : izraz3;

je
Prioritet ovog operatora je nii u odnosu na sve binarne operatore osim
dodela i operatora ,.
an
Izraz izraz1 se izraunava prvi. Ako on ima ne-nula vrednost (tj. ako ima
istinitosnu vrednost tano), onda se izraunava vrednost izraza izraz2 i to je
d

vrednost itavog uslovnog izraza. Inae se izraunava vrednost izraz3 i to je


vrednost itavog uslovnog izraza. Na primer, izraunavanjem izraza
iz

m = (a > b) ? a : b
o

vrednost promenljive m postavlja se na maksimum vrednosti promenljivih a i


sk

b, dok se izraunavanjem izraza

m = (a < 0) ? -a : a
n
ro

vrednost promenljive m postavlja na apsolutnu vrednost promenljive a.


Opisana semantika ternarnog operatora takoe je lenja. Na primer, nakon
kt

izvravanja kda

n = 0;
le

x = (2 > 3) ? n++ : 9;
E

promenljiva x imae vrednost 9, a promenljiva n e zadrati vrednost 0 jer se


drugi izraz nee izraunavati.

6.4.8 Operator zarez

Binarni operator zarez (,) je operator kao i svaki drugi (najnieg je pri-
oriteta od svih operatora u C-u) i prilikom izraunavanja vrednosti izraza
izgraenog njegovom primenom, izraunavaju se oba operanda, pri emu se
vrednost celokupnog izraza definie kao vrednost desnog operanda (ta vred-
123 6. Predstavljanje podataka i operacije nad njima

nost se esto zanemaruje). Ovaj operator se esto koristi samo da spoji dva
izraza u jedinstveni (da bi se takav sloeni izraz mogao upotrebiti na mestima
gde sintaksa zahteva navoenje jednog izraza). To je najee u inicijalizaciji i
koraku for petlje (o emu e vie rei biti u poglavlju 7.4). Jo jedna od estih
upotreba je i u kdu oblika

x = 3, y = 5; /* ekivalentno bi bilo i x = 3; y = 5; */

5)
Treba razlikovati operator , od zareza koji razdvajaju promenljive prilikom
deklaracije i poziva funkcije (koji nisu operatori). esta greka u korienju

01
ovog operatora je pri pokuaju pristupa viedimenzionom nizu (vie o nizovima
reeno je u poglavlju 6.6.4). Na primer A[1, 2] potpuno je ekvivalentno sa
A[2], A dvodimenzioni. Ispravan nain pristupa

(2
to je pogreno ako je niz
elementu na poziciji (1, 2) je A[1][2].

je
6.4.9 Operator sizeof
an
Veliinu u bajtovima koju zauzima neki tip ili neka promenljiva mogue
je odrediti korienjem operatora sizeof. Tako, sizeof(int) predstavlja
veliinu tipa int i na tridesetidvobitnim sistemima vrednost ovog izraza je
d

najee 4.
iz

Vrednost koju vraa operator sizeof ima tip size_t. Ovo je neoznaen
celobrojni tip, koji obino slui za reprezentovanje veliine objekata u memoriji.
Tip size_t ne mora nuno da bude jednak tipu unsigned int (standard C99
o

zahteva samo da vrednosti ovog tipa imaju barem dva bajta).


sk

Pitanja i zadaci za vebu


n

Pitanje 6.4.1.
ro

Kako se grade izrazi?

Pitanje 6.4.2. Nabrojati aritmetike, relacijske operatore, logike i bitovske


kt

operatore.

Pitanje 6.4.3. ta je to asocijativnost, a ta prioritet operatora?


le

Pitanje 6.4.4. Navesti neki operator jezika C koji ima levu i neki operator
E

koji ima desnu asocijativnost.

Pitanje 6.4.5. Kakav je odnos prioriteta unarnih i binarnih operatora?

Pitanje 6.4.6. Kakav je odnos prioriteta logikih, aritmetikih i relacijskih


operatora?

Pitanje 6.4.7. Kakav je odnos prioriteta operatora dodele i ternarnog opera-


tora?

Pitanje 6.4.8. Da li dodele spadaju u grupu nisko ili visoko prioritetnih op-
eratora?
124 6. Predstavljanje podataka i operacije nad njima

Pitanje 6.4.9. Koji operator jezika C ima najnii prioritet?

Pitanje 6.4.10. Imajui u vidu asocijativnost C operatora, ta je rezultat


izvravanja kda:

double a = 2.0, b = 4.0, c = -2.0;


printf("%f\n", (-b + sqrt(b*b - 4*a*c)) / 2.0 * a);
Kako popraviti kd tako da ispravno izraunava reenje kvadratne jednaine?

5)
Pitanje 6.4.11. Postaviti zagrade u naredne izraze u skladu sa podrazumevanim

01
prioritetom i u skladu sa podrazumevanom asocijativnou operatora (na primer,
3+4*5+7 (3+(4*5))+7).
(a) a = b = 4 (b) a = 3 == 5 (c) c = 3 == 5 + 7 <= 4

(2
(d) 3 - 4 / 2 < 3 && 4 + 5 * 6 <= 3 % 7 * 2
(e) a = b < c ? 3 * 5 : 4 < 7, 2 (f ) a = b + c && d != e

je
Pitanje 6.4.12. Koja je vrednost narednih izraza:
(a) 3 / 4 (b) 3.0 / 4 (c) 14 % 3 (d) 3 >= 7
an
(e) 3 && 7 (f ) 3 || 0 (g) a = 4 (h) (3 < 4) ? 1 : 2
(k) 3 < 5 < 5 (i) 3 < 7 < 5 (j) 6%3 || (6/3 + 5)
(j) 3 < 5 ? 6 + 2 : 8 + 3
d
iz

Pitanje 6.4.13. Da li su definisana deljenja 1/0 i 1.0/0.0?


Pitanje 6.4.14. Da li su ispravni sledei izrazi i ako jesu ta je njihova vred-
o

nost:
sk

(a) 2++ (b) a++ (c) 2** (d) a** (e) 2>>2 (f ) a>>2 (g) a &&= 0 (e) a ||= -7
Pitanje 6.4.15. Koje vrednosti imaju relevantne promenljive nakon navedenih
n

naredbi:
ro

1. a=b++;, ako pre ove naredbe promenljiva a ima vrednost 1, a promenljiva


b vrednost 3;
kt

2. a+=b;, ako pre ove naredbe promenljiva a ima vrednost 1, a promenljiva


b vrednost 3?
le

3. int a=5; b = (a++ - 3) + (a++ - 3);?


E

4. int a = 1, b = 1, x, y;
x = a++; y = ++b;

5. int a=3; b = (a++ - 3) && (a++ - 3);?


6. int a, b=2, c; c = b++; a = c>b && c++;
7. unsigned char a, b=255; a = ++b && b++;
Pitanje 6.4.16. ta znai to da se operatori &&, || i ?: izraunavaju lenjo?
Navesti primere.
125 6. Predstavljanje podataka i operacije nad njima

Pitanje 6.4.17. ta ispisuje naredni kd?

int a = 3, b = 0, c, d;
c = a % 2 || b++;
d = a % 3 || b++;
printf("%d %d %d %d", a, b, c, d);
Pitanje 6.4.18.

5)
1. Napisati izraz koji je taan ako je godina g prestupna.

2. Napisati izraz ija je vrednost jednaka manjem od brojeva a i b.

01
3. Napisati izraz ija je vrednost jednaka apsolutnoj vrednosti broja a.
4. Napisati izraz koji je, na ASCII sistemu, taan ako je karakter c malo

(2
slovo.

5. Napisati izraz koji je taan ako je karakter c samoglasnik.

je
6. Napisati izraz ija je vrednost jednaka vrednosti implikacije p q.
7. Napisati izraz koji ako je c malo slovo ima vrednost odgovarajueg velikog
an
slova, a inae ima vrednost c.

Zadatak 6.4.1. Napisati program koji za zadati sat, minut i sekund izraunava
d

koliko je vremena (sati, minuta i sekundi) ostalo do ponoi. Program treba i



iz

da izvri proveru da li su uneti podaci korektni.

Zadatak 6.4.2. Napisati program koji za uneta tri pozitivna broja u pokretnom
o

zarezu ispituje da li postoji trougao ije su duine stranica upravo ti brojevi



sk

uslov provere napisati u okviru jednog izraza.

Zadatak 6.4.3. Napisati program koji izraunava zbir cifara unetog etvoro-

n

cifrenog broja.
ro

Zadatak 6.4.4. Napisati program koji razmenjuje poslednje dve cifre unetog
prirodnog broja. Na primer, za uneti broj 1234 program treba da ispie 1243.
kt

Napisati program koji ciklino u levo rotira poslednje tri cifre unetog prirodnog

broja. Na primer, za uneti broj 12345 program treba da ispie 12453.
le

Zadatak 6.4.5. Napisati program koji za uneti par prirodnih brojeva izrau-
E

nava koji je po redu taj par u cik-cak nabrajanju datom na slici 3.1. Napisati i
program koji za dati redni broj u cik-cak nabrajanju odreuje odgovarajui par

prirodnih brojeva.

Zadatak 6.4.6. Napisati program koji za unete koeficijente i izraunava


reenje jednaine + = 0. Program treba da prijavi ukoliko jednaina nema

reenja ili ima vie od jednog reenja.

Zadatak 6.4.7. Napisati program koji za unete koeficijente 1 , 1 , 1 , 2 , 2


i 2 izraunava reenje sistema jednaina 1 + 1 = 1 , 2 + 2 = 2 .
Program treba da prijavi ukoliko sistem nema reenja ili ima vie od jednog

reenja.
126 6. Predstavljanje podataka i operacije nad njima

Zadatak 6.4.8. Napisati program koji za unete koeficijente , i ( =


0) izraunava (do na mainsku tanost) i ispisuje realna reenja kvadratne

2
jednaine + + = 0.

Zadatak 6.4.9. Napisati program koji ispisuje sve delioce unetog neoznaenog
celog broja (na primer, za unos 24 treeni delioci su 1, 2, 3, 4, 6, 8, 12i
24).
Zadatak 6.4.10.

5)
Napisati program koji uitava karaktere sa standardnog ulaza
sve dok se ne unese taka (karakter .), zarez (karakter ,) ili kraj datoteke (EOF)

01
i prepisuje ih na standardni izlaz menjajui svako malo slovo velikim. (Napom-
ena: za uitavanje karaktera koristiti funkciju getchar, a za ispis funkciju

putchar)

(2
6.5 Konverzije tipova

je
Konverzija tipova predstavlja pretvaranje vrednosti jednog tipa u vred-
nost drugog tipa. Jezik C je veoma fleksibilan po pitanju konverzije tipova
an
i u mnogim situacijama doputa korienje vrednosti jednog tipa tamo gde se
oekuje vrednost drugog tipa (ponekad se kae da je C slabo tipiziran jezik ).
d

Iako su konverzije esto neophodne da bi se osiguralo korektno funkcionisanje


programa i omoguilo meanje podataka razliitih tipova u okviru istog pro-
iz

grama, konverzije mogu dovesti i do gubitka podataka ili njihove loe inter-
pretacije. Jezik C je statiki tipiziran, pa, iako se prilikom konverzija konver-
o

tuju vrednosti izraza, promenljive sve vreme ostaju da budu onog tipa koji im
sk

je pridruen deklaracijom.
n

6.5.1 Vrste konverzija


ro

Postoje eksplicitne konverzije (koje se izvravaju na zahtev programera)


i implicitne konverzije (koje se izvravaju automatski kada je to potrebno).
kt

Neke konverzije je mogue izvesti bez gubitka informacija, dok se u nekim


sluajevima prilikom konverzije vri izmena same vrednosti podatka.
le

Jedan oblik konverzije predstavlja konverzija vrednosti nieg tipa u vred-


nost vieg tipa (na primer, short u long, int u float ili float u double) u
E

kom sluaju najee ne dolazi do gubitka informacije. Konverzija tog oblika


se ponekad naziva promocija (ili napredovanje ).
11

float a=4; /* 4 je int i implicitno se konvertuje u float */

Naglasimo da do gubitka informacija ipak moe da doe. Na primer, kd

11 C standard definie tzv. konverzioni rang svakog tipa i napredovanje i nazadovanje se


odnose na konverzioni rang.
127 6. Predstavljanje podataka i operacije nad njima

float f = 16777217;
printf("%f\n", f);

obino (tj. na velikom broju konkretnih sistema) ispisuje

16777216.00000

jer vrednost 16777217.0 ne moe da se zapie u okviru tipa float ako on koristi

5)
etiri bajta i IEEE 754 zapis. Naime, tada tip float za zapis mantise koristi
tri bajta, tj. dvadeset etiri binarne cifre i moe da reprezentuje sve pozitivne

01
celobrojne vrednosti do 224 ali samo neke vee od 224 . Broj 16777217 jednak
24
je 2 +1 i binarno se zapisuje kao 1000000000000000000000001, pa prilikom
float

(2
konverzije u (duine etiri bajta) dolazi do gubitka informacija.
Drugi oblik konverzije predstavlja konverzija vrednosti vieg tipa u vrednost
nieg tipa (na primer, long u short, double u int). Ovaj oblik konverzije

je
ponekad se naziva democija (ili nazadovanje ). Prilikom ovog oblika konverzije,
mogue je da doe do gubitka informacije (u sluaju da se polazna vrednost
an
ne moe predstaviti u okviru novog tipa). U takvim sluajevima, kompilator
obino izdaje upozorenje, ali ipak vri konverziju.
d

int b = 7.0f; /* 7.0f je float pa se vrsi konverzija u 7 */


int c = 7.7f; /* 7.7f je float i vrsi se konverzija u 7,
iz

pri cemu se gubi informacija */


unsigned char d = 256; /* d dobija vrednost 0 */
o
sk

Prilikom konverzije iz brojeva u pokretnom zarezu u celobrojne tipove po-


dataka i obratno potrebno je potpuno izmeniti interni zapis podataka (na
n

primer, iz IEEE754 zapisa brojeva u pokretnom zarezu u zapis u obliku pot-


punog komplementa). Ovo se najee vri uz odsecanje decimala, a ne
ro

zaokruivanjem na najblii ceo broj (tako je 7.7f konvertovano u 7, a ne u


8). Prilikom konverzija celobrojnih tipova istog internog zapisa razliite irine
kt

(razliitog broja bajtova), vri se odsecanje vodeih bitova zapisa (u sluaju


konverzija u ui tip) ili proirivanje zapisa dodavanjem vodeih bitova (u sluaju
le

konverzija u iri tip). U sluaju da je polazni tip neoznaen, konverzija u iri tip
se vri dodavanjem vodeih nula (engl. zero extension ). U sluaju da je polazni
E

tip oznaen, konverzija u iri tip se vri proirivanjem vodeeg bita (engl. sign
extension ) i na taj nain se zadrava vrednost i pozitivnih i negativnih brojeva
zapisanih u potpunom komplementu.
int c = 7.7f; ilustruje i zato vrednost izraza dodele nije
Navedeni primer
jednaka desnoj strani (u ovom sluaju 7.7f), nego vrednosti koja je dodeljena
objektu na levoj strani operatora dodele (u ovom sluaju 7).
esto se deava da se vrednost tipa char dodeljuje promenljivoj tipa int i
tom prilikom takoe dolazi do implicitne konverzije. S obzirom na to da stan-
dard ne definie da li je tip char oznaen ili neoznaen, rezultat konverzije se
razlikuje od implementacije do implementacije (ukoliko je karakter neoznaen,
128 6. Predstavljanje podataka i operacije nad njima

prilikom proirivanja dopisuju mu se vodee nule, a ukoliko je oznaen, dopisuje


se vrednost njegovog vodeeg bita), to moe dovesti do problema prilikom
prenoenja programa sa jedne na drugu platformu. Ipak, standard garantuje
da e svi karakteri koji mogu da se tampaju (engl. printable characters) uvek
imati pozitivne kdove (i vodei bit 0), tako da oni ne predstavljaju problem
prilikom konverzija (proirivanje se tada uvek vri nulama). Ukoliko se tip char
koristi za smetanje neeg drugog osim karaktera koji mogu da se tampaju,
preporuuje se eksplicitno navoenje kvalifikatora signed ili unsigned da bi se

5)
programi izvravali identino na svim raunarima.
Sve brojevne vrednosti (i cele i u pokretnom zarezu) prevode se u istinitosne

01
vrednosti tako to se 0 prevodi u 0, a sve ne-nula vrednosti se prevode u 1.

(2
6.5.2 Eksplicitne konverzije

Operator eksplicitne konverzije tipa ili operator kastovanja (engl. type cast

je
operator) se navodi tako to se ime rezultujueg tipa navodi u malim zagradama
ispred izraza koji se konvertuje:
an
(tip)izraz
d

Operator kastovanja je prefiksni, unaran operator i ima vii prioritet od svih


iz

binarnih operatora. U sluaju primene operatora kastovanja na promenljivu,


vrednost izraza je vrednost promenljive konvertovana u traeni tip, a vrednost
same promenljive se ne menja (i, naravno, ne menja se njen tip).
o

Eksplicitna konverzija moe biti neophodna u razliitim situacijama. Na


sk

primer, ukoliko se na celobrojne operande eli primena deljenja brojeva u


pokretnom zarezu:
n

int a = 13, b = 4;
ro

printf("%d\t", a/b);
printf("%f\n", (double)a/(double)b);
kt

3 3.250000
le

Prilikom prvog poziva funkcije printf upotrebljen je format %d, zbog toga
E

to je izraz a/b celobrojnog tipa navoenje %f umesto %d, naravno, ne utie


na njegov tip i rezultat i ne bi imalo smisla.
U nastavku e biti prikazano da je, u navedenom primeru, bilo dovoljno
konvertovati i samo jedan od operanada u tip double i u tom sluaju bi se
drugi operand implicitno konvertovao.

6.5.3 Implicitne konverzije

Prilikom primene nekih operatora vre se konverzije vrednosti operanada


implicitno, bez zahteva progremera.
129 6. Predstavljanje podataka i operacije nad njima

Ve je reeno da se prilikom primene operatora dodele vri (ukoliko je


potrebno) implicitna konverzija vrednosti desne strane dodele u tip leve strane
dodele, pri emu se je tip izraza dodele tip leve strane. Na primer,

int a;
double b = (a = 3.5);

U navedenom primeru, prilikom dodele promenljivoj a, vri se konverzija

5)
double konstante 3.5 u vrednost 3 tipa int, a zatim se, prilikom dodele pro-
menljivoj b, vri konverzija te vrednosti u double vrednost 3.0.

01
Prilikom primene nekih aritmetikih operatora vre se implicitne konverzije
(najee promocije) koje obezbeuju da operandi postanu istog tipa pogodnog
za primenu operacija. Ove konverzije se nazivaju uobiajene aritmetike kon-

(2
verzije (engl. usual arithmetic conversions ). Ove konverziju se, na primer,
primenjuju prilikom primene aritmetikih (+, -, *, /) i relacijskih binarnih
operatora (<, >, <=, >=, ==, !=), prilikom primene uslovnog operatora ?:.

je
Aritmetiki operatori se ne primenjuju na male tipove tj. na podatke tipa
char short
an
i (zbog toga to je u tim sluajevima verovatno da e doi do
prekoraenja tj. da rezultat nee moi da se zapie u okviru malog tipa), ve
se pre primene operatora mali tipovi promoviu u tip int. Ovo se naziva
d

celobrojna promocija (engl. integer promotion)


12 .
iz

unsigned char cresult, c1, c2, c3;


c1 = 100;
c2 = 3;
o

c3 = 4;
sk

cresult = c1 * c2 / c3;
n

U navedenom primeru, vri se celobrojna promocija promenljivih c1, c2 i


ro

c3 u int pre izvoenja operacija, a zatim se vri konverzija rezultata u tip


char prilikom upisa u promenljivu cresult. Kada se celobrojna promocija ne
bi vrila, mnoenje c1 * c2 bi dovelo do prekoraenja jer se vrednost 300 ne
kt

moe predstaviti u okviru tipa char i rezultat ne bi bio korektan (tj. ne bi bila
dobijena vrednost (100 3)/4).
le

Generalno, prilikom primene aritmetikih operacija primenjuju se sledea


E

pravila konverzije:

1. Ako je bar jedan od operanada tipa long double, onda se drugi konver-
tuje u long double;
2. inae, ako je jedan od operanada tipa double, onda se drugi konvertuje
u double;
12 Celobrojna promocija ima jednostavno opravdanje ako se razmotri prevedeni mainski
program. Naime, tip int odgovara irini registara u procesoru tako da se konverzija malih
vrednosti vri jednostavnim upisivanjem u registre. Procesori obino nemaju mainske in-
strukcije za rad sa kraim tipovima podataka i operacije nad kraim tipovima podataka
(sluaj u kojem se ne bi vrila celobrojna promocija) zapravo bi bilo tee ostvariti.
130 6. Predstavljanje podataka i operacije nad njima

3. inae, ako je jedan od operanada tipa float, onda se drugi konvertuje u


float;
4. inae, svi operandi tipa char i short promoviu se u int.
5. ako je jedan od operanada tipa long long, onda se drugi konvertuje u
long long;
6. inae, ako je jedan od operanada tipa long, onda se drugi konvertuje u
long.

5)
U sluaju korienja neoznaenih operanada (tj. meanja oznaenih i neo-

01
znaenih operanada), pravila konverzije su neto komplikovanija:

13 od oznaenog, oznaeni operand se kon-

(2
1. Ako je neoznaeni operand iri
vertuje u neoznaeni iri tip;

2. inae, ako je tip oznaenog operanda takav da moe da predstavi sve vred-

je
nosti neoznaenog tipa, tada se neoznaeni tip prevodi u iri, oznaeni.

3. inae, oba operanda se konvertuju u neoznaeni tip koji odgovara ozna-


an
enom tipu.
d

Problemi najee nastaju prilikom poreenja vrednosti razliitih tipova.


Na primer, ako int zauzima 16 bitova, a long 32 bita, tada je -1l < 1u.
iz

Zaista, u ovom primeru porede se vrednosti tipa signed long i unsigned int.
Poto signed long moe da predstavi sve vrednosti tipa unsigned int, vri
signed long i vri se poreenje. S druge strane,
o

se konverzija oba operanda u


-1l > 1ul. U ovom sluaju porede se vrednosti tipa signed long
sk

vai da je
i unsigned long. Poto tip signed long ne moe da predstavi sve vrednosti
tipa unsigned long, oba operanda se konvertuju u unsigned long. Tada se
n

-1l konvertuje u ULONG_MAX (najvei neoznaen broj koji se moe zapisati u


ro

32 bita), dok 1ul ostaje neizmenjen i vri se poreenje.


Ukoliko se eli postii uobiajeni poredak brojeva, bez obzira na irinu
kt

tipova na prenosiv nain, poreenje je poeljno izvriti na sledei nain:

signed si = /* neka vrednost */;


le

unsigned ui = /* neka vrednost */;


E

/* if (si < ui) - ne daje uvek korektan rezultat */


if (si < 0 || (unsigned)si < ui) {
...
}

13 Preciznije, standard uvodi pojam konverzionog ranga (koji raste od char ka long long)
i u ovoj situaciji se razmatra konverzioni rang.
131 6. Predstavljanje podataka i operacije nad njima

Pitanja i zadaci za vebu


Pitanje 6.5.1. ta su to implicitne, a ta eksplicitne konverzije? Na koji
nain se vrednost karakterske promenljive c se moe eksplicitno konvertovati u
celobrojnu vrednost?

Pitanje 6.5.2. ta su to promocije, a ta democije? Koje od narednih kon-


verzija su promocije, a koje democije: (a) int u short, (b) char u float, (c)

5)
double u long, (d) long u int?

Pitanje 6.5.3.

01
Navesti pravila koja se primenjuju prilikom primene aritmetikih
operatora.

(2
Pitanje 6.5.4. Koje vrednosti imaju promenljive e i f nakon kda
unsigned char c = 255, d = 1, e = c + d; int f = c + d;?
Koje vrednosti imaju promenljive e, g i f nakon kda

je
unsigned char c = 255, d = 1; signed char e = c;
char f = c + d; int g = c + d;?
an
Pitanje 6.5.5. Ako aritmetiki operator ima dva argumenta, a nijedan od njih
nije ni tipa long double, ni tipa double, ni tipa float, u koji tip se implicitno
d

konvertuju argumenti tipa char i short int?


iz

Pitanje 6.5.6.
1. Koju vrednost ima promenljiva x tipa float nakon naredbe
o

x = (x = 3/2);?
sk

2. Ako promenljiva c ima tip char, koji tip e ona imati nakon dodele
c = 1.5 * c;?
n

x tipa float
ro

3. Koju vrednost ima promenljiva nakon naredbe


x = 3/2 + (double)3/2 + 3.0/2;?
kt

4. Ako je promenljiva f tipafloat, a promenljiva a tipa int, koju vrednost


ima f nakon naredbe: f = (a = 1/2 < 0.5)/2;?
le

Pitanje 6.5.7. Koje vrednosti imaju promenljive iic nakon naredbi


E

char c, c1 = 130, c2 = 2; int i; i = (c1*c2)/4; c = (c1*c2)/4;


Pitanje 6.5.8. Da li nakon naredbe tip c1, c2, c = (c1*c2)/c2; promenljiva
c nuno ima vrednost promenljive c1 ako je
tip tip char,
tip tip int,
tip tip float?
132 6. Predstavljanje podataka i operacije nad njima

6.6 Nizovi i niske


esto je u programima potrebno korienje velikog broja srodnih promen-
ljivih. Umesto velikog broja pojedinano deklarisanih promenljivih, mogue je
koristiti nizove . Obrada elemenata nizova se onda obino vri na uniforman
nain, korienjem petlji.

5)
6.6.1 Primer korienja nizova

Razmotrimo, kao ilustraciju, program koji uitava 10 brojeva sa standard-

01
nog ulaza, a zatim ih ispisuje u obratnom poretku. Bez nizova, neophodno je
koristiti deset promenljivih. Da se u zadatku trailo ispisivanje unatrag 1000

(2
brojeva, program bi bio izrazito mukotrpan za pisanje i nepregledan.

Program 6.4.

je
#include <stdio.h> an
int main() {
int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9;
scanf("%d%d%d%d%d%d%d%d%d%d",
d

&b0, &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8, &b9);
iz

printf("%d %d %d %d %d %d %d %d %d %d",
b9, b8, b7, b6, b5, b4, b3, b2, b1, b0);
}
o
sk

1 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1
n
ro

Umesto 10 razliitih srodnih promenljivih, mogue je upotrebiti niz i time


znatno uprostiti prethodni program.
kt

Program 6.5.
#include <stdio.h>
le
E

int main() {
int b[10], i;
for (i = 0; i < 10; i++)
scanf("%d", &b[i]);
for (i = 9; i >= 0; i--)
printf("%d ", b[i]);
return 0;
}
133 6. Predstavljanje podataka i operacije nad njima

6.6.2 Deklaracija niza

Nizovi u programskom jeziku C mogu se deklarisati na sledei nain:

tip ime_niza[dimenzija];

Dimenzija predstavlja broj elemenata niza. Na primer, deklaracija

int a[10];

5)
uvodi niz a od 10 celih brojeva. Prvi element niza ima indeks 0, pa su elementi

01
niza:

a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]

(2
Indeksi niza su obino nenegativni celi brojevi (mada je u nekim prilikama kada
se nizovi koriste u kombinaciji sa pokazivaima doputeno koristiti i negativne

je
indekse, o emu e vie biti rei u poglavlju 10.3).
an
Prilikom deklaracije moe se izvriti i inicijalizacija:

int a[5] = { 1, 2, 3, 4, 5 };
d

Nakon ove deklaracije, sadraj niza a jednak je:


iz

1 2 3 4 5
o
sk

Veliina memorijskog prostora potrebnog za niz odreuje se u fazi pre-


voenja programa, pa broj elemenata niza (koji se navodi u deklaraciji) mora
biti konstantan izraz. U narednom primeru, deklaracija niza a je ispravna, dok
n

su deklaracije nizova bic neispravne


14 :
ro

int x;
char a[100+10];
kt

int b[];
float c[x];
le
E

Ako se koristi inicijalizacija, mogue je navesti veu dimenziju od broja


navedenih elemenata (kada se inicijalizuju samo poetni elementi deklarisanog
niza). Neispravno je navoenje manje dimenzije od broja elemenata inicijal-
izatora.
Dimenziju niza je mogue izostaviti samo ako je prilikom deklaracije izvrena
i inicijalizacija niza i tada se dimenzija odreuje na osnovu broja elemenata u
inicijalizatoru. Na primer, nakon deklaracije

14 Standardi nakon C99 uvode pojam niza promenljive duine (engl. variable length array,
VLA). U tom svetlu, deklaracija niza c bila bi ispravna. Ipak, u ovoj knjizi, VLA nee biti
razmatrani.
134 6. Predstavljanje podataka i operacije nad njima

int b[] = { 1, 2, 3 };

sadraj niza b jednak je:

1 2 3

Kada se operator sizeof primeni na ime niza, rezultat je veliina niza u

5)
bajtovima. Ukoliko dimenzija niza nije zadata eksplicitno ve je implicitno
odreena inicijalizacijom, broj elemenata moe se izraunati na sledei nain:

01
sizeof(ime niza)/sizeof(tip elementa niza)

(2
Kada se pristupa elementu niza, indeks moe da bude proizvoljan izraz
celobrojne vrednosti, na primer:

a[2*2+1]

je
a[i+2]
an
U fazi prevoenja (pa ni u fazi izvravanja
15 ) ne vri se nikakva provera da
li je indeks pristupa nizu u njegovim granicama i mogue je bez ikakve prijave
d

greke ili upozorenja od strane prevodioca pristupati i lokaciji koji se nalazi


iz

van opsega deklarisanog niza (na primer, mogue je koristiti element a[13], pa
ak element a[-1] u prethodnom primeru). Ovo najee dovodi do fatalnih
greaka prilikom izvravanja programa. S druge strane, ovakva (jednostavna)
o

politika upravljanja nizovima omoguava veu efikasnost programa.


sk

Pojedinani elementi nizova su l-vrednosti (tj. mogue im je dodeljivati


vrednosti). Meutim, nizovi nisu l-vrednosti i nije im mogue dodeljivati vred-
n

nosti niti ih menjati. To ilustruje sledei primer:


ro

int a[3] = {5, 3, 7};


int b[3];
kt

b = a; /* Neispravno - nizovi se ne mogu dodeljivati. */


a++; /* Neispravno - nizovi se ne mogu menjati. */
le
E

6.6.3 Niske

Posebno mesto u programskom jeziku C zauzimaju nizovi koji sadre karak-


tere niske karaktera ili krae niske (engl. strings ). Konstantne niske navode
se izmeu dvostrukih navodnika (na primer, "ja sam niska"). U okviru niski,
specijalni karakteri navode se korienjem specijalnih sekvenci (na primer,

15 U fazi izvravanja, operativni sistem obino proverava da li se pokuava upis van memo-
rije koja je dodeljena programu i ako je to sluaj, obino nasilno prekida izvravanje programa
(npr. uz poruku segmentation fault). O ovakvim grekama bie vie rei u poglavlju 9.3.5.
135 6. Predstavljanje podataka i operacije nad njima

"prvi red\ndrugi red"). Niske su interno reprezentovane kao nizovi karak-


tera na iji desni kraj se dopisuje karakter \0, tzv. zavrna, terminalna nula
(engl. null terminator ). Zbog toga, niske u C-u se nazivaju niske zavrene
nulom (engl. null terminated strings )
16 . Posledica ovoga je da ne postoji
ogranienje za duinu niske, ali je neophodno proi kroz celu nisku da bi se
odredila njena duina.
Niske mogu da se koriste i prilikom inicijalizacije nizova karaktera.

5)
char s1[] = {Z, d, r, a, v, o};
char s2[] = {Z, d, r, a, v, o, \0};

01
char s3[] = "Zdravo";

s1

(2
Nakon navedenih deklaracija, sadraj niza jednak je:

0 1 2 3 4 5
Z d r a v o

je
a sadraj nizova s2 i s3 jednak je:
0 1 2 3 4 5 6
an
Z d r a v o \0
Niz s1 sadri 6 karaktera (i zauzima 6 bajtova). Deklaracije za s2 i s3
d

su ekvivalentne i ovi nizovi sadre po 7 karaktera (i zauzimaju po 7 bajtova).


iz

Prvi niz karaktera nije terminisan nulom i ne moe se smatrati ispravnom


niskom. Potrebno je jasno razlikovati karakterske konstante (na primer, x)
koje predstavljaju pojedinane karaktere i niske (na primer, "x" koja sadri
o

dva karaktera, x i \0).


sk

Ukoliko se u programu niske nalaze neposredno jedna uz drugu, one se


automatski spajaju. Na primer:
n

printf("Zdravo, " "svima");


ro

je ekvivaletno sa
kt

printf("Zdravo, svima");
le

Standardna biblioteka definie veliki broj funkcija za rad sa niskama. Pro-


string.h.
E

totipovi ovih funkcija se nalaze u zaglavlju Na primer, funkcija


strlen slui za izraunavanje duine niske. Pri raunanju duine niske, ne
rauna se zavrna nula. Ilustracije radi, jedan od naina da se neposredno,
bez pozivanja neke funkcije (naravno, uvek je preporuljivo pozvati biblioteku
funkciju ukoliko je dostupna), izrauna duina niske s dat je sledeim kdom,
nakon ijeg izvravanja promenljiva i sadri traenu duinu:

16 Neki programski jezici koriste drugaije konvencije za interni zapis niski. Na primer,
u Pascal-u se pre samog sadraja niske zapisuju broj koji predstavlja njenu duinu (tzv. P-
niske).
136 6. Predstavljanje podataka i operacije nad njima

int i = 0;
while (s[i] != \0)
i++;

Kd koji obrauju niske obino ih obrauje karakter po karakter i obino


sadri petlju oblika:

while (s[i] != \0)

5)
...

01
ili oblika:

for (i = 0; s[i] != \0; i++)

(2
...

Kako zavrna nula ima numeriku vrednost 0 (to predstavlja istinitosnu

je
vrednost netano), poreenje u prethodnoj petlji moe da se izostavi:
an
for (i = 0; s[i]; i++)
...
d

Izuzetno neefikasan kd dobija se pozivanjem funkcije strlen za izrauna-


iz

vanje duine niske u svakom koraku iteracije:

for (i = 0; i < strlen(s); i++)


o

...
sk

Jo jedna funkcija standardne biblioteke za rad sa niskama je funkcija


strcpy koja, kada se pozove sa strcpy(dest, src), vri kopiranje niske src
n

u nisku dest, pretpostavljajui da je niska kojoj se dodeljuje vrednost dovoljno


ro

velika da primi nisku koja se dodeljuje. Ako to nije ispunjeno, vri se promena
sadraja memorijskih lokacija koje su van dimenzija niza to moe dovesti do
kt

fatalnih greaka prilikom izvravanja programa. Ilustracije radi, navedimo kako


se kopiranje niski moe izvesti neposredno. U nisku dest se prebacuje karakter
src
le

po karakter niske sve dok dodeljeni karakter ne bude zavrna nula:

int i = 0;
E

while ((dest[i] = src[i]) != \0)


i++;

Poreenje razliitosti sa terminalnom nulom se moe izostaviti, a petlja se,


moe preformulisati kao for petlja:

int i;
for (i = 0; dest[i] = src[i]; i++)
; /* prazno telo petlje */
137 6. Predstavljanje podataka i operacije nad njima

6.6.4 Viedimenzioni nizovi

Pored jednodimenzionih mogu se koristiti i viedimenzioni nizovi, koji se


deklariu na sledei opti nain:

tip ime_niza[dimenzija_1]...[dimenzija_2];

Dvodimenzioni nizovi (matrice) tumae se kao jednodimenzioni nizovi iji

5)
su elementi nizovi. Zato se elementima dvodimenzionog niza pristupa sa:

ime_niza[vrsta][kolona]

01
a ne sa ime_niza[vrsta, kolona] (greke do kojih moe doi zbog pokuaja
ovakvog pristupa ve su napomenute u kontekstu operatora zarez (,), u poglavlju

(2
6.4).
Elementi se u memoriji smetaju po vrstama pa se, kada se elementima

je
pristupa u redosledu po kojem su smeteni u memoriji, najbre menja poslednji
indeks. Niz se moe inicijalizovati navoenjem liste inicijalizatora u vitiastim
an
zagradama; poto su elementi opet nizovi, svaki od njih se opet navodi u okviru
vitiastih zagrada (mada je unutranje vitiaste zagrade mogue i izostaviti).
Razmotrimo, kao primer, jedan dvodimenzioni niz:
d

char a[2][3] = {
iz

{1, 2, 3},
{4, 5, 6}
o

};
sk

Kao i u sluaju jednodimenzionih nizova, ako je naveden inicijalizator, vred-


nost prvog indeksa mogue je i izostaviti (jer se on u fazi kompilacije moe
n

odrediti na osnovu broja inicijalizatora):


ro

char a[][3] = {
{1, 2, 3},
kt

{4, 5, 6}
};
le

U memoriji su elementi poreani na sledei nain: a[0][0], a[0][1],


E

a[0][2], a[1][0], a[1][1], a[1][2], tj. vrednosti elemenata niza poreane


su na sledei nain: 1, 2, 3, 4, 5, 6. U ovom primeru, element a[v][k] je -ti
po redu, pri emu je jednako 3*v+k. Pozicija elementa viedimenzionog niza
moe se slino izraunati i sluaju nizova sa tri i vie dimenzija.
17
Razmotrimo, kao dodatni primer, dvodimenzioni niz koji sadri broj dana
za svaki mesec, pri emu su u prvoj vrsti vrednosti za obine, a u drugoj vrsti
za prestupne godine:

17 Nekada programeri ovu tehniku izraunavanja pozicija eksplicitno koriste da matricu


smeste u jednodimenzioni niz, ali, poto jezik doputa korienje viedimenzionih nizova, za
ovim nema potrebe.
138 6. Predstavljanje podataka i operacije nad njima

char broj_dana[][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

Za skladitenje malih prirodnih brojeva koristi se tip char, a u nultu kolonu


su upisane nule da bi se podaci za mesec m nalazili upravo u kolonim (tj. da bi

5)
se mesecima pristupalo korienjem indeksa 1-12, umesto sa indeksa 0-11). Niz
broj_dana je mogue koristiti da bi se, na primer, promenljiva bd postavila na
mesec i godinu godina:

01
broj dana za mesec

int prestupna = (godina % 4 == 0 && godina % 100 != 0) ||


godina % 400 == 0;

(2
char bd = broj_dana[prestupna][mesec];

je
Vrednost logikog izraza je uvek nula (netano) ili jedan (tano), pa se
vrednost prestupna moe koristiti kao indeks pri pristupanju nizu.
an
Pitanja i zadaci za vebu
d

Pitanje 6.6.1. Napisati deklaraciju sa inicijalizacijom niza a tipa int koji za


iz

elemente ima neparne brojeve manje od 10.

Pitanje 6.6.2. Navesti primer inicijalizacije niza tipa char tako da mu sadraj
o

bude zdravo i to: (i) navoenjem pojedinanih karaktera izmeu vitiastih


sk

zagrada i (ii) konstantom niskom.

Pitanje 6.6.3. Ukoliko je niz a deklarisan kao float a[10];, koja je vrednost
n

izraza sizeof(a)?
ro

Pitanje 6.6.4. Ako je niz a tipa float inicijalizovan u okviru deklaracije i


njegova dimenzija nije navedena, kako se ona moe izraunati?
kt

Pitanje 6.6.5. Kada je u deklaraciji niza dozvoljeno izostaviti njegovu dimen-


le

ziju?

Pitanje 6.6.6.
E

Precrtati sve neispravne linije narednog kda.

int a[];
int b[] = {1, 2, 3};
int c[5] = {1, 2, 3};
int d[2] = {1, 2, 3};
c = b;
b++;
139 6. Predstavljanje podataka i operacije nad njima

Pitanje 6.6.7. Deklarisati i inicijalizovati dvodimenzioni niz koji sadri ma-


tricu ( )
1 2 3
.
4 5 6
Pitanje 6.6.8. Deklarisati i inicijalizovati dvodimenzioni niz koji sadri ma-
tricu
1 2

5)
3 4 .
5 6

01
Zadatak 6.6.1. Napisati program koji unosi prirodan broj ( < 1000), a
zatim i elemenata niza celih brojeva, nakon ega:

(2
1. ispisuje unete brojeve unatrag;

2. ispisuje najvei element unetog niza;

3. ispisuje zbir elemenata unetog niza.

je
an

Zadatak 6.6.2.
d

Sa standardnog ulaza se uitava tekst sve dok se ne unese taka


(tj. karakter .). Napisati program koji u unetom tekstu prebrojava pojavljivanje
iz


svake od cifara (broj pojavljivanja smestiti u niz od 10 elemenata).

Zadatak 6.6.3. Napisati program koji za uneti datum u obliku (dan, mesec,
o

godina) odreuje koji je to po redu dan u godini. Proveriti i koretknost unetog


sk

datuma. U obzir uzeti i prestupne godine. Koristiti niz koji sadri broj dana

za svaki mesec i uporediti sa reenjem zadatka bez korienja nizova.
n

Zadatak 6.6.4.
()
Paskalov trougao sadri binomne koeficijente . Napisati
ro

program koji ispisuje prvih redova trougla ( < 100). Na primer, za = 6


ispisuje se:
kt

1
1 1
le

1 2 1
1 3 3 1
E

1 4 6 4 1
1 5 10 10 5 1

() ( )
(primetite da su ivice trougla 1 tj. da vai 0 = = 1, kao da se svaki
unutranji lan trougla moe dobiti kao zbir odgovarajua dva lana prethodne
() (1) (1)
vrste, tj. da vai da je = 1 + ).

Zadatak 6.6.5. Napisati program koji sa standardnog ulaza unosi prvo dimen-
ziju matrice ( < 10) pa zatim elemente matrice. Na primer,
140 6. Predstavljanje podataka i operacije nad njima

4
1 2 3 4
5 6 7 8
9 1 2 3
4 5 6 7
Program izraunava i ispisuje sumu elemenata glavne dijagonale i sumu ele-
menata iznad sporedne dijagonale matrice. Suma elemenata glavne dijagonale

5)
matrice navedene u primeru je 1 + 6 + 2 + 7 = 16, a iznad sporedne dijagonale

je 1 + 2 + 3 + 5 + 6 + 9 = 26

01
Zadatak 6.6.6. Napisati program koji sa standardnog ulaza unosi prvo dimen-
ziju matrice ( < 100) pa zatim elemente matrice i zatim proverava da li je

(2
matrica donje-trougaona. Matrica je donje-trougaona ako se u gornjem trouglu
(iznad glavne dijagonale, ne ukljuujui je) nalaze sve nule. Na primer:

je

1 0 0 0
5 6 0 0
an

9 1 2 0
4 5 6 7
d


iz

6.7 Korisniki definisani tipovi


o

Programski jezik C ima svega nekoliko ugraenih osnovnih tipova. Ve ni-


sk

zovi i niske predstavljaju sloene tipove podataka. Osim nizova, postoji jo


nekoliko naina izgradnje sloenih tipova podataka. Promenljive se mogu or-
n

ganizovati u strukture (tj. slogove ), pogodne za specifine potrebe. Na taj


ro

nain se povezane vrednosti (ne nuno istog tipa) tretiraju kao jedna celina
i za razliku od nizova gde se pristup pojedinanim vrednostima vri na os-
kt

novu brojevnog indeksa, pristup pojedinanim vrednostima vri se na osnovu


imena polja strukture. Pored struktura, mogu se koristiti unije, koje su sline
strukturama, ali kod kojih se jedan isti memorijski prostor koristi za vie pro-
le

menljivih. Mogu se koristiti i nabrojivi tipovi, sa konanim skupom vrednosti.


E

Pored definisanja novih tipova, ve definisanim tipovima se moe pridruiti i


novo ime.

6.7.1 Strukture

Osnovni tipovi jezika C esto nisu dovoljni za pogodno opisivanje svih po-
dataka u programu. Ukoliko je neki podatak sloene prirode tj. sastoji se od
vie delova, ti njegovi pojedinani delovi mogu se uvati nezavisno (u zasebnim
promenljivama), ali to esto vodi programima koji su nejasni i teki za odra-
vanje. Umesto toga, pogodnije je koristiti strukture. Za razliku od nizova koji
objedinjuju jednu ili vie promenljivh istog tipa, struktura objedinjuje jednu ili
141 6. Predstavljanje podataka i operacije nad njima

vie promenljivih, ne nuno istih tipova. Definisanjem strukture uvodi se novi


tip podataka i nakon toga mogu da se koriste promenljive tog novog tipa, na
isti nain kao i za druge tipove.
Korienje struktura bie ilustrovano na primeru razlomaka. U jeziku C ne
postoji tip koji opisuje razlomke, ali moe se definisati struktura koja opisuje
razlomke. Razlomak moe da bude opisan parom koji ine brojilac i imenilac,
na primer, celobrojnog tipa. Brojilac (svakog) razlomka zvae se brojilac, a
imenilac (svakog) razlomka zvae se imenilac. Struktura razlomak moe se

5)
definisati na sledei nain:

struct razlomak {

01
int brojilac;
int imenilac;

(2
};

Kljuna re struct zapoinje definiciju strukture. Nakon nje, navodi se ime

je
strukture, a zatim, izmeu vitiastih zagrada, opis njenih lanova (ili polja).
an
Imena lanova strukture se ne mogu koristiti kao samostalne promenljive, one
postoje samo kao deo sloenijeg objekta. Prethodnom definicijom strukture
uveden je samo novi tip pod imenom struct razlomak, ali ne i promenljive
d

tog tipa.
iz

Strukture mogu sadrati promenljive proizvoljnog tipa. Na primer, mogue


je definisati strukturu koja sadri i niz.

struct student {
o

char ime[50];
sk

float prosek;
};
n
ro

Strukture mogu biti ugnjedene, tj. lanovi struktura mogu biti druge struk-
ture. Na primer:
kt

struct dvojni_razlomak {
struct razlomak gore;
le

struct razlomak dole;


};
E

Definicija strukture uvodi novi tip i nakon nje se ovaj tip moe koristiti
kao i bilo koji drugi. Definicija strukture se obino navodi van svih funkcija.
Ukoliko je navedena u okviru funkcije, onda se moe koristiti samo u okviru
te funkcije. Prilikom deklarisanja promenljivih ovog tipa, kao deo imena tipa,
neophodno je korienje kljune rei struct, na primer:

struct razlomak a, b, c;
142 6. Predstavljanje podataka i operacije nad njima

Definicijom strukture je opisano da se razlomci sastoje od brojioca i ime-


nioca, dok se navedenom deklaracijom uvode tri konkretna razlomka koja se
nazivaju a, b i c.
Mogua je i deklaracija sa inicijalizacijom, pri emu se inicijalne vrednosti
za lanove strukture navode izmeu vitiastih zagrada:

struct razlomak a = { 1, 2 };

5)
Redosled navoenja inicijalizatora odgovara redosledu navoenja lanova
strukture. Dakle, navedenom deklaracijom je uveden razlomak a iji je brojilac

01
1, a imenilac 2.
Definisanje strukture i deklarisanje i inicijalizacija promenljivih moe se
;

(2
uraditi istovremeno (otuda i neuobiajeni simbol nakon zatvorene vitiaste
zagrade prilikom definisanja strukture):

struct razlomak {

je
int brojilac;
int imenilac;
an
} a = {1, 2}, b, c;
d

lanu strukture se pristupa preko imena promenljive (iji je tip struktura)


iza kojeg se navodi taka a onda ime lana, na primer:
iz

a.imenilac
o

Na primer, vrednost promenljive a tipa struct razlomak moe biti ispisan


sk

na sledei nain:

printf("%d/%d", a.brojilac, a.imenilac);


n
ro

Naglasimo da je operator ., iako binaran, operator najvieg prioriteta (istog


nivoa kao male zagrade i unarni postfiksni operatori).
kt

Ne postoji konflikt izmeu imena polja strukture i istoimenih promenljivih,


pa je naredni kd korektan.
le

int brojilac = 5, imenilac = 3;


a.brojilac = brojilac; a.imenilac = imenilac;
E

Od ranije prikazanih operacija, nad promenljivama tipa strukture dozvol-


jene su operacije dodele a nisu dozvoljeni aritmetiki i relacijski operatori.
Operator sizeof se moe primeniti i na ime strukturnog tipa i na promen-
ljive tog tipa i u oba sluaja dobija se broj bajtova koje struktura zauzima u
memoriji. Napomenimo da taj broj moe nekada biti i vei od zbira veliina
pojedinanih polja, jer se zbog uslova poravnanja (engl. alignment), o kojem e
vie biti rei u glavi 10, ponekad izmeu dva uzastopna polja strukture ostavlja
prazan prostor.
143 6. Predstavljanje podataka i operacije nad njima

Nizovi struktura
esto postoji povezana skupina sloenih podataka. Umesto da se oni uvaju
u nezavisnim nizovima (to bi vodilo programima tekim za odravanje) bolje je
koristiti nizove struktura. Na primer, ako je potrebno imati podatke o imenima
i broju dana meseci u godini, mogue je te podatke uvati u nizu sa brojevima
dana i u (nezavisnom) nizu imena meseci. Bolje je, meutim, opisati strukturu
mesec koja sadri broj dana i ime:

5)
struct opis_meseca {
char ime[10];

01
int broj_dana;
};

(2
i koristiti niz ovakvih struktura:

struct opis_meseca meseci[13];

13 je
an
(deklarisan je niz duine da bi se meseci mogli referisati po svojim rednim
brojevima, pri emu se poetni element niza ne koristi).
Mogua je i deklaracija sa inicijalizacijom (u kojoj nije neophodno navoenje
d

broja elemenata niza)


18 :
iz

struct opis_meseca meseci[] = {


{ "",0 },
o

{ "januar",31 },
{ "februar",28 },
sk

{ "mart",31 },
...
n

{ "decembar",31 }
ro

};
kt

U navednoj inicijalizaciji unutranje vitiaste zagrade je mogue izostaviti:

struct opis_meseca meseci[] = {


le

"",0,
"januar",31,
E

"februar",28,
"mart",31,
...
"decembar",31
};

Nakon navedene deklaracije, ime prvog meseca u godini se moe dobiti sa


meseci[1].ime, njegov broj dana sa meseci[1].broj_dana itd.

18 U ovom primeru se zanemaruje injenica da februar moe imati i 29 dana.


144 6. Predstavljanje podataka i operacije nad njima

Kao i obino, broj elemenata ovako inicijalizovanog niza moe se izraunati


na sledei nain:

sizeof(meseci)/sizeof(struct opis_meseca)

6.7.2 Unije

5)
Unije su donekle sline strukturama. One objedinjuju nekoliko promenljivih
koje ne moraju imati isti tip. No, za razliku od struktura kod kojih se sva polja

01
u strukturi istovremeno popunjavaju i sva se mogu istovremeno koristiti, kod
unije se u jednom trenutku moe koristiti samo jedno polje. Veliina strukture
odgovara zbiru veliina njenih polja, dok veliina unije odgovara veliini njenog

(2
najveeg polja. Osnovna svrha unija je uteda memorije.
Skoro sva sintaksika pravila koja se odnose na strukutre se prenose i na
unije (jedina razlika je da se umesto kljune rei struct koristi kljuna re

je
union).
an
Naredna unija omoguava korisniku da izabere da li e vreme da pamti kao
ceo broj sekundi ili kao razlomljen broj (koji obuhvata i manje delove sekunde).
Oba polja unije nee se moi koristiti istovremeno.
d

union vreme {
iz

int obicno;
float precizno;
};
o
sk

Nakon definisanja tipa unije, mogue je definisati i promenljive ovog tipa,


na primer:
n

union vreme a;
ro

a.obicno = 17;
kt

Unije se esto koriste i kao lanovi struktura. Neka se, na primer, u pro-
gramu uvaju i obrauju informacije o studetima i zaposlenima na nekom fakul-
le

tetu. Za svakoga se uva ime, prezime i matini broj, za zaposlene se jo uva


i koeficijent za platu, dok se za studente uva broj indeksa:
E

struct akademac {
char ime_i_prezime[50];
char jmbg[14];
char vrsta;
union {
double plata;
char indeks[7];
} dodatno;
};
145 6. Predstavljanje podataka i operacije nad njima

U ovom sluaju poslednji lan strukture (dodatno) je unijskog tipa (sm


tip unije nije imenovan). lan strukture vrsta tipa char sadri informaciju
o tome da li se radi o zaposlenom (na primer, vrednost z) ili o studentu (na
primer, vrednost s). Promenljive plata i indeks dele zajedniki memorijski
prostor i podrazumeva se da se u jednom trenutku koristi samo jedan podatak
u uniji. Na primer:

int main() {

5)
struct akademac pera = {"Pera Peric", "0101970810001", z};
pera.dodatno.plata = 56789.5;

01
printf("%f\n", pera.dodatno.plata);

struct akademac ana = {"Ana Anic", "1212992750501", s};

(2
strcpy(ana.dodatno.indeks, "12/123");
printf("%s\n", ana.dodatno.indeks);
}

je
pera.dodatno.indeks bi naruio podatak o koefici-
an
Pokuaj promene polja
jentu plate, dok bi pokuaj promene polja ana.dodatno.koeficijent naruio
podatak o broju indeksa.
d

Navedimo, kao ilustraciju, kako se korienjem unije moe otkriti binarni


iz

zapis broja u pokretnom zarezu:

union { float x; unsigned char s[4]; } u;


u.x = 1.234f;
o

printf("%x%x%x%x", u.s[0], u.s[1], u.s[2], u.s[3]);


sk
n
ro

6.7.3 Polja bitova

Jo jedan od naina utede memorije u C programima su polja bitova


kt

(engl. bit fields). Naime, najmanji celobrojni tip podataka je char koji za-
uzima jedan bajt, a za predstavljanje neke vrste podataka dovoljan je manji
le

broj bitova. Na primer, zamislimo da elimo da predstavimo grafike karak-


teristike pravougaonika u nekom programu za crtanje. Ako je doputeno samo
E

osnovnih 8 boja (crvena, plava, zelena, cijan, magenta, uta, bela i crna) za
predstavljanje boje dovoljno je 3 bita. Vrsta okvira (pun, isprekidan, takast)
moe da se predstavi sa 2 bita. Na kraju, da li pravougaonik treba ili ne
treba popunjavati moe se kodirati jednim bitom. Ovo moe da se iskoristiti
za definisanje sledeeg polja bitova.

struct osobine_pravougaonika {
unsigned char popunjen : 1;
unsigned char boja : 3;
146 6. Predstavljanje podataka i operacije nad njima

unsigned char vrsta_okvira : 2;


};
Iza svake promenljive, naveden je broj bitova koji je potrebno odvojiti
za tu promenljivu. Veliina ovako definisanog polja bitova (vrednost izraza
sizeof(struct osobine_pravougaonika)) je samo 1 bajt (iako je potrebno
samo 6 bitova, svaki podatak mora da zauzima ceo broj bajtova, tako da je
odvojen 1 bit vie nego sto je potrebno). Da je u pitanju bila obina struktura,

5)
zauzimala bi 3 bajta.
Polje bitova se nadalje koristi kao obina struktura. Na primer:

...

01
#define ZELENA 02
...

(2
#define PUN 00

struct osobine_pravougaonika op;

je
op.popunjen = 0;
op.boja = ZELENA;
an
op.vrsta_okvira = PUN;
d
iz

6.7.4 Nabrojivi tipovi (enum)

U nekim sluajevima korisno je definisati tip podataka koji ima mali skup
o

doputenih vrednosti. Ovakvi tipovi se nazivaju nabrojivi tipovi . U jeziku C


sk

nabrojivi tipove se definiu korienjem kljune rei enum. Na primer:

enum znak_karte {
n

KARO,
ro

PIK,
HERC,
TREF
kt

};
le

Nakon navedene definicije, u programu se mogu koristiti imena KARO, PIK,


HERC, TREF, umesto nekih konkretnih konstantnih brojeva, to popravlja itljivost
E

programa. Pri tome, obino nije vano koje su konkretne vrednosti pridruene
imenima KARO, PIK, HERC, TREF, ve je dovoljno znati da su one sigurno me-
usobno razliite i celobrojne. U navedenom primeru, KARO ima vrednost 0,
PIK vrednost 1, HERC vrednost 2 i TREF vrednost 3. Mogue je i eksplicitno
navoenje celobrojnih vrednosti. Na primer:

enum znak_karte {
KARO = 1,
PIK = 2,
HERC = 4,
147 6. Predstavljanje podataka i operacije nad njima

TREF = 8
};
Vrednosti nabrojivih tipova nisu promenljive i njima se ne moe menjati
vrednost. S druge strane, promenljiva moe imati tip koji je nabrojiv tip i
koristiti se na uobiajene naine.
Slian efekat (uvoenja imena sa pridruenim celobrojnim vrednostima) se
moe postii i pretprocesorskom direktivom #define (videti poglavlje 9.2.1),
enum).

5)
ali u tom sluaju ta imena ne ine jedan tip (kao u sluaju da se koristi
Grupisanje u tip je pogodno zbog provera koje se vre u fazi prevoenja.

01
Slino kao i kod struktura i unija, uz definiciju tipa mogue je odmah deklar-
isati i promenljive. Promenljive se mogu i naknadno definisati (uz obavezno
ponovno korienje kljune rei enum). Na primer,

(2
enum znak_karte znak;

je
Nabrojivi tipovi mogu uestvovati u izgradnji drugih sloenih tipova.

struct karta {
an
unsigned char broj;
enum znak_karte znak;
d

} mala_dvojka = {2, TREF};


iz

Nabrojivi tipovi se esto koriste da zamene konkretne brojeve u programu,


na primer, povratne vrednosti funkcija. Mnogo je bolje, u smislu itljivosti
o

programa, ukoliko funkcije vraaju (razliite) vrednosti koje su opisane nabro-


sk

jivim tipom (i imenima koja odgovaraju pojedinim povratnim vrednostim) nego


konkretne brojeve. Tako, na primer, tip povratne vrednosti neke funkcije moe
n

da bude nabrojiv tip definisan na sledei nain:


ro

enum return_type {
OK,
FileError,
kt

MemoryError,
TimeOut
le

};
E

6.7.5 Typedef
U jeziku C mogue je kreirati nova imena postojeih tipova koristei kljunu
re typedef. Na primer, deklaracija

typedef int Length;


148 6. Predstavljanje podataka i operacije nad njima

uvodi ime Length kao sinonim za tip int. Ime tipa Length se onda moe
koristiti u deklaracijama, eksplicitnim konverzijama i slino, na isti nain kao
to se koristi ime int:
Length len, maxlen;

Novo ime tipa se navodi kao poslednje, na poziciji na kojoj se u deklaraci-


jama obino navodi ime promenljive, a ne neposredno nakon kljune rei typedef.

5)
Obino se novouvedena imena tipova piu velikim poetnim slovima da bi se
istakla.

01
Deklaracijom typedef se ne kreira novi tip ve se samo uvodi novo ime za
potojei tip. Staro ime za taj tip se moe koristiti i dalje.
Veoma esto korienje typedef deklaracija je u kombinaciji sa struktu-

(2
rama da bi se izbeglo pisanje kljune rei struct pri svakom korienju imenu
strukturnog tipa. Na primer:

je
struct point {
int x, y;
an
};
typedef struct point Point;
d

Point a, b;
iz

Definicija strukture i pridruivanje novog imena se mogu uraditi istovre-


o

meno. Na primer:
sk

typedef struct point {


int x, y;
n

} Point;
ro

Point a, b;
kt

Deklaracija typedef je slina pretprocesorskoj direktivi #define (videti


poglavlje 9.2.1), s tim to nju obrauje kompilator (a ne pretprocesor) i moe
le

da da rezultat i u sluajevima u kojima jednostavne pretprocesorske tekstualne


zamene to ne mogu. Na primer:
E

typedef int (*PFI)(char *, char *);

uvodi ime PFI, za tip pokaziva na funkciju koja prima dva char * argumenta
i vraa int (vie rei o pokazivaima na funkcije e biti u glavi 10) i koje se
moe koristiti na primer kao:

PFI strcmp, numcmp;


149 6. Predstavljanje podataka i operacije nad njima

Osim estetskih razloga, postoje dve osnovne svrhe za korienje kljune rei
typedef i imenovanje tipova. Prvi je parametrizovanje tipova u programu da
bi se dobilo na njegovoj prenosivosti. Ukoliko se typedef koristi za uvoenje
novih imena za tipove koji su mainski zavisni, u sluaju da se program prenosi
na drugu mainu, potrebno je promeniti samo typedef deklaracije. Jedan od
primera je korienje typedef za imenovanje celobrojnog tipa, i zatim odabir
short, int i long u zavisnosti od maine. Druga svrha upotrebe typedef
je poboljanje itljivosti programa tip sa imenom Point se jednostavnije

5)
razumeti nego dugo ime strukture.

01
Pitanja i zadaci za vebu

(2
Pitanje 6.7.1. Definisati strukturu complex koja ima dva lana tipa double.
Pitanje 6.7.2. Definisati strukturu student kojom se predstavljaju podaci o
studentu (ime, prezime, JMBG, prosena ocena).

Pitanje 6.7.3.
je
Da li je nad vrednostima koje su istog tipa strukture dozvoljeno
an
koristiti operator dodele i koje je njegovo dejstvo?

Pitanje 6.7.4. Navesti primer inicijalizacije niza struktura tipa


d

struct datum { unsigned dan, mesec, godina; } na dananji i sutranji


iz

datum.

Pitanje 6.7.5. Data je struktura:


o

struct tacka {
sk

int a, b;
char naziv[5];
n

}
ro

Ova struktura opisuje taku sa koordinatama (a,b) u ravni kojoj je dodel-


naziv. struct tacka, napisati naredbe koje
kt

jeno ime Za dve promenljive tipa


kopiraju sadraj prve promenljive u drugu promenljivu.
le

Pitanje 6.7.6. Kakva je veza izmeu nabrojivog (enum) i celobrojnog tipa u


C-u?
E

Pitanje 6.7.7. Na koji nain se postojeem tipu t moe dodeliti novo ime
NoviTip?
Pitanje 6.7.8. Uvesti novo ime real za tip double.
Pitanje 6.7.9. Definisati novi tip novitip koji odgovara strukturi koji ima
jedan lan tipa int i jedan lan tipa float.
Zadatak 6.7.1. Napisati program koji uitava 10 kompleksnih brojeva i meu
njima odreuje broj najveeg modula. Definisati i koristiti strukturu complex
.
150 6. Predstavljanje podataka i operacije nad njima

Zadatak 6.7.2. Napraviti program koji uitava redni broj dana u nedelji (broji
se od ponedeljka) i ispisuje da li je radni dan ili vikend (definisati i koristiti

nabrojivi tip podataka).

5)
01
(2
je
an
d
iz
o
sk
n
ro
kt
le
E
Glava 7

5)
Naredbe i kontrola toka

01
(2
je
Osnovni elementi kojima se opisuju izraunavanja u programima su naredbe.
Naredbe za kontrolu toka omoguavaju razliite naine izvravanja programa,
an
u zavisnosti od vrednosti promenljivih. Naredbe za kontrolu toka ukljuuju
naredbe grananja i petlje. Iako u jeziku C postoji i naredba skoka (goto), ona
nee biti opisivana, jer esto dovodi do loe strukturiranih programa, neitljivih
d

i tekih za odravanje a, dodatno, svaki program koji koristi naredbu goto moe
iz

se napisati i bez nje. Na osnovu teoreme o strukturnom programiranju, od


naredbi za kontrolu toka dovoljna je naredba grananja (tj. naredba if) i jedna
vrsta petlje (na primer, petlja while), ali se u programima esto koriste i druge
o

naredbe za kontrolu toka radi bolje itljivosti kda. Iako mogu da postoje opte
sk

preporuke za pisanje kda jednostavnog za razumevanje i odravanje, izbor


naredbi za kontrolu toka u konkretnim situacijama je najee stvar afiniteta
n

programera.
ro

7.1 Naredba izraza


kt

Osnovni oblik naredbe koji se javlja u C programima je takozvana naredba


izraza (ova vrsta naredbi obuhvata i naredbu dodele i naredbu poziva funkcije).
le

Naime, svaki izraz zavren karakterom ; je naredba. Naredba izraza se izvrava


tako to se izrauna vrednost izraza i izraunata vrednost se potom zanemaruje.
E

Ovo ima smisla ukoliko se u okviru izraza koriste operatori koji imaju bone
efekte (na primer, =, ++, += itd.) ili ukoliko se pozivaju funkcije. Naredba do-
dele, kao i naredbe koje odgovaraju pozivima funkcija sintaksiki su obuhvaene
naredbom izraza. Naredni primer ilustruje nekoliko vrsta naredbi:

3 + 4*5;
n = 3;
c++;
f();

151
152 7. Naredbe i kontrola toka

Iako su sve etiri navedene naredbe ispravne i sve predstavljaju naredbe izraza,
prva naredba ne proizvodi nikakav efekat i nema semantiko opravdanje pa se
retko sree u stvarnim programima. etvrta naredba predstavlja poziv funkcije
f (pri emu se, ako je ima, povratna vrednost funkcije zanemaruje).
Zanimljiva je i prazna naredba koja se obeleava samo znakom ; (na primer,
telo for petlje moe sadrati samo praznu naredbu).

7.2 Sloene naredbe (blokovi)

5)
U nekim sluajevima potrebno je vie razliitih naredbi tretirati kao jednu

01
jedinstvenu naredbu. Vitiaste zagrade {i} se koriste da grupiu naredbe u
sloene naredbe tj. blokove i takvi blokovi se mogu koristiti na svim mestima

(2
gde se mogu koristiti i pojedinane naredbe. Iza zatvorene vitiaste zagrade
ne pie se znak ;. Vitiaste zagrade koje okruuju vie naredbi neke petlje su
jedan primer njihove upotrebe, ali one se, za grupisanje naredbi, koriste i u

je
drugim kontekstima. Doputeno je i da blok sadri samo jednu naredbu, ali
tada nema potrebe koristiti blok (izuzetak je definicija funkcije koja ne moe da
an
bude pojedinana naredba ve mora biti blok i telo naredbe do-while koje uvek
mora biti navedeno izmeu vitiastih zagrada). Svaki blok, na poetku moe
d

da sadri (mogue praznu) listu deklaracija promeljivih (koje se mogu koristiti


samo u tom bloku). Vidljivost tj. oblast vaenja imena promenljivih odreena je
iz

pravilima dosega (o emu e vie rei biti u poglavlju 9.2.2). Nakon deklaracija,
navode se naredbe (elementarne ili sloene, tj. novi blokovi). Postoje razliite
o

konvencije za nazubljivanje vitiastih zagrada prilikom unosa izvornog kda.


sk

7.3 Naredbe grananja


n

Naredbe grananja (ili naredbe uslova), na osnovu vrednosti nekog izraza,


ro

odreuju naredbu (ili grupu naredbi) koja e biti izvrena.


kt

7.3.1 Naredba if-else


if
le

Naredba uslova ima sledei opti oblik:

if (izraz)
E

naredba1
else
naredba2

Naredba naredba1 i naredba2 su ili pojedinane naredbe (kada se za-


vravaju simbolom ;) ili blokovi naredbi zapisani izmeu vitiastih zagrada
(iza kojih se ne pie simbol ;).
Deo naredbe else je opcioni, tj. moe da postoji samo if grana. Izraz izraz
predstavlja logiki uslov i najee je u pitanju celobrojni izraz (ali moe biti i
izraz ija je vrednost broj u pokretnom zarezu) za koji se smatra, kao i uvek, da
153 7. Naredbe i kontrola toka

je taan (tj. da je uslov ispunjen) ako ima ne-nula vrednost, a inae se smatra
da je netaan. Na primer, nakon naredbe

if (5 > 7)
a = 1;
else
a = 2;

5)
promenljiva a e imati vrednost 2, a nakon naredbe

if (7)

01
a = 1;
else

(2
a = 2;

promenljiva a e imati vrednost 1.

je
Kako se ispituje istinitosna vrednost izraza koji je naveden kao uslov, ponekad
je mogue taj uslov zapisati krae. Na primer, if (n != 0) je ekvivalentno sa
an
if (n).
Dodela je operator, pa je naredni C kd je sintaksiki ispravan, ali verovatno
d

je semantiki pogrean (tj. ne opisuje ono to je bila namera programera):


iz

a = 3;
if (a = 0)
printf("a je nula\n");
o

else
sk

printf("a nije nula\n");


n

Naime, efekat ovog kda je da postavlja vrednost promenljive a na nulu


a jednako 0), a zatim ispisuje tekst a nije nula, jer
ro

(a ne da ispita da li je
je vrednost izraza a = 0 nula, to se smatra netanim. Zamena operatora ==
operatorom = u naredbi if je esta greka.
kt

if-else vieznanost. Naredbe koje se izvravaju uslovno mogu da sadre


le

nove naredbe uslova, tj. moe biti vie ugnjedenih if naredbi. Ukoliko vi-
else se odnosi na poslednji
E

tiastim zagradama nije obezbeeno drugaije,


prethodei neuparen if. Ukoliko se eli drugaije ponaanje, neophodno je
navesti vitiaste zagrade. U narednom primeru, else se odnosi na drugo a ne
na prvo if (iako nazubljivanje sugerie drugaije):

if (izraz1)
if (izraz2)
naredba1
else
naredba2
154 7. Naredbe i kontrola toka

U narednom primeru, else se odnosi na prvo a ne na drugo if :

if (izraz1) {
if (izraz2)
naredba1
} else
naredba2

5)
7.3.2 Konstrukcija else-if

01
Za viestruke odluke esto se koristi konstrukcija sledeeg oblika:

(2
if (izraz1)
naredba1
else if (izraz2)

je
naredba2
else if (izraz3)
an
naredba3
else
naredba4
d
iz

U ovako konstruisanoj naredbi, uslovi se ispituju jedan za drugim. Kada


je jedan uslov ispunjen, onda se izvrava naredba koja mu je pridruena i
naredba4 u gore navede-
o

time se zavrava izvravanje itave naredbe. Naredba


nom primeru se izvrava ako nije ispunjen nijedan od uslova izraz1, izraz2,
sk

izraz3. Naredni primer ilustruje ovaj tip uslovnog grananja.


n

if (a > 0)
printf("A je veci od nule\n");
ro

else if (a < 0)
printf("A je manji od nule\n");
kt

else /* if (a == 0) */
printf("A je nula\n");
le
E

7.3.3 Naredba if-else i operator uslova

Naredna naredba

if (a > b)
x = a;
else
x = b;
155 7. Naredbe i kontrola toka

odreuje i smeta u promenljivu x veu od vrednosti a i b. Naredba ovakvog


oblika se moe zapisati krae korienjem ternarnog operatora uslova ?: (koji
je opisan u poglavlju 6.4.7), na sledei nain:

x = (a > b) ? a : b;

switch

5)
7.3.4 Naredba

Naredba switch se koristi za viestruko odluivanje i ima sledei opti oblik:

01
switch (izraz) {
case konstantan_izraz1: naredbe1

(2
case konstantan_izraz2: naredbe2
...
default: naredbe_n

je
}
an
Naredbe koje treba izvriti oznaene su sluajevima (engl. case) za razliite
mogue pojedinane vrednosti zadatog izraza izraz. Svakom sluaju pridruen
izraz
d

je konstantni celobrojni izraz. Ukoliko zadati izraz ima vrednost kon-


stantnog izraza navedenog u nekom sluaju, onda se izvravanje nastavlja od
iz

prve naredbe pridruene tom sluaju, pa se nastavlja i sa izvravanjem naredbi


koje odgovaraju sledeim sluajevima iako izraz nije imao njihovu vrednost,
o

sve dok se ne naie na kraj ili naredbu break. Na sluaj default se prelazi
izraz default je
sk

ako vrednost izraza nije navedena ni uz jedan sluaj. Sluaj


opcioni i ukoliko nije naveden, a nijedan postojei sluaj nije ispunjen, onda se
ne izvrava nijedna naredba u okviru bloka switch. Sluajevi mogu biti nave-
n

deni u proizvoljnom poretku (ukljuujui i sluaj default), ali razliiti poretci


ro

mogu da daju razliito ponaanje programa. Iako to standard ne zahteva, sluaj


default se gotovo uvek navodi kao poslednji sluaj. I ukoliko sluaj default
kt

nije naveden kao poslednji, ako vrednost izraza izraz nije navedena ni uz jedan
drugi sluaj, prelazi se na izvravanje naredbi od naredbe pridruene sluaju
default.
le

U okviru naredbe switch esto se koristi naredba break. Kada se naie


E

na naredbu break, naputa se naredba switch. Najee se naredbe priduene


svakom sluaju zavravaju naredbom break (ak i nakon poslednje navedenog
sluaja, to je najee default). Time se ne menja ponaanje programa, ali se
obezbeuje da poredak sluajeva ne utie na izvravanje programa, te je takav
kd jednostavniji za odravanje.
Izostavljanje naredbe break, tj. previanje injenice da se, ukoliko nema
naredbi break, nastavlja sa izvravanjem naredbi narednih sluajeva, esto
dovodi do greaka u programu. S druge strane, izostavljanje naredbe break
moe biti pogodno (i opravdano) za pokrivanje vie razliitih sluajeva jednom
naredbom (ili blokom naredbi).
156 7. Naredbe i kontrola toka

U narednom primeru proverava se da li je uneti broj deljiv sa tri, kori


cenjem naredbe switch.
Program 7.1.
#include <stdio.h>

int main() {
int n;

5)
scanf("%i",&n);

01
switch (n % 3) {
case 1:

(2
case 2:
printf("Uneti broj nije deljiv sa 3");
break;

je
default: printf("Uneti broj je deljiv sa 3");
}
an
return 0;
}
d

U navedenom primeru, bilo da je vrednost izraza n % 3 jednaka 1 ili 2,


iz

bie ispisan tekstUneti broj nije deljiv sa 3, a inae e biti ispisan tekst
Uneti broj je deljiv sa 3. Da u nije navedena naredba break, onda bi u
sluaju da je vrednost izraza n % 3 jednaka 1 (ili 2), nakon teksta Uneti broj
o

nije deljiv sa 3, bio ispisan i tekst Uneti broj je deljiv sa 3 (jer bi


sk

bilo nastavljeno izvravanje svih naredbi za sve naredne sluajeve).


n

7.4 Petlje
ro

Petlje (ciklusi ili repetitivne naredbe) uzrokuju da se odreena naredba (ili


kt

grupa naredbi) izvrava vie puta (sve dok je neki logiki uslov ispunjen).
le

7.4.1 Petlja while


while
E

Petlja ima sledei opti oblik:

while(izraz)
naredba

U petlji while ispituje se vrednost izraza izraz i ako ona ima istinitosnu
vrednost tano (tj. ako je vrednost izraza razliita od nule), izvrava se naredba
(to je ili pojedinana naredba ili blok naredbi). Zatim se uslov izraz iznova
proverava i sve se ponavlja dok mu istinosna vrednost ne postane netano
(tj. dok vrednost ne postane jednaka nuli). Tada se izlazi iz petlje i nastavlja
sa izvravanjem prve sledee naredbe u programu.
157 7. Naredbe i kontrola toka

Ukoliko iza while sledi samo jedna naredba, onda, kao i oblino, nema
potrebe za vitiastim zagradama. Na primer:

while (i < j)
i++;

Sledea while petlja se izvrava beskonano:

5)
while (1)
i++;

01
for

(2
7.4.2 Petlja

Petlja for ima sledei opti oblik:

je
for (izraz1; izraz2; izraz3)
naredba
an
Komponente izraz1, izraz2 i izraz3 su izrazi. Obino su izraz1 i izraz3
izrazi dodele ili inkrementiranja, a izraz2 je relacijski izraz. Izraz izraz1 se
d

obino naziva inicijalizacija i koristi se za postavljanje poetnih vrednosti pro-


iz

menljivih, izraz izraz2 je uslov izlaska iz petlje, a izraz3 je korak i njime se


menjaju vrednosti relevantnih promenljivih. Naredba naredba naziva se telo
petlje.
o

Inicijalizacija (izraz izraz1) izraunava se samo jednom, na poetku izvr-


sk

avanja petlje. Petlja se izvrava sve dok uslov (izraz izraz2) ima ne-nula
vrednost (tj. sve dok mu je istinitosna vrednost tano), a korak (izraz izraz3)
n

izraunava se na kraju svakog prolaska kroz petlju. Redosled izvravanja je,


ro

dakle, oblika: inicijalizacija, uslov, telo, korak, uslov, telo, korak, . . . , uslov,
telo, korak, uslov, pri emu je uslov ispunjen svaki, osim poslednji put. Dakle,
gore navedena opta forma petlje for ekvivalentna je konstrukciji koja koristi
kt

petlju while:
izraz1;
le

while (izraz2) {
E

naredba
izraz3;
}

Petlja for se obino koristi kada je potrebno izvriti jednostavno poetno


dodeljivanje vrednosti promenljivama i jednostavno ih menjati sve dok je is-
punjen zadati uslov (pri emu su i poetno dodeljivanje i uslov i izmene lako
vidljivi u definiciji petlje). To ilustruje sledea tipina forma for petlje:
158 7. Naredbe i kontrola toka

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


...

Bilo koji od izraza izraz1, izraz2, izraz3 moe biti izostavljen, ali simboli
; i tada moraju biti navedeni. Ukoliko je izostavljen izraz izraz2, smatra se da
je njegova istinitosna vrednost tano. Na primer, sledea for petlja se izvrava
beskonano (ako u bloku naredbi koji ovde nije naveden nema neke naredbe
break return):

5)
koja prekida izvravanje, na primer, ili

for (;;)

01
...

izraz1, izraz2, izraz3

(2
Ako je potrebno da neki od izraza objedini vie
izraza, moe se koristiti operator ,.
for (i = 0, j = 10; i < j; i++, j--)

je
printf("i=%d, j=%d\t", i, j);
an
Prethodni kd ispisuje

i=0, j=10 i=1, j=9 i=2, j=8 i=3, j=7 i=4, j=6
d
iz

Sledei program, koji ispusuje tablicu mnoenja, ilustruje dvostruku for


petlje:
o

Program 7.2.
sk

#include<stdio.h>
n

int main() {
ro

int i, j, n=3;
for(i = 1; i <= n; i++) {
kt

for(j = 1; j <= n; j++)


printf("%i * %i = %i\t", i, j, i*j);
printf("\n");
le

}
E

return 0;
}

1 * 1 = 1 1 * 2 = 2 1 * 3 = 3
2 * 1 = 2 2 * 2 = 4 2 * 3 = 6
3 * 1 = 3 3 * 2 = 6 3 * 3 = 9
159 7. Naredbe i kontrola toka

7.4.3 Petlja do-while


Petlja do-while ima sledei opti oblik:

do {
naredbe
} while(izraz)

Telo (blok naredbi naredbe) naveden izmeu vitiastih zagrada se izvrava

5)
i onda se izraunava uslov (izraz izraz). Ako je on taan, telo se izvrava
ponovo i to se nastavlja sve dok izraz izraz nema vrednost nula (tj. sve dok

01
njegova istinitosna vrednost ne postane netano).
Za razliku od petlje while, naredbe u bloku ove petlje se uvek izvravaju

(2
barem jednom.
Naredni program ispisuje cifre koje se koriste u zapisu unetog neoznaenog
broja, zdesna na levo.

je
Program 7.3.
an
#include <stdio.h>
int main() {
unsigned n;
d

printf("Unesi broj: ");


iz

scanf("%u", &n);
do {
printf("%u ", n % 10);
o

n /= 10;
sk

} while (n > 0);


return 0;
n

}
ro

Unesi broj: 1234


kt

4 3 2 1

while, do-while
le

Primetimo da, u sluaju da je koriena a ne petlja, za


broj 0 ne bi bila ispisana ni jedna cifra.
E

7.4.4 Naredbe break i continue


U nekim situacijama pogodno je napustiti petlju ne zbog toga to nije ispun-
jen uslov petlje, ve iz nekog drugog razloga. To je mogue postii naredbom
break kojom se izlazi iz tekue petlje (ili naredbe switch)1 Na primer:

1 Naredbom break ne izlazi se iz bloka naredbe grananja.


160 7. Naredbe i kontrola toka

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


if(i > 10)
break;
...
}

Korienjem naredbe break se naruava strukturiranost kda i to moe da

5)
otea njegovu analizu (na primer, analizu ispravnosti ili analizu sloenosti).
U nekim situacijama, korienje naredbe break moe da pobolja itljivost,
ali kd koji koristi naredbu break uvek se moe napisati i bez nje. U datom

01
primeru, odgovarajui alternativni kd je, na primer:

for(i = 1; i<n && i<=10; i++)

(2
...

continue

je
Naredbom se prelazi na sledeu iteraciju u petlji. Na primer,

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


an
if (i % 10 == 0)
continue; /* preskoci brojeve deljive sa 10 */
...
d

}
iz

Slino kao za naredbu break, korienjem naredbe continue se naruava


o

strukturiranost kda, ali moe da se pobolja itljivost. Kd koji koristi naredbu


continue
sk

uvek se moe napisati i bez nje. U datom primeru, odgovarajui al-


ternativni kd je, na primer:
n

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


if (i % 10 != 0) /* samo brojevi koji nisu deljivi sa 10 */
ro

...
kt

U sluaju ugnjedenih petlji, naredbe break i continue imaju dejstvo samo


na unutranju petlju. Tako, na primer, fragment
le

for (i = 0; i < 3; i++)


E

for (j = 0; j < 3; j++) {


if (i + j > 2) break;
printf("%d %d ", i, j);
}

ispisuje

0 0 0 1 0 2 1 0 1 1 2 0
161 7. Naredbe i kontrola toka

Pitanja i zadaci za vebu


Pitanje 7.1. Koju vrednost ima promenljiva x nakon izvravanja kda

if (!x)
x += 2;
x *= 2;
0, 5?

5)
ako je pre navedenog kda imala vrednost a koju ako je imala vrednost

Pitanje 7.2. Koju vrednost ima promenljiva x nakon izvravanja kda

01
int x = 0;
if (x > 3);

(2
x++;
Pitanje 7.3. Navesti primer naredbe u kojoj se javlja if-else vieznanost.

je
Pitanje 7.4. Kako se, u okviru naredbe switch, oznaava sluaj koji pokriva
an
sve sluajeve koji nisu ve pokriveni?

Pitanje 7.5. Ako su promenljive i i j tipa int odrediti njihovu vrednost nakon
d

naredbi:
iz

i=2; j=5;
switch(j/i)
{
o

case 1: i++;
sk

break;
case 2: i += ++j;
n

default: j += ++i;
ro

}
Pitanje 7.6. Ako su promenljive i i j tipa int odrediti njihovu vrednost nakon
kt

naredbi:

int i=2, j=4;


le

switch (j%3) {
E

case 0: i=j++;
case 1: j++;++i;
case 2: j=i++;
default:
}
Pitanje 7.7. ta se ispisuje prilikom izvravanja narednog kda?

int i = 2; int j = 2;
while(i + 2*j <= 15) {
i++;
162 7. Naredbe i kontrola toka

if (i % 3 == 0)
j--;
else {
i+=2; j++;
}
printf("%d %d\n", i, j);
}

5)
Pitanje 7.8. ta se ispisuje prilikom izvravanja narednog kda?

for (i = 0; i < 5; i++)

01
for (j = 0; j < 5; j++) {
if ((i + j) % 3 == 0) break;

(2
printf("%d %d\n", i, j);
}
Pitanje 7.9.

je
ta se ispisuje prilikom izvravanja narednog kda?

for (i = 0; i < 5; i++)


an
for (j = 0; j < 5; j++) {
if ((i + j) % 3 == 0) continue;
d

printf("%d %d\n", i, j);


}
iz

Pitanje 7.10. ta se ispisuje prilikom izvravanja narednog kda?


o

for (i = 1, j = 10; i < j; i++, j--)


sk

for (k = i; k <= j; k++)


printf("%d ", k);
n

Pitanje 7.11. Ukoliko se u naredbi for (e1; e2; e3) ... izostavi izraz e2,
ro

koja e biti njegova podrazumevana vrednost?

Pitanje 7.12.
kt

Da li je u naredbi for(e1; e2; e3) ..., mogue da su izrazi


e1 i e3 sainjeni od vie nezavisnih podizraza i ako jeste, kojim operatorom su
le

povezani ti podizrazi?

Pitanje 7.13. Izraziti sledei kd (i) petljom while i (ii) petljom for
E

do {
naredbe
} while(izraz)
Pitanje 7.14. Izraziti sledei kd (i) petljom while i (ii) petljom do-while
for (izraz1; izraz2; izraz3)
naredba
Pitanje 7.15. Izraziti sledei kd (i) petljom for i (ii) petljom do-while
163 7. Naredbe i kontrola toka

while (izraz)
naredba
Pitanje 7.16. Transformisati naredni kd tako da ne sadri break:
while(A) {
if(B) break;
C;
}

5)
Pitanje 7.17. Transformisati naredni kd tako da ne sadri continue:

01
for(;A;) {
if(B) continue;

(2
C;
}

je
Pitanje 7.18. Transformisati naredni kd i zapisati ga u obliku while petlje
tako da ne sadri break:
an
1. for(i = 1;;i++) if (i++ == 3) break; else printf("%d", i);
for(i = 1;;) if (++i == 3) break; else printf("%d", i);
d

2.

3. for(i = 10;;) if (--i == 3) break; else printf("%d", i);


iz

4. for(i = 1; i<5;) if (++i == 5) break; else printf("%d", i);


o

Pitanje 7.19. Transformisati naredni kd i zapisati ga u obliku for petlje sa


break.
sk

sva tri izraza neprazna i bez korienja naredbe

1. for(i = 10;;) if (--i == 3) break; else printf("%d", i);


n

2. for(i = 1;;) if (i++ == 3) break; else printf("%d", i);


ro

Pitanje 7.20. Da li se naredne petlje zaustavljaju:


unsigned char c; for(c=10; c<9; c++) { ... }
kt

signed char c; for(c=10; c<9; c++) { ... }


signed char c; for(c=0; c<128; c++) { ... }
le

unsigned char c; for(c=0; c<128; c++) { ... }


char c; for(c=0; c<128; c++) { ... }
E

Zadatak 7.1. Napisati program koji ispisuje sve neparne brojeve manje od

unetog neoznaenog celog broja n.
Zadatak 7.2. Napisati program koji izraunava i ispisuje vrednost funkcije

sin() u 100 taaka intervala [0, 2] na jednakim rastojanjima.

Zadatak 7.3. Napisati program koji uitava realan broj i neoznaeni ceo

broj i izraunava .
Zadatak 7.4. Napisati programe koji ispisuje naredne dijagrame, pri emu se
dimenzija n unosi. Svi primeri su za = 4:
164 7. Naredbe i kontrola toka

a) **** b) **** c) * d) **** e) * f) * * * * g) *


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

Zadatak 7.5. Napisati program koji odreuje sumu svih delilaca broja (koristiti

injenicu da se delioci javljaju u paru).

5)
Zadatak 7.6. Napisati program koji odreuje da li je uneti neoznaeni ceo broj

01
prost.

Zadatak 7.7. Napisati program koji ispisuje sve proste inioce unetog neoz-

(2
naenog celog broja (na primer, za unos 24 traeni prosti inioci su 2, 2, 2,
3).

Zadatak 7.8.

je
Napisati program koji uitava cele brojeve sve dok se ne unese
0, a onda ispisuje:
an
1. broj unetih brojeva;

2. zbir unetih brojeva;


d

3. proizvod unetih brojeva;


iz

4. minimum unetih brojeva;

5. maksimum unetih brojeva;


o

1 +...+
6. aritmetiku sredinu unetih brojeva ( );
sk



7. geometrijsku sredinu unetih brojeva (
1 . . . );
n


8. harmonijsku sredinu unetih brojeva ( 1 );
1 +...+ 1
ro


kt

Zadatak 7.9. Napisati program koji uitava cele brojeve sve dok se ne unese

0 i izraunava duinu najdue serije uzastopnih jednakih brojeva.
le

Zadatak 7.10. Napisati program koji za uneti neoznaeni ceo broj


izrau-
nava ceo deo njegovo korena (koristiti algoritam opisan u primeru 3.3).
E

Zadatak 7.11. Korienjem injenice da niz definisan sa

2
0 = 1, +1 =
2

tei ka
, napisati program koji (bez korienja funkcije sqrt) procenjuje
vrednost . Iterativni postupak zaustaviti kada je |+1 | < 0.0001.
165 7. Naredbe i kontrola toka

Zadatak 7.12. Fibonaijev niz brojeva 1, 1, 2, 3, 5, 8, 13, 21, . . . je definisan


uslovima
0 = 1, 1 = 1, +2 = +1 + .
Napisati program koji ispisuje prvih lanova ovog niza (koristiti ideju da se
u svakom trenutku pamte, u dve promenljive, vrednosti dva prethodna elementa

niza).

Zadatak 7.13.

5)
Napisati program koji ispisuje sve cifre unetog neoznaenog
celog broja n, poevi od cifre jedinica. Napisati i program koji izraunava

sumu cifara unetog neoznaenog celog broja n.

01
Zadatak 7.14. Napisati program koji nadovezuje dva uneta neoznaena cela
123 i 456 program ispisuje 123456).

(2
broja (na primer, za unos Ako je jedan od
brojeva 0, rezultat treba da bude jednak drugom broju. Pretpostaviti da rezultat

staje u opseg tipa unsigned.

je
Zadatak 7.15. Napisati program koji izraunava broj dobijen obrtanjem cifara

unetog celog broja (na primer, za unos 1234 program ispisuje broj 4321).
an
Zadatak 7.16. Napisati program koji izbacuje sve neparne cifre iz zapisa un-
d

etog neoznaenog celog broja (na primer, za uneti broj 123456 program ispisuje

246).
iz

Zadatak 7.17. Napisati program koji umee datu cifru na datu poziciju
neoznaenog celog broja (pozicije se broje od 0 sa desna). Na primer, za unos
o


= 5, = 2, = 1234, program ispisuje 12534.
sk

Zadatak 7.18.
n

1. Napisati program koji razmenjuje prvu i poslednju cifru unetog neoz-


ro

naenog celog broja (na primer, za unos 1234 program ispisuje 4231).
2. Napisati program koji ciklino pomera cifre unetog neoznaenog celog
kt

broja u levo (na primer, za unos 1234 program ispisuje 2341).


le

3. Napisati program koji ciklino pomera cifre unetog neoznaenog celog


broja u desno (na primer, za unos 1234 program ispisuje 4123).
E

Zadatak 7.19. Napisati program koji za zadati dan, mesec i godinu ispituje da
li je uneti datum korektan (uzeti u obzir i prestupne godine). Koristiti switch

naredbu.

Zadatak 7.20. Definisati nabrojivi tip za predstavljanje kolskog uspeha. Na


osnovu prosene ocene i broja jedinica uenika odrediti kolski uspeh. Nakon

toga, upotrebom naredbe switch ispisati odgovarajuu poruku. .
166 7. Naredbe i kontrola toka

Zadatak 7.21. Napisati program koji uitava neoznaene cele brojeve sve dok
se ne unese 0, a na standardni prepisuje sve one brojeve koji su:

1. manji od minimuma prethodno unetih brojeva (prvi broj se uvek ispisuje);

2. vei od aritmetike sredine prethodno unetih brojeva.


5)
Zadatak 7.22. Napisati program koji ispisuje prvih (najvie 100000) ele-
menata niza koji se dobija tako to se kree od 0, a zatim prethodni niz ponovi

01
pri emu se svaki broj uvea za 1, tj. 0112122312232334...
Zadatak 7.23. Sa standardnog ulaza se unose dva niza brojeva (svaki sadri

(2
najvie 100 elemenata). Napisati program koji odreuje njihov presek, uniju i
razliku (redosled prikaza elemenata nije bitan, i u ulaznim nizovima se elementi

ne ponavljaju).

je
Zadatak 7.24. Napisati program koji proverava da li je data niska palindrom
an
(ita se isto sleva i zdesna, na primer, anavolimilovana). Prilagoditi program
tako da se razmaci zanemaruju i ne pravi se razlika izmeu malih i velikih slova

(na primer, Ana voli Milovana je palindrom).
d


Zadatak 7.25. Napisati program koji obre i ispisuje datu nisku.
iz

Zadatak 7.26. Napisati program koji sa standardnog ulaza unosi prvo dimenz-
o

iju matrice ( < 100) pa zatim elemente matrice i zatim ispisuje sve dijagonale
matrice paralelne sa sporednom dijagonalom. Na primer, unos:
sk

3 1 2 3 4 5 6 7 8 9
opisuje matricu
n
ro

1 2 3
4 5 6
7 8 9
kt

za koju program ispisuje


le

1
2 4
E

3 5 7
6 8
9

Zadatak 7.27. Napisati program koji sa standardnog ulaza uitava prvo di-
menzije matrice ( < 100 i < 100) a zatim redom i elemente matrice. Nakon
toga ispisuje indekse ( i ) onih elemenata matrice koji su jednaki zbiru svih
svojih susednih elemenata (pod susednim elementima podrazumevamo okolnih
8 polja matrice ako postoje). Na primer, za matricu:
167 7. Naredbe i kontrola toka

1 1 2 1 3
0 8 1 9 0
1 1 1 0 0
0 3 0 2 2

polja na pozicijama [1][1], [3][1], i [3][4] zadovoljavaju traeni uslov.

5)
01
(2
je
an
d
iz
o
sk
n
ro
kt
le
E
Glava 8

5)
Funkcije

01
(2
je
Svaki C program sainjen je od funkcija. Funkcija main mora da postoji
i, pojednostavljeno reeno, izvravanje programa uvek poinje izvravanjem
an
1
ove funkcije . Iz funkcije main (ali i drugih) pozivaju se druge funkcije, bilo
biblioteke (poput funkcije printf), bilo korisniki definisane (kakve e biti
2
d

opisane u nastavku) . Druge funkcije koriste se da bi kd bio krai, itljiviji,


modularniji, ire upotrebljiv itd. U funkciju se obino izdvaja neko izrau-
iz

navanje, neka obrada koja predstavlja celinu za sebe i koristi se vie puta u
programu. Tako se dobija ne samo krai i jednostavniji kd, ve i kvalitetniji
o

u smislu da je neko izraunavanje skriveno u funkciji i ona moe da se koristi


ak i ako se ne zna kako tano je ona implementirana, ve je dovoljno znati ta
sk

radi, tj. kakav je rezultat njenog rada za zadate argumente.


n

8.1 Primeri definisanja i pozivanja funkcije


ro

Naredni programi ilustruju jednostavne primere korienja funkcija. Poj-


kt

movi koji su u okviru ovog primera samo ukratko objanjeni bie detaljnije
objanjeni u nastavku teksta.
le

Program 8.1.
E

#include <stdio.h>

int kvadrat(int n);

int main() {
printf("Kvadrat broja %i je %i\n", 5, kvadrat(5));
1U glavi 9 bie objanjeno da se odreeni delovi izvrnog programa, obino sistemski
pozivi i odreene inicijalizacije, izvravaju i pre poziva funkcije main, kao i da se odreeni
delovi programa izvravaju i nakon njenog zavretka.
2 Iz funkcija programa moe se pozivati i funkcija main, ali to obino nema mnogo smisla
i generalno se ne preporuuje.

168
169 8. Funkcije

printf("Kvadrat broja %i je %i\n", 9, kvadrat(9));


return 0;
}

int kvadrat(int n) {
return n*n;
}
int kvadrat(int n); deklarie funkciju kvadrat koja e biti defin-

5)
Linija
3
isana kasnije u kdu . Iz deklaracije se vidi da funkcija kvadrat ima jedan
parametar tipa int i vraa rezultat tipa int. U funkciji main, u okviru poziva

01
funkcije printf poziva se funkcija kvadrat za vrednosti 5 i 9 (tipa int) i
ispisuje se rezultat, tipa int, koji ona vraa. Svaki poziv funkcije je izraz (koji

(2
moe ravnopravno dalje uestvovati u irim izrazima) iji je tip povratni tip
funkcije, a vrednost povratna vrednost funkcije. Na mestu poziva, tok izvra-
vanja programa prelazi na poetak funkcije koja je pozvana, a kada se zavri

je
izvravanje funkcije tok izvravanja programa vraa se na mesto poziva. Defini-
cija funkcije kvadrat je jednostavna: ona kao rezultat, korienjem naredbe
an
return vraa kvadrat celobrojne vrednosti n ija se vrednost postavlja na os-
novu vrednosti koja je prosleena prilikom poziva.
stepen izraunava celobrojni stepen realne
d

U narednom programu, funkcija


promenljive.
iz

Program 8.2.
o

#include <stdio.h>
sk

int max = 10;


n

float stepen(float x, unsigned n);


ro

int main() {
kt

unsigned i;
for(i = 0; i < max; i++)
printf("%d %f %f\n", i, stepen(2.0f,i), stepen(3.0f,i));
le

return 0;
E

float stepen(float x, unsigned n) {


unsigned i;
float s = 1.0f;
for(i = 1; i<=n; i++)
s *= x;

3 Za razliku od funkcija gde se deklaracije i definicije prilino jednostavno razlikuju, kod


promenljivih je ovu razliku mnogo tee napraviti. Odnos izmeu deklaracija i definicija
promenljivih u jeziku C bie diskutovan u glavi 9.
170 8. Funkcije

return s;
}
Primetimo da je promenljiva i deklarisana u funkciji main, druga promenljiva
i deklarisana je u funkciji stepen, a promenljiva max deklarisana je van svih
funkcija. Promenljive deklarisana u funkcijama nazivamo obino lokalne pro-
menljive i njih je mogue koristiti samo u okviru funkcije u kojoj su definisane,
dok promenljive deklarisana van svih funkcija nazivamo obino globalne pro-

5)
menljive i one su zajednike za vie funkcija. O ovoj temi bie vie rei u
poglavlju 9.2.2.

01
8.2 Deklaracija i definicija funkcije

(2
Deklaracija (ili prototip ) funkcije ima sledei opti oblik:

tip ime_funkcije(niz_deklaracija_parametara);

a definicija funkcije ima sledei opti oblik:


je
an
tip ime_funkcije(niz_deklaracija_parametara) {
deklaracije
d

naredbe
iz

Imena funkcija su identifikatori i za njih vae potpuno ista pravila kao i za


o

imena promenljivih. Radi itljivosti kda, poeljno je da ime funkcije oslikava


sk

ono ta ona radi.


U navedenim primerima, prvo je navedena deklaracija funkcije, a tek kasnije
n

njena definicija. U prvom primeru, deklaracija je


ro

int kvadrat(int n);


kt

a definicija

int kvadrat(int n) {
le

return n*n;
}
E

Definicija funkcija mora da bude u skladu sa navedenim prototipom, tj. mo-


raju da se podudaraju tipovi povratne vrednosti i tipovi parametara. Deklara-
cija ukazuje prevodiocu da e u programu biti koriena funkcija sa odreenim
tipom povratne vrednosti i parametrima odreenog tipa. Zahvaljujui tome,
kada prevodilac (na primer, u okviru funkcije main), naie na poziv funkcije
kvadrat, moe da proveri da li je njen poziv ispravan (ak iako je definicija
funkcije nepoznata u trenutku te provere). Poto prototip slui samo za prover-
avanje tipova u pozivima, nije neophodno navoditi imena parametara, ve je
171 8. Funkcije

dovoljno navesti njihove tipove. U navedenom primeru, dakle, prototip je mo-


gao da bude i

int kvadrat(int);

Nije neophodno za svaku funkciju navoditi najpre njen prototip, pa onda


definiciju. Ukoliko je navedena definicija funkcije, onda se ona moe koris-
titi u nastavku programa i bez navoenja prototipa (jer prevodilac iz defini-

5)
cije funkcije moe da utvrdi sve relevantne tipove). Meutim, kada postoji
vie funkcija u programu i kada postoje njihove meuzavisnosti, moe biti

01
veoma teko (i to nepotrebno teko) poreati njihove definicije na nain koji
omoguava prevoenje (sa proverom tipova argumenata). Zato je uobiajena

(2
praksa da se na poetku programa navode prototipovi funkcija, ime poredak
njihovih definicija postaje irelevantan.
Ukoliko se ni definicija ni deklaracije funkcije ne navedu pre prvog poziva
int

je
te funkcije, prevodilac pretpostavlja da funkcija vraa vrednost tipa i ne
vri nikakvu provera ispravnosti argumenata u pozivima funkcije. Ovakvo pon-
an
aanje je predvieno jedino zbog kompatibilnosti sa starim verzijama jezika C,
te je opta preporuka da se svakako pre prvog poziva funkcije navede njena
deklaracija ili definicija.
d

Definicije funkcija mogu se navesti u proizvoljnom poretku i mogu se nalaz-


iz

iti u jednoj ili u vie datoteka. U drugom sluaju, potrebno je instruirati


prevodilac da obradi vie izvornih datoteka i da napravi jednu izvrnu verziju
(videti poglavlje 9.2.6).
o

Postojanje dve deklaracije iste funkcije u okviru jednog programa je dozvol-


sk

jeno, ali postojanje dve deklaracije funkcije istog imena, a razliitih lista param-
etara dovodi do greke tokom prevoenja. Postojanje dve definicije funkcije
n

istog imena u jednom programu dovodi do greke tokom prevoenja ili povezi-
vanja (ak i ako su liste parametara razliite).
ro

Prototipovi funkcija iz standardne biblioteke dati su u datotekama zaglavlja


i da bi se one mogle bezbedno koristiti dovoljno je samo ukljuiti odgovarajue
kt

zaglavlje. Meutim, neki prevodioci (ukljuujui GCC) poznaju prototipove


funkcija standardne biblioteke, ak i kada zaglavlje nije ukljueno. Tako, ako se
le

u GCC-u ne ukljui potrebno zaglavlje, dobija se upozorenje, ali ne i greka jer


su prototipovi standardnih funkcija unapred poznati. Ipak, ovakav kd treba
E

izbegavati i zaglavlja bi uvek trebalo eksplicitno ukljuiti (tj. pre svakog poziva
funkcije trebalo bi osigurati da kompilator poznaje njen prototip).

8.3 Parametri funkcije


Funkcija moe imati parametre koje obrauje i oni se navode u okviru
definicije, iza imena funkcije i izmeu zagrada. Termini parametar funkcije i
argument funkcije se ponekad koriste kao sinonimi. Ipak, pravilno je termin
parametar funkcije koristiti za promenljivu koja ini deklaraciju funkcije, a
termin argument funkcije za izraz naveden u pozivu funkcije na mestu parame-
172 8. Funkcije

tra funkcije. Ponekad se argumenti funkcija nazivaju i stvarni argumenti, a


parametri funkcija formalni argumenti. U primeru iz poglavlja 8.1, n je param-
int kvadrat(int n);,
etar funkcije a 5 i 9 su njeni argumenti u pozivima
kvadrat(5) i kvadrat(9).
Parametri funkcije mogu se u telu funkcije koristiti kao lokalne promenljive
te funkcije a koje imaju poetnu vrednost odreenu vrednostima argumenata
u pozivu funkcije.
Kao i imena promenljivih, imena parametara treba da oslikavaju njihovo

5)
znaenje i ulogu u programu. Pre imena svakog parametra neophodno je navesti
njegov tip. Ukoliko funkcija nema parametara, onda se izmeu zagrada ne

01
navodi nita ili se navodi kljuna re void. Ukoliko se, umesto kljune rei void,
izmeu zagrada ne navede nita, prevodilac u pozivima funkcije ne proverava

(2
da li je ona zaista pozvana bez argumenata.
Promenljive koje su deklarisane kao parametri funkcije lokalne su za tu
funkciju i njih ne mogu da koriste druge funkcije. tavie, bilo koja druga

je
funkcija moe da koristi isto ime za neki svoj parametar ili za neku svoju
lokalnu promenljivu.
an
Kvalifikatorom const mogu, kao i sve promenljive, biti oznaeni parametri
funkcije ime se obezbeuje da neki parametar ili sadraj na koji ukazuje neki
parametar nee biti menjan u funkciji. Ovo se najee koristi u kombinaciji
d

sa prenosom nizova (videti poglavlje 8.7) i pokazivaa (videti glavu 10).


iz

Funkcija main moe biti bez parametara ili moe imati dva parametra un-
apred odreenog tipa (videti poglavlje 12.4).
Prilikom poziva funkcije, vri se prenos argumenata, to e biti opisano u
o

narednom poglavlju.
sk

8.4 Prenos argumenata


n
ro

Na mestu u programu gde se poziva neka funkcija kao njen argument se


moe navesti promenljiva, ali i bilo koji izraz istog tipa (ili izraz ija vrednost
kvadrat iz primera
kt

moe da se konvertuje u taj tip). Na primer, funkcija iz


poglavlja 8.1 moe biti pozvana sa kvadrat(5), ali i sa kvadrat(2+3).
Argumenti funkcija se uvek prenose po vrednosti. To znai da se vrednost
le

koja se koristi kao argument funkcije kopira kada pone izvravanje funkcije i
E

onda funkcija radi samo sa tom kopijom, ne menjajui original. Na primer,


ako je funkcijakvadrat deklarisana sa int kvadrat(int n) i ako je pozvana
sa kvadrat(a), gde je a neka promenljiva, ta promenljiva e nakon izvrenja
funkcije kvadrat ostati nepromenjena, ma kako da je funkcija kvadrat defin-
isana. Naime, kada pone izvravanje funkcije kvadrat, vrednost promenljive
a bie iskopirana u lokalnu promenljivu n koja je navedena kao parametar
funkcije i funkcija e koristiti samo tu kopiju u svom radu. Prenos argumenata
ilustruje i funkcija swap definisana na sledei nain:

Program 8.3.
173 8. Funkcije

#include <stdio.h>

void swap(int a, int b) {


int temp = a;
a = b;
b = temp;
}

5)
int main() {
int x = 3, y = 5;

01
swap(x, y);
printf("x = %d, y = %d\n", x, y);

(2
}

U funkciji swap argumenti a ib zamenjuju vrednosti, ali ako je funkcija

je
pozvana iz neke druge funkcije sa swap(x, y), onda e vrednosti promenljivih
xiy ostati nepromenjene nakon ovog poziva.
an
x = 3, y = 5
d

Mogue je i da se ime parametra funkcije poklapa sa imenom promenljive


iz

koja je prosleena kao stvarni argument. Na primer:

Program 8.4.
o

#include <stdio.h>
sk

void f(int a) {
n

a = 3;
printf("f: a = %d\n", a);
ro

}
kt

int main() {
int a = 5;
le

f(a);
printf("main: a = %d\n", a);
E

I u ovom sluaju radi se o dve razliite promenljive (promenljiva u pozvanoj


funkciji je kopija promenljive iz funkcije u kojoj se poziv nalazi).

f: a = 3
main: a = 5

Ukoliko je potrebno promeniti neku promenljivu unutar funkcije, onda se


kao argument ne alje vrednost te promenljive nego njena adresa (i ta adresa
174 8. Funkcije

se onda prenosi po vrednosti). O tome e biti rei u glavi 10. Alternativno,


mogue je da funkcija samo izrauna vrednost i vrati je kao rezultat, a da se
izmena promenljive izvri u funkciji pozivaocu, naredbom dodele.
Prenos nizova vri se na specifian nain, o emu e biti rei u poglavlju
8.7.

8.5 Konverzije tipova argumenata funkcije

5)
Prilikom poziva funkcije, ukoliko je poznata njena deklaracija, vri se implic-
itna konverzija tipova argumenata u tipove parametara (ako se oni razlikuju).

01
Slino, prilikom vraanja vrednosti funkcije (putem return naredbe) vri se
konverzija vrednosti koja se vraa u tip povratne vrednosti funkcije.

(2
U sledeem primeru, prilikom poziva funkcije f vri se konverzija double
vrednosti u celobrojnu vrednost i program ispisuje 3:
Program 8.5.
#include <stdio.h>
je
an
void f(int a) {
printf("%d\n", a);
d

}
iz

int main() {
f(3.5);
o

}
sk

Ukoliko ni deklaracija ni definicija funkcije nisu navedene pre njenog poziva,


n

u fazi prevoenja ne vri se nikakva provera ispravnosti tipova argumenata i


ro

povratne vrednosti a u situacijama gde se poziva funkcija vre se podrazumevane


promocije argumenata: one obuhvataju celobrojne promocije (tipovi char i
short int), float double.
kt

se promoviu u i promociju tipa u tip U ovom


sluaju (da deklaracija ni definicija nisu navedene pre poziva funkcije), uko-
liko je definicija funkcije ipak navedena negde u programu, zbog kompilacije
le

koja je sprovedena na opisani nain, izvrni program moe da bude gener-


E

isan, ali njegovo ponaanje moe da bude neoekivano. Ako je u navedenom


primeru definicija funkcije f navedena nakon definicije funkcije main, prilikom
prevoenja funkcije main nee biti poznat tip funkcije f i kao parametar u
okviru poziva bie preneta vrednost 3.5 zapisana u pokretnom zarezu (kao
vrednost tipa double). f (koja je kompilirana nakon
S druge strane, funkcija
funkcije main) prima parametar tipa int i dobijenu vrednost 3.5 tumaie kao
ceo broj zapisan u potpunom komplementu. Zato bi tako modifikovani program
ispisao vrednost 0 (a ne 3.5, kao u prvom sluaju).
175 8. Funkcije

8.6 Povratna vrednost funkcije


Funkcija moe da vraa rezultat i tip oznaava tip vrednosti koja se vraa
kao rezultat. Funkcija rezultat vraa naredbom return r; gde je r izraz
zadatog tipa ili tipa koji se moe konvertovati u taj tip. Naredba return r; ne
samo da vraa vrednost r kao rezultat rada funkcije, nego i prekida njeno izvra-
vanje. Ako funkcija koja treba da vrati vrednost ne sadri return naredbu,
kompilator moe da prijavi upozorenje, a u fazi izvravanja rezultat poziva e

5)
biti nedefinisana vrednost (to je obino pogreno). Ako funkcija ne treba da
vraa rezultat, onda se kao tip povratne vrednosti navodi specijalan tip void

01
i tada naredba return nema argumenata (tj. navodi se return;). tavie, u
tom sluaju nije ni neophodno navoditi naredbu return iza poslednje naredbe

(2
u funkciji.
Funkcija koja je pozvala neku drugu funkciju moe da ignorie, tj. da ne
koristi vrednost koju je ova vratila.
const

je
Kvalifikator moe se primeniti i na tip povratne vrednosti funkcije.
To nema mnogo smisla, osim u kombinaciji sa pokazivaima (videti glavu 10)
an
i retko se koristi.
Iako je sintaksiki ispravno i drugaije, funkcija main uvek treba da ima
int kao tip povratne vrednosti (jer okruenje iz kojeg je program pozvan uvek
d

kao povratnu vrednost oekuje tip int).


iz

8.7 Nizovi i funkcije


o

Niz se ne moe preneti kao argument funkcije. Umesto toga, mogue je


sk

kao argument navesti ime niza. Tokom kompilacije, imenu niza pridruena je
informacija o adresi poetka niza, o tipu elemenata niza i o broju elemenata
n

niza. Kada se ime niza navede kao argument funkcije, onda do te funkcije
ro

(iji je tip odgovarajueg parametra pokazivaki tip, o emu e biti vie rei u
glavi 10) stie informacija o adresi poetka niza i o tipu elemenata (ali ne i o
imenu niza niti o broju elemenata niza). Poto funkcija koja je pozvana dobija
kt

informaciju o adresi poetka originalnog niza, ona moe da neposredno menja


njegove elemente (i takve izmene e biti sauvane nakon izvrenja funkcije), to
le

je drugaije u odnosu na sve ostale tipove podataka. Prenos adrese poetka


niza vri se kao i uvek po vrednosti. U pozvanoj funkciji, adresa poetka
E

niza navedenog u pozivu se kopira i moe se menjati (tj. ona je l-vrednost), pri
emu te izmene ne utiu na njenu originalnu vrednost (adresu poetka niza).
S druge strane, kao to je ranije reeno, originalna adresa poetka niza nije
l-vrednost i ne moe se menjati.
Funkcija koja prima niz moe biti deklarisana na neki od narednih naina:

tip ime_funkcije(tip ime_niza[dimenzija]);


tip ime_funkcije(tip ime_niza[]);
176 8. Funkcije

S obzirom na to da se u funkciju prenosi samo adresa poetka niza, a ne i


dimenzija niza, prvi oblik deklaracije nema puno smisla tako se znatno ree
koristi.
Ukoliko se ime dvodimenzionalnog niza koristi kao argument u pozivu funkcije,
deklaracija parametra u funkciji mora da ukljui broj kolona; broj vrsta je nebi-
tan, jer se i u ovom sluaju prenosi samo adresa (tj. pokaziva na niz vrsta, pri
emu je svaka vrsta niz). U sluaju niza sa vie od dve dimenzije, samo se prva
dimenzija moe izostaviti (dok je sve naredne dimenzije neophodno navesti).

5)
tip ime_funkcije(tip ime_niza[bv][bk]);

01
tip ime_funkcije(tip ime_niza[][bk]);

(2
Naredni primer ilustruje injenicu da se u funkciju ne prenosi ceo niz. Uko-
liko se program pokrene na maini na kojoj su tip int i adresni tip reprezento-
vani sa 4 bajta) program ispisuje, u okviru funkcije main, broj 20 (5 elemenata
4
tipa int ija je veliina 4 bajta) i, u okviru funkcije f, broj 4 (veliina adrese

je
koja je preneta u funkciju). Dakle, funkcija f nema informaciju o broju eleme-
nata niza a.
an
Program 8.6.
d

#include <stdio.h>
iz

void f(int a[]) {


printf("f: %d\n", sizeof(a));
o

}
sk

int main() {
n

int a[] = {1, 2, 3, 4, 5};


printf("main: %d\n", sizeof(a));
ro

f(a);
return 0;
kt

}
le

main: 20
f: 4
E

Prilikom prenosa niza (tj. adrese njegovog poetka) u funkciju, pored imena
niza, korisnik obino treba da eksplicitno prosledi i broj elemenata niza kao
dodatni argument (da bi pozvana funkcija imala tu informaciju).
Povratni tip funkcije ne moe da bude niz. Funkcija ne moe da kreira niz
koji bi bio vraen kao rezultat, a rezultat moe da vrati popunjavanjem niza
koji joj je prosleen kao argument.

4 sizeof je operator, a ne funkcija, te prilikom poziva sizeof(a) nema prenosa argume-


nata u kojem bi bila izgubljena informacija o broju elemenata niza a.
177 8. Funkcije

U narednom programu, funkcija ucitaj_broj ne uspeva da uita i promeni


vrednost broja x, dok funkcija ucitaj_niz ispravno unosi i menja elemente
niza y (jer joj je poznata adresa poetka niza y).

Program 8.7.
#include <stdio.h>

void ucitaj_broj(int a) {

5)
printf("Ucitaj broj: ");
scanf("%d", &a);

01
}

(2
void ucitaj_niz(int a[], int n) {
int i;
printf("Ucitaj niz: ");

je
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
an
}

int main() {
d

int x = 0;
iz

int y[3] = {0, 0, 0};


ucitaj_broj(x);
ucitaj_niz(y, 3);
o

printf("x = %d\n", x);


sk

printf("y = %d %d %d\n", y[0], y[1], y[2]);


}
n
ro

Unesi broj: 5
Unesi niz: 1 2 3
kt

x = 0
y = 1, 2, 3
le

Prenos niski u funkciju se vri kao i prenos bilo kog drugog niza. Jedina
E

razlika je to nije neophodno funkciji prosleivati duinu niske, jer se ona moe
odrediti i u samoj funkciji zahvaljujui postojanju terminalne nule.
5 Na primer,
funkcija strchr stanadardne biblioteke proverava da li data niska sadri dati
karakter. U nastavku je navedena jedna njena mogua implementacija, zas-
novana na takozvanoj linearnoj pretrazi niza. Ova implementacija pronalazi
prvu poziciju karaktera c u niski s a vraa -1 ukoliko s ne sadrzi c, to je neto
drugaije ponaenja u odnosu na implementaciju iz standardne biblioteke.

5 Naravno, duina niske ne odreuje duinu niza karaktera u koji je smetena, ve samo
broj karaktera do zavrne nule.
178 8. Funkcije

int strchr(const char s[], char c) {


int i;
for (i = 0; s[i] != \0; i++)
if (s[i] == c)
return i;

return -1;
}

5)
U nekim sluajevima elimo da spreimo da funkcija izmeni niz (ili nisku)

01
koji joj je prosleen i tada je uz niz potrebno navesti da je konstantan (kljunom
reju const), to je i uraeno i u sluaju funkcije strchr. Kljuna re const

(2
mogla bi se navesti i uz parametar c, ali to bi (kako se karakter prenosi po
vrednosti) znailo samo da ovaj parametar ne moe da se menja unutar tela
funkcije.

8.8 Korisniki definisani tipovi i funkcije


je
an
Parametri funkcija mogu biti i strukture i drugi korisniki definisani tipovi.
d

Pored toga, funkcije kao tip povratne vrednosti mogu imati tip strukture.
Prenos argumenta se i u ovom sluaju vri po vrednosti.
iz

Funkcija kreiraj_razlomak od dva cela broja kreira i vraa objekat tipa


struct razlomak:
o

struct razlomak kreiraj_razlomak(int brojilac, int imenilac) {


sk

struct razlomak rezultat;


rezultat.brojilac = brojilac;
rezultat.imenilac = imenilac;
n

return rezultat;
ro

}
kt

Navedni primer pokazuje i da ne postoji konflikt izmeu imena parametara


i istoimenih lanova strukture. Naime, imena lanova strukture su uvek vezana
le

za ime promenljive (u ovom primeru rezultat).


Sledei primer ilustruje funkcije sa parametrima i povratnim vrednostima
E

koji su tipa strukture:

struct razlomak saberi_razlomke(struct razlomak a,


struct razlomak b) {
struct razlomak c;
c.brojilac = a.brojilac*b.imenilac + a.imenilac*b.brojilac;
c.imenilac = a.imenilac*b.imenilac;
return c;
}
179 8. Funkcije

Na slian nain mogu se implementirati i druge operacije nad razlomcima,


kao mnoenje razlomaka, skraivanje razlomka, poreenje razlomaka, itd.
Kao i drugi tipovi argumenata, strukture se u funkcije prenose po vrednosti.
Tako, naredna funkcija ne moe da promeni razlomak koji je upotrebljen kao
argument.

void skrati(struct razlomak r) {


int n = nzd(r.brojilac, r.imenilac);

5)
r.brojilac /= n; r.imenilac /= n;
}

01
Naime, nakon poziva

(2
struct razlomak r = {2, 4};
skrati(r);

je
razlomak r ostaje jednak 2/4 (u funkciji se menja kopija originalne strukture
nastala prilikom prenosa po vrednosti).
an
8.9 Rekurzija
d

Funkcije mogu da pozivaju druge funkcije. Funkcija moe da pozove i sama


iz

sebe (u tom sluaju argumenti u pozivima su obino razliiti). Na primer,


funkcija
o

double stepen(double x, unsigned n) {


sk

if (n == 0)
return 1;
n

else
return x*stepen(x, n-1);
ro

}
kt

. Na primer, stepen(2.0, 3) = 2.0*stepen(2.0, 2)


izraunava vrednost
= 4.0*stepen(2.0, 1) = 8.0*stepen(2.0, 0) = 8.0*1 = 8.0.
le

Rekurzivna reenja esto su vremenski i memorijski zahtevna (jer esto za-


uzimaju mnogo prostora na steku poziva), ali zato esto daju krai i razumljiviji
E

izvorni kd. Rekurzija je veoma vana programerska tehnika i o njenoj upotrebi


e vie rei biti u drugom delu ove knjige.

8.10 Funkcije sa promenljivim brojem argumenata


Ponekad je korisno definisati funkcije koje u pozivu mogu da imaju promenljiv
broj argumenata. Primetimo da su takve funkcije printf i scanf a takve
mogu biti i korisniki definisane funkcije. Podrka za definisanje funkcija sa
promenljivim brojem argumenata data je kroz zaglavlje <stdarg.h> i tip po-
dataka va_list kojim se predstavlja lista argumenata prosleenih funkciji,
180 8. Funkcije

makro va_start kojim se zapoinje obrada takve liste argumenata, makro


va_arg koji uzima naredni argument iz liste i konvertuje ga u podatak eljenog
tipa i makro va_end kojim se zavrava obrada liste argumenata. U deklaraciji
funkcije sa promenljivim brojem argumenata navode se samo tri take.
Na primer, funkcija u narednom kodu izraunava i vraa zbir prosleenih
argumenata (osim prvog). Njen prvi parametar je broj argumenata koje treba
sabrati (prvi argument ne uestvuje u zbiru).

5)
#include <stdio.h>
#include <stdarg.h>

01
int sumof(int n_args, ...) {
int i;

(2
int sum;
va_list args;
va_start(args, n_args);

je
sum = 0;
for (i = 1; i <= n_args; i++)
an
sum += va_arg(args, int);
va_end(args);
d

return sum;
}
iz

int main() {
o

printf("%d\n", sumof(5, 1, 2, 3, 4, 5));


sk

return 0;
}
n
ro

Pitanja i zadaci za vebu


kt

Pitanje 8.1. ta je to deklaracija funkcije, a ta je to definicija funkcije? U


kojim sluajevima se koriste deklaracije?
le

Pitanje 8.2. ta je to parametar, a ta argument funkcije?


E

Pitanje 8.3. Koliko najmanje parametara moe da ima funkcija? Kako izgleda
deklaracija funkcije koja nema parametara? Koja je razlika izmeu deklaracija
void f(void); i void f();?
Pitanje 8.4. ta se navodi kao tip povratne vrednosti za funkciju koja ne
vraa rezultat? Da li takva funkcija mora da sadri naredbu return? Da li
takva funkcija moe da sadri naredbu return? ta se navodi kao argument
return naredbe?

Pitanje 8.5. Kako se prenose argumenti funkcija u C-u? Da li se u nekoj


situaciji prenos argumenata ne vri po vrednosti?
181 8. Funkcije

Pitanje 8.6. ta ispisuje sledei program?

int f(int x) {
x = x+2;
return x+4;
}

int main() {

5)
int x = 1, y = 2;
x = f(x+y);

01
printf("%d\n", x);
}

(2
Pitanje 8.7. ta ispisuje sledei program?

void f(int n,int k) {

je
n = 3 * k++;
}
an
int main() {
int n = 4, k = 5;
d

f(n, k);
iz

printf("%d %d\n", n, k);


}
o

Pitanje 8.8. ta ispisuje sledei program?


sk

void f(int x) {
x += (++x) + (x++);
n

}
ro

int main() {
int x = 2;
kt

f(x); f(x);
printf("%d\n", x);
}
le

Pitanje 8.9.
E

1. Koje su informacije od navedenih, tokom kompilacije, pridruene imenu


niza: (i) adresa poetka niza; (ii) tip elemenata niza; (iii) broj elemenata
niza; (iv) adresa kraja niza?

2. Koje se od ovih informacija uvaju u memoriji tokom izvravanja pro-


grama?

3. ta se sve od navedenog prenosi kada se ime niza navede kao argument


funkcije: (i) adresa poetka niza; (ii) elementi niza; (iii) podatak o broju
elemenata niza; (iv) adresa kraja niza.
182 8. Funkcije

Pitanje 8.10. ta ispisuje naredni program:

void f(int a[]) {


int i;
for(i = 0; i < sizeof(a)/sizeof(int); i++)
a[i] = a[i] + 1;
}

5)
int main() {
int i;

01
int a[] = {1, 2, 3, 4};
f(a);
for(i = 0; i < sizeof(a)/sizeof(int); i++)

(2
printf("%d ", a[i]);
}

je
Pitanje 8.11. Koja funkcija standardne biblioteke se koristi za izraunavanje
duine niske i u kojoj datoteci zaglavlja je ona deklarisana?
an
Pitanje 8.12. emu je jednako sizeof("abc"), a emu strlen("abc")?
d

Pitanje 8.13. Navesti neku implementaciju funkcije strlen.


iz

Pitanje 8.14. Navesti liniju kda koja obezbeuje da se prilikom poziva void
funkcije f celobrojni argument 2 implicitno prevede u double vrednost.
o

int main() {
sk

f(2);
return 0;
n

}
ro

Pitanje 8.15. Kako se vri prenos unija u funkciju?


kt

Zadatak 8.1. Napisati funkciju koja proverava da li je uneti pozitivan ceo broj
prost i program koji je testira. Uporediti sa reenjem koje sadri samo funkciju

le

main.
Zadatak 8.2.
E

Napisati program koji za tri take Dekartove ravni zadate paro-


vima svojih koordinata (tipa double) izraunava povrinu trougla koji obrazuju
(koristiti
Heronov obrazac, na osnovu kojeg je povrina trougla jednaka vred-
nosti ( )( )( ), gde su , i duine stranica, a njegov poluo-
bim; napisati i koristiti funkciju koja rauna rastojanje izmeu dve take Dekar-

tove ravni).

Zadatak 8.3. Za prirodan broj se kae da je savren ako je jednak zbiru svih
svojih pravih delioca (koji ukljuuju broj 1, ali ne i sam taj broj). Ispisati sve

savrene brojeve manje od 10000.
183 8. Funkcije

Zadatak 8.4. Napisati funkciju koja poredi dva razlomka i vraa 1 ako je prvi

vei, 0 ako su jednaki i -1 ako je prvi manji.

Zadatak 8.5. Napisati funkciju (i program koji je testira) koja:

1. proverava da li dati niz sadri dati broj;

2. pronalazi indeks prve pozicije na kojoj se u nizu nalazi dati broj (-1 ako
niz ne sadri broj).

5)
3. pronalazi indeks poslednje pozicije na kojoj se u nizu nalazi dati broj (-1
ako niz ne sadri broj).

01
4. izraunava zbir svih elemenata datog niza brojeva;

5. izraunava prosek (aritmetiku sredinu) svih elemenata datog niza bro-

(2
jeva;

6. izraunava najmanji element datog elemenata niza brojeva;

je
7. odreuje poziciju najveeg elementa u nizu brojeva (u sluaju vie pojavlji-
vanja najveeg elementa, vratiti najmanju poziciju);
an
8. proverava da li je dati niz brojeva ureen neopadajue.

d

Zadatak 8.6.
iz

Napisati funkciju (i program koji je testira) koja:

1. izbacuje poslednji element niza;


o

2. izbacuje prvi element niza (napisati varijantu u kojoj je bitno ouvanje


sk

redosleda elemenata i varijantu u kojoj nije bitno ouvanje redosleda);

3. izbacuje element sa date pozicije k;


n

4. ubacuje element na kraj niza;


ro

5. ubacuje element na poetak niza;

6. ubacuje dati element x na datu poziciju k;


kt

7. izbacuje sva pojavljivanja datog elementa x iz niza.


le

Napomena: funkcija kao argument prima niz i broj njegovih trenutno popun-
jenih elemenata, a vraa broj popunjenih elemenata nakon izvoenja zahtevane
E


operacije.

Zadatak 8.7. Napisati funkciju (i program koji je testira) koja:

1. odreuje duinu najdue serije jednakih uzastopnih elemenata u datom


nizu brojeva;

2. odreuje duinu najveeg neopadajueg podniza datog niza celih brojeva;

3. odreuje da li se jedan niz javlja kao podniz uzastopnih elemenata drugog;

4. odreuje da li se jedan niza javlja kao podniz elemenata drugog (elementi


ne moraju da budu uzastopni, ali se redosled pojavljivanja potuje);
184 8. Funkcije

5. obre dati niz brojeva;

6. rotira sve elemente datog niza brojeva za k pozicija ulevo;

7. rotira sve elemente datog niza brojeva za k pozicija udesno;

8. izbacuje viestruka pojavljivanja elemenata iz datog niza brojeva (napisati


varijantu u kojoj se zadrava prvo pojavljivanje i varijantu u kojoj se
zadrava poslednje pojavljivanje).

5)
9. spaja dva niza brojeva koji su sortirani neopadajui u trei niz brojeva
koji je sortiran neopadajui.

01
Zadatak 8.8. Napisati funkciju koja poredi dve niske leksikografski (kao u

(2
reniku, u skladu sa kdnim rasporedom sistema). Funkcija vraa negativan
rezultat ukoliko prva niska leksikografski prethodi drugoj, nulu u sluaju da su
niske jednake i pozitivan rezultat ukoliko druga niska leksikografski prethodi

je

prvoj.
an
Zadatak 8.9. Napisati funkciju koja za dve niske proverava da li je:

1. prva podniz druge (karakteri prve niske se ne moraju nalaziti na susednim


d

pozicijama u drugoj).
iz

2. prva podniska druge (karakteri prve niske se moraju nalaziti na susednim


pozicijama u drugomq). Ako jeste funkcija treba da vrati poziciju prve
1.
o

niske u drugoj, a ako nije, onda Napisati funkciju koja proverava da


li jedna zadata niska sadri drugu zadatu nisku.
sk

Zadatak 8.10. Napisati funkciju koja za dve niske proverava da li je jedan


ro

permutacija druge (niska je permutacija druge ako se od nje dobija samo pre-
metanjem njegovih karaktera - bez ikakvog brisanja ili dodavanja karaktera).
"abc" "cba" "aab"i
kt

Na primer, funkcija odredi da niske i jesu, a da niske


"ab" nisu permutacija.
le

Zadatak 8.11. Za datu kvadratnu matricu kaemo da je magini kvadrat ako


je suma elemenata u svakoj koloni i svakoj vrsti jednaka. Napisati program
E

koji sa standardnog ulaza uitava prirodni broj ( < 10) i zatim elemente
kvadratne matrice, proverava da li je ona magini kvadrat i ispisuje odgovara-
juu poruku na standardni izlaz. Koristiti pomone funkcije za izraunavanje
zbira elemenata vrsta, kolona i dijagonala matrice.
Primer, matrica:
7 12 1 14
2 13 8 11

16 3 10 5
9 6 15 4

je magini kvadrat.
185 8. Funkcije

Zadatak 8.12. Definisati strukturu kojom se moe predstaviti matrica dimen-


zija najvie 100100 (struktura sadri dvodimenzionalni niz brojeva tipa float
i dimenzije).

1. Napisati funkciju koja uitava matricu sa standardnog ulaza.

2. Napisati funkciju koja ispisuje matricu na standardni izlaz.

3. Napisati funkciju koja sabira dve matrice istih dimenzija.

5)
4. Napisati funkciju koja mnoi dve matrice odgovarajuih dimenzija.

01

Zadatak 8.13.

(2
Napisati funkcije koji vre unos, ispis, sabiranje i mnoenje
velikih prirodnih brojeva (za koje nije dovoljna veliina tipa unsigned long).
Cifre velikog broja smetati u niz i pretpostaviti da brojevi nemaju vie od1000

je
cifara.
an
d
iz
o
sk
n
ro
kt
le
E
Glava 9

5)
Organizacija izvornog i izvrnog

01
programa

(2
je
an
U prethodnim glavama ve je naglaeno da se izvorni program pie najee
na viim programskim jezicima i predstavlja sredstvo komunikacije izmeu pro-
d

gramera i raunara, ali i izmeu programera smih. Specijalizovani programi,


prevodioci (kompilatori), prevode izvorni u izvrni program , napisan na main-
iz

skom jeziku raunara, tj. na jeziku neposredno podranom arhitekturom pro-


cesora.
o

Postoji mnotvo pisanih pravila (tj. pravila koja proistiu iz standarda


sk

jezika koji se koristi) i nepisanih pravila (razne konvencije i obiaji) za or-


ganizovanje kda izvornog programa. Ta pravila olakavaju programiranje i
omoguuju programeru kreiranje razumljivih programa koji se efikasno mogu
n

prevoditi i izvravati. Svi programi u prethodnim poglavljima bili su jednos-


ro

tavni, imali su svega nekoliko funkcija i nije bilo potrebno previe obraati
panju na pomenuta pravila. Meutim, sa uslonjavanjem programa, poto-
kt

vanje kodeksa postaje kljuno jer u protivnom pisanje i odravanje kda postaje
1
veoma komplikovano . Takoe, postoje pravila za organizovanje kda izvrnog
programa koja omoguavaju njegovo efikasno izvravanje na odreenom rau-
le

naru, u sklopu odreenog operativnog sistema, to podrazumeva i nesmetano


E

izvravanje istovremeno sa drugim programima. Zato pravila organizacije kda


izvrnog programa zavise i od vieg programskog jezika koji se koristi, ali i od
hardvera, mainskog jezika, kao i operativnog sistema raunara na kome e se
program izvravati.
I u fazi prevoenja i u fazi izvravanja mogu se javiti greke i one moraju
biti ispravljene da bi program funkcionisao ispravno. Greke tokom izvravanja
mogu biti takve da program ne moe dalje da nastavi sa izvravanjem (npr. ako
program pokua da pristupi delu memorije koji mu nije dodeljen, operativni

1 Postoji izreka da dobre od loih programera vie razlikuje disciplina pri programiranju
nego pamet.

186
187 9. Organizacija izvornog i izvrnog programa

sistem nasilno prekida njegovo izvravanje), ali mogu da budu takve da se pro-
gram nesmetano izvrava, ali da su rezultati koje prikazuje pogreni. Dakle, to
to ne postoje greke u fazi prevoenja i to to se program nesmetano izvrava,
jo uvek ne znai da je program ispravan, tj. da zadovoljava svoju specifikaciju
i daje tane rezultate za sve vrednosti ulaznih parametara. Ispravnost pro-
grama u tom, dubljem smislu zahteva formalnu analizu i ispitivanje te vrste
ispravnosti najee je van moi automatskih alata. Ipak, sredstva za pri-
javljivanje greaka tokom prevoenja i izvravanja programa znatno olakavaju

5)
proces programiranja i esto ukazuju i na sutinske propuste koji naruavaju
ispravnost programa.

01
9.1 Od izvornog do izvrnog programa

(2
Iako proces prevoenja jednostavnih programa poetnicima izgleda kao
jedan, nedeljiv proces, on se sastoji od vie faza i podfaza (a od kojih se svaka

je
i sama sastoji od vie koraka). U sluaju jezika C, ti koraci su obino pretpro-
cesiranje (engl. preprocessing), kompilacija (engl. compilation) i povezivanje
an
(engl. linking). Tokom svih faza prevoenja, moe biti otkrivena i prijavljena
greka u programu. U izvetajima o grekama obino se ne navodi eksplicitno
u kojoj fazi je greka detektovana (mada se ta informacija moe rekonstruisati
d

na osnovu vrste greke).


iz

Pretprocesiranje. Faza pretprocesiranja je pripremna faza kompilacije. Ona


o

omoguava da se izvre neke jednostavne transformacije izvornog teksta pro-


sk

grama pre nego to on bude prosleen kompilatoru kompilator, dakle, ne


obrauje tekst programa koji je programer napisao, ve samo tekst koji je nas-
tao njegovim pretprocesiranjem. Jedan od najvanijih zadataka pretprocesora
n

je da omogui da se izvorni kd pogodno organizuje u vie ulaznih datoteka.


ro

Pretprocesor izvorni kd iz razliitih datoteka objedinjava u tzv. jedinice pre-


voenja i prosleuje ih kompilatoru. Na primer, u .c datoteke koje sadre
.h
kt

izvorni kd ukljuuju se datoteke zaglavlja (engl. header files) koje sadre


deklaracije promenljivih, funkcija i tipova podataka tako da svaka jedinica pre-
voenja sadri zajedniki tekst nekoliko .h (na primer, ve pomenuta zaglavlja
le

standardne biblioteke) i jedne .c datoteke.


E

Program koji vri pretprocesiranje naziva se pretprocesor (engl. preproces-


sor). Rezultat rada pretprocesora, moe se dobiti korienje GCC prevodioca
navoenjem opcije -E (na primer, gcc -E program.c).
Vie rei o fazi pretprocesiranja bie u poglavlju 9.2.1.

Kompilacija. Kompilator kompilira (tj. prevodi) svaku jedinicu prevoenja


zasebno, sprovoenjem sledeih faza (videti poglavlje 4.3):

leksika analiza izdvajanje leksema , osnovnih jezikih elemenata;

sintaksika analiza kreiranje sintaksnog stabla;


188 9. Organizacija izvornog i izvrnog programa

semantika analiza provera semantike i transformacija kreiranog sta-


bla;

generisanje meukda generisanje kda na jeziku interne reprezentacije;

optimizacija meukda optimizovanje generisanog kda;

generisanje kda na mainskom jeziku prevoenje optimizovanog kda


u objektne module.

5)
Kompilacijom se od svake jedinice prevoenja gradi zasebni objektni modul

01
2
(engl. object module) . Objektni moduli sadre programe (mainski kd funkcija)
i podatke (memorijski prostor rezervisan za promenljive). Iako su u mainskom

(2
obliku, objektni moduli se ne mogu izvravati.
Na primer, rezultat rada GCC kompilatora moe se dobiti navoenjem op-
cije -c (na primer, gcc -c program.c). Ovim se dobija datoteka program.o

je
objektni modul koji je na Linux sistemu u ELF formatu (engl. Executable
and Linkable Format), formatu u kome je i izvrna datoteka, pri emu objek-
an
tni modul nije izvran (kae se da je relokatibilan jer je u njemu tek potrebno
razreiti memorijske adrese) i mora se povezati da bi se dobila izvrna datoteka.
Mnoge dodatne opcije utiu na rezultat kompilacije. Na primer, ukoliko se
d

navede opcija gcc -O3, vri se najopsenija optimizacija kda to uzrokuje da


-Wall
iz

se rezultujui kd (esto znatno) bre izvrava. Ukoliko se navede opcija


navode se sva upozorenja na mogue greke tokom kompilacije.
o

Povezivanje. Povezivanje je proces kreiranja jedinstvene izvrne datoteke


sk

od jednog ili vie objektnih modula koji su nastali ili kompilacijom izvornog
kda programa ili su objektni moduli koji sadre mainski kd i podatke stan-
3
n

dardne ili neke nestandardne biblioteke . Program koji vri povezivanje zove
se poveziva , linker (engl. linker) ili ureiva veza.
ro

Nakon kompilacije, u objektnim modulima adrese mainskog kda funkcija


i adrese nekih promenljivih nisu razreene (moe se smatrati da su prazne jer su
kt

umesto stvarnih postavljene specijalne, fiktivne adrese esto vrednost 0) i tek


tokom povezivanja vri se njihovo korektno razreavanje (zamena ispravnim
le

vrednostima). Zato je faza povezivanja neophodna, ak iako jedan objektni


modul sadri sve promenljive i funkcije koje se koriste u programu (to najee
E

nije sluaj jer se koriste funkcije standardne biblioteke iji se izvrni kd ne


nalazi u objektnim modulima nastalim kompilacijom izvornog kda).
Kao to je ve reeno, pored objektnih modula nastalih kompilacijom izvor-
nog kda koje je programer napisao, izvrnom programu se moe dodati un-
apred pripremljen mainski kd nekih biblioteka. Taj kd je mogao nastati

2 Objektni moduli na sistemu Linux najee imaju ekstenziju .o, a na sistemu Windows
.obj.
3 Biblioteke na sistemu Linux najee imaju ekstenziju .a (jer su arhive koje sadre
nekoliko .o datoteka), dok na sistemu Windows najee imaju ekstenziju .lib.
189 9. Organizacija izvornog i izvrnog programa

prevoenjem sa jezika C, ali to ne mora biti sluaj. Na primer, neke bib-


lioteke su pisane u asembleru, a neke su nastale prevoenjem sa drugih viih
programskih jezika. Mehanizam povezivanja, dakle, doputa i kombinovanje
kda napisanog na razliitim programskim jezicima. Mehanizam povezivanja
bitno skrauje trajanje kompilacije jer se kompilira samo tanak sloj korisnikog
izvornog kda dok se kompleksan kd biblioteka koristi unapred kompiliran.
Takve biblioteke uvek su praene datotekama zaglavlja koje slue kompilatoru
da proveri i ispravno prevede kd u kojem se poziva na funkcionalnost bib-

5)
lioteke.
Najei primer korienja unapred pripremljenog mainskog kda sa prate-

01
im datotekama zaglavlja je korienje standardne biblioteke (pregled sadraja
standardne biblioteke bie dat u glavi 11). Kd standardne biblioteke je obino

(2
dostavljen uz prevodilac i dat je samo u obliku mainskog, a ne izvornog kda
(na primer, obino nije mogue videti izvorni kd funkcije printf). Deo stan-
dardne biblioteke su datoteke zaglavlja koje sadre samo potrebne deklaracije

je
(na primer, deklaracija funkcije printf moe se pronai u zaglavlju <stdio.h>).
Pored statikog povezivanja , koje se vri nakon kompilacije, postoji i di-
an
namiko povezivanje , koje se vri tokom izvravanja programa (zapravo na nje-
govom poetku). Naime, statiko povezivanje u izvrnu datoteku umee main-
ski kd svih bibliotekih funkcija. Da bi se smanjila veliina izvrnih datoteka,
d

mainski kd nekih esto korienih funkcija se ne ukljuuje u izvrnu datoteku


iz

programa ve postoji u tzv. bibliotekama koje se dinamiki povezuju (engl. dy-


4
namic link library) . Izvrne datoteke sadre nerazreene pozive funkcija koje
se dinamiki povezuju i tek prilikom pokretanja programa, dinamike biblioteke
o

se zajedno sa programom uitavaju u memoriju raunara i adrese poziva bib-


sk

liotekih funkcija se razreavaju. Zato, prilikom izvravanja programa bib-


lioteke sa dinamikim povezivanjem moraju biti prisutne na sistemu. Na primer,
n

funkcije standardne biblioteke intenzivno pozivaju funkcije rantajm biblioteke


ro

(o tome je bilo rei na strani 86) i razreavanje takvih poziva vri se dinamiki,
u fazi izvravanja programa. Jo jedna prednost dinamikog povezivanja bib-
liotekih funkcija je da se nove verzije biblioteka (esto sa ispravljenim sitnijim
kt

grekama) mogu koristiti bez potrebe za izmenom programa koji te biblioteke


koristi.
le

Proces povezivanja obino se automatski pokree odmah nakon kompilacije.


Na primer, ako se koristi GCC prevodilac, prevoenje programa od jedne da-
E

toteke i povezivanje sa objektnim modulima standardne biblioteke se postie


na sledei nain:

gcc -o program program.c

Ovim se dobija izvrni program program (u ELF formatu).


U procesu povezivanja, automatski se ukljuuju potrebni objektni moduli
standardne biblioteke (na operativnom sistemu Linux ovaj modul se obino zove

4 Ekstenzija ovih datoteka u sistemu Linux je obino .so (shared object), dok je u sistemu
Windows .dll.
190 9. Organizacija izvornog i izvrnog programa

libc ili slino). Dodatne biblioteke mogu se ukljuiti navoenjem parametra


-l. Na primer,
gcc -o program -lm program.c
u proces povezivanja ukljuuje i standardnu biblioteku libm koja nije po-
drazumevano obuhvaena povezivanjem.

5)
Odvojena kompilacija i povezivanje. Korak kompilacije i korak povezi-
vanja mogue je razdvojiti tj. mogue je prvo prevesti datoteku sa izvornim

01
programom i eksplicitno napraviti objektni modul (u ovom sluaju bi se zvao
program.o), a tek zatim ovako napravljeni objektni modul povezati (ukljuu-
jui i objektni kd standardne biblioteke) i dobiti izvrni program. U sluaju

(2
da se koristi GCC, to se postie sa:

gcc -c program.c

je
gcc -o program program.o
-c govori GCC prevodiocu da ne treba da vri
an
Kao to je ve reeno, parametar
povezivanje ve samo kompilaciju i da rezultat eksplicitno sauva kao objek-
tni modul u datoteci program.o. Drugi poziv vri povezivanje tog objektnog
d

modula i daje izvrni program program.


iz

Program se moe izgraditi i iz vie jedinica prevoenja. Na primer, ako


je izvorni kd programa razdvojen u dve datoteke program1.c i program2.c,
izvrni program mogue je dobiti komandom:
o

gcc -o program program1.c program2.c


sk

Naravno, svaka datoteka se odvojeno prevodi i tek onda se vri povezivanje.


n

Dakle, efekat je isti kao da je pozvano


ro

gcc -c program1.c
gcc -c program2.c
kt

gcc -o program program1.o program2.o


Razdvajanjem kompilacije i povezivanja u dve faze omogueno je da se mod-
le

uli koji se ne menjaju ne prevode iznova svaki put kada je potrebno napraviti
E

novu verziju izvrnog program (kada se promeni neka izvorna datoteka). Na


primer, ako je promena izvrena samo u datoteci program1.c, nije potrebno
ponovo prevoditi datoteku program2.c.

Program make. Program make (prvu verziju napisao je Sjuart Feldman,


1977. godine) olakava generisanje izvrnih programa od izvornih datoteka.
Iako postoje varijante za razne platforme, program make najee se koristi na
operativnom sistemu Linux. Alternativa (ili nadogradnja) programa make su
savremena integrisana razvojna okruenja (engl. integrated development en-
vironment, IDE) u kojima programer na interaktivni nain specifikuje proces
kompilacije.
191 9. Organizacija izvornog i izvrnog programa

Prilikom korienja programa make, korisnik u datoteci koja obino ima


ime Makefile specifikuje nain dobijanja novih datoteka od starih. Speci-
fikuju se zavisnosti izmeu datoteka i akcije koje se izvravaju ukoliko su te
zavisnosti naruene (linije koje sadre akcije moraju da ponu tabulatorom, a
ne razmacima). Nakon pokretanja programa make, ukoliko se desi da je neka
zavisnost naruena, izvrava se odgovarajua akcija i rekurzivno se nastavlja sa
proverom zavisnosti, sve dok se sve zavisnosti ne ispune. Na primer, datoteka
Makefile sledeeg sadraja

5)
program: program1.o program2.o biblio.o

01
gcc -o program program1.o program2.o biblio.o

program1.o : program1.c biblio.h

(2
gcc -c program1.c

program2.o : program2.c biblio.h


gcc -c program2.c
je
an
kae da datoteka program zavisi od sledeih datoteka: program1.o, program2.o
i biblio.o. Ukoliko je zavisnost naruena (to znai, ukoliko je bilo koja od
d

datoteka program1.o, program2.o ili biblio.o novijeg datuma od datoteke


program), izvrava se povezivanje. Slino, ako je datoteka program1.c ili
iz

biblio.h novijeg datuma o program1.o koji od njih zavisi (verovatno tako


to datoteka program1.c ukljuuje zaglavlje biblio.h), vri se njena kompi-
o

lacija. Analogno je i za program2.


sk

Program make ima jo mnogo opcija ali one nee ovde biti opisivane.
n

Izvravanje programa. Nakon uspenog prevoenja moe da sledi faza


ro

izvravanja programa.
Izvrni programi smeteni su u datotekama na disku i pre pokretanja uita-
vaju se u glavnu memoriju. Za ovo je zaduen program koji se naziva punilac
kt

ili louder (engl. loader) koji je obino integrisan u operativni sistem. Njegov
zadatak je da proveri da li korisnik ima pravo da izvri program, da prenese
le

program u glavnu memoriju, da prekopira argumente komandne linije (o kojima


e vie biti rei u poglavlju 12.4) u odgovarajui deo memorije programa koji
E

se pokree, da inicijalizuju odreene registre u procesoru i na kraju da pozovu


poetnu funkciju programa. Nasuprot oekivanju, to nije funkcija main, ve
funkcija _start koja poziva funkciju main, ali pre toga i nakon toga vri do-
datne pripremne i zavrne operacije sa programom, korienjem usluga rantajm
biblioteke tj. operativnog sistema.
Pre konane distribucije korisnicima, program se obino intenzivno testira
tj. izvrava za razliite vrednosti ulaznih parametara. Ukoliko se otkrije da
postoji greka tokom izvravanja (tzv. bag od engl. bug), vri se pronalaenje
uzroka greke u izvornom programu (tzv. debagovanje ) i njeno ispravljanje. U
pronalaenju greke mogu pomoi programi koji se nazivaju debageri i koji
192 9. Organizacija izvornog i izvrnog programa

omoguavaju izvravanje programa korak po korak (ili pauziranje njegovog


izvravanja u nekim karakteristinim takama), uz prikaz meuvrednosti pro-
menljivih. Da bi program mogao da bude analiziran primenom debagera, on
mora biti preveden na poseban nain, tako da izvrna verzija sadri i informacije
o izvornom kdu. Na primer, za prevodilac GCC potrebno je koristiti opciju
-g. Takva verzija izvrnog programa zove se razvojna (debag) verzija, a verzija
koja se isporuuje krajnjem korisniku zove se objavljena (riliz) (engl. release).

5)
Pitanja i zadaci za vebu

01
Pitanje 9.1.1. Navesti i objasniti osnovne faze na putu od izvornog do izvrnog
programa.

(2
Pitanje 9.1.2. U kom obliku se isporuuje standardna C biblioteka?

Pitanje 9.1.3. Kako se korienjem GCC prevodioca moe izvriti samo faza

je
pretprocesiranja kda i prikazati rezultat? Kako se moe izvriti samo faza
kompilacije, bez povezivanja objektnih modula?
an
Pitanje 9.1.4. ta su jedinice prevoenja? ta su datoteke zaglavlja? U kojoj
fazi se od vie datoteka grade jedinice prevoenja?
d

Pitanje 9.1.5.
iz

ta su objektni moduli? ta sadre? Da li se mogu izvravati?


Kojim procesom nastaju objektni moduli? Kojim procesom se od objektnih mod-
ula dobija izvrni program?
o

Pitanje 9.1.6.
sk

Da li svi objektni moduli koji se ukljuuju u kreiranje izvrnog


programa moraju nastati kompilacijom sa programskog jezika C? Da li je mogue
kombinovati razliite programske jezike u izgradnji izvrnih programa?
n

Pitanje 9.1.7.
ro

ta znai da u objektnim modulima pre povezivanja adrese nisu


korektno razreene?
kt

Pitanje 9.1.8. U kom formatu su objektni moduli i izvrne datoteke na Linux


sistemima?
le

Pitanje 9.1.9. ta je statiko, a ta dinamiko povezivanje? Koje su prednosti


E

dinamikog u odnosu na statiko povezivanje?

Pitanje 9.1.10. U kojim fazama prevoenja programa se vri prijavljivanje


greaka?

Pitanje 9.1.11. Kako se korienjem GCC prevodioca vri odvojena kompi-


lacija i povezivanje programa u datotekama p1.c i p2.c.
Pitanje 9.1.12. ta je i emu slui program make?
Pitanje 9.1.13. Kako se zove alat koji omoguava da se program izvrava
korak-po-korak i da se prate vrednosti promenljivih?
193 9. Organizacija izvornog i izvrnog programa

9.2 Organizacija izvornog programa


Vii programski jezici namenjeni su prvenstveno oveku (a ne raunaru).
Obino je daleko lake neki algoritam pretoiti u program na viem program-
skom jeziku nego u program na mainskom jeziku. Isto vai i za razumevanje
programa koje je napisao neko drugi. Ipak, pisanje, razumevanje i odravanje
veoma dugih programa moe da predstavlja veliki izazov za programera, ak
i kada je program napisan na viem programskom jeziku. Da bi se olakalo

5)
pisanje, razumevanje i odravanje programa, za jezik C (kao i za svaki drugi
vii programski jezik), programeru su na raspolaganju mnoga sredstva koja

01
omoguavaju da program bude krai, pregledniji, da se efikasnije kompilira, da
se bre izvrava itd.

(2
Osnovni koraci u organizovanju sloenijih programa obino su podela slo-
enih zadataka na jednostavnije poslove i njihovo izdvajanje u zasebne funkcije
(tzv. funkcionalna dekompozicija ), definisanje odgovarajuih tipova podataka

je
i organizovanje podataka definisanjem odgovarajuih promenljivih. Funkcije u
programu trebalo bi da budu to nezavisnije i to slabije meusobno uslovljene
an
(loe je ako je za razumevanje rada neke funkcije potrebno potpuno razumeti
tano kako radi neka druga funkcija). Veliki programi se organizuju u vie
datoteka tj. modula koji sadre podatke i funkcije koji objedinjavaju odreenu
d

funkcionalnost (npr. definiciju tipa podataka za predstavljanje kompleksnih


iz

brojeva i funkcije za izvoenje operacija nad tim tipom ima smisla grupisati u
zaseban modul za rad sa kompleksnim brojevima). Mnogi moduli mogu biti
korieni u razliitim programima i tada se obino grade kao biblioteke.
o

Opisani pristup programiranju omoguavaju i olakavaju razliiti meha-


sk

nizmi jezika C. Na primer, pretprocesor, izmeu ostalog, olakava korienje


standardne, ali i korisniki definisanih biblioteka. Pretprocesor sa linkerom
n

omoguava da se program podeli na vie datoteka, tako da se te datoteke


ro

mogu spojiti u jedinstven izvrni program. Doseg identifikatora (engl. scope)


odreuje da li se neka imena mogu koristiti u itavim jedinicama prevoenja ili
samo u njihovim manjim delovima (najee funkcijama ili jo uim blokovima).
kt

Postojanje promenljivih ija je upotreba ograniena na samo odreene uske


delove izvornog kda olakava razumevanje programa i smanjuje mogunost
le

greaka i smanjuje meusobnu zavisnost izmeu raznih delova programa. iv-


otni vek promenljivih (engl. lifetime) odreuje da li je neka promenljiva dos-
E

tupna tokom itavog izvravanja programa ili samo tokom nekog njegovog dela.
Postojanje promenljivih koje traju samo pojedinano izvravanje neke funkcije
znatno tedi memoriju, dok postojanje promenljivih koje traju itavo izvra-
vanje programa omoguava da se preko njih vri komunikacija izmeu razliitih
funkcija modula. Povezanost identifikatora (engl. linkage) u vezi je sa deljen-
jem podataka izmeu razliitih jedinica prevoenja i daje mogunost korienja
zajednikih promenljivih i funkcija u razliitim jedinicama prevoenja (mod-
ulima), ali i mogunost sakrivanja nekih promenljivih tako da im se ne moe
pristupiti iz drugih jedinica prevoenja. Odnos izmeu dosega, ivotnog veka
i povezanosti je veoma suptilan i sva ova tri aspekta objekata odreuju se na
194 9. Organizacija izvornog i izvrnog programa

osnovu mesta i naina deklarisanja tj. definisanja objekata, ali i primenom kval-
ifikatora auto, register, static i extern o emu e biti vie rei u nastavku
ovog poglavlja.
Sva ova sredstva donose potencijalna olakanja u proces programiranja, ali
sva stvaraju i mogunosti raznih greaka ukoliko se ne koriste na ispravan nain.

9.2.1 Pretprocesor

5)
Kada se od izvornog programa napisanog na jeziku C proizvodi program
na mainskom jeziku (izvrni program), pre samog prevodioca poziva se C

01
pretprocesor. Pretprocesiranje, dakle, predstavlja samo pripremnu fazu, pre
kompilacije. Sutinski, pretprocesor vri samo jednostavne operacije nad tek-

(2
stualnim sadrajem programa i ne koristi nikakvo znanje o jeziku C. Pretpro-
cesor ne analizira znaenje naredbi napisanih u jeziku C ve samo pretpro-
cesorske direktive (engl. preprocessing directive) (one se ne smatraju delom

je
jezika C) na osnovu kojih vri odreene transformacije teksta izvornog pro-
grama. Neke od operacija pretprocesora su zamena komentara belinama ili
an
spajanje linija razdvojenih u izvornom programu simbolom \. Dve najee
koriene pretprocesorske direktive su #include (za ukljuivanje sadraja neke
druge datoteke) i #define koja zamenjuje neki tekst, makro, drugim tekstom.
d

Pretprocesor omoguava i definisanje makroa sa argumentima, kao i uslovno


iz

prevoenje (odreeni delovi izvornog programa se prevode samo ukoliko su is-


punjeni zadati uslovi). Da bi mogao da izvri svoje zadatke, tokom izvravanja
pretprocesor izvrava odreenu jednostavniju leksiku analizu (tokenizaciju) na
o

koju se nadovezuje kasnija faza leksike analize, koja se vri tokom kompilacije.
sk

Ukljuivanje datoteka zaglavlja


n

Veliki programi obino su podeljeni u vie datoteka, radi preglednosti i


ro

lakeg odravanja. esto je potrebno da se iste promenljive, funkcije i koris-


niki definisani tipovi koriste u vie datoteka i neophodno je da kompilator
kt

prilikom prevoenja svake od njih poznaje deklaracije


5 promenljivih, funkcija
i tipova podataka koji se u njoj koriste. ak i kada programer pie samo jednu
le

datoteku, program po pravilu koristi i funkcionalnost koju mu prua stan-


dardna biblioteka i da bi ona mogla da se koristi, neophodno je da kompilator
E

tokom kompilacije zna deklaracije standardnih funkcija (npr. funkcije printf),


promenljivih i tipova. Jedno reenje bilo bi da programer u svakoj datoteci,
na poetku navede sve potrebne deklaracije, ali ponavljanje istih deklaracija
na vie mesta u izvornom kdu je mukotrpan posao i otvara prostor za greke.
Bolje reenje (koja se obino i koristi) je da se deklaracije izdvajaju u zasebne

5 Podsetimo, deklaracije sadre informacije koje su potrebne kompilatoru da prevede


odreenu datoteku i tako generie objektni modul (npr. prototip funkcije je deklaracija),
dok definicije sadre mnogo vie informacija informacije koje su dovoljne da nakon faze
povezivanja nastane izvrni program (npr. ceo kd funkcije predstavlja njenu definiciju).
Kompilator ne mora da poznaje definiciju funkcije da bi generisao njen poziv, ali linker
mora, da bi mogao da taj poziv razrei.
195 9. Organizacija izvornog i izvrnog programa

datoteke koje se onda ukljuuju gde god je potrebno. Takve datoteke zovu se
datoteke zaglavlja (engl. header files). Osnovni primer datoteka zaglavlja su za-
glavlja standardne biblioteke. Primer kreiranja korisniki definisanih datoteka
zaglavlja bie naveden u poglavlju 9.2.6. U datoteku koja se prevodi, sadraj
neke druge datoteke ukljuuje se direktivom #include. Linija oblika:

#include "ime_datoteke"

5)
i linija oblika

#include <ime_datoteke>

01
zamenjuju se sadrajem datoteke ime_datoteke. U prvom sluaju, datoteka

(2
koja se ukljuuje trai se u okviru posebnog skupa direktorijuma include path
(koja se veini kompilatora zadaje korienjem opcije -I) i koja obino po-
drazumevano sadri direktorijum u kojem se nalazi datoteka u koju se vri

je
ukljuivanje. Ukoliko je ime, kao u drugom sluaju, navedeno izmeu znakova
< i >, datoteka se trai u sistemskom include direktorijumu u kojem se nalaze
an
standardne datoteke zaglavlja, ija lokacija zavisi od sistema i od C prevodioca
koji se koristi.
d

Ukoliko se promeni ukljuena datoteka, sve datoteke koje zavise od nje


moraju biti iznova prevedene. Datoteka koja se ukljuuje i sama moe sadrati
iz

direktive #include.
Poto, zahvaljujui #include direktivi, do kompilatora stie program koji
o

ne postoji fiziki u jednoj datoteci, ve je kombinacija teksta koji se nalazi


u razliitim datotekama, kae se da se program sastoji od razliitih jedinica
sk

prevoenja (a ne od vie datoteka). Jedinice prevoenja imaju mnogo direk-


tniju vezu sa objektnim modulima, povezivanjem i izvrnim programom od
n

pojedinanih fizikih datoteka koje programer kreira.


ro

Makro zamene
kt

Pretprocesorska direktiva #define omoguava zamenjivanje niza karaktera


u datoteci, makroa (engl. macro) drugim nizom karaktera pre samog pre-
le

voenja. Njen opti oblik je:


E

#define originalni_tekst novi_tekst

U najjednostavnijem obliku, ova direktiva koristi se za zadavanje vrednosti


nekom simbolikom imenu, na primer:

#define MAX_LEN 80

Ovakva definicija se koristi da bi se izbeglo navoenje iste konstantne vred-


nosti na puno mesta u programu. Umesto toga, koristi se simboliko ime
koje se moe lako promeniti izmenom na samo jednom mestu. U nave-
denom primeru, MAX_LEN je samo simboliko ime i nikako ga ne treba meati sa
196 9. Organizacija izvornog i izvrnog programa

promenljivom (ak ni sa promenljivom koja je const). Naime, za ime MAX_LEN


u memoriji se ne rezervie prostor tokom izvravanja programa, ve se svako
njeno pojavljivanje, pre samog prevoenja zamenjuju zadatom vrednou (u
navedenom primeru vrednou 80). Tako MAX_LEN nije vidljiva ni kompila-
toru, ni linkeru, niti u bilo kom obliku postoji u izvrnom programu.
Makro moe biti bilo koji identifikator, pa ak i kljuna re jezika C. Tekst
zamene (novi_tekst u navedenom optem obliku) je tekst do kraja reda, a
mogue je da se prostire na vie redova ako se navede simbol \ na kraju svakog

5)
reda koji se nastavlja. Direktive zamene mogu da koriste direktive koje im
prethode.

01
Zamene se ne vre u konstantnim niskama niti u okviru drugih simbolikih
6 #define MAX_LEN 80 nee uticati
imen a . Na primer, gore navedena direktiva

(2
na naredbu printf( "MAX_LEN is 80"); niti na simboliko ime MAX_LEN_VAL.
Mogue je defisati i pravila zamene sa argumentima od kojih zavisi tekst
zamene. Na primer, sledea definicija

je
#define max(A, B) ((A) > (B) ? (A) : (B))
an
max(A, B) koji zavisi od argumenata. Ukoliko je u nas-
definie tekst zamene za
tavku datoteke, u nekoj naredbi navedeno max(2, 3), to e, pre prevoenja,
d

biti zamenjeno sa ((2) > (3) ? (2) : (3)). Slino, tekst max(x+2, 3*y)
bie zamenjen tekstom ((x+2) > (3*y) ? (x+2) : (3*y)). Tekst max(2, 3)
iz

i slini ne predstavljaju poziv funkcije i nema nikakvog prenosa argumenata kao


kod pozivanja funkcija. Postoje i druge razlike u odnosu na poziv funkcije. Na
o

primer, ukoliko je negde u programu navedeno max(a++, b++), na osnovu date


sk

definicije, ovaj tekst bie zamenjen tekstom


((a++) > (b++) ? (a++) : (b++)),
a i b inkrementira dva puta (to
n

to e dovesti do toga da se vea od vrednosti


moda nije planirano).
ro

Vano je voditi rauna i o zagradama u tekstu zamene, da bi bio ouvan


poredak primene operacija. Na primer, ukoliko se definicija
kt

#define kvadrat(x) x*x


le

primeni na kvadrat(a+2), tekst zamene e biti a+2*a+2, a ne (a+2)*(a+2),


E

kao to je verovatno eljeno i zbog toga bi trebalo koristiti:

#define kvadrat(x) (x)*(x)

Navedena definicija, meutim, i dalje ne daje ponaanje koje je verovatno


eljeno. Naime, kada se makro primeni na izraz a/kvadrat(b), on e biti
zamenjen izrazom a/(b)*(b), to je ekvivalentno sa (a/b)*b (a ne sa sa
a/(b*b)). Zbog toga je bolja definicija:

6 Zato je neophodno da pretprocesor tokom svog rada izvri jednostavniju leksiku analizu.
197 9. Organizacija izvornog i izvrnog programa

#define kvadrat(x) ((x)*(x))

Tekst zamene moe da sadri itave blokove sa deklaracijama, kao u sledeem


primeru:

#define swap(t, x, y) { t z; z=x; x=y; y=z; }

koji definie zamenjivanje vrednosti dve promenljive tipa t. Celobrojnim pro-

5)
menljivamaa i b se, zahvaljujui ovom makrou, mogu razmeniti vrednosti sa
swap(int, a, b).

01
Ukoliko se u direktivi #define, u novom tekstu, ispred imena parametra
navede simbol #, kombinacija e biti zamenjena vrednou parametra nave-

(2
denog izmeu dvostrukih navodnika. Ovo moe da se kombinuje sa nadovezi-
vanjem niski. Na primer, naredni makro moe da poslui za otkrivanje greaka:

#define dprint(expr) printf(#expr " = %g\n", expr)

je
an
Tako e tekst:

dprint(x/y);
d

unutar nekog programa biti zamenjen tekstom:


iz

printf("x/y" " = %g\n", x/y);


o

Poto se niske automatski nadovezuju, efekat ove naredbe je isti kao efekat
sk

naredbe:

printf("x/y = %g\n", x/y);


n
ro

esta je potreba da se prilikom pretprocesiranja dve niske nadoveu da bi


se izgradio sloeni identifikator. Ovo se postie petprocesorskim operatorom
kt

##. Na primer,

#define dodaj_u_niz(ime, element) \


le

niz_##ime[brojac_##ime++] = element
E

Poziv

dodaj_u_niz(a, 3);

se tada zamenjuje naredbom

niz_a[brojac_a++] = 3;

Dejstvo primene direktive zamene je od mesta na kojem se nalazi do kraja


datoteke ili do reda oblika:
198 9. Organizacija izvornog i izvrnog programa

#undef originalni_tekst

Kao to je reeno, makro zamene i funkcije se, iako mogu izgledati slino,
sutinski razlikuju. Kod makro zamena nema provere tipova argumenata niti
implicitnih konverzija to moe da dovodi do greaka u kompilaciji ili do neoeki-
vanog rada programa. Argument makroa u zamenjenoj verziji moe biti nave-
den vie puta to moe da utie na izvravanje programa, a izostavljanje zagrada

5)
u tekstu zamene moe da utie na redosled izraunavanja operacija. S druge
strane, kod poziva makroa nema prenosa argumenata te se oni izvravaju bre

01
nego odgovarajue funkcije. Ipak, moderni kompilatori kratke funkcije obino
umeu (engl. inline) itavo telo funkcije na mesto poziva, starajui se o argu-
mentima i tada nema gubitaka u vremenskoj ili prostornoj efikasnosti u odnosu

(2
na korienje makroa. Sve u svemu, makro zamene sa argumentima mogu imaju
i dobre i loe strane i treba ih koristiti oprezno.
Mnoge standardne funkcije za ulaz i izlaz iz standardne C biblioteke mogu

je
biti, alternativno, implementirane i kao makroi (na primer, getchar koji koristi
funkciju getc, printf koji koristi funkciju fprintf, itd.).
an
Uslovno prevoenje
d

Pretprocesorskim direktivama je mogue iskljuiti delove kda iz procesa


iz

prevoenja, u zavisnosti od vrednosti uslova koji se rauna u fazi pretprocesir-


anja. Direktiva #if izraunava vrednost konstantnog celobrojnog izraza (koji
moe, na primer, da sadri konstante i simbolika imena definisana direktivom
o

#define). Ukoliko je dobijena vrednost jednaka nuli, onda se ignoriu (ne


sk

prevode) sve linije programa do direktive #endif ili do direktive #else ili do
#elif koja ima znaenje kao else-if. U okviru argumenta direktive
direktive
n

#if, moe se koristiti izraz defined(ime) koji ima vrednost 1 ako je simboliko
ime ime definisano nekom prethodnom direktivom, a 0 inae. Krai zapis za
ro

#if defined(ime) je #ifdef ime, a krai zapis za #if !defined(ime) je


#ifndef ime. Na primer, sledei program:
kt

Program 9.1.
le

#define SRPSKI
#include <stdio.h>
E

int main()
{
#ifdef SRPSKI
printf("Zdravo, svete");
#else
printf("Hello, world");
#endif
199 9. Organizacija izvornog i izvrnog programa

return 0;
}
ispisuje tekst na srpskom jeziku, a izostavljanjem direktive iz prvog reda ispi-
sivao bi tekst na engleskom jeziku.
7 Ovo grananje se sutinski razlikuje od
grananja zasnovanog na C naredbi if. Naime, u navedenom primeru prevodi
se samo jedna od dve verzije programa i dobijeni izvrni program nema nikakvu
informaciju o drugoj verziji. Za razliku od toga, kada se koristi grananje koje

5)
koristi C jezik (a ne pretprocesor), program sadri kd za sve mogue grane.

01
9.2.2 Doseg identifikatora

Jedna od glavnih karakteristika dobrih programa je da se promenljive vei-

(2
nom definiu u funkcijama (ili ak nekim uim blokovima) i upotreba promen-
ljive je uglavnom ograniena na funkciju (ili blok) u kojoj je deklarisana. Ovim
se smanjuje zavisnost izmeu funkcija i ponaanje funkcije odreeno je samo

je
njenim ulaznim parametrima, a ne nekim globalnim stanjem programa. Time
se omoguava i da se analiza rada programa zasniva na analizi pojedinanih
an
funkcija, nezavisnoj od konteksta celog programa. Ipak, u nekim sluajevima
prihvatljivo je da funkcije meusobno komuniciraju korienjem zajednikih
d

promenljivih. Doseg, tj. vidljivost identifikatora (engl. scope of identifiers)


odreena je nainom tj. mestom u izvornom kdu na kojem su uvedeni.
iz

Doseg identifikatora odreuje deo teksta programa u kojem je mogue ko-


ristiti odreeni identifikator i u kojem taj identifikator identifikuje odreeni ob-
o

jekat (na primer, promenljivu ili funkciju). Svaki identifikator ima neki doseg.
sk

Jezik C spada u grupu jezika sa statikim pravilima dosega to znai da se doseg


svakog identifikatora moe jednoznano utvrditi analizom izvornog kda (bez
obzira na mogue tokove izvravanja programa). U jeziku C postoje sledee
n

vrste dosega:
ro

doseg nivoa datoteke (engl. file level scope) koji podrazumeva da ime vai
od take uvoenja do kraja datoteke;
kt

doseg nivoa bloka (engl. block level scope) koji podrazumeva da ime vai
le

od take uvoenja do kraja bloka u kojem je uvedeno;


E

doseg nivoa funkcije (engl. function level scope) koji podrazumeva da ime
vai u celoj funkciji u kojoj je uvedeno; ovaj doseg imaju jedino labele
koje se koriste uz goto naredbu;

doseg nivoa prototipa funkcije (engl. function prototype scope) koji pod-
razumeva da ime vai u okviru prototipa (deklaracije) funkcije; ovaj
doseg imaju samo imena parametara u okviru prototipova funkcije; on
omoguava da se u prototipovima funkcija navode i imena (a ne samo
tipovi) argumenata, to nije obavezno, ali moe da olaka razumevanje i
dokumentovanje kda.

7 Za lokalizaciju programa bolje je koristiti specijalizovane alate npr. GNU gettext.


200 9. Organizacija izvornog i izvrnog programa

Najznaajni nivoi dosega su doseg nivoa datoteke i doseg nivoa bloka. Iden-
tifikatori koji imaju doseg nivoa datoteke najee se nazivaju spoljanji ili glob-
alni , dok se identifikatori koji imaju ostale nivoe dosega (najee doseg nivoa
bloka) nazivaju unutranji ili lokalni .
8 Na osnovu diskusije sa poetka ovog
poglavlja, jasno je da je poeljno koristiti identifikatore promenljivih lokalnog
dosega kada god je to mogue. S druge strane, funkcije su obino globalne. Ima-
jui u vidu podelu na globalne i lokalne objekte, C program se esto definie
kao skup globalnih objekata (promenljivih, funkcija, tipova podataka itd.).

5)
U ranim verzijama jezika C nije bilo mogue definisati funkciju u okviru
definicije druge funkcije, tj. funkciju sa dosegom nivoa bloka, pa nije bilo

01
lokalnih funkcija , dok je od standarda C99 i takva mogunost predviena.
Doseg globalnih funkcija je doseg nivoa datoteke i protee se od mesta deklaracije

(2
pa do kraja datoteke. U nastavku su dati primeri razliitih dosega:

int a;
/* a je globalna promenljiva - doseg nivoa datoteke */

/* f je globalna funkcija - doseg nivoa datoteke */


je
an
void f(int c) {
/* c je lokalna promenljiva -
d

doseg nivoa bloka (tela funkcije f) */


int d;
iz

/* d je lokalna promenljiva -
doseg nivoa bloka (tela funkcije f) */
o
sk

void g() { printf("zdravo"); }


/* g je lokalna funkcija -
doseg nivoa bloka (tela funkcije f) */
n
ro

for (d = 0; d < 3; d++) {


int e;
kt

/* e je lokalna promenljiva -
doseg nivoa bloka (tela petlje) */
le

...
}
E

kraj:
/* labela kraj - doseg nivoa funkcije */
}

/* h je globalna funkcija - doseg nivoa datoteke */


void h(int b); /* b - doseg nivoa prototipa funkcije */

8 Iako tekst standarda koristi termine spoljanji i unutranji, u nastavku teksta e u veini
biti korieni termini globalni i lokalni, da bi se izbegla zabuna sa spoljanjom i unutranjom
povezanou objekata.
201 9. Organizacija izvornog i izvrnog programa

Jezik C doputa tzv. konflikt identifikatora tj. mogue je da postoji vie


identifikatora istog imena. Ako su njihovi dosezi jedan u okviru drugog, tada
identifikator u uoj oblasti dosega sakriva identifikator u iroj oblasti dosega.
Na primer, u narednom programu, promenljiva a inicijalizovana na vrednost 5
sakriva promenljivu a inicijalizovanu na vrednost 3.
void f() {
int a = 3, i;

5)
for (i = 0; i < 4; i++) {
int a = 5;

01
printf("%d ", a);
}
}

(2
5 5 5 5

je
Ovim je omogueno da prilikom uvoenja novih imena programer ne mora
an
da brine da li je takvo ime ve upotrebljeno u irem kontekstu.
d

9.2.3 ivotni vek objekata i kvalifikatori static i auto


iz

U odreenoj vezi sa dosegom, ali ipak nezavisno od njega je pitanje trajanja


objekata (pre svega promenljivih). ivotni vek (engl. storage duration, lifetime)
promenljive je deo vremena izvravanja programa u kojem se garantuje da je za
o

tu promenljivu rezervisan deo memorije i da se ta promenljiva moe koristiti.


sk

U jeziku C postoje sledee vrste ivotnog veka:


n

statiki (engl. static) ivotni vek koji znai da je objekat dostupan tokom
celog izvravanja programa;
ro

automatski (engl. automatic) ivotni vek koji najee imaju promenljive


kt

koje se automatski stvaraju i uklanjaju prilikom pozivanja funkcija;

dinamiki (engl. dynamic) ivotni vek koji imaju promenljive koje se


le

alociraju i dealociraju na eksplicitan zahtev programera (videti poglavlje


10.9).
E

ivotni vek nekog objekta se odreuje na osnovu pozicije u kdu na kojoj je


objekat uveden i na osnovu eksplicitnog korienja nekog od kvalifikatora auto
(automatski ivotni vek) ili static (statiki ivotni vek).

Lokalne automatske promenljive


Najee koriene lokalne promenljive su promenljive deklarisane u okviru
bloka. Rekli smo da je njihov doseg nivoa bloka, one su dostupne od take
202 9. Organizacija izvornog i izvrnog programa

deklaracije do kraja bloka i nije ih mogue koristiti van bloka u kojem su deklar-
isane. Ne postoji nikakva veza izmeu promenljivih istog imena deklarisanih u
razliitim blokovima.
Lokalne promenljive podrazumevano su automatskog ivotnog veka (sem
ako je na njih primenjen kvalifikator static ili kvalifikator extern o emu
e biti rei u narednim poglavljima). Iako je mogue i eksplicitno okarakterisati
ivotni vek korienjem kvalifikatora auto, to se obino ne radi jer se za ovakve
promenljive automatski ivotni vek podrazumeva. Poetna vrednost lokalnih

5)
automatskih promenljivih nije odreena i zavisi od ranijeg sadraja memorijske
lokacije koja je pridruena promenljivoj, pa se smatra nasuminom.

01
Automatske promenljive postoje samo tokom izvravanja funkcije u kojoj
su deklarisane i prostor za njih je rezervisan u stek okviru te funkcije (videti

(2
poglavlje 9.3.3). Ne postoji veza izmeu promenljive jednog imena u razliitim
aktivnim instancama jedne funkcije (jer svaka instanca funkcije ima svoj stek
okvir i u njemu prostor za promenljivu tog imena). Dakle, ukoliko se u jednu

je
funkciju ue rekurzivno, kreira se prostor za novu promenljivu, potpuno neza-
visan od prostora za prethodnu promenljivu istog imena.
an
Formalni parametri funkcija, tj. promenljive koje prihvataju argumente
funkcije imaju isti status kao i lokalne automatske promenljive.
Na lokalne automatske promenljive i parametre funkcija mogue je primeniti
d

i kvalifikator register ime se kompilatoru sugerie da se ove promenljive u-


iz

vaju u registrima procesora, a ne u memoriji (o organizaciji izvrnog programa


i organizaciji memorije tokom izvravanja programa bie vie rei u poglavlju
9.3.3). Meutim, s obzirom na to da dananji kompilatori tokom optimizacije
o

kda veoma dobro mogu da odrede koje promenljive ima smisla uvati u reg-
sk

istrima, ovaj kvalifikator se sve ree koristi.


n

Globalne statike promenljive i funkcije


ro

Globalne promenljive deklarisane su van svih funkcija i njihov doseg je nivoa


datoteke tj. mogu se koristiti od take uvoenja, u svim funkcijama do kraja
kt

datoteke. ivotni vek ovih promenljivih uvek je statiki, tj. prostor za ove
promenljive rezervisan je tokom celog izvravanja programa: prostor za njih
le

se rezervie na poetku izvravanja programa i oslobaa onda kada se zavri


izvravanje programa. Prostor za ove promenljive obezbeuje se u segmentu
E

podataka (videti poglavlje 9.3.3). Ovakve promenljive se podrazumevano ini-


cijalizuju na vrednost 0 (ukoliko se ne izvri eksplicitna inicijalizacija).
S obzirom na to da ovakve promenljive postoje sve vreme izvravanja pro-
grama i na to da moe da ih koristi vie funkcija, one mogu da zamene prenos
podataka izmeu funkcija. Meutim, to treba initi samo sa dobrim razlogom
(na primer, ako najvei broj funkcija treba da koristi neku zajedniku promen-
ljivu) jer, inae, program moe postati neitljiv i teak za odravanje.
Globalne promenljive uvek imaju statiki vek. Na njih je mogue primeniti
kvalifikator static, meutim, on ne slui da bi se naglasio statiki ivotni vek
203 9. Organizacija izvornog i izvrnog programa

(to se podrazumeva), ve naglaava da one imaju unutranju povezanost (videti


poglavlje 9.2.4).

Lokalne statike promenljive


U nekim sluajevima poeljno je uvati informaciju izmeu razliitih poziva
funkcije (npr. potrebno je brojati koliko puta je pozvana neka funkcija). Jedno
reenje bilo bi uvoenje globalne promenljive, statikog ivotnog veka, meu-

5)
tim, zbog globalnog dosega tu promenljivu bilo bi mogue koristiti (i promeniti)
i iz drugih funkcija, to je nepoeljno. Zato je poeljna mogunost definisanja

01
promenljivih koje bi bile statikog ivotnog veka (da bi uvale vrednost tokom
itavog izvravanja programa), ali lokalnog dosega (da bi se mogle koristiti i

(2
menjati samo u jednoj funkciji).
U deklaraciji lokalne promenljive moe se primeniti kvalifikator static i
u tom sluaju ona ima statiki ivotni vek kreira se na poetku izvrava-

je
nja programa i oslobaa prilikom zavretka rada programa. Tako modifiko-
vana promenljiva ne uva se u stek okviru svoje funkcije, ve u segmentu po-
an
dataka (videti poglavlje 9.3.3). Ukoliko se vrednost statike lokalne promenljive
promeni tokom izvravanja funkcije, ta vrednost ostaje sauvana i za sledei
poziv te funkcije. Ukoliko inicijalna vrednost statike promenljive nije nave-
d

dena, podrazumeva se vrednost 0. Statike promenljive se inicijalizuju samo


iz

jednom, konstantnim izrazom, na poetku izvravanja programa tj. prilikom


njegovog uitavanja u memoriju .
9 Doseg ovih promenljivih i dalje je nivoa
bloka tj. promenljive su i dalje lokalne, to daje eljene osobine.
o

Naredni program ilustruje kako se promenljiva a u funkciji f iznova kreira


sk

tokom svakog poziva, dok promenljiva a u funkciji g zadrava svoju vrednost


tokom raznih poziva.
n

Program 9.2.
ro

#include <stdio.h>
kt

void f() {
int a = 0;
le

printf("f: %d ", a);


a = a + 1;
E

void g() {
static int a = 0;
printf("g: %d ", a);
a = a + 1;
}

9 Ovakvo ponaanje je razliito u odnosu na jezik C++ gde se inicijalizacija vri prilikom
prvog ulaska u blok u kojem je ovakva promenljiva definisana.
204 9. Organizacija izvornog i izvrnog programa

int main() {
f(); f();
g(); g();
return 0;
}
Kada se prevede i pokrene, prethodni program ispisuje:

f: 0 f: 0 g: 0 g: 1

5)
01
9.2.4 Povezanost identifikatora i kvalifikatori static i extern

(2
Pravila dosega daju mogunost progameru da odredi da li eli da je neko ime
vidljivo samo u jednoj ili u vie funkcija definisanih u jednoj jedinici prevoenja.
Meutim, kada se program sastoji od vie jedinica prevoenja, doseg nije do-

je
voljan i potrebna je malo finija kontrola. Poeljno je da postoji mogunost
definisanja: (i) objekata (promenljivih ili funkcija) koji se mogu koristiti samo
an
u pojedinanim funkcijama, (ii) objekata koji se mogu koristiti u vie funkcija
neke jedinice prevoenja, ali ne i van te jedinice prevoenja i (iii) objekata koji
se mogu koristiti u svim funkcijama u celom programu, mogue i u razliitim
d

jedinicama prevoenja.
iz

Identifikator koji je deklarisan u razliitim dosezima ili u istom dosegu vie


puta moe da oznaava isti objekat ili razliite objekte. ta e biti sluaj,
odreuje se na osnovu povezanosti identifikatora (engl. linkage of identifiers).
o

Povezanost identifikatora tesno je vezana za fazu povezivanja programa i naj-


sk

ee se koristi da odredi meusobni odnos objekata u razliitim jedinicama


prevoenja. Izmeu ostalog, ona daje odgovor na pitanje da li je i kako mogue
n

objekte definisane u jednoj jedinici prevoenja koristiti u nekoj drugoj jedinici


ro

prevoenja. Na primer, da li je doputeno da se u dve razliite jedinice pre-


voenja koristi identifikator a za neku globalnu celobrojnu promenljivu i da li
a
kt

taj identifikator oznaava jednu istu ili dve razliite promenljive.


Jezik C razlikuje identifikatore:
le

bez povezanosti (engl. no linkage),

identifikatore sa spoljanjom povezanou (engl. external linkage) i

identifikatore sa unutranjom povezanou (engl. internal linkage).

Ne treba meati spoljanju i unutranju povezanost sa spoljanjim i un-


utranjim dosegom (otuda i korienje alternativnih termina globalni i lokalni
za doseg) povezanost (i unutranja i spoljanja) se odnosi iskljuivo na glob-
alne objekte (tj. objekte spoljanjeg dosega), dok su lokalni objekti (tj. objekti
unutranjeg dosega) najee bez povezanosti (osim ako im se povezanost ne
promeni kvalifikatorom extern o emu e biti rei u nastavku). Unutranja i
spoljanja povezanost identifikatora ima smisla samo kod objekata sa statikim
ivotnim vekom, dok su objekti automatskog ivotnog veka bez povezanosti.
205 9. Organizacija izvornog i izvrnog programa

Da bi se povezanost potpuno razumela, potrebno je skrenuti panju na jo


jedan znaajan aspekt jezika C i razjasniti ta se podrazumeva pod deklaraci-
jom , a ta pod definicijom objekta. Deklaracijom se uvodi novi identifikator
i opisuje se njegov tip (bez obzira da li je u pitanju promenljiva ili funkcija).
Deklaracija je ono to je kompilatoru potrebno da bi se taj identifikator mogao
dalje koristiti u programu (na primer, da bismo mogli da pozivamo funkciju f,
dovoljno je samo da znamo njenu deklaraciju void f();). Deklaraciju moemo
shvatiti kao deo koda kojim kompilatoru kaemo negde u programu postoji

5)
objekat tog i tog tipa. Definicijom se taj objekat zaista implementira tj. in-
stancira (definicijom kaemo evo ga objekat taj i taj). Definicija je ono to

01
je u fazi povezivanja potrebno da bi sva korienja nekog identifikatora u pro-
gramu mogla da budu razreena. Na primer, definicija funkcije f implementira

(2
tu funkciju tj. uvodi njen kod i svaki poziv te funkije se onda razreava tako
to se poziva upravo taj kd. Definicija globalne promenljive int a = 3; kae
da u objektnom kodu generisanom na osnovu te jedinice prevoenja treba da

je
bude rezervisana memorija za jedan podatak tipa int, da taj podatak treba da
bude incijalno postavljen na vrednost 3 i da se sva korienja te promenljive au
an
tekstu programa u stvari odnosne na taj podatak. Objekti mogu da imaju vei
broj (istovetnih) deklaracija, ali mogu da imaju samo jednu definiciju. Ako su
date samo deklaracije, a ne i definicije, faza kompilacije proi e uspeno, ali
d

e doi do greke u fazi povezivanja.


iz

U sluaju funkcija, pitanje ta je deklaracija, a ta je definicija jasno je na


osnovu toga da li prototip prati i telo funkcije. Slino, svaka deklaracija lokalne
promenljive koja je do sada prikazana bila je ujedno i njena definicija. Meu-
o

tim, to je neto drugaije kod globalnih promenljivih. Naime, ako deklaraciju


sk

prati inicijalizacija, ona se uvek ujedno smatra i definicijom. Ali, ako je globalna
promenljiva uvedena bez inicijalizacije (npr. ako postoji globalna deklaracija
n

int a;), nije jasno da li je u pitanju deklaracija ili definicija. Takav kod se
ro

smatra naelnom definicijom (engl. tentative definition). Ako postoji stvarna


definicija iste promenljive (na primer, deklaracija sa inicijalizacijom), onda se
naelna definicija smatra samo deklaracijom. Meutim, ako stvarne definicije
kt

nema, onda se naelna definicija smatra definicijom (uz inicijalnu vrednost 0),
tj. ponaanje je isto kao da pored naelne definicije postoji stvarna definicija
le

sa inicijalizatorom 0. Takoe, ako se pri deklaraciji promenljive (bilo lokalne,


bilo globalne) navede kvalifikator extern (o kome e biti rei u nastavku) ta
E

deklaracija obavezno prestaje da bude definicija (i tada nije doputeno navoditi


inicijalnu vrednost).

Identifikatori bez povezanosti


Identifikatori bez povezanosti nisu vidljivi prilikom procesa povezivanja i
mogu se potpuno nezavisno ponavljati u razliitim funkcijama, bilo da su one
navedene u jednoj ili u vie jedinica prevoenja. Svaka deklaracija identifika-
tora bez povezanosti ujedno je i njegova definicija i ona odreuje jedinstveni
nezavisni objekat. Bez povezanosti su najee lokalne promenljive (bez obzira
206 9. Organizacija izvornog i izvrnog programa

da li su automatskog ili statikog ivotnog veka), parametri funkcija, korisniki


definisani tipovi, labele itd. Na primer, ako su funkcije f, funkcije g1 i g2
i struktura i definisane u razliitim jedinicama prevoenja, sva pojavljivanja
imena i i sva pojavljivanja imena j oznaavaju razliite, potpuno nezavisne
promenljive i tipove.

int f() { int g1(int i) { struct i { int a; }


int i; static int j;

5)
... }
} int g2() {

01
static int i, j;
...
}

(2
Spoljanja povezanost i kvalifikator extern

je
Spoljanja povezanost identifikatora omoguava da se isti objekat koristi u
an
vie jedinica prevoenja. Sve deklaracije identifikatora sa spoljanjom poveza-
nou u skupu jedinica prevoenja odreuju jedan isti objekat (tj. sve pojave
d

ovakvog identifikatora u razliitim jedinicama prevoenja odnose se na jedan


isti objekat), dok u celom programu mora da postoji tano jedna definicija tog
iz

objekta. Tako se jedan identifikator koristi za isti objekat u okviru vie jedinica
prevoenja.
extern
o

Kvalifikator najee se koristi iskljuivo kod programa koji se sas-


sk

toje od vie jedinica prevoenja i slui da naglasi da neki identifikator ima


spoljanju povezanost. Jedino deklaracije mogu biti okvalifikovane kvalifika-
torom extern. Samim tim, nakon njegovog navoenja nije mogue navoditi
n

inicijalizaciju promenljivih niti telo funkcije (to ima smisla samo prilikom
ro

definisanja). Slino, za niz u extern deklaraciji nije potrebno navoditi dimen-


ziju. Poto extern deklaracije nisu definicije, njima se ne rezervie nikakva
kt

memorija u programu ve se samo naglaava da se oekuje da negde (najee


u drugim jedinicama prevoenja) postoji definicija objekta koji je se extern
le

deklaracijom deklarie (i time uvodi u odgovarajui doseg).


Ipak, postoje sluajevi kada se spoljanja povezanost podrazumeva i nije
extern
E

neophodno eksplicitno upotrebiti deklaraciju. Sve globalne funkcije


jezika C podrazumevano imaju spoljanju povezanost (osim ako na njih nije
primenjen kvalifikator static kada je povezanost unutranja) i zato se prilikom
njihove deklaracije retko kad eksplicitno navodi kvalifikator extern. Slino kao
kod funkcija, podrazumevana povezanost globalnih promenljivih je spoljanja
(osim ako na njih nije primenjen kvalifikator static kada je povezanost un-
utranja). Ranije je ve reeno da deklaracije globalnih promenljivih (oblika
npr. int x;), ako nisu praene inicijalizacijom, predstavljaju samo naelne
definicije, tj. ako negde postoji stvarna definicija objekta sa tim imenom, one
nisu definicije ve samo deklaracije. U tom svetlu, kvalifikator extern nije
207 9. Organizacija izvornog i izvrnog programa

neophodno navesti uz takve deklaracije. Ipak, za razliku od funkcija, njegova


upotreba se savetuje da bi se eksplicitno naglasilo da se eli samo deklaracija (a
ne i definicija) promenljive i kako odgovor na pitanje da li je neto deklaracija
ili definicija ne bi zavisio od ostatka izvornog teksta programa.
Kod lokalnih promenljivih, svaka deklaracija ujedno je i definicija i ovakvi
objekti su po pravilu bez povezanosti (ak i ako je na njih primenjen kvali-
fikator static). Zato je kod lokalnih promenljivih neophodno koristiti kvali-
fikator extern kada se eli naglasiti da je u pitanju deklaracija (a ne defini-

5)
cija) promenljive i da je ta promenljiva definisana na nekom drugom mestu
(tj. da promenljiva ima spoljanju povezanost). Ovom, donekle neobinom kon-

01
strukcijom, postie se da globalna promenljiva (sa spoljanjom povezanou)
u nekoj drugoj jedinici prevoenja, u tekuoj jedinici prevoenja ima samo

(2
lokalni doseg.
Razmotrimo naredni primer koji sadri dve jedinice prevoenja.

Program 9.3.
#include <stdio.h> #include <stdio.h>
je
an
int a; extern int a;
int b = 3; void f();
d

int g() {
iz

void f() { extern int b;


printf("a=%d\n", a); printf("b=%d\n", b);
} f();
o

}
sk

int main() {
n

a = 1;
ro

/* b = 2; */
g();
return 0;
kt

}
le

b=3
E

a=0

U prvoj jedinici prevoenja postoje globalne promenljive a i b kao i globalna


funkcija f. Sve one podrazumevano imaju statiki ivotni vek i spoljanju
povezanost. Linija int b = 3; sadri inicijalizaciju tako da je jasno da je u
pitanju definicija (i to stvarna). U prvom trenutku nije jasno da li je int a;
deklaracija ili definicija i u pitanju je samo naelna definicija, meutim, poto
ne postoji druga definicija promenljive a, ova linija se ipak tumai kao definicija,
uz podrazumevanu inicijalnu vrednost 0. Druga jedinica prevoenja sadri
dve deklaracije: deklaraciju promenljive a i funkcije f. U sluaju promenljive
208 9. Organizacija izvornog i izvrnog programa

eksplicitno je upotrebljen kvalifikator extern ime je naglaeno da je u pitanju


promenljiva sa spoljanjom povezanou i da se eli pristup promenljivoj a iz
prve jedinice prevoenja. ak i da kvalifikator extern nije naveden, program
bi radio identino (obe definicije bile bi naelne i tek tokom povezivanja bio
bi napravljen jedinstven objekat statikog ivotnog veka inicijalizovan na nulu
na koji bi se obe ove promenljive odnosile). U sluaju funkcije f nije bilo
neophodno navoditi extern jer je jasno da je u pitanju deklaracija. Identifikator
b je uveden u lokalni doseg funkcije g uz kvalifikator extern ime je naglaeno

5)
da se misli na promenljivu b definisanu u prvoj jedinici prevoenja. U funkciji
main postavlja se vrednost promenljive a na 1. Pokuaj postavljanja vrednosti

01
promenljive b (pod komentarom) ne bi uspeo jer identifikator b nije u dosegu
funkcije main.

(2
Unutranja povezanost i kvalifikator static

je
Globalni objekat ima unutranju povezanost ako se kvalifikuje kvalifika-
torom static.10 Unutranja povezanost povezuje sva pojavljivanja istog iden-
an
tifikatora, na nivou tano jedne jedinice prevoenja. Ukoliko se u istoj je-
dinici prevoenja, u istom dosegu naie na vie deklaracija identifikatora sa
unutranjom povezanou sve one se odnose na isti objekat i za taj objekat
d

mora postojati tano jedna definicija u toj jedinici prevoenja. S druge strane,
static
iz

kvalifikator onemoguava korienje promenljive ili funkcije u drugim


datotekama koje ine program. Povezivanje, naravno, nee uspeti ni ukoliko
je za neku promenljivu ili funkciju deklarisanu sa static u drugoj datoteci
o

navedena deklaracija na koju je primenjen kvalifikator extern. Ukoliko neka


sk

druga jedinica prevoenja sadri deklaraciju istog identifikatora neophodno je


da postoji definicija odgovarajueg objekta, razliitog od objekta iz prve je-
n

dinice prevoenja.
Osnovna uloga unutranje povezanosti obino je u tome da neki deo kda
ro

uini zatvorenim, u smislu da je nemogue menjanje nekih njegovih globalnih


promenljivih ili pozivanje nekih njegovih funkcija iz drugih datoteka. Time se
kt

taj deo kda enkapsulira i iz drugih jedinica prevoenja mu se moe pris-


tupiti samo kroz preostale take komunikacije (funkcije i globalne promenljive
le

sa spoljanjom povezanou). Cilj programera, dakle, nije da onemogui druge


programere da koriste deo njegovog kda, ve da im omogui jasan interfejs
E

ijim korienjem se onemoguuju nehotine greke. Ukoliko je u jednoj je-


dinici prevoenja deklarisana globalna promenljiva ili funkcija sa unutranjom
povezanou, i u drugim jedinicama prevoenja se moe deklarisati globalna
promenljiva ili funkcija istog imena, ali e se ona odnositi na drugi, nezavisan
objekat.
Kao primer, razmotrimo naredne dve jedinice prevoenja

Program 9.4.
10 Kvalifikator static u programskom jeziku C ima potpuno drugu ulogu i dejstvo kada se
primenjuje na lokalne promenljive: u tom sluaju, ovaj kvalifikator oznaava da promenljiva
ima statiki ivotni vek (videti poglavlje 9.2.3).
209 9. Organizacija izvornog i izvrnog programa

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


static int a = 3;
int b = 5; int g(); /* int f(); */
int a, b;
static int f() {
printf("f: a=%d, b=%d\n", int main() {
a, b); printf("main: a=%d, b=%d\n",
} a, b);

5)
g(); /* f() */
int g() { return 0;

01
f(); }
}

(2
Program ispisuje

main: a=0, b=5

je
f: a=3, b=5
an
U prvoj jedinici prevoenja promenljiva a i funkcija f imaju unutranju
povezanost (dok promenljiva b i funkcija g imaju spoljanju povezanost (o ko-
d

joj e biti rei u nastavku poglavlja). U drugoj jedinici prevoenja promenljive


a, b g i main a
iz

i funkcije imaju spoljanju povezanost. Ime u dve razliite je-


dinice prevoenja odnosi se na dve razliite promenljive. Promenljiva a, defin-
isana u prvoj jedinici prevoenja, kljunom reju static sakrivena je i nije joj
o

mogue pristupiti iz ostalih jedinica prevoenja. Slino je i sa funkcijom f (uk-


sk

lanjanje komentara u drugoj jedinici prevoenja dovelo bi do greke prilikom


povezivanja jer je definicija funkcije f skrivena u prvoj jedinici prevoenja).
b
n

Sa druge strane, sva pojavljivanja imena odnose se na istu promenljivu i sva


pojavljivanja imena g odnose se na istu funkciju.
ro

Poto u prvoj jedinici prevoenja postoji definicija promenljive b, u dru-


goj jedinici prevoenja int b; je samo njena deklaracija. Poto je definicija
kt

promenljive a iz prve jedinice prevoenja skrivena, prilikom povezivanja druge


jedinice prevoenja nije dostupna definicija promenljive a, onda se int a; u
le

drugoj jedinici prevoenja smatra njenom definicijom (uz podrazumevanu vred-


nost 0).
E

9.2.5 Greke u fazi prevoenja i povezivanja

U procesu generisanja izvrnog programa od izvornog programa, greke


(engl. error) mogu biti otkrivene u nekoliko faza. Poruke o otkrivenim grekama
se obino usmeravaju na standardni izlaz za greke, stderr (videti poglavlje
12.1). ak i u veoma kratkim programima, za svaku od ovih faza moe da pos-
toji greka koja tada moe biti otkrivena. Razmotrimo jednostavan (vetaki,
bez konkretne svrhe) program naveden u nastavku i greke do kojih moe doi
210 9. Organizacija izvornog i izvrnog programa

malim izmenama:

Program 9.5.
#include<stdio.h>
int main() {
int a = 9;
if (a == 9)
printf("9");

5)
return 0;
}

01
(2
Pretprocesiranje. Na primer, ukoliko je, umesto pretprocesorske direktive
#include, u programu navedeno #Include, bie prijavljena greka (jer ne pos-
toji direktiva #Include):

je
primer.c:1:2: error: invalid preprocessing directive #Include
an
Kompilacija
d
iz

Leksika analiza: Na primer, ukoliko u programu pie int a = 09; umesto


int a = 9;, bie detektovana neispravna oktalna konstanta 09, tj. neis-
pravna leksema 09 i bie prijavljena greka:
o
sk

primer.c:4:9: error: invalid digit "9" in octal constant


n

if (a == 9) ...,
ro

Sintaksika analiza: Na primer, ukoliko, umesto u


programu postoji naredba if a == 9 ..., zbog izostavljenih zagrada
bie prijavljena greka:
kt

primer.c:5:6: error: expected ( before a


le
E

Semantika analiza: Na primer, ukoliko u programu, umesto naredbe


if (a == 9) ..., postoji naredba if ("a" * 9) ..., tokom provere
tipova bie prijavljena greka (jer operator * nije mogue primenjivati
nad niskama):

primer.c:5:8: error: invalid operands to binary *


(have char * and int)
211 9. Organizacija izvornog i izvrnog programa

Povezivanje. Na primer, ukoliko u programu, umesto printf(...);, postoji


naredba print(...);, bie prijavljena greka (jer nije raspoloiva definicija
funkcije print):
primer.c:(.text+0x20): undefined reference to print
collect2: ld returned 1 exit status

Sve greke u izvornom programu koje mogu biti otkrivene u celokupnom

5)
procesu prevoenja bivaju otkrivene u fazi leksike, sintaksike ili semantike
analize, ili u fazi linkovanja. Dakle, tokom generisanja i optimizacije meukda,

01
kao i tokom generisanja kda ne moe biti prijavljenih greaka (barem kada je
prevodilac ispravan). Dobro je poznavati faze u prevoenju i vrste greaka koje
u tim fazama mogu biti otkrivene, jer se sa tim znanjem prijavljene greke lake

(2
otklanjaju.
Ukoliko prevodilac naie na greku ili greke u izvornom kdu, to prijavljuje
(navodi vrstu greke, liniju i kolonu programa u kojoj je greka) i ne generie

je
izvrni program. Izvrni program se, naravno, ne generie ni u sluaju ako je
an
greka otkrivena u fazi povezivanja. Programer tada treba da otkloni detekto-
vane greke i ponovi proces prevoenja.
Pored prijave greaka, kompilator moe da izda i upozorenja (engl. warn-
d

ing) koja ukazuju na potencijalne propuste u programu, ali se i pored njih


iz

izvrni program moe generisati. Na primer, ukoliko navedeni program, umesto


naredbe if (a == 9) ... sadri naredbu if (a = 9) ..., prevodilac moe
izdati upozorenje:
o

primer.c:4: warning: suggest parentheses around assignment


sk

used as truth value


n

Ukoliko navedeni program, umesto naredbe if (a == 9) ... sadri naredbu


if (9 == 9) ...,
ro

prevodilac moe izdati upozorenje:

primer.c:3: warning: unused variable a


kt

Ukoliko navedeni program, umesto naredbe if (a == 9) ... sadri naredbu


le

if (a / 0) ..., prevodilac moe izdati upozorenje:


E

primer.c:4: warning: division by zero

Ukoliko se u fazi izvravanja naie na celobrojno deljenje nulom (kao to je


to sluaj sa navedenim primerom), na veini sistema e doi do greke u fazi
izvravanja (videti poglavlje 9.3.5). Ipak, standard proglaava ponaanje pro-
grama u sluaju deljenja nulom nedefinisanim, pa navedeni kd dovodi u fazi
prevoenja samo do upozorenja, a ne do greke.
Upozorenja, ak i kada je uspeno generisan izvrni kd, ne treba ignorisati
i dobra praksa moe da bude da se izvorni kd modifikuje sve dok ima ijednog
upozorenja.
212 9. Organizacija izvornog i izvrnog programa

Svaki prevodilac predvia vie vrsta upozorenja i te vrste zavise od konkretnog


prevodioca. Na primer, u prevodiocu GCC, sve vrste upozorenja se omoguavaju
opcijom -Wall.

9.2.6 Primer organizacije programa u vie datoteka

Kao primer programa sastavljenog iz vie datoteka i vie jedinica pre-

5)
voenja, razmotrimo definisanje jednostavne korisnike biblioteke za rad sa
pozitivnim razlomcima. Biblioteka je projektovana tako da prikae to vie
koncepata opisanih u ovoj glavi (u realnoj situaciji neka reenja moda bi

01
bila drugaija). Biblioteka prua definiciju tipa za reprezentovanje razlomaka
(RAZLOMAK), funkcije za kreiranje razlomka na osnovu brojioca i imenioca, za

(2
skraivanje razlomka i za sabiranje razlomaka. Primena ovih funkcija moe
dovesti do dva (u ovom primeru) ishoda: da je operacija izvrena uspeno i
da se prilikom primene operacije javlja neispravan razlomak (onaj kojem je

je
imenilac 0). Ovim ishodima odgovaraju lanovi OK i IMENILAC_0 nabrojivog
tipa RAZLOMAK_GRESKA. Kako im nisu eksplicitno pridruene vrednosti ovim
an
imenima e implicitno biti pridruene vrednosti 0 i 1. Tekstualni opis moguih
ishoda uva se u nizu razlomak_poruka. Ishod rada funkcije bie uvan u
globalnoj promenljivoj razlomak_greska.
d

Datoteka razlomak.h je javni interfejs biblioteke i sadri deklaracije funkcija


iz

i promenljivih koje su na raspolaganju njenim korisnicima.

#ifndef __RAZLOMAK_H_
o

#define __RAZLOMAK_H_
sk

typedef struct razlomak {


unsigned brojilac;
n

unsigned imenilac;
ro

} RAZLOMAK;
kt

typedef enum {OK, IMENILAC_0} RAZLOMAK_GRESKA;


extern RAZLOMAK_GRESKA razlomak_greska;
le

extern char* razlomak_poruka[];


E

RAZLOMAK napravi_razlomak(unsigned br, unsigned im);


RAZLOMAK skrati_razlomak(RAZLOMAK r);
RAZLOMAK saberi_razlomke(RAZLOMAK r1, RAZLOMAK r2);

#endif

Kao to je ve reeno, u deklaracijama funkcija nije bilo neophodno navoditi


kvalifikator extern (jer funkcije podrazumevano imaju spoljanju povezanost),
dok je on upotrebljen kod promenljive razlomak_greska i niza razlomak_poruka
da bi se nedvosmisleno naglasilo da su u pitanju deklaracije, a ne definicije.
213 9. Organizacija izvornog i izvrnog programa

Datoteka zaglavlja razlomak.h poinje i zavrava se pretprocesorskim di-


rektivama. Generalno, u datoteci zaglavlja, pored prototipova funkcija, mogu
da se nalaze i neke definicije (najee korisniki definisanih tipova, a ponekad
ak i funkcija i promenljivih). U sluaju da se datoteka zaglavlja ukljui vie
puta u neku jedinicu prevoenja (to se esto dogaa preko posrednog ukljui-
vanja), desilo bi se da neka jedinica prevoenja sadri viestruko ponavljanje
nekih definicija to bi dovelo do greke prilikom kompilacije. Ovaj problem se
moe reiti korienjem pretprocesorskih direktiva i uslovnim prevoenjem. Da

5)
bi se spreilo viestruko ukljuivanje, svaka datoteka zaglavlja u tom sluaju
treba da ima sledei opti oblik:

01
#ifndef IME
#define IME

(2
...

je
#endif
an
gde je tekst IME karakteristian za tu datoteku (na primer njeno ime obo-
gaeno nekim specijalnim simbolima). Prilikom prvog ukljuivajna ove da-
d

toteke, definie se simboliko ime IME i zbog toga naredna ukljuivanja (iz iste
iz

datoteke) ignoriu celokupan njen sadraj. Ovaj mehanizam olakava odra-


vanje datoteka zaglavlja i primenjen je i u navedenom primeru.
Implementacija biblioteke sadrana je u datoteci razlomak.c.
o

#include "razlomak.h"
sk

char* razlomak_poruka[] = {
n

"Operacija uspesno sprovedena",


"Greska: imenilac je nula!"
ro

};
kt

static const RAZLOMAK NULA = {0, 0};


RAZLOMAK_GRESKA razlomak_greska;
le

static int nzd(unsigned a, unsigned b) {


E

return (b == 0) ? a : nzd(b, a % b);


}

RAZLOMAK skrati_razlomak(RAZLOMAK r) {
if (r.imenilac == 0) {
razlomak_greska = IMENILAC_0; return NULA;
}
unsigned n = nzd(r.brojilac, r.imenilac);
r.brojilac /= n;
r.imenilac /= n;
214 9. Organizacija izvornog i izvrnog programa

razlomak_greska = OK;
return r;
}

RAZLOMAK napravi_razlomak(unsigned br, unsigned im) {


RAZLOMAK rez;
rez.brojilac = br;
rez.imenilac = im;

5)
if (im == 0) {
razlomak_greska = IMENILAC_0; return NULA;

01
}
return skrati_razlomak(rez);
}

(2
RAZLOMAK saberi_razlomke(RAZLOMAK r1, RAZLOMAK r2) {
if (r1.imenilac == 0 || r2.imenilac == 0) {

je
razlomak_greska = IMENILAC_0; return NULA;
}
an
return napravi_razlomak(
r1.brojilac*r2.imenilac + r1.imenilac*r2.brojilac,
d

r1.imenilac*r2.imenilac);
}
iz

Prvi korak predstavlja ukljuivanje datoteke zaglavlja razlomak.h. Iako


o

ovaj korak nije uvek neophodan (ukoliko se pre svakog poziva funkcije javlja
sk

njena definicija), svakako se preporuuje pre definicija ukljuiti deklaracije da


bi se tokom kompilacije proverilo da li su deklaracije i definicije uparene.
Datoteka sadri definiciju globalnog niza razlomak_poruka i globalne pro-
n

menljive razlomak_poruka. Elementi niza razlomak_poruka inicijalizovani su


ro

kao pokazivai na konstantne niske. Imenima OK i IMENILAC_0 odgovaraju


vrednosti 0 i 1, pa pokaziva razlomak_poruka[OK] ukazuje na konstantnu
nisku "Operacija uspesno sprovedena", a razlomak_poruka[IMENILAC_0]
kt

na konstantnu nisku "Greska: imenilac je nula!". Datoteka sadri kon-


stantu NULA koja se koristi kao specijalna povratna vrednost u sluaju greke,
le

kao i pomonu funkciju nzd za pronalaenje najveeg zajednikog delioca dva


E

broja. S obzirom na to da se ne predvia da ih korisnik biblioteke direktno


koristi, one su sakrivene postavljanjem unutranjeg povezivanja korienjem
kvalifikatora static.
Program koji demonstrira upotrebu biblioteke dat je u zasebnoj datoteci
program.c.
Program 9.6.
#include <stdio.h>
#include "razlomak.h"
215 9. Organizacija izvornog i izvrnog programa

int main() {
unsigned a, b;
RAZLOMAK r1, r2, r;

scanf("%u%u", &a, &b);


r1 = napravi_razlomak(a, b);
if (razlomak_greska != OK) {
printf("%s\n", razlomak_poruka[razlomak_greska]);

5)
return 1;
}

01
scanf("%u%u", &a, &b);
r2 = napravi_razlomak(a, b);

(2
if (razlomak_greska != OK) {
printf("%s\n", razlomak_poruka[razlomak_greska]);
return 1;

je
}
an
r = saberi_razlomke(r1, r2);
if (razlomak_greska != OK)
d

printf("%s\n", razlomak_poruka[razlomak_greska]);
iz

printf("Rezultat je: %u/%u\n", r.brojilac, r.imenilac);


return 0;
o

}
sk

Program uitava dva razlomka, sabira ih i ispisuje rezultat, proverava-


jui u pojedinim koracima vrednost promenljive razlomak_greska. U sluaju
n

da je dolo do greke, opis greke se ispisuje na standardni izlaz. Datoteka


ro

razlomak.h nesmetano je ukljuena u obe jedinice prevoenja.


Odgovarajua Makefile datoteka moe da bude:
kt

program : program.o razlomak.o


gcc -o program program.o razlomak.o
le

program.o : program.c razlomak.h


E

gcc -c -O3 -Wall program.c

razlomak.o : razlomak.c razlomak.h


gcc -c -O3 -Wall razlomak.c

Pitanja i zadaci za vebu


Pitanje 9.2.1. emu slui direktiva #include?
216 9. Organizacija izvornog i izvrnog programa

Pitanje 9.2.2. Gde se trai datoteka filename ako se ukljui pretprocesorskom


direktivom #include "filename", a gde ako se ukljui direktivom #include
<filename>? Kada se obino koristi direktiva #include "filename", a kada
direktiva #include <filename> ?

Pitanje 9.2.3. Dokle vai dejstvo pretprocesorske direktive


#define NUM_LINES 1000?
Kako se ponitava njeno dejstvo?

5)
Pitanje 9.2.4. Kako se od neke take u programu ponitava dejstvo pretpro-

01
cesorske direktive
#define x(y) y*y+y?
Pitanje 9.2.5.

(2
Kako se moe proveriti da li je neko simboliko ime definisano
u trenutnoj jedinici prevoenja?

Pitanje 9.2.6. Objasniti razliku izmeu makroa i funkcija. U kojoj fazi se vri

je
razreavanje poziva makroa, a u kojoj poziva funkcija? Objasniti ta znai da
an
kod makroa nema prenosa argumenata, a kod funkcija ima.

Pitanje 9.2.7. Kojoj operaciji u tekstualnom editoru je slian efekat direktive


#define, #include?
d

a kojoj efekat direktive


iz

Pitanje 9.2.8. Kojim parametrom se GCC navodi da izvri samo pretproce-


siranje i prikae rezultat faze pretprocesiranja?
o

Pitanje 9.2.9. Za svaki od programa koji slede, navesti ta je rezultat pret-


sk

procesiranja programa i ta se ispisuje kada se program prevede i izvri.

1. #include <stdio.h>
n

#define x(y) y*y+y


ro

int main() { printf("%d", x(3 + 5)); /* ispis */ }


2. #include <stdio.h>
kt

#define A(x,y) (x > 2) ? x*y : x-y


int main() { printf("%d %d", A(4,3+2), A(4,3*2); }
le

3. #include <stdio.h>
E

#define A(x,y) (y>=0) ? x+y : x-y


int main() {
if (b<=0) c = A(a+b,b); else c = A(a-b,b);
printf("%d\n", c);
}
4. #include <stdio.h>
#define proizvod(x,y) x*y
int main() { printf("%d\n", proizvod(2*3+4, 2+4*5)); }
217 9. Organizacija izvornog i izvrnog programa

5. #include <stdio.h>
#define A(x,y) (x > 2) ? x*y : x-y
int main() { printf("%d %d\n", A(4,3+2), A(4,3*2)); }
Pitanje 9.2.10.
1. Navesti primer poziva makroa kvadrat za kvadriranje koji pri definiciji

#define kvadrat(x) x*x daje pogrean rezultat dok pri

5)
definiciji #define kvadrat(x) ((x)*(x)) daje ispravan rezultat.

dvostruko

01
2. Navesti primer poziva makroa za koji pri definiciji

#define dvostruko(x) (x)+(x) daje pogrean rezultat dok pri definiciji


#define dvostruko(x) ((x)+(x))

(2
daje ispravan rezultat.

Pitanje 9.2.11. Napisati makro za:

je
1. izraunavanje ostatka pri deljenju prvog argumenta drugim,

2. izraunavanje treeg stepena nekog broja,


an
3. izraunavanje maksimuma dva broja,
d

4. izraunavanje maksimuma tri broja,

5. izraunavanje apsolutne vrednosti broja,


iz

Pitanje 9.2.12. Koji program pravi od ulaznih datoteka jedinice prevoenja?


o

Pitanje 9.2.13. Prilikom generisanja izvrne verzije programa napisanog na


sk

jeziku C, koja faza prethodi kompilaciji, a koja sledi nakon faze kompilacije?

Pitanje 9.2.14. Kako se zove faza prevoenja programa u kojoj se od objektnih


n

modula kreira izvrni program? Koji program sprovodi tu fazu?


ro

Pitanje 9.2.15. Kako se korienjem GCC kompilatora moe zasebno prevesti


datoteka prva.c, zatim zasebno prevesti datoteka druga.c i na kraju povezati
kt

sve u program pod imenom program?


le

Pitanje 9.2.16. Kako se obino obezbeuje da nema viestrukog ukljuivanja


neke datoteke?
E

Pitanje 9.2.17. Koji sve nivoi dosega identifikatora postoje u jeziku C? Koja
su dva najee koriena? Kog nivoa je doseg lokalnih, a kog nivoa doseg
globalnih promenljivih?

Pitanje 9.2.18. Nabrojati tri vrste identifikatora prema vrsti povezivanja.

Pitanje 9.2.19. Koju povezanost imaju lokalne promenljive?

Pitanje 9.2.20. Koji kvalifikator se koristi da bi se onemoguilo da se globalna


promenljiva koristi u nekoj drugoj jedinici prevoenja? Koje povezivanje se u
tom sluju primenjuje?
218 9. Organizacija izvornog i izvrnog programa

Pitanje 9.2.21. Koji kvalifikator se koristi da bi se globalna promenljiva deklar-


isana u drugoj jedinici prevoenja mogla da koristi u drugoj? Koje povezivanje
se u tom sluju primenjuje?

Pitanje 9.2.22. Ako je za neku spoljanju promenljivu navedeno da je static


a za istu tu promenljivu u nekoj drugoj datoteci navedeno da je extern, u kojoj
fazi e biti prijavljena greka?

Pitanje 9.2.23.

5)
Da li je naredna deklaracija ispravna i koliko bajtova je njome
alocirano:

01
1. int a[] = {1, 2, 3};
2. extern int a[];

(2
3. extern int a[] = {1, 2, 3};
Pitanje 9.2.24. Koje vrste ivotnog veka postoje u jeziku C? Koji ivotni vek

je
imaju lokalne promenljive (ako na njih nije primenjen kvalifikator static)?
Koji ivotni vek imaju globalne promenljive?
an
Pitanje 9.2.25. Ukoliko je za neku lokalnu promenljivu naveden kvalifikator
static, ta e biti sa njenom vrednou nakon kraja izvravanja funkcije u
d

kojoj je deklarisana?
iz

Pitanje 9.2.26. Navesti primer funkcije f u okviru koje je deklarisana lokalna


statika promenljiva a i lokalna automatska promenljiva b. Koja je podrazu-
o

mevana vrednost promenljive a, a koja promenljive b?


sk

Pitanje 9.2.27. ta ispisuju naredni programi:

int i = 2;
n

int f() { static int i; return ++i; }


ro

int g() { return ++i; }


int main() {
kt

int i;
for(i=0; i<3; i++)
le

printf("%d", f()+g());
}
E

int i=2;
void f() { static int i; i++; printf("%d",i); }
void g() { i++; printf("%d",i); }
void h() { int i = 0; i++; printf("%d",i); }
int main() {
f(); g(); h(); f(); g(); h();
}
Pitanje 9.2.28. Ukoliko je na globalnu promenljivu primenjen kvalifikator
static, kakva joj je povezanost, ta je njen doseg i koliki joj je ivotni vek?
219 9. Organizacija izvornog i izvrnog programa

9.3 Organizacija izvrnog programa


Ukoliko je od izvornog programa uspeno generisana izvrna verzija, ta,
generisana verzija moe biti izvrena. Zahtev za izvravanje se izdaje opera-
tivnom sistemu (npr. navoenjem imena programa u komandnoj liniji). Pro-
gram koji treba da bude izvren najpre se uitava sa spoljne memorije u radnu
memoriju raunara. Operativni sistem mu stavlja odreenu memoriju na raspo-
laganje, vri se dinamiko povezivanje poziva rutina niskog nivoa rantajm bib-

5)
liotekom, vre se potrebne inicijalizacije i onda izvravanje moe da pone. U
fazi izvravanja mogue je da doe do greaka koje nije bilo mogue detektovati

01
u fazi prevoenja i povezivanja.
Izvrni program generisan za jedan operativni sistem moe se izvravati

(2
samo na tom sistemu. Mogue je da izvrni program koji je generisan za jedan
operativni sistem ne moe da se izvrava na raunaru koji koristi taj operativni
sistem, ali ima arhitekturu koja ne odgovara generisanoj aplikaciji (na primer,

je
64-bitna aplikacija ne moe da se izvrava na 32-bitnom sistemu).
an
9.3.1 Izvrno okruenje operativni sistem i rantajm biblioteka

Kao to je ranije reeno, izvrni program koji je dobijen prevoenjem i


d

povezivanjem ne moe da se izvrava autonomno. Naime, taj program sadri


iz

pozive rutina niskog nivoa koje nisu ugraene u program (jer bi inae izvrni
program bio mnogo vei). Umesto toga, te rutine su raspoloive u vidu rantajm
biblioteke (engl. runtime library). Funkcije rantajm biblioteke obino su reali-
o

zovane kao niz zahteva operativnom sistemu (najee zahteva koji se odnose
sk

na ulaz/izlaz i na memoriju). Kada se program uita u memoriju, vri se


takozvano dinamiko povezivanje (engl. dynamic linking) i pozivi funkcija i
n

promenljivih iz izvrnog programa se povezuju sa funkcijama i promenljivama


ro

iz rantajm biblioteke koja je uitana u memoriju u vreme izvravanja.


Svojstva rantajm biblioteke zavise od kompilatora i operativnog sistema
i u najveem delu nisu specifikovana standardom. tavie, ni granica koja
kt

definie ta je implementirano u vidu funkcija standardne biblioteke a ta u vidu


funkcija rantajm biblioteke nije specifikovana niti kruta i zavisi od konkretnog
le

kompilatora. Funkcije rantajm biblioteke su raspoloive programima gener-


isanim od strane kompilatora ali obino nisu neposredno raspoloive aplika-
E

tivnom programeru. Ako se izvrava vie programa kompiliranih istim kompi-


latorom, svi oni koriste funkcije iste rantajm biblioteke.
Opisani pristup primenjuje se, ne samo na C, ve i na druge programske
jezike. Biblioteka za C ima i svoje specifinosti. U operativnom sistemu Linux,
funkcije C rantajm biblioteke smatraju se i delom operativnog sistema i moraju
da budu podrane. Nasuprot tome, operativni sistem Windows ne sadri nuno
podrku za C bibliteku i ona se isporuuje uz kompilatore. Zbog toga se i delovi
rantajm biblioteke mogu statiki ugraditi u izvrni program (da se ne bi zavisilo
od njenog postojanja na ciljnom sistemu).
Osnovni skup rantajm funkcija koju jezik C koristi je sadran u modulu
220 9. Organizacija izvornog i izvrnog programa

crt0 (od nulta faza za C Run-Time). Zadatak ovog modula je da pripremi


izvravanje programa i obavlja sledee zadatke: priprema standardnih ulaz-
no/izlaznih tokova, inicijalizacija segmenata memorije, priprema argumenata
funkcije main, poziv funkcije main i slino.

9.3.2 Vrste i organizacija podataka

5)
Pojam tipova u viem programskom jeziku kao to je C namenjen je oveku,
a ne raunaru. Prevonjem se sve konstrukcije jezika vieg nivoa prevode na
jednostavne konstrukcije neposredno podrane od strane procesora. Isto se

01
deava i sa tipovima: svi tipovi koji se pojavljuju u programu bie svedeni
na osnovne tipove neposredno podrane od strane procesora obino samo

(2
na cele brojeve i brojeve u pokretnom zarezu. Dakle, u izvornom programu,
promenljivama je pridruen tip, ali u izvrnom programu nema eksplicitnih
informacija o tipovima. Sve informacije o tipovima se razreavaju u fazi pre-

je
voenja i u mainskom kdu se koriste instrukcije za konkretne vrste oper-
anada. Na primer, u izvornom programu, operator + se moe primenjivati nad
an
raznim tipovima podataka, dok u mainskom jeziku postoje zasebne instrukcije
za sabiranje celih brojeva i za sabiranje brojeva u pokretnom zarezu.
Veliina osnovnih vrsta podataka u bajtovima u izvrnom programu zav-
d

isi od opcija prevoenja i treba da bude prilagoena sistemu na kojem e se


iz

izvravati.
11 Faktori koje treba uzeti u obzir su hardver, ali i operativni sistem
raunara na kojem e se program izvravati. Centralni hardver je danas obino
ili 32-bitni ili 64-bitni tj. obino registri procesora, magistrale i podaci tipa int
o

imaju veliinu 32 bita ili 64 bita. I softver (i aplikativni i sistemski) danas je


sk

najee ili 32-bitni ili 64-bitni. Na 64-bitnom hardveru moe da se izvrava


i 32-bitni softver, ali ne i obratno. Pod 32-bitnim softverom se najee pod-
n

razumeva softver koji koristi 32-bitni adresni prostor tj. moe da adresira 232
ro

bajtova memorijskog prostora (analogno vai i za 64-bitni). Dakle, i operativni


sistemi i aplikacije su danas najee 32-bitni ili 64-bitni. S obzirom na to da se
aplikativni programi izvravaju pod okriljem operativnog sistema, oni moraju
kt

biti prilagoeni operativnom sistemu na kojem e se izvravati. Na 64-bitnom


operativnom sistemu mogu da se izvravaju i 32-bitni i 64-bitni programi, a na
le

32-bitnom operativnom sistemu mogu da se izvravaju 32-bitni ali ne i 64-bitni


programi.
E

Na veini 32-bitnih sistema konkretni tipovi podataka imaju brojeve bitova


kao to je navedeno u tabeli 6.1 u glavi 6). Prilikom kompilacije za 64-bitne
sisteme, postoji izbor koji tipove podataka e zauzeti 32, a koji 64 bita. Postoji
nekoliko konvencija koje su imenovane tako da se iz imena vidi koji tipovi po-
dataka zauzimaju 64 bita: ILP64 (integer, long, pointer), LP64 (long, pointer),
LLP64 (long long, pointer). Savremeni Linux sistemi i GCC obino koriste

11 Sistem na kome se vri prevoenje ne mora biti isti kao onaj na kome e se vriti izvra-
vanje i neki prevodioci doputaju da se u zavisnosti od opcija prevoenja kreiraju aplikacije
koje su namenjene izvravanju na sasvim drugaijoj arhitekturi raunara pod drugim oper-
ativnim sistemom.
221 9. Organizacija izvornog i izvrnog programa

LP64 sistem, dok Windows koristi LLP64 sistem.

tip 32 ILP64 LP64 LLP64


char 8 8 8 8
short 16 16 16 16
int 32 64 32 32
long 32 64 64 32
long long 64 64 64 64

5)
pointer 32 64 64 64

01
Za precizno specifikovanje broja bitova mogu se koristiti tipovi iz zaglavlja
<stdint.h>.

(2
Kada se koristi kompilator GCC, podrazumevani broj bitova odgovara sis-
temu na kojem se vri prevoenje. Ovo se moe promeniti opcijama -m32 i -m64
kojima se moe zadati da se generisani kd aplikacije bude 32-bitni ili 64-bitni

je
(bez obzira da li se prevoenje vri na 32-bitnom ili 64-bitnom sistemu).
an
9.3.3 Organizacija memorije

Nain organizovanja i korienja memorije u fazi izvravanja programa moe


d

se razlikovati od jednog do drugog operativnog sistema. Tekst u nastavku


iz

odnosi se, ako to nije drugaije naglaeno, na irok spektar platformi, pa su,
zbog toga, nainjena i neka pojednostavljivanja.
Kada se izvrni program uita u radnu memoriju raunara, biva mu dodel-
o

jena odreena memorija i zapoinje njegovo izvravanje. Dodeljena memorija


sk

organizovana je u nekoliko delova koje zovemo segmenti ili zone:

segment kda (engl. code segment ili text segment) ;


ro

segment podataka (engl. data segment) ;

stek segment (engl. stack segment) ;


kt

hip segment (engl. heap segment).


le

U nastavku e biti opisana prva tri, dok e o hip segmentu biti vie rei u
poglavlju 10.9, posveenom dinamikoj alokaciji memorije.
E

Segmentacija je u odreenoj vezi sa ivotnim vekom promenljivih (o kome


je bilo rei u poglavlju 9.2.3): promenljive statikog ivotnog veka se obino
uvaju u segmentu podataka, promenljive automatskog ivotnog veka se obino
uvaju u stek segmentu, dok se promenljive dinamikog ivotnog veka obino
uvaju u hip segmentu.

Segment kda
Fon Nojmanova arhitektura raunara predvia da se u memoriji uvaju po-
daci i programi. Dok su ostala tri segmenta predviena za uvanje podataka,
u segmentu kda se nalazi sm izvrni kd programa njegov mainski kd
222 9. Organizacija izvornog i izvrnog programa

koji ukljuuje mainski kd svih funkcija programa (ukljuujui kd svih ko-


rienih funkcija koje su povezane statiki). Na nekim operativnim sistemima,
ukoliko je pokrenuto vie instanci istog programa, onda sve te instance dele isti
prostor za izvrni kd, tj. u memoriji postoji samo jedan primerak kda. U
tom sluaju, za svaku instancu se, naravno, zasebno uva informacija o tome
do koje naredbe je stiglo izraunavanje.

Segment podataka

5)
U segmentu podataka uvaju se odreene vrste promenljivih koje su zajed-

01
nike za ceo program (one koje imaju statiki ivotni vek, najee globalne
promenljive), kao i konstantni podaci (najee konstantne niske). Ukoliko

(2
se istovremeno izvrava vie instanci istog programa, svaka instanca ima svoj
zaseban segment podataka. Na primer, u programu

Program 9.7.
#include <stdio.h>
je
an
int a;
int main() {
int b;
d

static double c;
iz

printf("Zdravo\n");
return 0;
}
o
sk

u segmentu podataka e se nalaziti promenljive a i c, kao i konstantna niska


"Zdravo\n" (sa zavrnom nulom, a bez navodnika). Promenljiva b je lokalna
n

automatska i ona e se uvati u segmentu steka. Ukoliko se ista konstantna


ro

niska javlja na vie mesta u programu, standard ne definie da li e za nju


postojati jedna ili vie kopija u segmentu podataka.
kt

Stek segment
le

U stek segmentu (koji se naziva i stek poziva (engl. call stack) ili programski
stek ) uvaju se svi podaci koji karakteriu izvravanje funkcija. Podaci koji
E

odgovaraju jednoj funkciji (ili, preciznije, jednoj instance jedne funkcije jer,
na primer, rekurzivna funkcija moe da poziva samu sebe i da tako u jednom
trenutku bude aktivno vie njenih instanci) organizovani su u takozvani stek
okvir (engl. stack frame). Stek okvir jedne instance funkcije obino, izmeu
ostalog, sadri:

argumente funkcije;

lokalne promenljive (promenljive deklarisane unutar funkcije);

meurezultate izraunavanja;
223 9. Organizacija izvornog i izvrnog programa

adresu povratka (koja ukazuje na to odakle treba nastaviti izvravanje


programa nakon povratka iz funkcije);

adresu stek okvira funkcije pozivaoca.

Stek poziva je struktura tipa LIFO ("last in - first out")


12 . To znai da
se stek okvir moe dodati samo na vrh steka i da se sa steka moe ukloniti
samo okvir koji je na vrhu. Stek okvir za instancu funkcije se kreira onda kada

5)
funkcija treba da se izvri i taj stek okvir se oslobaa (preciznije, smatra se
nepostojeim) onda kada se zavri izvravanje funkcije.

01
Ako izvravanje programa poinje izvravanjem funkcije main, prvi stek
okvir se kreira za ovu funkciju. Ako funkcija main poziva neku funkciju f,
na vrhu steka, iznad stek okvira funkcije main, kreira se novi stek okvir za

(2
ovu funkciju (ilustrovano na slici 9.1). Ukoliko funkcija f poziva neku treu
funkciju, onda e za nju biti kreiran stek okvir na novom vrhu steka. Kada
se zavri izvravanje funkcije f, onda se vrh steka vraa na prethodno stanje i

je
prostor koji je zauzimao stek okvir za f se smatra slobodnim (iako on nee biti
an
zaista obrisan).
d
iz

Vrh steka

o

f(),
sk

njeni argumenti,
Vrh steka lokalne promenljive, Vrh steka
...
n

main(), main(), main(),


ro

njeni argumenti, njeni argumenti, njeni argumenti,


lokalne promenljive, lokalne promenljive, lokalne promenljive,
kt

... ... ...


le

Slika 9.1: Organizacija steka i ilustracija izvravanja funkcije. Levo: tokom


izvravanja funkcije main(). Sredina: tokom izvravanja funkcije f()
E

neposredno pozvane iz funkcije main(). Desno: nakon povratka iz funkcije


f() nazad u funkciju main().

Veliina stek segmenta obino je ograniena. Zbog toga je poeljno izbe-


gavati smetanje jako velikih podataka na segment steka. Na primer, sasvim
je mogue da u programu u nastavku, sa leve strane, niz a nee biti uspeno
alociran i doi e do greke prilikom izvravanja programa, dok e u programu

12 Ime stack je zajedniko ime za strukture podataka koje su okarakterisane ovim nainom
pristupa.
224 9. Organizacija izvornog i izvrnog programa

sa desne strane niz biti smeten u segment podataka i sve e tei oekivano.
Predefinisana veliina steka C prevodioca se moe promeniti zadavanjem odgo-
varajue opcije.

int main() { int a[1000000];


int a[1000000]; int main() {
... ...
} }

5)
Opisana organizacija steka omoguava jednostavan mehanizam meusobnog

01
pozivanja funkcija, kao i rekurzivnih poziva.

Prenos argumenata u funkciju preko steka.

(2
Ilustrujmo prenos argume-
nata u funkciju na sledeem jednostavnom primeru.

Program 9.8.
int f(int a[], int n) {
je
an
int i, s = 0;
for (i = 0; i < n; i++)
s += ++a[i];
d

return s;
iz

int main() {
o

int a[] = {1, 2, 3};


sk

int s;
s = f(a, sizeof(a)/sizeof(int));
n

printf("s = %d, a = {%d, %d, %d}\n",


s, a[0], a[1], a[2]);
ro

return 0;
}
kt

U funkciji main deklarisane su dve lokalne promenljive: niz a i ceo broj s.


le

Na poetku izvravanja funkcije main na steku se nalazi samo jedan stek okvir
sledeeg oblika
13 :
E

main: 114: ...


112: ? <- s
108: 3
104: 2
100: 1 <- a
Nakon poziva funkcije f, iznad stek okvira funkcije main postavlja se stek
okvir funkcije f. Ona ima etiri lokalne promenljive (od ega dve potiu od

13 U tekstu se pretpostavlja da se sve lokalne promenljive uvaju na steku, mada u realnoj


situaciji kompilator moe da odlui da se neke promenljive uvaju u registrima procesora.
225 9. Organizacija izvornog i izvrnog programa

argumenta). Argumenti se inicijalizuju na vrednosti koje su prosleene pri-


likom poziva. Primetimo da je umesto niza preneta samo adresa njegovog
poetka, dok je vrednost parametra n postavljena na 3 jer je to vrednost izraza
sizeof(a)/sizeof(int) u funkciji main (iako se sintaksiki ovaj izraz nalazi
u okviru poziva funkcije f, on nema nikakve veze sa funkcijom i izraunava se
u funkciji main).

f: 134: ...
130: 0 <- s

5)
126: ? <- i
122: 3 <- n

01
118: 100 <- a
main: 114: ...

(2
112: ? <- s
108: 3
104: 2

je
100: 1 <- a
Nakon izvravanja koraka petlje, stanje steka je sledee.
an
f: 134: ... f: 134: ...
130: 2 <- s 130: 5 <- s
d

126: 0 <- i 126: 1 <- i


122: 3 <- n 122: 3 <- n
iz

118: 100 <- a 118: 100 <- a


main: 114: ... main: 114: ...
o

112: ? <- s 112: ? <- s


sk

108: 3 108: 3
104: 2 104: 3
100: 2 <- a 100: 2 <- a
n
ro

f: 134: ...
130: 9 <- s
kt

126: 2 <- i
122: 3 <- n
le

118: 100 <- a


main: 114: ...
E

112: ? <- s
108: 4
104: 3
100: 2 <- a

Primetimo da originalna vrednost niza a u funkciji main menja tokom


izvravanja funkcije f, zato to se niz ne kopira ve se u funkciju prenosi adresa
njegovog poetka.
Kada funkcija f zavi svoje izvravanje, njen stek okvir je:
226 9. Organizacija izvornog i izvrnog programa

f: 134: ...
130: 9 <- s
126: 3 <- i
122: 3 <- n
118: 100 <- a
i tada je potrebno vratiti vrednost s pozivaocu (u ovom sluaju funkciji main).
Ovo se najee postie tako to se vrednost promenljive s upie u registar ax
(preciznije, eax ili rax), odakle je main preuzima i naredbom dodele upisuje u

5)
svoju promenljivu s. Kada funkcija f zavri svoje izvravanje, stanje steka je:

main: 114: ...

01
112: 9 <- s
108: 4

(2
104: 3
100: 2 <- a
Nakon toga dolazi do poziva funkcije printf. Na stek okvir funkcije main

je
postavlja se njen stek okvir i prosleuju se argumenti. Prvi argument je adresa
konstantne niske (koja se nalazi negde u segmentu podataka), a ostali su kopije
an
etiri navedene vrednosti iz funkcije main. Nakon ispisa, uklanja se stek okvir
funkcije printf, a zatim i funkcije main, poto se dolo do njenog kraja.
d

Implementacija rekurzije.
iz

Navedeno je da je rekurzija situacija u kojoj


jedna funkcija poziva sebe samu direktno ili indirektno. Razmotrimo, kao
primer, funkciju koja rekurzivno izraunava faktorijel:
14
o

Program 9.9.
sk

#include <stdio.h>
n

int faktorijel(int n) {
ro

if (n <= 0)
return 1;
kt

else
return n*faktorijel(n-1);
le

}
E

int main() {
int n;
while(scanf("%d",&n) == 1)
printf("%d! = %d\n", n, faktorijel(n));
return 0;
}

Ukoliko je funkcija faktorijel pozvana za argument 5, onda e na steku


poziva da se formira isto toliko stek okvira, za pet nezavisnih instanci funkcije.

14 Vrednost faktorijela se, naravno, moe izraunati i iterativno, bez korienja rekurzije.
227 9. Organizacija izvornog i izvrnog programa

U svakom stek okviru je drugaija vrednost argumenta n. No, iako u jednom


trenutku ima 5 aktivnih instanci funkcije faktorijel, postoji i koristi se samo
jedan primerak izvrnog kda ove funkcije (u kd segmentu), a svaki stek okvir
pamti za svoju instancu dokle je stiglo izvravanje funkcije, tj. koja je naredba
u kd segmentu tekua.

9.3.4 Primer organizacije objektnih modula i izvrnog programa

5)
U ovom poglavlju emo pokuati da proces kompilacije i povezivanja ogolimo
do krajnjih granica tako to emo pokazati kako je mogue videti mainski

01
kd tj. sadraj objektnih modula i izvrnog programa nastalih kompilacijom
sledeeg jednostavnog programa
15 . Detalji ovog primera svakako u velikoj meri

(2
prevazilaze osnove programiranja u programskom jeziku C, ali zainteresovanom
itaocu mogu da poslue da prilino demistifikuje proces prevoenja i izvra-
vanja C programa.

je
Program 9.10.
an
#include <stdio.h>
int n = 10, sum;
d

int main() {
iz

int i;
for (i = 0; i < n; i++)
sum += i;
o

printf("sum = %d\n", sum);


sk

return 0;
}
n
ro

itav izvorni program sadran je u jednoj jedinici prevoenja, tako da je za


dobijanje izvrnog kda potrebno prevesti tu jedinicu i povezati nastali objek-
kt

tni modul sa potrebnim delovima (objektnim modulima) unapred kompilirane


standardne biblioteke.
Nakon kompilacije sa gcc -c program.c, sadraj dobijenog objektnog mod-
le

ulaprogram.o moe se ispitati korienjem pomonih programa poput nm ili


objdump.
E

Na primer, nm --format sysv program.o daje rezultat

Name Value Class Type Size Section


main |00000000| T | FUNC |00000050| .text
n |00000000| D | OBJECT |00000004| .data
printf | | U | NOTYPE | | *UND*
sum |00000004| C | OBJECT |00000004| *COM*
15 Primer podrazumeva korienje GCC prevodioca na 32-bitnom Linux sistemu. Ve na
64-bitnim Linux sistemima stvari izgledaju donekle drugaije.
228 9. Organizacija izvornog i izvrnog programa

Tabela pokazuje da u objektnom modulu postoje etiri imenovana simbola.


Simbol main je funkcija, nalazi se u segmentu kda (na ta ukazuju sekcija
.text i klasa T) i to na njegovom samom poetku (na ta ukazuje vrednost
00000000) i mainske instrukcije dobijene njegovim prevoenjem zauzimaju
(50)16 bajtova. Simbol n nalazi se u segmentu podataka (na ta ukazuje sek-
cija .data), na njegovom samom poetku (na ta ukazuje vrednost 00000000)
i inicijalizovan je (na ta ukazuje klasa D). Simbol printf je simbol ija je
definicija nepoznata (na ta ukazuje oznaka sekcije *UND*, i klase U). Simbol

5)
sum se nalazi u zoni neinicijalizovanih podataka (na ta ukazuje klasa C i sekcija
*COM*) naime, u ranijim razmatranjima smatrali smo da se neinicijalizovani

01
podaci nalaze u istom segmentu podataka kao i inicijalizovani, to je tano,
ali dublji uvid pokazuje da se oni cuvaju u posebnom delu segmenta podataka

(2
(esto se ovaj segment naziva .bss segment). S obzirom na to da ovaj segment
podrazumevano treba da bude ispunjen nulama, za razliku od inicijalizovanih
podataka koji moraju biti upisani u izvrnu datoteku, kod neinicijalizovanih po-

je
dataka, umesto da se nule upisuju u izvrnu datoteku, u njoj se samo naglaava
njegova veliina. Prilikom uitavanja programa u glavnu memoriju, pre pozi-
an
vanja funkcije main vri se inicijalizacija ovog segmenta na nulu. Primetimo
i da ime lokalne automatske promenljive i uopte nije vidljivo u objektnom
modulu (to se i moglo oekivati, jer je ova promenljiva bez povezanosti).
d

Mainski kd funkcije main moe da se vidi, na primer, komandom


iz

objdump -d program.o.

00000000 <main>:
o

0: 55 push ebp
sk

1: 89 e5 mov ebp,esp
3: 83 e4 f0 and esp,0xfffffff0
6: 83 ec 20 sub esp,0x20
n

9: c7 44 24 1c 00 00 00 mov DWORD PTR [esp+0x1c],0x0


ro

10: 00
11: eb 16 jmp 29 <main+0x29>
13: 8b 15 00 00 00 00 mov edx,DWORD PTR ds:0x0
kt

19: 8b 44 24 1c mov eax,DWORD PTR [esp+0x1c]


1d: 01 d0 add eax,edx
le

1f: a3 00 00 00 00 mov ds:0x0,eax


24: 83 44 24 1c 01 add DWORD PTR [esp+0x1c],0x1
E

29: a1 00 00 00 00 mov eax,ds:0x0


2e: 39 44 24 1c cmp DWORD PTR [esp+0x1c],eax
32: 7c df jl 13 <main+0x13>
34: a1 00 00 00 00 mov eax,ds:0x0
39: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
3d: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0
44: e8 fc ff ff ff call 45 <main+0x45>
49: b8 00 00 00 00 mov eax,0x0
4e: c9 leave
4f: c3 ret
229 9. Organizacija izvornog i izvrnog programa

Adrese u generisanom kdu su relativne (npr. adrese skokova su sve date u


odnosu na poetak funkcije main) i neke od njih e nakon povezivanja i tokom
izvravanja biti promenjene. Na primer, u instrukciji 29: mov eax,ds:0x0
vri se kopiranje odreene vrednosti iz memorije u registar eax. Trenutni kd
govori da je to sadraj sa adrese 0 (u odnosu na poetak segmenta podataka
ds), ali u izvrnom programu to nee ostati tako. Zadatak linkera je da na
ovom mestu adresu 0 zameni adresom promenljive n (jer je zadatak ove in-
strukcije upravo da vrednost promenljive n prepie u registar eax). Slino

5)
tome, na mesto poziva funkcije printf u mainskom kdu generisan je poziv
44: call 45 <main+0x45> koji nije ispravan i nema smisla jer ukazuje up-
ravo na adresu argumenta tog poziva (koji je na 44). Zadatak linkera je da

01
ovaj poziv korektno razrei. Spisak stavki koje linker treba da razrei mogu da
se vide komandom objdump -r program.o (tzv. relokacije) i sam kd nema

(2
nikakvo smisleno znaenje dok se u obzir ne uzme tabela relokacije i dok se sve
navedene stavke ne razree (to je zadatak linkera).

RELOCATION RECORDS FOR [.text]:

je
OFFSET TYPE VALUE
00000015 R_386_32 sum
an
00000020 R_386_32 sum
0000002a R_386_32 n
00000035 R_386_32 sum
d

00000040 R_386_32 .rodata


iz

00000045 R_386_PC32 printf


o

RELOCATION RECORDS FOR [.eh_frame]:


sk

OFFSET TYPE VALUE


00000020 R_386_PC32 .text
n

Iz ovoga se vidi se da je jedan od zadataka linkera da korektnu adresu mainskog


printf umetne 45 u segment kda (.text) ime e
ro

kda funkcije na adresu


poziv funkcije printf postati ispravan. Slino, potrebno je razreiti i adresu
n 2a
kt

promenljive koja se koristi na mestu u maniskom kdu (za koju je, kao
to smo opisali, trenutno pretpostavljeno da se nalazi na adresi 0 u segmentu
podataka, to ne mora biti sluaj u konanom kdu), adresu promenljive sum
le

i to na tri mesta na kojima joj se pristupa (15, 20 i 35) i adresu poetka


.rodata (engl. read only data) u kome se nalazi konstantna niska
E

segmenta
sum = %d\n, za koji se trenutno pretpostavlja da je 0, a koja se koristi na
mestu 40 u mainskom kdu. Takoe, potrebno je da linker postavi i pravu
adresu segmenta kda (.text) u zaglavlju izvrne datoteke (.eh_frame).
Iako nije od presudnog znaaja za programiranje u C-u, analiza prethodnog
kda moe biti zanimljiva i pokazuje kako se pojednostavljeno opisani meha-
nizmi funkcionisanja izvrnog okruenja, realno odvijaju u praksi. Prevedeni
mainski kd podrazumeva da stek u memoriji raste od viih ka niim adresama
(engl. downward growing). Registar esp sadri uvek adresu vrha steka, dok reg-
istar ebp sadri adresu poetka stek okvira tekue funkcije (koja je potrebna
pri zavretku funkcije da bi stek okvir mogao da se ukloni). Prve etiri in-
230 9. Organizacija izvornog i izvrnog programa

strukcije predstavljaju tzv. prolog tekue funkcije i slue da pripreme novi stek
okvir (uva se adresa poetka starog stek okvira i to opet na steku, adresa
poetka novog stek okvira postaje adresa tekueg vrha steka, a vrh steka se
podie (ka manjim adresama) da bi se napravio prostor za uvanje lokalnih
promenljivih i slinih podataka na steku). Prevedeni kd podrazumeva da se
promenljiva i nalazi na steku i to odmah iznad sauvane vrednosti poetka
prvobitnog stek okvira (to je adresa esp+0x1c nakon pomeranja vrha steka).
Naredba u liniji 9 je inicijalizacija petlje na vrednost 0. Nakon nje, skae se

5)
na proveru uslova u liniji 29. Prvo se kopira vrednost promenljive n u registar
eax, a zatim se vri poreenje vrednosti promenljive i (smetene u steku na

01
adresi esp+0x1c) sa registrom eax. i manje od n, vri se povratak
Ukoliko je
na telo petlje (instrukcije od linije 13 do 23) i korak (instrukcija 24). Telo

(2
petlje kopira vrednost promenljive sum (sa adrese koja e biti promenjena) u
registar edx, kopira vrednost promenljive i (sa adrese esp+0x1c) u registar
eax, sabira dva registra i rezultat iz registra eax kopira nazad na adresu pro-

je
menljive sum. Korak uveava vrednost promenljive i (na adresi esp+0x1c) za
jedan. Po zavretku petlje, kada uslov petlje vie nije ispunjen prelazi se na
an
poziv funkcije printf (instrukcije od adrese 34 do 48). Poziv funkcije tee tako
to se na stek (u dva mesta ispod njegovog vrha) upisuju argumenti poziva (u
obratnom poretku vrednost promenljive sum koja se kopira preko registra x),
d

i zatim adresa format niske (iako je u trenutnom kdu za obe adrese upisana
iz

0 to e biti promenjeno tokom povezivanja). Poziv call iznad ova 3 argume-


nata postavlja adresu povratka i prenosi kontrolu izvravanja na odgovarajuu
adresu kda (koju e biti ispravno postavljena tokom povezivanja). Funkcija
o

main vraa povratnu vrednost tako to je u liniji 49 upisuje u registar eax.


sk

Instrukcije leave i ret main. Instrukcija leave


predstavljaju epilog funkcije
skida stek okvir tako to u registar esp upisuje vrednost koja se nalazi u ebp, i
n

zatim restaurie vrednost poetka prethodnog stek okvira tako to je skida sa


ro

steka (jer se pretpostavlja da je u prologu funkcije ova vrednost postavljena na


stek). Instrukcija ret zatim tok izvravanja prenosi na adresu povratka (koja
se sada nalazi na vrhu steka).
kt

Dakle, u fazi prevoenja se svaki poziv spoljanje funkcije i svako referisanje


na spoljanje promenljive i konstante ostavlja nerazreenim i od linkera se
le

oekuje da ovakve reference razrei. Ako se u datoteci sa ulaznim programom


poziva funkcija ili koristi promenljiva koja nije u njoj definisana (ve samo
E

deklarisana, kao to je sluaj sa funkcijom printf u prethodnom primeru),


kreiranje objektnog modula bie uspeno, ali samo od tog objektnog modula
nije mogue kreirati izvrni program (jer postoje pozivi funkcije iji main-
ski kd nije poznat usled nedostatka definicije ili postoje promenljive za koju
nije odvojena memorija, tako da referisanje na njih u objekntim modulima nisu
ispravna). U fazi povezivanja, linker razreava pozive funkcija i adrese promen-
ljivih i konstanti traei u svim navedenim objektnim modulima (ukljuujui i
objektne module standardne biblioteke) odgovarajuu definiciju (maniski kd)
takve funkcije tj. definiciju (dodeljenu adresu) takve promenljive.
231 9. Organizacija izvornog i izvrnog programa

Ukoliko su svi takvi zahtevi uspeni, linker za sve pozive funkcija upisuje
adrese konkretne funkcije koje treba koristiti, za sva korienja promenljivih
ukazuje na odgovarajue konkretne promenljive i povezuje sve objektne module
u jedan izvrni program.
Kd dobijen povezivanjem obino i dalje nije u stanju da se autonomno
izvrava. Naime, funkcije standardne biblioteke su implementirane tako da
koriste odreen broj rutina niskog nivoa, ali se sve te rutine ne ugrauju u
izvrni kd jer bi on tako bio ogroman. Umesto da se te rutine ugrauju u

5)
izvrni kd programa, one ine tzv. rantajm biblioteku i pozivaju se dinamiki,
tek u fazi izvravanja (videti poglavlje 9.3.1).

01
Pretpostavimo da je prethodno opisani objektni modul povezan na sledei
nain: gcc -o program program.o, dajui izvrni program program. Ako se

(2
on pregleda (npr. komandom nm), moe da se vidi da postoji znatno vie imeno-
vanih simbola (npr. _start, printf@@GLIBC_2.0 itd.). Ako se pogleda main-
ski kd funkcije main (npr. komandom objdump), moe da se vidi da su sporne

je
adrese uspeno razreene. Meutim, na mestu poziva funkcije printf poziva se
funkcija printf@plt, a ona poziva nedefinisanu funkciju printf@@GLIBC_2.0.
an
Naime, ova funkcija implementirana je u rantajm biblioteci i njena adresa bie
povezana dinamiki, na poetku izvravanja programa.
d
iz

9.3.5 Greke u fazi izvravanja

U fazi pisanja izvornog i generisanja izvrnog programa od izvornog nije


mogue predvideti neke greke koje se mogu javiti u fazi izvravanja programa.
o

Osim to je mogue da daje pogrean rezultat (emu je najei razlog pogrean


sk

algoritam ili njegova implementacija), mogue je i da uspeno kompiliran pro-


gram prouzrokuje greke tokom rada koje uzrokuju prekid njegovog izvra-
n

vanja. Te greke mogu biti posledica greke programera koji nije predvideo
ro

neku moguu situaciju a mogu biti i greke koje zavise od okruenja u kojem
se program izvrava.
Jedna od najeih greaka u fazi izvravanja je nedozvoljeni pristup mem-
kt

oriji. Ona se, na primer, javlja ako se pokua pristup memoriji koja nije dodel-
jena programu ili ako se pokua izmena memorije koja je dodeljena programu,
le

ali samo za itanje (engl. read only). Operativni sistem detektuje ovakve
situacije i signalizira, to najee prouzrokuje prekid programa uz poruku
E

Segmentation fault

Najei uzrok nastanka ovakve greke je pristup neadekvatno alociranom bloku


memorije (npr. ako je niz definisan sa int a[10];, a pokua se pristup el-
ementu a[1000] ili se pokua pristup nealociranoj bloku memorije za koji
je predviena dinamika alokacija o emu e biti rei u glavi 10), derefer-
enciranje NULL pokazivaa (o emu e biti rei u glavi 10), ili prepunjenje
steka usled velikog broja funkcijskih poziva (nastalih najee neadekvatno
232 9. Organizacija izvornog i izvrnog programa

obraenim izlaskom iz rekurzije ili definisanjem prevelikih lokalnih automatskih


promenljivih). Odgovornost za nastanak ovakve greke obino je na progameru.
Ukoliko tokom izvravanja programa doe do (celobrojnog) deljenja nulom,
bie prijavljena greka:

Floating point exception

i program e prekinuti rad. Neposredna odgovornost za ovu greku pripada au-

5)
toru programa jer je prevideo da moe doi do deljenja nulom i nije u programu
predvideo akciju koja to spreava.

01
U fazi izvravanja moe doi i do greaka u komunikaciji sa periferijama (na
primer, pokuaj itanja iz nepostojee datoteke na disku) itd.

(2
Pitanja i zadaci za vebu
Pitanje 9.3.1.

je
Opisati ukratko memorijske segmente koji se dodeljuju svakom
programu koji se izvrava. ta se, tokom izvravanja programa, uva: u stek
an
segmentu, u hip segmentu, u segmentu podataka?

Pitanje 9.3.2. U kom segmentu memorije se tokom izvravanja programa u-


d

vaju: lokalne promenljive;


iz

Pitanje 9.3.3. Da li se memorijski prostor za neku promenljivu moe nalaziti


u stek segmentu?
o

Pitanje 9.3.4. Navesti barem tri vrste podataka koje se uvaju u svakom stek
sk

okviru.

Pitanje 9.3.5. Kako se u prenose argumenti funkcija? Objasniti kako se taj


n

proces odvija u memoriji raunara?


ro

Pitanje 9.3.6. Ukoliko se na steku poziva istovremeno nae vie instanci neke
f, f?
kt

funkcije kakva mora biti ta funkcija

Pitanje 9.3.7. Da li se za svaku instancu rekurzivne funkcije stvara novi stek


le

okvir? Da li se za svaku instancu rekurzivne funkcije stvara nova kopija funkcije


u kd segmentu?
E

Pitanje 9.3.8. Da li se tokom izvravanja programa menja sadraj: (a) seg-


menta kda, (b) segmenta podataka, (c) stek segmenta?

Pitanje 9.3.9. U kom segmentu memorije ima izmena prilikom pozivanja


funkcija? Ukoliko se umesto funkcija koriste makroi, da li e u tom segmentu
memorije biti izmena u fazi izvravanja?

Pitanje 9.3.10. U kom segmentu memorije se uva izvrni mainski kd pro-


grama? U kojim segmentima memorije se uvaju podaci? Kakva je veza izmeu
ivotnog veka promenljivih i segmenata memorije u kojima se uvaju? U kom
233 9. Organizacija izvornog i izvrnog programa

segmentu memorije se uvaju automatske promenljive, u kom statike promen-


ljive, a u kom dinamike?

Pitanje 9.3.11. U kom segmentu memorije se uvaju argumenti funkcija i


kakav im je ivotni vek? U kom segmentu memorije se uvaju mainske in-
strukcije prevedenog C programa?

Pitanje 9.3.12. Za svaku promenljivu u narednom kdu rei koji joj je nivo

5)
dosega, kakav joj je ivotni vek u kom memorijskom segmentu je smetena i
kakva joj je inicijalna vrednost.

01
int a;
int f() {

(2
double b; static float c;
}

je
an
d
iz
o
sk
n
ro
kt
le
E
Glava 10

5)
Pokazivai i dinamika alokacija

01
memorije

(2
je
an
U jeziku C, pokazivai imaju veoma znaajnu ulogu i teko je napisati iole
kompleksniji program bez upotrebe pokazivaa. Dodatne mogunosti u radu
d

sa pokazivaima daje dinamika alokacija memorije. Programer, u radu sa


pokazivaima, ima veliku slobodu i irok spektar mogunosti. To otvara i
iz

veliki prostor za efikasno programiranje ali i za greke.

10.1 Pokazivai i adrese


o
sk

Memorija raunara organizovana je u niz uzastopnih bajtova. Uzastopni


bajtovi mogu se tretirati kao jedinstven podatak. Na primer, dva (ili etiri, u
n

zavisnosti od sistema) uzastopna bajta mogu se tretirati kao jedinstven podatak


ro

celobrojnog tipa. Pokazivai (engl. pointer) predstavljaju tip podataka u C-u


ije su vrednosti memorijske adrese. Na 16-bitnim sistemima adrese zauzimaju
kt

dva bajta, na 32-bitnim sistemima etiri, na 64-bitnim osam, i slino. Iako


su, sutinski, pokazivake vrednosti (adrese) celi brojevi, pokazivaki tipovi
le

se razlikuju od celobrojnih i ne mogu se meati sa njima. Jezik C razlikuje


vie pokazivakih tipova. Za svaki tip podataka (i osnovni i korisniki) postoji
E

odgovarajui pokazivaki tip. Pokazivai implicitno uvaju informaciju o tipu


1
onoga na ta ukazuju . Ipak, informacija o tipu pokazivaa (kao i za sve os-
novne tipove) ne postoji tokom izvravanja programa tu informaciju iskoristi
kompilator tokom kompilacije da u mainskom kdu generie instrukcije koje
odgovaraju konkretnim tipovima. Dakle, tokom izvravanja programa, pokazi-
vaka promenljiva zauzima samo onoliko bajtova koliko zauzima podatak o
adresi.

1 Jedino pokaziva tipa void* nema informaciju o tipu podataka na koji ukazuje, i o tom
tipu e biti rei u nastavku.

234
235 10. Pokazivai i dinamika alokacija memorije

Tip pokazivaa koji ukazuje na podatak tipa int zapisuje se int *. Slino
vai i za druge tipove. Prilikom deklaracije, nije bitno da li postoji razmak
izmeu zvezdice i tipa ili zvezdice i identifikatora i kako god da je napisano,
zvezdica se vezuje uz identifikator. U narednom primeru, p1, p2 i p3 su pokazi-
vai koji ukazuju na int dok je p4 tipa int:
int *p1;
int* p2;

5)
int* p3, p4;

01
Pokazivaka promenljiva moe da sadri adrese promenljivih ili elemenata
niza
2 i za to se koristi operator &. Unarni operator &, operator referenciranja ili
adresni operator vraa adresu svog operanda. On moe biti primenjen samo na

(2
promenljive i elemente niza, a ne i na izraze ili konstante. Na primer, ukoliko
je naredni kd deo neke funkcije

je
int a=10, *p;
p = &a;
an
onda se prilikom izvravanja te funkcije, za promenljive a i p rezervie prostor
a 10, a u prostor za p
d

u njenom stek okviru. U prostor za se upisuje vrednost


adresa promenljive a. Za promenljivu p tada kaemo da pokazuje na a.
iz

Unarni operator * (koji zovemo operator dereferenciranja 3 ) se primenjuje


na pokazivaku promenljivu i vraa sadraj lokacije na koju ta promenljiva
o

pokazuje, vodei rauna o tipu. Dereferencirani pokaziva moe biti l-vrednost


sk

i u tom sluaju izmene dereferenciranog pokazivaa utiu neposredno na prostor


na koji se ukazuje. Na primer, nakon kda
n

int a=10, *p;


p = &a;
ro

*p = 5;
kt

promenljiva p ukazuje na a, a u lokaciju na koju ukazuje p je upisana vrednost


5. Time je i vrednost promenljive a postala jednaka 5.
le

Dereferencirani pokaziva nekog tipa, moe se pojaviti u bilo kom kontekstu


u kojem se moe pojaviti podatak tog tipa. Na primer, u datom primeru
E

ispravna bi bila i naredba *p = *p+a+3;.


Kao to je ve reeno, pokazivaki i celobrojni tipovi su razliiti. Tako
je, na primer, ako je data deklaracija int *pa, a;, naredni kd neispravan:
pa = a;. Takoe, neispravno je i a = pa;, kao i pa = 1234. Mogue je ko-
ristiti eksplicitne konverzije (na primer a = (int)pa; ili pa = (int*)a;), ali
ove mogunosti treba veoma oprezno koristiti i iskljuivo sa jasnim ciljem (koji

2 Postoje i pokazivai na funkcije i u tom sluaju pokazivaka promenljiva ukazuje na


adresu funkcije u segmentu koda, u emu e vie rei biti u poglavlju 10.8.
3 Simbol * koristi se i za oznaavanje pokazivakih tipova i za operator dereferenciranja i
treba razlikovati ove njegove dve razliite uloge.
236 10. Pokazivai i dinamika alokacija memorije

ne moe da se pogodno ostvari drugaije). Jedina celobrojna vrednost koja se


moe koristiti i kao pokazivaka vrednost je vrednost 0 mogue je dodeliti
nulu pokazivakoj promenljivoj i porediti pokaziva sa nulom. Uvek se podrazu-
meva da pokaziva koji ima vrednost 0 ne moe da pokazuje ni na ta smisleno
(tj. adresa 0 ne smatra se moguom). 0 nije mogue
Pokaziva koji ima vrednost
dereferencirati, tj. pokuaj dereferenciranja dovodi do greke tokom izvravanja
programa (najee segmentation fault). Umesto konstante 0 obino se ko-
risti simbolika konstanta NULL, definisana u zaglavlju <stdio.h>, kao jasniji

5)
pokazatelj da je u pitanju specijalna pokazivaka vrednost.
Pokazivai se mogu eksplicitno (pa ak i implicitno) konvertovati iz jednog

01
u drugi pokazivaki tip. Na primer int a; char* p = (char*)&a; ili ak
int a; char* p = &a;, pri emu prevoenjem drugog kda dobijamo upo-

(2
zorenje. Meutim, prilikom konverzije pokazivaa vri se samo prosta do-
dela adrese, bez ikakve konverzije podataka na koje pokaziva ukazuje, to
esto moe dovesti do neeljenih efekata, te stoga ovakve konverzije pokazivaa

je
neemo esto koristiti. Konverzije izmeu pokazivaa na podatke i pokazivaa
na funkcije (o kojima e vie biti rei u poglavlju 10.8) nisu doputene (dovode
an
do nedefinisanog ponaanja programa).
U nekim sluajevima, poeljno je imati mogunost opteg pokazivaa,
tj. pokazivaa koji moe da ukazuje na promenljive razliitih tipova. Za to
d

se koristi tip void*. Izraze ovog tipa je mogue eksplicitno konvertovati u


iz

bilo koji konkretni pokazivaki tip, a i ukoliko je potrebno, a nije upotrebljena


eksplicitna konverzija bie primenjena implicitna konverzija prilikom dodele.
Naravno, nije mogue vriti dereferenciranje pokazivaa tipa void* jer nije
o

mogue odrediti tip takvog izraza kao ni broj bajtova u memoriji koji pred-
sk

stavljaju njegovu vrednost. Pre dereferenciranja, neophodno je konvertovati


vrednost ovog pokazivakog tipa u neki konkretan pokazivaki tip. Takoe,
n

za razliku od ostalih pokazivakih tipova, nad pokazavaima tipa void* nije


ro

mogue vriti aritmetike operacije (o kojima e vie rei biti u poglavlju 10.4).
const. Tako const int* p
I na pokazivae se moe primeniti kljuna re
oznaava pokaziva na konstantnu promenljivu tipa int, int *const p oz-
kt

naava konstantni pokaziva na promenljivu tipa int, dok const int *const p
oznaava konstantni pokaziva na konstantnu promenljivu tipa int. U prvom
le

sluaju mogue je menjati pokaziva, ali ne i ono na ta on pokazuje, u dru-


gom je mogue menjati ono na ta ukazuje pokaziva, ali ne i sam pokaziva,
E

dok u treem sluaju nije mogue menjati ni jedno ni drugo. Trik koji po-
mae u razumevanju ovakvih deklaracija je da se tip ita unatrag. Tako bi
prva deklaracija oznaavala pokaziva na int koji je konstantan (konstantan
je int, a ne pokaziva), druga konstantni pokaziva na int (konstantan je
pokaziva, a ne int), a trea konstantni pokaziva na int koji je konstantan
(konstantna su oba).
Vrednost pokazivaa moe se dodeliti pokazivau istog tipa. Ukoliko se
vrednost pokazivaa p dodeli pokazivau q, nee na adresu na koju ukazuje
q biti iskopiran sadraj na koji ukazuje p, nego e samo promenljiva q dobiti
vrednost promenljive p i obe e ukazivati na prostor na koji je pre toga ukazivao
237 10. Pokazivai i dinamika alokacija memorije

pokaziva p. Taj nain kopiranja nekada se naziva plitko kopiranje. Nasuprot


njemu, duboko kopiranje podrazumeva da se napravi kopija sadraja koji nalazi
na adresi na koju pokaziva pokazuje, a da se pokaziva q usmeri ka kopiji tog
sadraja (slika 10.1). Plitko kopiranje je est uzrok greaka jer nakon plitkog
kopiranja oba pokazivaa ukazuju isti sadraj i izmenom sadraja preko jednog
pokazivaa, moda neoekivano, biva izmenjen sadraj na koji ukazuje i drugi
pokaziva.

5)
originalni pokaziva sadraj originalni pokaziva sadraj

01
(2
kopija pokazivaa novi pokaziva kopija sadraja

je
an
Slika 10.1: Ilustracija plitkog i dubokog kopiranja

Korienjem pokazivaa mogue je stei uvid u nain smetanja promenlji-


d

vih u memoriju raunara. esto se zahteva da podaci u memoriji budu porav-


iz

nati (engl. aligned), to znai, na primer, da svaki podatak koji zauzima etiri
bajta u memoriji mora poinjati na memorijskoj adresi koja je deljiva sa etiri
(ovo je u vezi sa nainom na koji savremeni procesori preuzimaju podatke iz
o

memorije, jer se ne prebacuju pojedinani bajtovi ve ire binarne rei). Po-


sk

ravnanje dodatno moe da otea konverzije pokazivaa (npr. tip char zauzima
uvek jedan bajt i char* moe da ukae na bilo koju adresu, pa njegova ek-
n

splicitna konverzija u tip int* moe dovesti do problema na sistemu gde se, na
int int* moraju biti
ro

primer, tip poravnava na etiri bajta jer tada vrednosti


deljive sa etiri, dok vrednosti char* ne moraju).
Primetimo da se u primeru int a=10, *p=&a; sadraju iste memorijske
kt

lokacije moe pristupiti na dva razliita naina (i preko promenljive a i preko


dereferenciranja pokazivaa *p). Ta pojava se naziva alijasovanje (preklapanje)
le

(engl. aliasing). U primeru, *p predstavlja alijas (drugo ime) promenljive a i


sadraji te dve promenljive su isti. Programi u kojima se alijasovanje intenzivno
E

koristi mogu biti tei za analizu i debagovanje, pa bi ovo svojstvo programskog


jezika trebalo koristiti oprezno. Alijasovanje oteava optimizaciju koda tokom
kompilacije i stoga standardi jezika C uvode odreena ogranienja na tip ali-
jasovanja koji je doputen. Na primer, jedno od pravila striktnog alijasovanja
(engl. strict aliasing rule) je da se ne doputa da dva pokazivaa razliitog tipa
ukazuju na isti sadraj (pokazivaki tipovi koji se razlikuju samo po nekom
od kvalifikatora const, signed, unsigned se ne smatraju razliiti, dok char*
i void* i u odreenoj meri pokazivai na strukture ili unije ine odreene
izuzetke od ovog pravila). Pod tim pravilom kompilator pretpostavlja da e se
dereferenciranjem pokazivaa razliitih tipova dobiti memorijski sadraji koji
238 10. Pokazivai i dinamika alokacija memorije

se ne preklapaju (koji nisu alijasovani), to mu daje mogunost da agresivnije


vri neke optimizacije koda. Konverzija pokazivaa u neki drugi pokazivaki
tip i njegovo dereferenciranje dovode do naruavanja ovog pravila striktnog
alijasovanja. Na primer, prilikom kompilacije pomou gcc -Wall -O2 dobija
se upozorenje da naredni kd kojim se pokuava ispisati heksadekadni sadraj
promenljive tipa float (ime bi se odredio njegov binarni memorijski zapis)
naruava pravila striktnog alijasovanja.

5)
float f = 3.5f;
printf("%x\n", *( ( unsigned* )&f ));

01
Pravi nain da se ovaj zadatak rei je korienje unije, koja vri alijasovanje
promenljivih, ali na standardom doputeni nain jer kompilator prilikom or-

(2
ganizovanja unije vodi rauna o pitanjima koja mogu naruiti ispravnost rada
programa (na primer, vodi se rauna o poravnanju elemenata unije).

je
union { float x; unsigned y } u;
u.x = 3.5f;
an
printf("%x", u.y);
I sledei primer ilustruje pravila striktnog alijasovanja.
d

int a;
iz

void f(double *p) {


a = 1;
o

*p = 1.234;
sk

g(a);
}
n

Pod pravilima striktnog alijasovanja, moe se pretpostaviti da se izmenom vred-


ro

nosti *p u prethodnom kdu ne vri posredno izmena promenljive i (jer su *p i


a drugog tipa i ne mogu biti alijasovani i preklopljeni u memoriji). Zato se moe
izvriti optimizacija koda koja pretpostavlja da se funkciji g uvek alje vrednost
kt

1. Ako bi neko pokuao poziv f((double*)&a) koji bi doveo do alijasovanja


ova dva objekta, iako je sintaksno ovakva konverzija pokazivaa doputena,
le

kompilator bi ga upozorio da time naruava pravila striktnog alijasovanja i


E

da takav poziv moe naruiti ispravnost koda u svetlu pomenute optimizacije.


Pre uvoenja pravila striktnog alijasovanja, kompilatori su veoma restriktivno
vrili optimizaciju koda, dok pravila striktnog alijasovanja daju mogunosti
mnogo veih optimizacija i dovode do znatno breg koda (koji je ispravan ako
programer potuje pravila striktnog alijasovanja).

Pitanja i zadaci za vebu


Pitanje 10.1.1. Koje su informacije pridruene pokazivau? Koje od ovih
informacija se u fazi izvravanja uvaju u memoriji dodeljenoj pokazivakoj
promenljivoj?
239 10. Pokazivai i dinamika alokacija memorije

Pitanje 10.1.2. Koliko bajtova zauzima podatak tipa unsigned char? Koliko
bajtova zauzima podatak tipa unsigned char*?
Pitanje 10.1.3. Ako je veliina koju zauzima element nekog tipa t 8 bajtova,
koliko onda bajtova zauzima pokaziva na vrednost tipa t: (a) 4; (b) 8; (c) 16;
(d) zavisi od sistema?

Pitanje 10.1.4. Ako je promenljiva tipa double * na konkretnom sistemu

5)
zauzima 4 bajta, koliko bajtova na istom sistemu zauzima promenljiva tipa
unsigned char *?

01
Pitanje 10.1.5. Na ta se moe primeniti operator referenciranja? Na ta se
moe primeniti operator dereferenciranja? Kako se oznaavaju ovi operatori?

(2
ta je njihovo dejstvo? Kakav im je prioritet?

Pitanje 10.1.6. Ako je promenljiva p pokazivakog tipa, da li je dozvoljeno


koristiti izraz &p? Da li se umesto vrednosti p moe pisati i *(&p)? Da li se

je
umesto vrednosti p moe pisati i &(*p)?
an
Pitanje 10.1.7. Kako se oznaava generiki pokazivaki tip? Za ta se on
koristi? Da li se pokaziva ovog tipa moe referencirati? Zato?
d

Pitanje 10.1.8. Ako je promenjliva tipa int*, da li joj se moe dodeliti celo-
iz

brojna vrednost? Ako je promenjliva tipa double*, da li joj se moe dodeliti


celobrojna vrednost?
o

Pitanje 10.1.9. Kog tipa je promenljiva a, a kog tipa promenljiva b nakon


sk

deklaracije short* a, b;? Koju vrednost ima promenljiva b nakon izvravanja


naredbi b = 2; a = &b; *a = 3; b++;?
n

10.2 Pokazivai i argumenti funkcija


ro

U jeziku C argumenti funkcija se uvek prenose po vrednosti . To znai da


4
kt

promenljiva koja je upotrebljena kao argument funkcije ostaje nepromenjena


nakon poziva funkcije. Na primer, ako je definisana sledea funkcija
le

void swap(int a, int b) {


E

int tmp = a;
a = b;
b = tmp;
}

i ukoliko je ona pozvana iz neke druge funkcije na sledei nain: swap(x, y)


(pri emu su x i y promenljive tipa int), onda se kreira stek okvir za swap,
4 Kao to je reeno, u sluaju nizova, po vrednosti se ne prenosi itav niz, ve samo adresa
njegovog poetka.
240 10. Pokazivai i dinamika alokacija memorije

obezbeuje se prostor za argumente a i b, vrednosti x i y se kopiraju u taj pros-


tor i funkcija se izvrava koristei samo te kopije. Nakon povratka iz funkcije,
vrednosti xiy ostaju nepromenjene.
Ukoliko se eli da funkcija swap zaista zameni vrednosti argumentima, onda
ona mora biti definisana drugaije:

void swap(int *pa, int *pb) {


int tmp = *pa;

5)
*pa = *pb;
*pb = tmp;

01
}

Zbog drugaijeg tipa argumenata, funkcija swap vie ne moe biti pozvana

(2
na isti nain kao ranije swap(x, y). Njeni argumenti nisu tipaint, ve su
pokazivakog tipa int*. Zato se kao argumenti ne mogu koristiti vrednosti x i y
ve njihove adrese &x i &y. Nakon poziva swap(&x, &y), kreira se stek okvir

je
za swap, obezbeuje se prostor za argumente pokazivakog tipa pa i pb i zatim
se vrednosti &x i &y kopiraju u taj prostor. Time se prenos argumenata vri po
an
vrednosti, kao i uvek. Funkcija se izvrava koristei samo kopije pokazivaa,
ali zahvaljujui njima ona zaista pristupa promenljivama xiy i razmenjuje im
d

vrednosti. Nakon povratka iz funkcije, vrednosti xiy su razmenjene. Na ovaj


iz

nain, prenoenjem adrese promenljive, mogue je njeno menjanje u okviru


funkcije. Isti taj mehanizam koristi se i u funkciji scanf koja je ve koriena.
Prenos pokazivaa moe da se iskoristi i da funkcija vrati vie razliitih
o

vrednosti (preko argumenata). Na primer, naredna funkcija rauna kolinik i


sk

ostatak pri deljenju dva cela broja (ne koristei pritom operatore / i %):
Program 10.1.
n

#include <stdio.h>
ro

void deljenje(unsigned a, unsigned b,


kt

unsigned* pk, unsigned* po) {


*pk = 0; *po = a;
le

while (*po >= b) {


(*pk)++;
E

*po -= b;
}
}

int main() {
unsigned k, o;
deljenje(14, 3, &k, &o);
printf("%d %d\n", k, o);
return 0;
}
241 10. Pokazivai i dinamika alokacija memorije

Pitanja i zadaci za vebu


Pitanje 10.2.1. Kako se prenose argumenti pokazivakog tipa? ta to znai?
Objasniti na primeru.

Pitanje 10.2.2. ta ispisuje naredni program?

1. int a = 1, b = 2;

5)
void f(int* p) {
p = &b;
}

01
int main() {
int *p = &a

(2
f(p);
printf("%d\n", *p);
}
2. #include<stdio.h>
je
an
void f(int* p) { p++; p += 2; p--; p = p + 1; }

int main() {
d

int a[] = {1, 2, 3, 4, 5}, *p = a, *q = a;


iz

f(p); printf("%d\n", *p);


q++; q += 2; q--; q = q + 1; printf("%d\n", *q);
}
o
sk

Pitanje 10.2.3. Ukoliko je potrebno da funkcija vrati vie od jedne vrednosti,


kako se to moe postii?
n

Pitanje 10.2.4. Napisati funkciju f sa povratnim tipom void koja ima samo
ro

jedan argument tipa int i vraa ga udvostruenog.


Napisati funkciju f sa povratnim tipom void koja ima tano jedan argu-
kt

ment, a koja prosleenu celobrojnu vrednost vraa sa promenjenim znakom.


Napisati funkciju sa tipom povratne vrednosti void koja slui za izrauna-
le

vanje zbira i razlike svoja prva dva argumenta.

Zadatak 10.2.1. Napisati funkciju koja za uneti broj sekundi (0 < 86400)
E

proteklih od prethodne ponoi izraunava trenutno vreme tj. broj sati, minuta i

sekundi.

10.3 Pokazivai i nizovi


Postoji vrsta veza izmeu pokazivaa i nizova. Operacije nad nizovima
mogu se iskazati i u terminima korienja pokazivaa i u daljem tekstu esto
neemo praviti razliku izmeu dva naina za pristupanje elementima niza.
Deklaracija int a[10]; deklarie niz od 10 elemenata tipa int. Izraz
sizeof(a) imae istu vrednost kao izraz 10*sizeof(int). Poetni element
242 10. Pokazivai i dinamika alokacija memorije

niza je a[0], a deseti element je a[9] i oni su u memoriji poreani uzastopno.


U fazi kompilacije, imenu niza a pridruena je informacija o adresi poetnog
elementa niza, o tipu elemenata niza, kao i o broju elemenata niza. Poslednje
dve informacije koriste se samo tokom kompilacije i ne zauzimaju memorijski
prostor u fazi izvravanja.
Za razliku od pokazivaa koji jesu l-vrednosti, imena nizova (u navedenom
primeru ime a) a uvek ukazuje na isti prostor koji je rezervisan za
to nisu (jer
elemente niza). Vrednost a nije pokazivakog tipa, ali mu je vrlo bliska. Izuzev

5)
u sluaju kada je u pitanju argument operatora sizeof ili operatora &, ime niza
tipa T konvertuje se u pokaziva na T. Dakle, ime jednodimenzionog niza (na

01
primer, a za deklaraciju int a[10]) bie, sem ako je navedeno kao argument
sizeof ili & operatora, implicitno konvertovano u pokaziva (tipa int*) na prvi

(2
element niza. Vrednosti a odgovara pokaziva na prvi element niza, vrednosti
a+1 odgovara pokaziva na drugi element niza, itd. Dakle, umesto &a[i] moe
se pisati a+i, a umesto a[i] moe se pisati *(a+i) (o tim izrazima se koristi

je
tzv. pokazivaka aritmetika o kojoj e biti rei u narednom poglavlju). Naravno,
kao i obino, nema provere granica niza, pa je dozvoljeno pisati (tj. prolazi
an
fazu prevoenja) i a[100], *(a+100), a[-100], *(a-100), iako je veliina niza
samo 10. U fazi izvravanja, pristupanje ovim lokacijama moe da promeni
vrednost podataka koji se nalaze na tim lokacijama ili da dovede do prekida
d

rada programa zbog pristupanja memoriji koja mu nije dodeljena.


iz

Kao to se elementima niza moe pristupati korienjem pokazivake arit-


metike, ako je p pokaziva nekog tipa (npr. int* p) na njega moe biti pri-
menjen nizovski indeksni pristup (na primer, p[3]). Vrednost takvog izraza
o

odreuje se tako da se poklapa sa odgovarajuim elementom niza koji bi poin-


sk

jao na adresi p (bez obzira to p nije niz nego pokaziva). Na primer, ako bi
na sistemu na kojem je sizeof(int) jednako 4 pokaziva p ukazivao na adresu
n

100 na kojoj poinje niz celih brojeva, tada bi p[3] bio ceo broj koji se nalazi
poevi adrese 112. Isti broj bio bi dobijen i izrazom
ro

smeten u memoriji
*(p+3) u kome se koristi pokazivaka aritmetika.
Dakle, bez obzira da li je x pokaziva ili niz, x[n] isto je to i *(x+n),
kt

tj. x+n isto je to i &x[n]. Ovako tesna veza pokazivaa i nizova daje puno
prednosti (na primer, iako funkcije ne primaju niz ve samo pokaziva na prvi
le

element, implementacija tih funkcija moe to da zanemari i sve vreme da koristi


indeksni pristup kao da je u pitanju niz, a ne pokaziva, to je korieno u
E

ranijim poglavljima). Meutim, ovo je i est izvor greaka, najee prilikom


alokacije memorije. Na primer,

int a[10];
int *b;
a[3] = 5; b[3] = 8;

niza je ispravno deklarisan i mogue je pristupiti njegovom elementu na poziciji


3, dok u sluaju pokazivaa b nije izvrena alokacija elemenata na koje se
pokazuje i, iako se pristup b[3] ispravno prevodi, on bi najverovatnije doveo do
243 10. Pokazivai i dinamika alokacija memorije

greke prilikom izvravanja programa. Zato, svaki put kada se koristi operator
indeksnog pristupa, potrebno je osigurati ili proveriti da je memorija alocirana
na odgovarajui nain (jer kompilator ne vri takve provere).
Kao to je reeno, izraz a ima vrednost adrese poetnog elementa i tip
int [10] koji se, po potrebi, moe konvertovati u int *. Izraz &a ima istu
vrednost (tj. sadri istu adresu) kao i a, ali drugaiji tip tip int (*)[10]
to je tip pokazivaa na niz od 10 elemenata koji su tipa int. Taj tip ima u
narednom primeru promenljiva c:

5)
int a[10];

01
int *b[10];
int (*c)[10];

(2
U navedenom primeru, promenljiva a je niz od 10 elemenata tipa char i zauz-
ima 10 bajtova. Promenljiva b je niz 10 pokazivaa na char i zauzima prostor
c je (jedan) pokaziva na

je
potreban za smetanje 10 pokazivaa. Promenljiva
niz od 10 elemenata tipa char i zauzima onoliko prostora koliko i bilo koji drugi
pokaziva. Vrednost se promenljivoj c moe pridruiti, na primer, naredbom
an
c=&a;. Tada je (*c)[0] isto to i a[0]. Ukoliko je promenljiva c deklar-
isana, na primer, sa char (*c)[5];, u dodeli c=&a; e biti izvrena implicitna
d

konverzija i ova naredba e proi kompilaciju (uz upozorenje da je izvrena


iz

konverzija neusaglaenih tipova), ali to bi trebalo izbegavati.


Kao to je reeno u poglavlju 8.4, niz se ne moe preneti kao argument
funkcije. Umesto toga, kao argument funkcije se moe navesti ime niza i time
o

se prenosi samo pokaziva koji ukazuje na poetak niza. Funkcija koja prihvata
sk

takav argument za njegov tip moe da ima char a[] ili char *a. Ovim se u
funkciju kao argument prenosi (kao i uvek po vrednosti) samo adresa poetka
n

niza, ali ne i informacija o duini niza.


Kako se kao argument nikada ne prenosi itav niz, ve samo adresa poetka,
ro

mogue je umesto adrese poetka proslediti i pokaziva na bilo koji element


niza, kao i bilo koji drugi pokaziva odgovarajueg tipa. Na primer, ukoliko je
kt

a f ima prototip int f(int x[]); (ili ekvivalentno


ime niza i ako funkcija
int f(int *x);), onda se funkcija moe pozivati i za poetak niza (sa
le

f(a)) ili za pokaziva na neki drugi element niza (na primer, f(&a[2]) ili
ekvivalentno f(a+2)). Naravno, ni u jednom od ovih sluajeva funkcija f
E

nema informaciju o tome koliko elemenata niza ima nakon prosleene adrese.

Pitanja i zadaci za vebu


Pitanje 10.3.1. Ako je u okviru funkcije deklarisan niz sa char a[10]; na
koji deo memorije ukazuje a+9?
Pitanje 10.3.2. Ako je niz deklarisan sa int a[10];, ta je vrednost izraza
a? ta je vrednost izraza a + 3? ta je vrednost izraza *(a+3)?
244 10. Pokazivai i dinamika alokacija memorije

Pitanje 10.3.3. Da li se komanda ip = &a[0] moe zameniti komandom


(odgovoriti za svaku pojedinano) (a) ip = a[0]; (b) ip = a; (c) ip = *a;
(d) ip = *a[0]?
Pitanje 10.3.4. Da li je vrednost a[i] ista to i (odgovoriti za svaku pojedi-
nano) (a) a[0]+i; (b) a+i; (c) *(a+i); (d) &(a+i)?
Pitanje 10.3.5. Ukoliko je niz deklarisan na sledei nain: float* x[10],

5)
kako se moe dobiti adresa drugog elementa niza?

Pitanje 10.3.6. Neka je niz a deklarisan sa int a[10], i neka je p pokaziva

01
tipa int*. Da li je naredba a = p; ispravna? Da li je naredba p = a; ispravna?
Koja je vrednost izraza sizeof(p) nakon naredbe p = a;, ako int* zauzima

(2
etiri bajta?

Pitanje 10.3.7. Niz je deklarisan sa int a[10];. Da li je a+3 ispravan izraz?


Da li mu je vrednost ista kao i vrednost a[3], &a[3], *a[3] ili a[10]? Da li je

je
*(a+3) ispravan izraz? Da li mu je vrednost ista kao i vrednost a[3], a[10],
*a[3] ili *a[10]?
an
Pitanje 10.3.8. ta ispisuje naredni kd:
d

int a = 3, b[] = {8, 6, 4, 2}, *c = &a, *d = b + 2; *c = d[-1];


iz

printf("%d\n", a);

int a[] = {7, 8, 9}; int *p; p = a;


o

printf("%d %d\n", *(p+2), sizeof(p));


sk

Pitanje 10.3.9. Ako je kao argument funkcije navedeno ime niza, ta se sve
prenosi u funkciju: (i) adresa poetka niza; (ii) elementi niza; (iii) podatak o
n

broju elemenata niza; (iv) adresa kraja niza?


ro

Pitanje 10.3.10. Koji prototip je ekvivalentan prototipu int f(char s[])?


void f(char *x); i void f(char x[]);?
kt

Da li ima razlike izmeu prototipova


Da li ima razlike izmeu deklaracija char* x; i char x[];? Da li su obe is-
pravne?
le

10.4 Pokazivaka aritmetika


E

U prethodnom poglavlju je reeno da nakon deklaracije int a[10];, vred-


nosti a odgovara pokaziva na prvi element niza, vrednosti a+1 odgovara pokazi-
va na drugi element niza, itd. Izraz p+1 i slini ukljuuju izraunavanje koje
koristi pokazivaku aritmetiku i koje se razlikuje od obinog izraunavanja.
Naime, izraz p+1 ne oznaava dodavanje vrednosti 1 na p, ve dodavanje duine
jednog objekta tipa na koji ukazuje p. Na primer, ako p ukazuje na int, onda
p+1 i p mogu da se razlikuju za dva ili etiri za onoliko koliko bajtova
na tom sistemu zauzima podatak tipa int (tj. vrednosti p+1 ip se razlikuju
za sizeof(int) bajtova). Na primer, ako je p pokaziva na int koji sadri
245 10. Pokazivai i dinamika alokacija memorije

adresu 100, na sistemu na kojem int zauzima 4 bajta, vrednost p+3 e biti
adresa 100 + 3 4 = 112. Analogno vai za druge tipove.
Od pokazivaa je mogue oduzimati cele brojeve (na primer, p-n), pri emu
je znaenje ovih izraza analogno znaenju u sluaju sabiranja. Na pokazivae
je mogue primenjivati prefiksne i postfiksne operatore ++ i --, sa slinom
semantikom. Dva pokazivaa je mogue oduzimati. I u tom sluaju se ne
vri prosto oduzimanje dve adrese, ve se razmatra veliina tipa pokazivaa, sa
kojom se razlika deli. Dva pokazivaa nije mogue sabirati.

5)
Pored dereferenciranja, dodavanja i oduzimanja celih brojeva, i oduzimanja
pokazivaa, nad pokazivaima se (samo ako su istog tipa) mogu primenjivati i

01
relacijski operatori (na primer, p1 < p2, p1 == p2, . . . ). Tako je, na primer,
p1 < p2 tano akko p1 ukazuje na raniji element (u smislu linearno ureene

(2
memorije) niza od pokazivaa p2.
Unarni operatori &i* imaju vii prioritet nego binarni aritmetiki opera-
tori. Zato je znaenje izraza *p+1 zbir sadraja lokacije na koju ukazuje pi

je
vrednosti 1 (a ne sadraj na adresi p+1). Unarni operatori &, *, i prefiksni ++
se primenjuju zdesna nalevo, pa naredba ++*p; inkrementira sadraj lokacije
an
na koju ukazuje p. Postfiksni operator ++, kao i svi unarni operatori koji se
primenjuju sleva na desno, imaju vii prioritet od unarnih operatora koji se pri-
menjuju zdesna nalevo, pa *p++ vraa sadraj na lokaciji p, dok se kao boni
d

efekat p inkrementira.
iz

Vezu pokazivaa i nizova i pokazivaku aritmetiku ilustruje naredni jednos-


tavni primer.

Program 10.2.
o
sk

#include <stdio.h>
int main() {
n

int a[] = {8, 7, 6, 5, 4, 3, 2, 1};


int *p1, *p2, *p3;
ro

/* Ispis elemenata niza */


printf("%d %d %d\n", a[0], a[3], a[5]);
kt

/* Inicijalizacija pokazivaca -
le

ime niza se konvertuje u pokazivac */


p1 = a; p2 = &a[3]; p3 = a + 5;
E

printf("%d %d %d\n", *p1, *p2, *p3);

/* Pokazivaci se koristi u nizovskoj sintaksi */


printf("%d %d %d\n", p1[1], p2[2], p3[-1]);

/* Pokazivacka aritmetika */
p1++; --p2; p3 += 2; /* a++ nije dozvoljeno */
printf("%d %d %d %lu\n", *p1, *p2, *p3, p3 - p1);

/* Odnos izmedju prioriteta * i ++ */


246 10. Pokazivai i dinamika alokacija memorije

*p1++; printf("%d ", *p1);


*(p1++); printf("%d ", *p1);
(*p1)++; printf("%d\n", *p1);
return 0;
}

8 5 3
8 5 3

5)
7 3 4
7 6 1 6

01
6 5 6

(2
Pitanja i zadaci za vebu

je
Pitanje 10.4.1. Ako je p tipa int*, ta je efekat naredbe *p += 5; a ta
efekat naredbe p += 5;?
an
Pitanje 10.4.2. Ako su promenljive p i q tipa double *, za koliko e se ra-
zlikovati vrednosti p i q nakon komande p = q+n (pri emu je n celobrojna
d

promenljiva)?
iz

Pitanje 10.4.3. p tipa double *, za koliko e se promeniti


Ako je promenljiva
njena vrednost (a) nakon komande ++*p;? (b) nakon komande ++(*p);? (c)
o

nakon komande *p++;? (d) nakon komande (*p)++;?


Da li postoji razlika izmeu komandi (*p)++ i *(p++)?
sk

Pitanje 10.4.4. Koje binarne operacije su dozvoljene nad vrednostima istog


n

pokazivakog tipa (npr. nad promenljivima p1 i p2 tipa int*)?


ro

Pitanje 10.4.5. Koje binarne aritmetike operacije se mogu primeniti nad


pokazivaem i celim brojem (npr. nad promenljivom p tipa int* i promenljivom
kt

n tipa int)?
Pitanje 10.4.6. Ako su p1, p2 pokazivai istog tipa, da li postoji razlika izmeu
le

vrednosti: (p1-p2)+d i p1-(p2-d) ? Kog su tipa navedene vrednosti?


E

Pitanje 10.4.7. Ako su p1, p2, p3 pokazivai istog tipa, da li je naredna


konstrucija ispravna:
(1) p=NULL; (2) p=10; (3) p1=p1+0;
(4) p1=p1+10; (5) p1=(p1+p1)+0; (6) p1=(p1+p1)+10;
(7) p1=(p1-p1)+0; (8) p1=(p1-p1)+10; (9) p1=(p1-p2)+10;
(10) p1=p1-(p2+10); (11) p1=p1+(p1-p2); (12) p1=p1-(p1-p2);
(13) p1=p1+(p2-p3); (14) p1=(p1+p2)-p3; (15) p1=(p1+p2)+p3;
(16) p1=p1+(p2+p3);

Pitanje 10.4.8.
247 10. Pokazivai i dinamika alokacija memorije

1. Za koliko bajtova se menja vrednost pokazivaa p tipa int * nakon naredbe


p += 2;?
2. Ako je p tipa float*, za koliko se razlikuju vrednosti p+5 i p?
3. Za koliko bajtova se menja vrednost pokazivaa p tipa T * (gde je T neki
konkretan tip), nakon naredbe p += 2;?
4. Ako je p pokaziva tipa T, emu je jednako p + 3?

5)
Pitanje 10.4.9.

01
1. Kog je tipa x, a kog tipa y nakon deklaracije int *x, *y;? Da li su
dozvoljene operacije x+y i x-y?

(2
2. Kog je tipa a, a kog tipa b nakon deklaracije int* a, b;? Da li su
dozvoljene operacije a+b i a-b?

je
Pitanje 10.4.10. Da li nakon int n[] = {1, 2, 3, 4, 5}; int* p = n;,
n, vrednost pokazivaa p, ili ni jedno ni
an
sledee naredbe menjaju sadraj niza
drugo ili oba: (a) *(p++); (b) (*p)++; (c) *p++;?
d

10.5 Pokazivai i niske


iz

Doslovne tj. konstantne niske (engl. string literal), na primer "informatika",


u memoriji su realizovane slino kao nizovi karaktera: karakteri su poreani re-
o

dom na uzastopnim memorijskim lokacijama i iza njih se nalazi zavrna nula


sk

(\0) koja oznaava kraj niske. Konstantne niske se u memoriji uvek uvaju
u segmentu podataka. Kada se u pozivu funkcije navede konstantna niska
printf("%d", x);),
n

(na primer, u pozivu funkcija umesto niske prihvata kao


argument zapravo adresu poetka konstantne niske u segmentu podataka.
ro

Razmotrimo, kao primer, deklaraciju char *p = "informatika";. Uko-


liko je takva deklaracija navedena u okviru neke funkcije, onda se u njenom stek
kt

okviru rezervie prostor samo za promenljivu p ali ne i za nisku "informatika"


ona se uva u segmentu podataka i p ukazuje na nju. Situacija je slina
le

ako je deklaracija sa inicijalizacijom char *p = "informatika"; spoljanja


(tj. globalna): promenljiva p e se uvati u segmentu podataka i ukazivati na
E

nisku "informatika" koja se uva negde drugde u segmentu podataka. U


oba sluaja, promenljiva p ukazuje na poetak niske "informatika" i p[0] je
jednako i, p[1] je jednako n i tako dalje. Pokuaj da se promeni sadraj
lokacije na koji ukazuje p (na primer, p[0]=x; ili p[1]=x;) prolazi fazu
prevoenja, ali u fazi izvravanja dovodi do nedefinisanog ponaanja (najee
do greke u fazi izvravanja i prekida izvravanja programa). S druge strane,
promena vrednosti samog pokazivaa p (na primer, p++;) je u oba sluaja
dozvoljena. Dakle, p jeste l-vrednost, ali, na primer, p[0] nije.
U gore navedenim primerima koristi se pokaziva p tipa char*. Znaenje
inicijalizacije je bitno drugaije ako se koristi niz. Na primer, deklaracijom
248 10. Pokazivai i dinamika alokacija memorije

sa inicijalizacijom char a[] = "informatika"; a duine 12 i


kreira se niz
prilikom inicijalizacije on se popunjava karakterima niske "informatika". U
ovoj situaciji, dozvoljeno je menjanje vrednosti a[0], a[1], ..., a[9], ali nije
dozvoljeno menjanje vrednosti a. Dakle, a nije l-vrednost, ali, na primer, a[0]
jeste.
Razmotrimo kao ilustraciju rada sa pokazivaima na karaktere nekoliko
moguih implementacija funkcije strlen koja vraa duinu zadate niske. Njen
argument s je pokaziva na poetak niske (koja se, kao i obino, moe tretirati

5)
kao niz). Ukoliko se funkcija pozove sa strlen(p), promenljiva s nakon poziva
predstavlja kopiju tog pokazivaa p i moe se menjati bez uticaja na originalni

01
pokaziva koji je prosleen kao argument:

size_t strlen(const char *s) {

(2
size_t n;
for (n = 0; *s != \0; s++)
n++;

je
return n;
}
an
Jo jedna verzija ove funkcije koristi mogunost oduzimanja pokazivaa
d

(umesto *s != \0, korien je krai, a ekvivalentan izraz *s):


iz

size_t strlen(const char *s) {


const char* t = s;
while(*s)
o

s++;
sk

return s - t;
}
n
ro

Zbog neposrednih veza izmeu nizova i pokazivaa, kao i naina na koji


se obrauju, navedena funkcija strlen moe se pozvati za argument koji je
strlen("informatika");, za argument koji je
kt

konstantna niska (na primer,


ime niza (na primer, strlen(a); za niz deklarisan sa char a[10];), kao i za
pokaziva tipa char* (na primer, strlen(p); za promenljivu p deklarisanu sa
le

char *p;.
E

Kao drugi primer, razmotrimo nekoliko moguih implementacija funkcije


strcpy koja prihvata dva karakterska pokazivaa (ili dve niske) i sadraj druge
kopira u prvu. Da bi jedna niska bila iskopirana u drugu nije, naravno, dovoljno
prekopirati pokaziva, ve je neophodno prekopirati svaki karakter druge niske
pojedinano. Na primer, naredna dodela je sintaksno ispravna, ali njome se
samo pokaziva t usmerava ka postojeoj niski s, bez kopiranja sadraja. Ovo
je plitko kopiranje i to esto nije ono to programer eli (npr. bilo kakva izmena
sadraja putem pokazivaa t menja sadraj niske s).
249 10. Pokazivai i dinamika alokacija memorije

char s[] = "abcd";


char *t;
...
t = s;

Slino, naredna dodela nije doputena, jer nizove nije mogue dodeljivati.

char s[] = "abcd";

5)
char t[5];
...

01
t = s;

s dodeli niski t

(2
Dakle, kao to je i ranije reeno, ispravan nain da se niska
nije dodela, ve poziv funkcije strcpy, iju emo jednu moguu implementaciju
opisati.

je
char s[] = "abcd";
char t[5];
an
...
strcpy(t, s);
d
iz

U narednom kdu, oba pokazivaa se poveavaju za po jedan (operatorom


inkrementiranja), sve dok drugi pokaziva ne doe do zavrne nule, tj. do kraja
niske (kako se argumenti prenose po vrednosti, promenljivama s i t se moe
o

menjati vrednost, a da to ne utie na originalne pokazivae ili nizove koji su


sk

prosleeni kao argumenti):

void strcpy(char *s, const char *t) {


n

while ((*s = *t) != \0) {


ro

s++;
t++;
}
kt

}
le

Inkrementiranje koje se koristi u navedenom kdu, moe se izvriti (u post-


while
E

fiksnom obliku) i u okviru same petlje:

void strcpy(char *s, const char *t) {


while ((*s++ = *t++) != \0);
}

Konano, poreenje sa nulom moe biti izostavljeno jer svaka ne-nula vred-
nost ima istinitosnu vrednost tano:

void strcpy(char *s, const char *t) {


while (*s++ = *t++);
250 10. Pokazivai i dinamika alokacija memorije

}
U navedenoj implementaciji funkcije strcpy kvalifikatorom const obezbeeno
je da se sadraj lokacija na koje ukazuje t nee menjati (u protivnom dolazi do
greke u fazi prevoenja.

Pitanja i zadaci za vebu


Pitanje 10.5.1.

5)
U kom segmentu memorije se tokom izvravanja programa
uvaju: konstantne niske?

01
Pitanje 10.5.2. Nakon koda

void f() {

(2
char* a = "Zdravo";
char b[] = "Zdravo";
...

je
}
an
u kom segmentu memorije je smetena promenljiva a? u kom je segmentu ono
na ta pokazuje promenljiva a? U kom segmentu su smeteni elementi niza b?
aib a, p?
d

Da li se vrednosti mogu menjati? Koliko bajtova zauzima a koliko


iz

Pitanje 10.5.3. Koju vrednost ima promenljiva i nakon naredbi:

int i;
o

char a[] = "informatika";


sk

i = strlen(a+2) ? strlen(a+4) : strlen(a+6);


Pitanje 10.5.4. Razmotriti funkciju:
n
ro

char *dan(int n) {
static char *ime[] = {
"neispravna vrednost", "ponedeljak", "utorak", "sreda",
kt

"cetvrtak", "petak","subota", "nedelja"


};
le

return (n < 1 || n > 7) ? ime[0] : ime[n];


}
E

ta se postie navedenim kvalifikatorom static?


Koliko bajtova zauzima niz ime i u kom delu memorije?
U kojem delu memorije bi se nalazio niz ime da nije naveden kvalifikator
static?
Na koji deo memorije ukazuje ime[0]?
Zadatak 10.5.1. Navesti jednu moguu implementaciju funkcije strlen. Navesti
jednu moguu implementaciju funkcije strcpy. Navesti jednu moguu imple-
mentaciju funkcije strcmp. Navesti jednu moguu implementaciju funkcije
251 10. Pokazivai i dinamika alokacija memorije

strrev. Navesti jednu moguu implementaciju funkcije strchr. Navesti jednu


moguu implementaciju funkcije strstr. Napomena: sve vreme koristiti pokazi-

vaku sintaksu.

10.6 Nizovi pokazivaa i viedimenzioni nizovi


Kao to je reeno u poglavlju 10.3, izuzev u sluaju ako je u pitanju argu-
sizeof ili & operatora, ime niza tipa T se konvertuje u pokaziva na T. To

5)
ment
vai i za viedimenzione nizove. Ime niza tipa T a[d1][d2]...[dn] se konver-
tuje u pokaziva na n-1-dimenzioni niz, tj. u pokaziva tipa T (*)[d2]...[dn].

01
Ovako neto se, na primer, deava prilikom prenosa viedimenzionih nizova u
funkcije, o emu je ve bilo rei u poglavlju 8.7. Razmotrimo dalje, na primer,

(2
deklaraciju dvodimenzionog niza:

int a[10][20];

je
Svaki od izraza a[0], a[1], a[2], ... oznaava niz od 20 elemenata tipa int,
int [20] (koji se, po potrebi, implicitno konvertuje u tip int*).
an
te ima tip
Dodatno, a[0] sadri adresu elementa a[0][0] i, optije, a[i] sadri adresu
elementa a[i][0]. Sem u sluaju kada je argument sizeof ili & operatora,
d

vrednost a se konvertuje u vrednost tipa int (*)[20] ovo je tip pokazivaa


na niz od 20 elemenata. Slino, izrazi &a[0], &a[1], ... su tipa pokazivaa
iz

na niz od 20 elemenata, tj. int (*)[20]. Izrazi a i a[0] imaju istu vrednost
(adresu poetnog elementa dvodimenzionalnog niza), ali razliite tipove: prvi
o

ima tip int (*)[20], a drugi tip int*. Ovi tipovi ponaaju se u skladu sa
sk

optim pravilima pokazivake aritmetike. Tako je, na primer, a+i jednako


a[i], a a[0]+i je jednako &a[0][i].
n

Razmotrimo razliku izmeu dvodimenzionog niza i niza pokazivaa. Ako


su date deklaracije
ro

int a[10][20];
kt

int *b[10];

a[3][4] i b[3][4] su sintaksiki ispravna referisanja na pojedinani int. Ali


le

i
a je pravi dvodimenzioni niz: 200 lokacija za podatak tipa int je rezervisano
E

tj. alocirano i uobiajena raunica 20 * v + k se koristi da bi se pristupilo


elementu a[v][k]. Za niz b, meutim, nije alociran prostor za smetanje ele-
menata niza, ve deklaracija alocira samo 10 pokazivaa i ne inicijalizuje ih
inicijalizacija se mora izvriti eksplicitno, bilo statiki (navoenjem inicijaliza-
tora) ili dinamiki (tokom izvravanja programa). Pod pretpostavkom da svaki
element niza b zaista pokazuje na niz od 20 elemenata, u memoriji e biti 200
lokacija za podataka tipa int i jo dodatno 10 lokacija za pokazivae. Elementi
niza a su sigurno smeteni na uzastopnim memorijskim lokacijama, dok lokacije
na koje ukazuju elementi niza b ne moraju da budu. Kljuna prednost niza
pokazivaa nad dvodimenzionim nizom je injenica da vrste na koje pokazuju
252 10. Pokazivai i dinamika alokacija memorije

ovi pokazivai mogu biti razliite duine. Tako, svaki element niza b ne mora
da pokazuje na 20-to elementni niz neki mogu da pokazuju na 2-elementni
niz, neki na 50-elementi niz, a neki mogu da budu NULL i da ne pokazuju nigde.
Razmotrimo primer niza koji treba da sadri imena meseci. Jedno reenje
je zasnovano na dvodimenzionom nizu (u koji se, prilikom inicijalizacije upisuju
imena meseci):

char meseci[][10] = {

5)
"Greska", "Januar", "Februar", "Mart", "April",
"Maj", "Jun", "Jul", "Avgust", "Septembar",

01
"Oktobar", "Novembar", "Decembar"};

(2
Poto meseci imaju imena razliite duine, bolje reenje je napraviti niz
pokazivaa na karaktere i inicijalizovati ga da pokazuje na konstantne niske
smetene u segmentu podataka (primetimo da nije potrebno navesti broj ele-

je
menata niza poto je izvrena inicijalizacija):

char *meseci[] = {
an
"Greska", "Januar", "Februar", "Mart", "April",
"Maj", "Jun", "Jul", "Avgust", "Septembar",
d

"Oktobar", "Novembar", "Decembar"};


iz

Slika 10.2 ilustruje slian primer memoriju koja odgovara nizovima aib
deklarisanim u okviru neke funkcije:
o

char a[][5] = {"abcd", "ef", "ghi"};


sk

char* b[] = {"abcd", "ef", "ghi"};


n

Pitanja i zadaci za vebu


ro

Pitanje 10.6.1.
kt

Navesti primer inicijalizacije dvodimenzionog niza brojeva


tipa int. Navesti primer deklaracije trodimenzionog niza brojeva tipa float.
le

Pitanje 10.6.2. Da li se matrica a dimenzije3 3 deklarie na sledei nain


(odgovoriti za svaku pojedinano): (a) a[3][3][3]; (b) a[3,3,3]; (c) a[3,3];
E

(d) a[3][3]; (e) a[3*3]?


Pitanje 10.6.3. Nakon deklaracije int a[10]; kog tipa je vrednost a? Nakon
deklaracije int b[10][20]; kog tipa je vrednost b? a kog vrednost b[0]?
Pitanje 10.6.4. Objasniti naredne dve deklaracije i u emu se razlikuju:
char *name[] = { "Illegal month", "Jan", "Feb", "Mar" };
char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" };
Pitanje 10.6.5. Kako se deklarie trodimenzioni niz a dimenzija 10, 9, 8?
253 10. Pokazivai i dinamika alokacija memorije

Segment podataka

a b c d 0

e f 0

g h i 0

5)
01
Stek segment

(2
[0] [1] [2]
a a b c d 0 e f 0 ? ? g h i 0 ?
[0][0] [0][1] [0][2] [0][3] [0][4] [1][0] [1][1] [1][2] [1][3] [1][4] [2][0] [2][1] [2][2] [2][3] [2][4]

je
an
Slika 10.2: Niz pokazivaa i dvodimenzioni niz u memoriji
d

Pitanje 10.6.6.
iz

Koliko bajtova e biti rezervisano za naredne nizove:


char *name[] = { "Illegal month", "Jan", "Feb", "Mar" };
char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" };
o

Pitanje 10.6.7.
sk

1. Da li je nakon deklaracije char a[3][3], naredba a[0][0] = a; (a)


n

sintaksiki ispravna? (b) semantiki ispravna?


ro

2. Da li je nakon deklaracije char* a[3];, naredba a[0][0] = a; (a)


sintaksiki ispravna? (b) semantiki ispravna?
kt

Pitanje 10.6.8. Koliko bajtova zauzima i u kom delu memorije niz koji je u
char* a[10];
le

okviru neke funkcije deklarisan sa

Pitanje 10.6.9. Koja inicijalizacija dvodimenzionog niza je ispravna:


E

(a) int a[][2]=(1,2,3,4); (b) int a[2][]=(1,2,3,4);


(c) int a[][2]={1,2,3,4}; (d) int a[2][]={1,2,3,4};
Pitanje 10.6.10. Koliko je, na 32-bitnim sistemima, sizeof(a) i sizeof(b)
nakon deklaracija int* a[4]; int b[3][3];? Koliko je u optem sluaju?

Pitanje 10.6.11. Navesti primer inicijalizacije niza pokazivaa. Opisati efekat


narednih deklaracija: int a[10][20]; int *b[10];
Pitanje 10.6.12. Pretpostavimo da se naredni kd izvrava na 32-bitnom sis-
temu.
254 10. Pokazivai i dinamika alokacija memorije

char* a[] = {"Dobar dan!", "Zdravo, zdravo"};


char b[][15] = {"Dobar dan!", "Zdravo, zdravo"};
emu je jednako sizeof(a) a emu sizeof(b)?
Pitanje 10.6.13. Razmotriti naredni kd:

void f() {
char* a = "Zdravo";

5)
char b[] = "Zdravo";
...

01
}
U kom segmentu memorije se nalazi promenljiva a? U kom segmentu memorije

(2
je ono na ta pokazuje promenljiva a? U kom segmentu memorije je ono na
ta pokazuje promenljiva b?
Pitanje 10.6.14. Koliko bajtova e na steku biti rezervisano za niz

je
char *s[] = { "jedan", "dva", "tri" }; koji je lokalna promenljiva?
an
10.7 Pokazivai i strukture
d

Mogue je definisati i pokazivae na strukture. U sluaju da se lanovima


* i ., mogue
iz

strukture pristupa preko pokazivaa, umesto kombinacije operatora


je koristiti operator ->. I operator -> je operator najvieg prioriteta. Na
primer, umesto (*pa).imenilac moe se pisati pa->imenilac:
o

struct razlomak *pa = &a;


sk

printf("%d/%d", pa->brojilac, pa->imenilac);


n

Prilikom dodele struktura, vri se plitko kopiranje pokazivaa koji se u njima


ro

nalaze. Neka su a i b promenljive istog tipa strukture koja ima lan pokazi-
vakog tipa p. Dodelom a=b; bie iskopirane vrednosti svih lanova strukture
kt

iz b u a, ukljuujui i vrednost lana p, ali nee biti iskopiran sadraj na koji


je ukazivao pokaziva b.p. Umesto toga, nakon a=b; i a.p i b.p ukazivae na
b.p).
le

istu adresu (na onu na koju je ukazivao Iako se takvo plitko kopranje
koristi (pre svega zbog utede memorije), ono je est uzrok greaka i pokazi-
E

vae sadrane u strukturama esto je poeljnije duboko kopirati (to zahteva


dodatan trud programera).
Strukture se kao argumenti u funkciju prenose po vrednosti. S obzirom
na to da strukture esto zauzimaju vie prostora od elementarnih tipova po-
dataka, est je obiaj da se umesto struktura u funkcije proslede njihove adrese,
tj. pokazivai na strukture. Na primer,

void saberi_razlomke(struct razlomak *pa, struct razlomak *pb,


struct razlomak *pc)
{
pc->brojilac = pa->brojilac*pb->imenilac +
255 10. Pokazivai i dinamika alokacija memorije

pa->imenilac*pb->brojilac;
pc->imenilac = pa->imenilac*pb->imenilac;
}

10.8 Pokazivai na funkcije


Funkcije se ne mogu direktno prosleivati kao argumenti drugim funkci-

5)
jama, vraati kao rezultat funkcija i ne mogu se dodeljivati promenljivima.
Ipak, ove operacije je mogue posredno izvriti ukoliko se koriste pokazivai na

01
funkcije. Razmotrimo naredni ilustrativni primer.

Program 10.3.

(2
#include <stdio.h>

void inc1(int a[], int n, int b[]) {

je
int i;
for (i = 0; i < n; i++)
an
b[i] = a[i] + 1;
}
d

void mul2(int a[], int n, int b[]) {


iz

int i;
for (i = 0; i < n; i++)
o

b[i] = a[i] * 2;
sk

void even0(int a[], int n, int b[]) {


n

int i;
ro

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


b[i] = a[i] % 2 == 0 ? 0 : a[i];
kt

}
le

void print(int a[], int n) {


int i;
E

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


printf("%d ", a[i]);
putchar(\n);
}

#define N 8
int main() {
int a[N] = {1, 2, 3, 4, 5, 6, 7, 8}, b[N];

inc1(a, N, b); print(b, N);


256 10. Pokazivai i dinamika alokacija memorije

mul2(a, N, b); print(b, N);


even0(a, N, b); print(b, N);

return 0;
}
Sve funkcije u prethodnom programu kopiraju elemente niza a u niz b,
prethodno ih transformiui na neki nain. Mogue je izdvojiti ovaj zajedniki

5)
postupak u zasebnu funkciju koja bi bila parametrizovana operacijom transfor-
macije koja se primenjuje na elemente niza a:

01
#include <stdio.h>

void map(int a[], int n, int b[], int (*f) (int)) {

(2
int i;
for (i = 0; i < n; i++)
b[i] = (*f)(a[i]);
}
je
an
int inc1(int x) { return x + 1; }
int mul2(int x) { return 2 * x; }
d

int parni0(int x) { return x % 2 == 0 ? 0 : x; }


iz

void ispisi(int a[], int n) {


int i;
o

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


sk

printf("%d ", a[i]);


putchar(\n);
n

}
ro

#define N 8
int main() {
kt

int a[N] = {1, 2, 3, 4, 5, 6, 7, 8}, b[N];


le

map(a, N, b, &inc1); ispisi(b, N);


map(a, N, b, &mul2); ispisi(b, N);
E

map(a, N, b, &parni0); ispisi(b, N);

return 0;
}

Funkcija map ima poslednji argument tipa int (*)(int), to oznaava


pokaziva na funkciju koja prima jedan argument tipa int i vraa argument
tipa int.
Pokazivai na funkcije se razlikuju po tipu funkcije na koje ukazuju (po
tipovima argumenata i tipu povratne vrednosti). Deklaracija promenljive tipa
257 10. Pokazivai i dinamika alokacija memorije

pokazivaa na funkciju se vri tako to se ime promenljive kojem prethodi karak-


ter * navede u zagradama kojima prethodi tip povratne vrednosti funkcije, a za
kojima sledi lista tipova parametara funkcije. Prisustvo zagrada je neophodno
da bi se napravila razlika izmeu pokazivaa na funkcije i samih funkcija. U
primeru

double *a(double, int);


double (*b)(double, int);

5)
promenljiva a oznaava funkciju koja vraa rezultat tipa*double, a prima

01
argumente tipa double i int, dok promenljiva b oznaava pokaziva na funkciju
koja vraa rezultat tipa double, a prima argumente tipa double i int.

(2
Najee koriene operacije sa pokazivaima na funkcije su, naravno, ref-
erenciranje (&) i dereferenciranje (*).
5
Mogue je kreirati i nizove pokazivaa na funkcije. Ovi nizovi se mogu i

je
incijalizovati (na uobiajeni nain). U primeru

int (*a[3]) (int) = {&inc1, &mul2, &parni0};


an
a predstavlja niz od 3 pokazivaa na funkcije koje vraaju int, i primaju argu-
d

ment tipa int. Funkcije ije se adrese nalaze u nizu se mogu direktno i pozvati.
Na primer, naredni kd ispisuje vrednost 4:
iz

printf("%d", (*a[0])(3));
o
sk

Pitanja i zadaci za vebu


n

Pitanje 10.8.1. Deklarisati funkciju f povratnog tipaint koja kao argument


ro

ima pokaziva na funkciju koja je povratnog tipa char i ima (samo jedan)
argument tipa float.
kt

Pitanje 10.8.2. Deklarisati funkciju f iji poziv moe da bude f(strcmp),


gde je strcmp funkcija deklarisana u string.h.
le

Pitanje 10.8.3. Ako se funkcija int faktorijel(int n) moe koristi kao


E

(jedini) argument funkcije f tipa float, kako je deklarisana funkcija f?


Pitanje 10.8.4. Da li je ispravan sledei prototip funkcije iji je prvi od dva
argumenta pokaziva na funkciju koja ima jedan argument:
(a) int f(int* g(char), int x);
(b) int f(void g(char), int x);
(c) int f(int (*g)(char), int x);
(d) int f(int (*g)(void), int x);
5 Iako kod nekih kompilatora oznake ovih operacija mogu da se izostave i da se koristi
samo ime pokazivaa (na primer, u prethodnom programu je bilo mogue navesti map(a, N,
b, inc1) i b[i] = f(a[i])), ovo se ne preporuuje zbog prenosivosti programa.
258 10. Pokazivai i dinamika alokacija memorije

Pitanje 10.8.5. Da li je ispravan sledei prototip funkcije:


int poredi(char *a, char *b, (*comp)(char *, char *)); ?
Obrazloiti odgovor.

Pitanje 10.8.6. Date su deklaracije:

int *a1 (int* b1);


int (*a2) (int *b2);

5)
Kog je tipa a1, a kog a2?

01
Pitanje 10.8.7. Kog je tipa x deklarisano sa:

1. double (*x[3])(int);

(2
2. int (*x) (double);
3. int *x (double);
4. double* (*x) (float*);
je
an
5. int (*f) (float*);
d

Zadatak 10.8.1. Napisati funkciju koja u nizu odreuje najduu seriju el-
iz

emenata koji zadovoljavaju dato svojstvo. Svojstvo dostaviti kao parametar


funkcije. Iskoristiti je da bi se nala najdua serija parnih kao i najdua serija

pozitivnih elemenata niza.
o
sk

10.9 Dinamika alokacija memorije


n

U veini realnih aplikacija, u trenutku pisanja programa nije mogue pre-


cizno predvideti memorijske zahteve programa. Naime, memorijski zahtevi
ro

zavise od interakcije sa korisnikom i tek u fazi izvravanja programa korisnik


svojim akcijama implicitno odreuje potrebne memorijske zahteve (na primer,
kt

koliko elemenata nekog niza e biti korieno). U nekim sluajevima, mogue je


predvideti neko gornje ogranienje, ali ni to nije uvek zadovoljavajue. Ukoliko
le

je ogranienje premalo, program nije u stanju da obrauje vee ulaze, a ukoliko


je preveliko, program zauzima vie memorije nego to mu je stvarno potrebno.
E

Reenje ovih problema je dinamika alokacija memorije koja omoguava da


program u toku svog rada, u fazi izvravanja, zahteva (od operativnog sis-
tema) odreenu koliinu memorije. U trenutku kada mu memorija koja je di-
namiki alocirana vie nije potrebna, program moe i duan je da je oslobodi i
tako je vrati operativnom sistemu na upravljanje. Alociranje i oslobaanje vri
se funkcijama iz standardne biblioteke i pozivima rantajm biblioteke. U fazi
izvravanja, vodi se evidencija i o raspoloivim i zauzetim blokovima memorije.
259 10. Pokazivai i dinamika alokacija memorije

10.9.1 Funkcije standardne biblioteke za rad sa dinamikom


memorijom

Standardna biblioteka jezika C podrava dinamiko upravljanje memorijom


kroz nekoliko funkcija (sve su deklarisane u zaglavlju <stdlib.h>). Prostor za
dinamiki alociranu memoriju nalazi se u segmentu memorije koji se zove hip
(engl. heap).
Funkcija malloc ima sledei prototip:

5)
void *malloc(size_t n);

01
Ona alocira blok memorije (tj. niz uzastopnih bajtova) veliine n bajtova i
vraa adresu alociranog bloka u vidu generikog pokazivaa (tipa void*). U

(2
sluaju da zahtev za memorijom nije mogue ispuniti (na primer, zahteva se
vie memorije nego to je na raspolaganju), ova funkcija vraa NULL. Memorija
na koju funkcija malloc vrati pokaziva nije inicijalizovana i njen sadraj je,

je
u principu, nedefinisan (tj. zavisi od podataka koji su ranije bili uvani u tom
delu memorije).
an
Funkcija malloc oekuje argument tipa size_t. Podsetimo se, ovo je neneg-
ativni celobrojni tip za ije vrednosti je rezervisano najmanje dva bajta. Ovaj
unsigned int, ali su meusobne konverzije mogue i,
d

tip se razlikuje od tipa


zapravo, trivijalne. Tip size_t moe da se koristi za uvanje bilo kog indeksa
iz

niza, a on je i tip povratne vrednosti operatora sizeof.


Funkcija calloc ima sledei prototip:
o

void *calloc(size_t n, size_t size)


sk

Ona vraa pokaziva na blok memorije veliine n objekata navedene veliine


n

size. U sluaju za zahtev nije mogue ispuniti, vraa se NULL. Za razliku od


malloc, alocirana memorija je inicijalizovana na nulu.
ro

Dinamiki objekti alocirani navedenim funkcijama su neimenovani i bitno


su razliiti od promenljivih. Ipak, dinamiki alociranim blokovima se pristupa
kt

na slian nain kao nizovima.


Navedene funkcije su generike i koriste se za dinamiku alokaciju memorije
le

za podatke bilo kog tipa. Da bi se dobijenoj memoriji moglo pristupati slino


kao u sluaju nizova, potrebno je (poeljno eksplicitno) konvertovati dobijeni
E

pokaziva tipa void* u neki konkretni pokazivaki tip.


Nakon poziva funkcije malloc() ili calloc() obavezno treba proveriti pov-
ratnu vrednost da bi se utvrdilo da li je alokacija uspela. Ukoliko alokacija ne
uspe, pokuaj pristupa memoriji na koju ukazuje dobijeni pokaziva dovodi do
dereferenciranja NULL pokazivaa i greke. Ukoliko se utvrdi da je funkcija
malloc() (ili calloc()) vratila vrednost NULL, moe se prijaviti korisniku
odgovarajua poruka ili pokuati neki metod oporavka od greke. Dakle, na-
jei scenario upotrebe funkcije malloc je sledei:
260 10. Pokazivai i dinamika alokacija memorije

int* p = (int*) malloc(n*sizeof(int));


if (p == NULL)
/* pokusati oporavak od greske ili prijaviti gresku */

Ponekad se podrazumeva da e alokacija biti uspena i ne preduzima se


oporavak od greke i takvo mesta se u kdu oznaavaju funkcijom assert
(videti poglavlje 11.5).

5)
U gore navedenom primeru, nakon uspene alokacije, u nastavku programa
se p moe koristiti kao (statiki alociran) niz celih brojeva. Napomenimo da
n*sizeof(int) moe dovesti do prekoraenja za veoma velike

01
raunanje izraza
vrednosti n (ukoliko je prekoraenje problem, savetuje se upotreba funkcije
calloc).

(2
U trenutku kada dinamiki alociran blok memorije vie nije potreban, poeljno
je osloboditi ga. To se postie funkcijom free:
void free(void* p);

je
an
free(p) oslobaa memoriju na koju ukazuje pokaziva p (a ne mem-
Poziv
orijski prostor koji sadri sm pokaziva p), pri emu je neophodno da p
pokazuje na blok memorije koji je alociran pozivom funkcije malloc ili calloc.
d

Oslobaanje memorije koja nije alocirana na ovaj nain dovodi do nedefinisanog


iz

ponaanja (najee do greke prilikom izvravanja programa). Slino, ne sme


se koristiti neto to je ve osloboeno niti se sme dva puta oslobaati ista
memorija jer je i u tim sluajevima ponaanje programa nedefinisano. Re-
o

dosled oslobaanja memorije ne mora da odgovara redosledu alociranja.


sk

Ukoliko neki dinamiki alociran blok nije osloboen ranije, on e biti osloboen
prilikom zavretka rada programa, zajedno sa svom drugom memorijom koja je
n

dodeljena programu. Ipak, ne treba se oslanjati na to i preporueno je eksplic-


ro

itno oslobaanje sve dinamiki alocirane memorije pre kraja rada programa, a
poeljno im taj prostor nije potreban.
Upotrebu ovih funkcija ilustruje naredni primer u kojem se unosi i obrnuto
kt

ispisuje niz iji broj elemenata nije unapred poznat (niti je poznato njegovo
gornje ogranienje), ve se unosi sa ulaza tokom izvravanja programa.
le

Program 10.4.
E

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

int main() {
int n, i, *a;

/* Unos broja elemenata */


scanf("%d", &n);
/* Alocira se memorija */
if ((a = (int*)malloc(n*sizeof(int))) == NULL) {
261 10. Pokazivai i dinamika alokacija memorije

printf("Greska prilikom alokacije memorije\n");


return 1;
}
/* Unos elemenata */
for (i = 0; i < n; i++) scanf("%d",&a[i]);
/* Ispis elemenata u obrnutom poretku */
for (i = n-1; i >= 0; i--) printf("%d ",a[i]);
/* Oslobadjanje memorije */

5)
free(a);

01
return 0;
}

(2
U nekim sluajevima potrebno je promeniti veliinu ve alociranog bloka
memorije. To se postie korienjem funkcije realloc, iji je prototip:

void *realloc(void *memblock, size_t size);

je
an
Parametar memblock je pokaziva na prethodno alocirani blok memorije, a
parametar size je nova veliina u bajtovima. Funkcija realloc vraa pokazi-
va tipa void* na realociran blok memorije, a NULL u sluaju da zahtev ne moe
d

biti ispunjen. Zahtev za smanjivanje veliine alociranog bloka memorije uvek


iz

uspeva. U sluaju da se zahteva poveanje veliine alociranog bloka memorije,


pri emu iza postojeeg bloka postoji dovoljno slobodnog prostora, taj prostor
se jednostavno koristi za proirivanje. Meutim, ukoliko iza postojeeg bloka
o

ne postoji dovoljno slobodnog prostora, onda se u memoriji trai drugo mesto


sk

dovoljno da prihvati proireni blok i, ako se nae, sadraj postojeeg bloka se


kopira na to novo mesto i zatim se stari blok memorije oslobaa. Ova operacija
n

moe biti vremenski zahtevna.


realloc ilustrovana je programom koji uitava cele bro-
ro

Upotreba funkcije
jeve i smeta ih u memoriju, sve dok se ne unese-1 za kraj. S obzirom na to da
se broj elemenata ne zna unapred, a ne zna se ni gornje ogranienje, neophodno
kt

je postepeno poveavati skladini prostor tokom rada programa. Kako esta


realokacija moe biti neefikasna, u narednom programu se izbegava realokacija
le

prilikom unosa svakog sledeeg elementa, ve se vri nakon unoenja svakog


desetog elementa. Naravno, ni ovo nije optimalna strategija u praksi se
E

obino koristi pristup da se na poetku realokacije vre relativno esto, a onda


sve ree i ree (na primer, svaki put se veliina niza dvostruko uvea).

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

#define KORAK 256


int main() {
int* a = NULL; /* Niz je u pocetku prazan */
262 10. Pokazivai i dinamika alokacija memorije

int duzina = 0; /* broj popunjenih elemenata niza */


int alocirano = 0; /* broj elemenata koji mogu biti smesteni */
int i;

do {
printf("Unesi ceo broj (-1 za kraj): ");
scanf("%d", &i);

/* Ako nema vise slobodnih mesta, vrsi se prosirivanje */

5)
if (duzina == alocirano) {
alocirano += KORAK;

01
a = realloc(a, alocirano*sizeof(int));
if (a == NULL) return 1;
}

(2
a[duzina++] = i;
} while (i != -1);

je
/* Ispis elemenata */
printf("Uneto je %d brojeva. Alocirano je %d bajtova\n",
an
duzina, alocirano*sizeof(int));
printf("Brojevi su : ");
for (i = 0; i<duzina; i++)
d

printf("%d ", a[i]);


iz

/* Oslobadjanje memorije */
free(a);
o
sk

return 0;
}
n

Bez upotrebe funkcije realloc centralni blok navedene funkcije main bi


ro

mogao da izgleda ovako:

if (duzina == alocirano) {
kt

/* Kreira se novi niz */


int* new_a;
alocirano += KORAK;
le

new_a = malloc(alocirano*sizeof(int));
/* Kopira se sadrzaj starog niza u novi */
E

for (i = 0; i < duzina; i++) new_a[i] = a[i];


/* Oslobadja se stari niz */
free(a);
/* a ukazuje na novi niz */
a = new_a;
}

U ovoj implementaciji, prilikom svake realokacije vri se premetanje mem-


orije, tako da je ona neefikasnija od verzije sa realloc.
U gore navedenom primeru koristi se konstrukcija:
263 10. Pokazivai i dinamika alokacija memorije

a = realloc(a, alocirano*sizeof(int));

U nekim sluajevima ova konstrukcija moe da bude neadekvatna ili opasna.


Naime, ukoliko zahtev za proirenje memorijskog bloka ne uspe, vraa se vred-
nost NULL, upisuje u promenljivu a i time se gubi jedina veza sa prethodno
alociranim blokom (i on, na primer, ne moe ubudue da bude osloboen).

5)
10.9.2 Greke u radu sa dinamikom memorijom

Dinamiki alocirana memorija nudi mnoge mogunosti i dobra reenja ali

01
esto je i uzrok problema. Neki od najeih problema opisani su u nastavku.

(2
Curenje memorije. Jedna od najopasnijih greaka u radu sa dinamiki
alociranom memorijom je tzv. curenje memorije (engl. memory leak). Curenje
memorije je situacija kada se u tekuem stanju programa izgubi informacija o

je
lokaciji dinamiki alociranog, a neosloboenog bloka memorije. U tom sluaju,
program vie nema mogunost da oslobodi taj blok memorije i on biva zauvek
an
(zapravo do kraja izvravanja programa) izgubljen (rezervisan za korienje
od strane programa koji vie nema naina da mu pristupi). Curenje memorije
d

ilustruje naredni primer.


iz

char* p;
p = (char*) malloc(1000);
....
o

p = (char*) malloc(5000);
sk

Inicijalno je 1000 bajtova dinamiki alocirano i adresa poetka ovog bloka mem-
n

orije smetena je u pokazivaku promenljivu p. Kasnije je dinamiki aloci-


ro

rano 5000 bajtova i adresa poetka tog bloka memorije je opet smetena u
promenljivu p. Meutim, poto originalnih 1000 bajtova nije osloboeno ko-
rienjem funkcije free, a adresa poetka tog bloka memorije je izgubljena
kt

promenom vrednosti pokazivake promenljive p, tih 1000 bajtova biva nepov-


ratno izgubljeno za program.
le

Naroito su opasna curenja memorije koja se dogaaju u okviru neke petlje.


U takvim situacijama moe da se gubi malo po malo memorije, ali tokom dug-
E

trajnog izvravanja programa, pa ukupna koliina izgubljene memorije moe


da bude veoma velika. Mogue je da u nekom trenutku program iscrpi svu
raspoloivu memoriju i onda e njegovo izvravanje biti prekinuto od strane
operativnog sistema. ak i da se to ne desi, mogue je da se iscrpi raspoloivi
prostor u glavnoj memoriji i da se, zbog toga, sadraj glavne memorije pre-
bacuje na disk i obratno (tzv. swapping), to onda ekstremno usporava rad
programa.
Curenje memorije je naroito opasno zbog toga to esto ne biva odmah
uoeno. Obino se tokom razvoja program testira kratkotrajno i na malim
ulazima. Meutim, kada se program pusti u rad i kada pone da radi dui vre-
264 10. Pokazivai i dinamika alokacija memorije

menski period (moda i bez prestanka) i da obrauje vee koliine ulaza, curenje
memorije postaje vidljivo, ini program neupotrebljivim i moe da uzrokuje ve-
like tete.
Veina programa za otkrivanje greaka (debagera) detektuje da u programu
postoji curenje memorije, ali ne moe da pomogne u lociranju odgovarajue
greke u kdu. Postoje specijalizovani programi profajleri za curenje memo-
rije (engl. memory leaks profilers) koji olakavaju otkrivanje uzroka curenja
memorije.

5)
Pristup osloboenoj memoriji. Nakon poziva free(p), memorija na koju

01
pokazuje pokaziva p se oslobaa i ona vie ne bi trebalo da se koristi. Meu-
tim, poziv free(p) ne menja sadraj pokazivaa p. Mogue je da naredni

(2
poziv funkcije malloc vrati blok memorije upravo na toj poziciji. Naravno,
ovo ne mora da se desi i nije predvidljivo u kom e se trenutku desiti, tako
da ukoliko programer nastavi da koristi memoriju na adresi p, mogue je da

je
e greka proi neopaeno. Zbog toga, preporuuje se da se nakon poziva
free(p), odmah p postavi na NULL. Tako se osigurava da e svaki pokuaj
an
pristupa osloboenoj memoriji biti odmah prepoznat tokom izvravanja pro-
grama i operativni sistem e zaustaviti izvravanje programa sa porukom o
segmentation fault).
d

greci (najee
iz

Oslobaanje istog bloka vie puta. Nakon poziva free(p), svaki naredni
poziv free(p) za istu vrednost pokazivaa p prouzrokuje nedefinisano pon-
o

aanje programa i trebalo bi ga izbegavati. Takozvana viestruka oslobaanja


sk

mogu da dovedu do pada programa a poznato je da mogu da budu i izvor


bezbednosnih problema.
n

Oslobaanje neispravnog pokazivaa. Funkciji free(p) doputeno je pros-


ro

lediti iskljuivo adresu vraenu od strane funkcije malloc, calloc ili realloc.
ak i prosleivanje pokazivaa na lokaciju koja pripada alociranom bloku (a
kt

nije njegov poetak) uzrokuje probleme. Na primer,

free(p+10); /* Oslobodi sve osim prvih 10 elemenata bloka */


le
E

nee osloboditi sve osim prvih 10 elemenata bloka i sasvim je mogue da e


dovesti do neprijatnih posledica, pa ak i do pada programa.

Prekoraenja i potkoraenja bafera. Nakon dinamike alokacije, pristup


memoriji je dozvoljen samo u okviru granica bloka koji je dobijen. Kao i u
sluaju statiki alociranih nizova, pristup elementima van granice moe da
prouzrokuje ozbiljne probleme u radu programa. Upis van granica bloka na-
jee je opasniji od itanja. U sluaju dinamiki alociranih blokova memorije,
obino se nakon samog bloka smetaju dodatne informacije potrebne alokatoru
memorije da bi uspeno vodio evidenciju koji delovi memorije su zauzeti, a
265 10. Pokazivai i dinamika alokacija memorije

koji slobodni. Zato, i malo prekoraenje granice bloka prilikom upisa moe da
promeni te dodatne informacije i da uzrokuje pad sistema za dinamiko up-
ravljanje memorijom. Postoje i mnogi drugi sluajevi u kojima prekoraenje i
potkoraenje bafera mogu da narue ispravno izvravanje programa ili dovedu
do njegovog pada.

10.9.3 Fragmentisanje memorije

5)
est je sluaj da ispravne aplikacije u kojima ne postoji curenje memorije (a

01
koje esto vre dinamiku alokaciju i dealokacije memorije) tokom dugog rada
pokazuju degradaciju u performansama i na kraju prekidaju svoj rad na nepred-
vieni nain. Uzrok ovome je najee fragmentisanje memorije. U sluaju

(2
fragmentisane memorije, u memoriji se esto nalazi dosta slobodnog prostora,
ali on je rascepkan na male, nepovezane parie. Razmotrimo naredni (mini-
jaturizovan) primer. Ukoliko 0 oznaava slobodni bajt i 1 oznaava zauzeti

je
bajt, a memorija trenutno ima sadraj 100101011000011101010110, postoji
ukupno 12 slobodnih bajtova. Meutim, pokuaj alokacije 5 bajtova ne moe
an
da uspe, jer u memoriji ne postoji prostor dovoljan za smetanje 5 povezanih ba-
jtova. S druge strane, memorija koja ima sadraj 111111111111111100000000
d

ima samo 8 slobodnih bajtova, ali jeste u stanju da izvri alokaciju 5 traenih
bajtova. Memoriju na hipu dodeljenu programu nije mogue automatski reor-
iz

ganizovati u fazi izvravanja, jer nije mogue u toj fazi aurirati sve pokazivae
koji ukazuju na objekte na hipu.
o

Postoji nekoliko pristupa za izbegavanje fragmentisanja memorije. Ukoliko


sk

je mogue, poeljno je izbegavati dinamiku alokaciju memorije. Naime, aloci-


ranje svih struktura podataka statiki (u sluajevima kada je to mogue) dovodi
do breg i predvidljivijeg rada programa (po cenu veeg utroka memorije).
n

Alternativno, ukoliko se ipak koristi dinamika alokacija memorije, poeljno je


ro

memoriju alocirati u veim blokovima i umesto alokacije jednog objekta aloci-


rati prostor za nekoliko njih odjednom (kao to je to, na primer, bilo raeno
realloc).
kt

u primeru datom prilikom opisa funkcije Na kraju, postoje tehnike


efikasnijeg rukovanja memorijom (na primer, memory pooling ), u kojima se
le

programer manje oslanja na sistemsko reenje, a vie na svoje reenje koje je


prilagoeno specifinim potrebama.
E

10.9.4 Hip i dinamiki ivotni vek

U poglavlju 9.3.3, reeno je da je memorija dodeljena programu organizo-


vana u segment kda, segment podataka, stek segment i hip segment. Hip
segment predstavlja tzv. slobodnu memoriju iz koje se crpi memorija koja se
dinamiki alocira. Dakle, funkcije malloc, calloc ili realloc, ukoliko uspeju,
vraaju adresu u hip segmentu. Objekti koji su alocirani u slobodnom mem-
orijskom prostoru nisu imenovani, ve im se pristupa iskljuivo preko adresa.
Poto ovi objekti nisu imenovani, oni nemaju definisan doseg (a doseg pokazi-
266 10. Pokazivai i dinamika alokacija memorije

vaa putem kojih se pristupa dinamikim objektima podlee standardnim prav-


ilima). Svi objekti koji su dinamiki alocirani imaju dinamiki ivotni vek. Ovo
znai da se memorija i alocira i oslobaa iskljuivo na eksplicitni zahtev i to
tokom rada programa.
Hip segment obino poinje neposredno nakon segmenta podataka, a na
suprotnom kraju memorije od stek segmenta. Obino se podaci na hipu slau
od manjih ka veim adresama (engl. upward growing), dok se na steku slau
od veih ka manjim adresama (eng;. downward growing). Ovo znai da se u

5)
trenutku kada se iscrpi memorijski prostor dodeljen programu, hip i stek po-
tencijalno sudaraju, ali operativni sistemi obino to spreavaju i tada obino

01
dolazi do nasilnog prekida rada programa.

Pitanja i zadaci za vebu

(2
Pitanje 10.9.1.

je
Navesti prototip, opisati njeno ponaanje i primer korienja funkcije:
an
malloc;
calloc;
d

realloc;
iz

free.
o

U kom zaglavlju su deklarisane ove funkcije?


sk

Pitanje 10.9.2. Fukcija realloc ima isti efekat kao funkcija free ako je:
n

vrednost njenog prvog argumenta nula;


ro

vrednost njenog drugog argumenta nula;


kt

vrednost oba njena argumenta nula;

vrednost oba njena argumenta razliita od nule.


le

Pitanje 10.9.3. Kada je, nakon komandi


E

char *p;
p = (char*)malloc(n);

komanda strcpy(p,"test"); bezbedna?

Pitanje 10.9.4. U kom segmentu memorije se tokom izvravanja programa


uvaju dinamiki alocirani blokovi memorije.

Pitanje 10.9.5. Kako se zove pojava kada se izgubi vrednost poslednjeg pokazi-
vaa na neki dinamiki alocirani blok memorije? Zato je ova pojava opasna?
267 10. Pokazivai i dinamika alokacija memorije

Pitanje 10.9.6. Da li e doi do curenja memorije ako nakon komande


p = (int*)malloc(sizeof(int)*5)
slede komanda/komande:

q = (int*)malloc(sizeof(int)*5);
p = (int*)malloc(sizeof(int)*5);
free(p); free(p);

5)
free(p+1);

01
Pitanje 10.9.7. Zaokruiti komandu u narednom kdu koja dovodi do greke?

(2
int *f = malloc(4*sizeof(int));
int* g = f;
free(g);

je
free(f);
f=g;
an
Pitanje 10.9.8. Koje su prednosti korienja dinamike alokacije memorije
(u odnosu na statiku i automatsku alokaciju)? Koji su nedostaci?
d

Pitanje 10.9.9. free(p),vrednost p,


iz

ta je, nakon poziva pokazivaa a ta


vrednost *p?
Pitanje 10.9.10.
o

Koja naredba treba da se, prema dobroj praksi, izvri nakon


free(p);?
sk

ta se time postie?

Pitanje 10.9.11. Kako sistem upravlja dinamikom memorijom? Zato se ne


n

sme dinamiki alociran prostor osloboditi dva puta?


ro

Pitanje 10.9.12. Opisati ukratko bar etiri este greke koje se javljaju u vezi
sa dinamikom alokacijom memorije.
kt

Pitanje 10.9.13. free


Da li je u nekoj situaciji dozvoljeno funkciji proslediti
malloc, calloc i realloc?
le

pokaziva koji nije dobijen funkcijama

Pitanje 10.9.14. Kako se zove situacija u kojoj je memorija na hipu podeljena


E

na mnotvo malih i nepovezanih blokova delova?

Pitanje 10.9.15. Da li se fragmentisanje memorije moe umanjiti ako se

ne koristi rekurzija?

ne koristi memorija na hipu?

koristi to manje lokalnih promenljivih?

koristi to manje globalnih promenljivih?


268 10. Pokazivai i dinamika alokacija memorije

Zadatak 10.9.1. ,
Napisati program koji sa standardnog ulaza uitava broj
a zatim i niz od sa standardnog ulaza.
celih brojeva, pa zatim i ceo broj
Program treba da vrati indeks broja u nizu (ako se nalazi u ) ili indeks

elementa niza koji je po apsolutnoj vrednosti najblii broju .

Zadatak 10.9.2. Napisati program koji sa standardnog ulaza uitava cele bro-
jeve dok ne uita nulu kao oznaku za kraj. Na standardni izlaz ispisati koji
broj se pojavio najvie puta meu tim brojevima. Na primer, ako se na ulazu

5)
pojave brojevi: 2 5 12 4 5 2 3 12 15 5 6 6 5 program treba da vrati broj

5. Zadatak reiti korienjem niza koji se dinamiki realokacira.

01
Zadatak 10.9.3. Napisati program koji sa standardnog ulaza uitava prvo
dimenzije matrice ( i ) a zatim redom i elemente matrice (ne postoje pret-

(2
postavke o dimenziji matrice), a zatim ispisuje matricu spiralno, krenuvi od

gornjeg levog ugla.

Zadatak 10.9.4.

je
int skalarni_proizvod(int* a,int* b, int n) koja
an
1. Napisati funkciju
rauna skalarni proizvod vektora a i b duine n. (Sklarni proizvod dva vek-
tora = (1 , . . . , ) i = (1 , . . . , ) je suma = 1 1 + 2 2 + . . . +
d

)
iz

2. Napisati funkciju int ortonormirana(int** A, int n) kojom se prover-


ava da li je zadata kvadratna matrica A dimenzije ortonormirana.
o

Za matricu emo rei da je ortonormirana ako je skalarni proizvod svakog


sk

para razliitih vrsta jednak 0, a skalarni proizvod vrste sa samom sobom


1. Funkcija vraa 1 ukoliko je matrica ortonormirana, 0 u suprotnom.
n

3. Napisati glavni program u kome uitava dimenzija kvadratne matrice n, a


ro

zatim i elementi i pozivom funkcije se utvru


. je da li je matrica ortonormi-
rana. Maksimalna dimenzija matrice nije unapred poznata.

kt
le
E
Glava 11

5)
Pregled standardne biblioteke

01
(2
je
Jezik C ima mali broj naredbi i mnoge funkcionalnosti obezbeene su kroz
funkcije standardne biblioteke (engl. standard library). Standardna biblioteka
an
obezbeuje funkcije, ali i makroe i definicije tipova za potrebe rada sa da-
totekama, niskama, alokacijom memorije i slino. Standardna C biblioteka
d

nije jedna, konkretna biblioteka koja se koristi za povezivanje sa programima


napisanom u jeziku C, ve ona pre predstavlja standard koji mora da potuje
iz

svaki C prevodilac. Kako je implementirana standardna biblioteka zavisi od


konkretnog sistema.
o

Programski interfejs za pisanje aplikacija (API) za standardnu biblioteku


dat je u vidu datoteka zaglavlja, od kojih svaka sadri deklaracije funkcije,
sk

definicije tipova i makroa. Skupu petnaest datoteka zaglavlja propisanih stan-


dardom ANSI C (C89), dokument Normative Addendum 1 (NA1) dodao je tri
n

datoteke 1995. godine. Verzija C99 uvela je jo est datoteka zaglavlja, a verz-
ro

ija C11 jo pet (od kojih tri mogu da ne budu podrane od implementacija, a
da se te implementacije i dalje smatraju standardnim). U tabeli 11.1 dat je
kt

pregled svih dvadeset devet zaglavlja. Ove datoteke, zajedno sa izvornim k-


dom programa bivaju ukljuene u proces kompilacije (korienjem pretproce-
sora i njegove direktive #include). Pored ovih datoteka, standardna biblioteka
le

sadri i definicije funkcija koje obino nisu raspoloive u izvornom, ve samo u


E

prevedenom, objektnom (mainskom) obliku, spremnom za povezivanje sa ob-


jektnim modulima programa korisnika. Ovaj (vei) deo standardne biblioteke
biva ukljuen u fazi povezivanja (i ne obrauje se tokom kompilacije).
U daljem tekstu e biti ukratko opisane neke od funkcija deklarisanih u
nekim od ovih zaglavlja. Najkompleksnije zaglavlje stdio.h bie opisano u
narednoj glavi, posveenoj ulazu i izlazu C programa.

11.1 Zaglavlje string.h

269
270 11. Pregled standardne biblioteke

Datoteka zaglavlja Od Deklaracije i definicije koje sadri

<assert.h> C89 Makro assert.


<complex.h> C99 Funkcije za rad sa kompleksnim brojevima.
<ctype.h> C89 Funkcije za klasifikovanje i konvertovanje karaktera,

5)
nezavisno od karakterske tabele koja se koristi (ASCII
ili neke druge);
<errno.h> C89 Podrka za rad sa kdovima greaka koje vraaju bib-

01
lioteke funkcije.
<fenv.h> C99 Funkcije za kontrolu okruenja za brojeve u pokretnom
zarezu.

(2
<float.h> C89 Konstante koje specifikuju svojstva brojeva u pokret-
nom zarezu zavisna od implementacije.
<inttypes.h> C99 Proirena podrka za celobrojne tipovi fiksne irine.
<iso646.h> NA1 Makroi za opisivanje standardnih tokena.

je
<limits.h> C89 Konstante koje specifikuju svojstva celih brojeva zav-
isna od implementacije.
an
<locale.h> C89 Funkcije za lokalizaciju.
<math.h> C89 esto korie matematike funkcije.
<setjmp.h> C89 Makroi setjmp i longjmp.
d

<signal.h> C89 Funkcije za upravljanje signalima.


<stdalign.h> C11 Podrka sa ravnanje objekata.
iz

<stdarg.h> C89 Podrka za funkcije sa promenljivim brojem param-


etara
<stdatomic.h> C11 Podrka za atomike operacije nad podacima deljenim
o

izmeu niti.
<stdbool.h> bool.
sk

C99 Tip
<stddef.h> C89 Nekoliko raznorodnih tipova i konstanti (ukljuujui
NULL i size_t).
n

<stdint.h> C99 Podrka za celobrojne tipove fiksne irine.


<stdio.h> C89 Deklaracije osnovnih ulazno/izlaznih funkcija;
ro

<stdlib.h> C89 Deklaracije funkcija za konverzije, za generisanje pseu-


dosluajnih brojeva, za dinamiku alokaciju memorije,
kt

prekid programa itd.


<stdnoreturn.h> C11 Podrka za specifikovanje funkcija bez povratka
<string.h> C89 Funkcije za obradu niski.
le

<tgmath.h> C99 Matematike funkcije generikog tipa.


<threads.h> C11 Funkcije za upravljanje nitima.
<time.h>
E

C89 Funkcije za rad sa datumima i vremenom


<uchar.h> C11 Tipovi i deklaracije za rad sa Unicode karakterima.
<wchar.h> NA1 Funkcije za rad sa niskama karaktera vee irine.
<wctype.h> NA1 Funkcije za klasifikovanje i konvertovanje karaktera
vee irine.

Tabela 11.1: Pregled zaglavlja standardne biblioteke


271 11. Pregled standardne biblioteke

size_t strlen (char* str);

char* strcpy (char* destination, char* source);


char* strncpy (char* destination, char* source, size_t num);
char* strcat (char* destination, char* source);
char* strncat (char* destination, char* source, size_t num);

int strcmp (char* str1, char* str2);

5)
int strncmp (char* str1, char* str2, size_t num);

01
char* strchr (char* str, int character);
char* strrchr (char* str, int character);

(2
char* strstr (char* str1, char * str2);

size_t strspn (char* str1, char* str2);

je
size_t strcspn (char* str1, char* str2);
char* strpbrk (char* str1, char* str2);
an
char* strtok (char * str, char* delimiters);
d
iz

strlen Funkcija strlen izraunava duinu prosleene niske karaktera (termi-


nalna nula se ne rauna).
o

strcpy Funkcija strcpy kopira drugu navedenu nisku u prvu (pretpostavlja-


sk

jui da u prvoj ima dovoljno mesta za sve karaktere druge, ukljuujui i


terminalnu nulu). Funkcija strncpy takoe vri kopiranje, ali se dodat-
n kontrolie najvei broj karaktera koji moe biti kopi-
n

nim parameterom
ran (u sluaju da je niska koja se kopira kraa od broja n ostatak se pop-
ro

unjava nulama, a ukoliko je dua od broja n terminalna nula se ne dodaje


automatski na kraj). Korienje funkcije strncpy smatra se bezbednijim
kt

od strcpy jer je mogue kontrolisati mogunost prekoraenja bafera.

strcat Funkcija strcat nadovezuje drugu navedenu nisku na prvu, pretpostavl-


le

jajui da prva niska ima dovoljno prostora da smesti i sve karaktere druge,
E

ukljuujui i njenu terminalnu nulu (terminalna nula prve niske se brie).


Funkcija strncat dodatnim parametrom n kontrolie najvei broj druge
niske koji e biti nadovezan na prvu.

strchr Funkcija strchr proverava da li data niska sadri dati karakter i vraa
pokaziva na prvu poziciju na kojoj je karakter naen ili NULL ako karak-
ter nije naen. Funkcija strrchr radi slino, ali vraa pokaziva na
poslednje pojavljivanje karaktera.

strpbrk Funkcija strpbrk vraa pokaziva na prvo pojavljivanje nekog karak-


tera iz druge navedene niske u prvoj navedenoj niski ili NULL pokaziva
ako takvog karaktera nema.
272 11. Pregled standardne biblioteke

strstr Funkcija strstr proverava da li je druga navedena niska podniska


prve. Ako jeste, vraa se pokaziva na prvo njeno pojavljivanje unutar
prve niske, a ako nije, vraa se pokaziva NULL.
strspn Funkcija strspn vraa duinu poetnog dela prve navedene niske koji
se sastoji samo od karaktera sadranih u drugoj navedenoj niski. Slino,
funkcija strcspn vraa duinu poetnog dela prve navedene niske koji se
sastoji samo od karaktera koji nisu sadrani u drugoj navedenoj niski.

5)
strtok Niz poziva funkcije strtok slue da podele nisku u tokene koji su niske
uzastopnih karaktera razdeljene karakterima navedenim u drugoj niski.

01
U prvom pozivu funkcije kao prvi argument navodi se niska koja se deli,
a u drugom navodi se NULL. Na primer, kd

(2
char str[] ="- Ovo, je jedna niska.", delims[] = " ,.-";
char *s = strtok (str, delims);

je
while (s != NULL) {
printf ("%s\n", s);
an
s = strtok (NULL, delims);
}
d
iz

ispisuje

Ovo
o

je
sk

jedna
niska
n
ro

11.2 Zaglavlje stdlib.h


kt

void *malloc(size_t n);


void *calloc(size_t n, size_t size);
le

void *realloc(void *memblock, size_t size);


void free(void* p);
E

int rand(void);
void srand(unsigned int);

int system(char* command);


void exit(int);

Ve smo naveli da se u zaglavlju stdlib.h nalaze deklaracije funkcija malloc,


calloc, realloc i free koje slue za dinamiku alokaciju i oslobaanje mem-
orije. Pored njih najznaajnije funkcije ovog zaglavlja su i sledee.
273 11. Pregled standardne biblioteke

rand Funkcijaint rand(void); generie pseudo-sluajne cele brojeve u inter-


RAND_MAX (koja je takoe definisana u zaglavlju
valu od 0 do vrednosti
<stdlib.h>). Termin pseudo-sluajni se koristi da se naglasi da ovako
dobijeni brojevi nisu zaista sluajni, ve su dobijeni specifinim izrauna-
vanjima koja proizvode nizove brojeva nalik na sluajne. Funkcija rand()
vraa sledei pseudo-sluajan broj u svom nizu. Pseudo-sluajni brojevi
brojevi u pokretnom zarezu iz intervala [0, 1) mogu se dobiti na sledei
nain:

5)
((double) rand() / (RAND_MAX+1.0))

01
Funkcija rand() moe biti jednostavno upotrebljena za generisanje pseudo-

(2
sluajnih brojeva u celobrojnom intervalu [, ]:

n+rand()%(m-n+1)

je
an
Ipak, bolja svojstva (bolju raspodelu) imaju brojevi koji se dobijaju na
sledei nain:
d

n+(m-n+1)*((double) rand() / (RAND_MAX+1.0))


iz

srand Funkcija rand niz pseudo-sluajnih brojeva generie uvek poev od iste
o

podrazumevane vrednosti (koja je jednaka 1). To znai da e se u svakom


sk

pokretanju programa dobiti isti niz pseudo-sluajnih brojeva. Funkcija


void srand(unsigned); postavlja vrednost koja se u sledeem pozivu
n

funkcije rand (a time, indirektno, i u svim narednim) koristi u gener-


isanju tog niza. Za bilo koju vrednost funkcije srand, itav niz brojeva
ro

koji vraa funkcija rand je uvek isti. To je pogodno za ponavljanje (na


kt

primer, radi debagovanja) procesa u kojima se koriste pseudo-sluajni


brojevi. esto se kao argument u pozivu funkcije srand navodi trenutno
vreme (npr. srand(time(NULL)), gde se koristi funkcija time iz zaglavlja
le

<time.h>) da bi se postiglo da se pri svakom pokretanju programa dobije


razliit niz vrednosti.
E

system Prototip funkcije system je int system(char* command); Pozivom


system(komanda), izvrava se navedena komanda komanda (potencijalno
sa argumentima) kao sistemski poziv kojim se izvrava komanda opera-
tivnog sistema ili neki drugi program (u okviru tekueg programa, a ne
iz komandne linije). Nakon toga, nastavlja se izvravanje programa.

Na primer, pozivom system("date"); aktivira se program date koji


ispisuje tekui datum i koji je esto prisutan na razliitim operativnim
sistemima.
274 11. Pregled standardne biblioteke

Ukoliko je argument funkcije system vrednost NULL, onda se samo prover-


ava da li je raspoloiv komandni interpretator koji bi mogao da izvrava
zadate komande. U ovom sluaju, funkcija system vraa ne-nulu, ako je
komandni interpretator raspoloiv i nulu inae.

Ukoliko argument funkcije system nije vrednost NULL, onda funkcija


vraa istu vrednost koju je vratio komandni interpretator (obino nulu
ukoliko je komanda izvrena bez greaka). Vrednost 1 vraa se u sluaju

5)
greke (na primer, komandni interpretator nije rasploiv, nema dovoljno
memorije da se izvri komanda, lista argumenata nije ispravna).

01
exit Funkcija void exit(int); zaustavlja rad programa (bez obzira iz koje
funkcije je pozvana) i vraa svoj argument kao povratnu vrednost pro-

(2
grama. Obino povratna vrednost nula oznaava uspeno izvrenje pro-
grama, a vrednost ne-nula ukazuje na neku greku tokom izvravanja.
esto se koriste i simbolike konstante EXIT_SUCCESS EXIT_FAILURE za

je
uspeh i za neuspeh. Naredba exit(e); u okviru funkcije main ekviva-
lentna je naredbi return e;. Funkcija exit automatski poziva fclose
an
za svaku datoteku otvorenu u okviru programa (vie o funkciji fclose
bie reeno u glavi 12).
d

11.3 Zaglavlje ctype.h


iz

int isalpha(int c); int isdigit(int c);


o

int isalnum(int c); int isspace(int c);


sk

int isupper(int c); int islower(int c);


int toupper(int c); int tolower(int c);
n

Zaglavlje ctype.h sadri deklaracije nekoliko funkcija za ispitivanje i kon-


ro

vertovanje karaktera. Sve funkcije navedene u nastavku imaju argument tipa


int i imaju int kao tip povratne vrednosti.
kt

isalpha(c) vraa ne-nula vrednost ako je c slovo, nulu inae;


le

isupper(c) vraa ne-nula vrednost ako je slovo c veliko, nulu inae;


E

islower(c) vraa ne-nula vrednost ako je slovo c malo, nulu inae;

isdigit(c) vraa ne-nula vrednost ako je c cifra, nulu inae;

isalnum(c) vraa ne-nula vrednost ako je c slovo ili cifra, nulu inae;

isspace(c) vraa ne-nula vrednost ako je c belina (razmak, tab, novi red,
itd), nulu inae;

toupper(c) vraa karakter c konvertovan u veliko slovo ili, ukoliko je to nemogue


sm karakter c;
275 11. Pregled standardne biblioteke

tolower(c) vraa karakter c konvertovan u malo slovo ili, ukoliko je to nemogue


sm karakter c;

11.4 Zaglavlje math.h


double sin(double); double asin(double);
double cos(double); double acos(double);

5)
double tan(double); double atan(double); double atan2(double);
double log(double); double log10(double); double log2(double);
double pow(double); double exp(double); double sqrt(double);

01
Zaglavlje math.h sadri deklaracije vie od dvadeset esto korienih matem-

(2
atikih funkcija. Svaka od njih ima jedan ili dva argumenta tipa double i ima
double kao tip povratne vrednosti.

je
sin(x) vraa vrednost funkcije sin(), smatra se da je zadato u radijanima;
an
cos(x) vraa vrednost funkcije cos(), smatra se da je zadato u radijanima;

atan2(y, x) vraa vrednost koja odgovara uglu u odnosu na -osu take , .


d

Ugao je u radijanima iz intervala (, ]. Vrednost nije definisana za


iz

koordinatni poetak.

exp(x) vraa vrednost ;


o

log(x) vraa vrednost ln (mora da vai > 0); logaritam log (za , > 0,
sk

= 1) moe se izraunati kao log(b)/log(a).


n

log10(x) vraa vrednost log10 (mora da vai > 0);


ro

log2(x) vraa vrednost log12 (mora da vai > 0);


pow(x,y) ;
kt

vraa vrednost

sqrt(x) vraa vrednost (mora da vai > 0); vrednost

moe se
le

izraunati kao pow(x, 1/n).


E

fabs(x) vraa apsolutnu vrednost od .

Pored funkcija u ovom zaglavlju su definisane mnoge vane matematike


konstante. Na primer,
M_PI ima vrednost broja , M_E broja , M_SQRT2 broja
2 itd.
276 11. Pregled standardne biblioteke

11.5 Zaglavlje assert.h


void assert(int)

assert assert koristi se u fazi razvoja programa da ukae na mogue


Funkcija
assert(izraz) vrednost celobrojnog izraza
greke. Ukoliko je pri pozivu
izraz jednaka nuli, onda e na standardni tok za greke (stderr) biti

5)
ispisana poruka nalik sledeoj:

01
Assertion failed: expression, file filename, line nnn

(2
i bie prekinuto izvravanje programa. Ukoliko je definisano simboliko
ime NDEBUG (direktivom #define) pre nego to je ukljueno zaglavlje
<assert.h>, pozivi funkcije assert se ignoriu. Funkcija assert se

je
obino koristi u toku razvoja programa, u takozvanoj debug verziji. Kada
an
je program spreman za korienje, proizvodi se release verzija i tada se
pozivi funkcije assert ignoriu (jer je tada obino definisano ime NDEBUG).
U fazi razvoja programa, upozorenja koja generie assert ukazuju na
d

krupne propuste u programu, ukazuju da tekui podaci ne zadovoljavaju


iz

neki uslov koji je podrazumevan, te pomau u ispravljanju tih propusta.


Funkcija assert ne koristi se da ukae na greke u fazi izvravanja pro-
grama koje nisu logike (na primer, neka datoteka ne moe da se otvori)
o

ve samo na one logike greke koje ne smeju da se dogode. U zavrnoj


sk

verziji programa, pozivi funkcije assert imaju i dokumentacionu ulogu


oni itaocu programa ukazuju na uslove za koje je autor programa
n

podrazumevao da moraju da vae u nekoj taki programa.


ro

Pitanja i zadaci za vebu


kt

Pitanje 11.5.1. U kojoj datoteci zaglavlja je deklarisana funkcija cos? Kako


se u programu moe dobiti vrednost konstanti i ?
le

Pitanje 11.5.2. Navesti prototip i opisati dejstvo funkcije strcpy. Navesti i


E

jednu moguu implementaciju.

Pitanje 11.5.3. U kom zaglavlju standardne C biblioteke je deklarisana funkcija


rand? Kako se moe, korienjem funkcije iz standardne biblioteke, dobiti pseu-
dosluajan ceo broj iz intervala [10,20]? Kako se moe dobiti pseudosluajan
ceo broj izmeu nim (pri emu vai n<m)?
Pitanje 11.5.4. ta je efekat funkcije exit?
Pitanje 11.5.5. U kojim situacijama se koristi makro assert? Kakav je
efekat makroa assert u release izvrnoj verziji?
Glava 12

5)
Ulaz i izlaz programa

01
(2
je
Jezik C je dizajniran kao mali jezik i ulazno/izlazne operacije nisu direktno
podrane samim jezikom, ve specijalizovanim funkcijama iz standardne bib-
an
lioteke jezika (koja je prisutna u svakom C okruenju). Poto su ove funkcije
deo standarda jezika C, mogu se koristiti u programima uz garantovanu preno-
d

sivost programa izmeu razliitih sistema. Svaka izvorna datoteka u kojoj


se koriste ulazno/izlazne funkcije, trebalo bi da ukljui standardno zaglavlje
iz

<stdio.h>.

12.1 Standardni tokovi


o
sk

Standardna biblioteka implementira jednostavan model tekstualnog ulaza i


izlaza. Ulaz i izlaz se modeluju tzv. tokovima (engl. stream) podataka (obino
n

pojedinanih bajtova ili karaktera). Standardni ulaz obino ine podaci koji
ro

se unose sa tastature. Podaci koji se upuuju na standardni izlaz se obino


prikazuju na ekranu. Pored standardnog izlaza, postoji i standardni izlaz za
kt

greke na koji se obino upuuju poruke o grekama nastalim tokom rada pro-
grama i koji se, takoe, obino prikazuje na ekranu.
le

U mnogim okruenjima, mogue je izvriti preusmeravanje (redirekciju)


standardnog ulaza tako da se, umesto sa tastature, karakteri itaju iz neke
E

datoteke. Na primer, ukoliko se program pokrene sa

./prog < infile

onda program prog ita karaktere iz datoteke infile, umesto sa tastature.


Takoe, mnoga okruenja omoguavaju da se izvri preusmeravanje (redi-
rekcija) standardnog izlaza u neku datoteku. Na primer, ukoliko se program
pokrene sa

./prog > outfile

277
278 12. Ulaz i izlaz programa

onda program prog upisuje karaktere u datoteku outfile, umesto na ekran.


Ukoliko bi se poruke o grekama tampale na standardni izlaz, zajedno sa
ostalim rezultatima rada programa, onda, u sluaju preusmeravanja standard-
nog izlaza u datoteku, poruke o grekama korisniku ne bi bile prikazane na
ekranu i korisnik ih ne bi video. Ovo je glavni razlog uvoenja standardnog
izlaza za greke koji se obino prikazuje na ekranu. Mogue je izvriti redirek-
ciju i standardnog izlaza za greke u datoteku, na primer:

5)
./prog 2> errorfile

01
12.1.1 Ulaz i izlaz pojedinanih karaktera

(2
Najjednostavniji mehanizam itanja sa standardnog ulaza je itanje jednog
po jednog karaktera korienjem funkcije getchar:

je
int getchar(void);
an
Funkcija getchar vraa sledei karakter sa ulaza, svaki put kada se pozove, ili
EOF kada doe do kraja toka. Simbolika konstanta EOF je definisana u zaglavlju
d

<stdio.h>. Njena vrednost je obino -1, ali umesto ove konkretne vrednosti
ipak treba koristiti ime EOF. Funkcija getchar (kao i jo neke srodne funkcije)
iz

umesto tipa char koristi tip int koji je dovoljno irok da u njega mogu da se
smeste kako ASCII vrednosti od 0 do 127, tako i specijalna vrednost EOF, tj. -1.
o

Ako bi povratni tip funkcije getchar bio char, a povratna vrednost u nekom
sk

konkretnom sluaju jednaka EOF (tj. konstantna vrednost -1), na sistemima na


kojima je tip char podrazumevano neoznaen, vrednost EOF bi se konvertovala
u 255, pa bi poreenje getchar() == EOF bilo netano (jer bi sa leve strane
n

bila vrednost 255 koja bi pre poreenja sa -1 bila promovisana u tip int).
ro

Funkcija getchar() najee se realizuje tako to karaktere uzima iz privre-


menog bafera koji se puni itanjem jedne po jedne linije ulaza. Dakle, u in-
kt

teraktivnom radu sa programom, getchar() nee imati efekta sve dok se ne


unese prelazak u novi red ili oznaka za kraj datoteke.
1
le

Najjednostavniji mehanizam pisanja na standardni izlaz je pisanje jednog


po jednog karaktera korienjem funkcije putchar:
E

int putchar(int);

Funkcija putchar(c) tampa karakter c na standardni izlaz, a vraa karakter


koji je ispisala ili EOF ukoliko je dolo do greke.
Kao primer rada sa pojedinanim karakterima, razmotrimo program koji
prepisuje standardni ulaz na standardni izlaz, pretvarajui pri tom velika u

1U standardnoj biblioteci ne postoji funkcija koja ita samo jedan karakter, ne cekajuci
kraj ulaza.
279 12. Ulaz i izlaz programa

mala slova.

Program 12.1.
#include <stdio.h>
#include <ctype.h>

int main()
{

5)
int c;
while ((c = getchar()) != EOF)

01
putchar(tolower(c));
return 0;

(2
}

Funkcija tolower deklarisana je u zaglavlju <ctype.h> i prevodi karaktere

je
velikih slova u karaktere malih slova, ne menjajui pri tom ostale karaktere.
Funkcije poput getchar i putchar iz <stdio.h> i tolower iz <ctype.h>
an
su u mnogim implementacijama zapravo makroi (zasnovani na drugim funkci-
jama iz standardne biblioteke), ime se izbegava dodatno vreme i prostor potre-
d

ban za realizaciju funkcijskog poziva.


iz

12.1.2 Linijski ulaz i izlaz

gets:
o

Biblioteka funkcija
sk

char* gets(char* s);


n

ita karaktere sa standardnog ulaza do kraja tekue linije ili do kraja datoteke
ro

i karaktere smeta u nisku s. Oznaka kraja reda se ne smeta u nisku, a niska


se automatski zavrava nulom. Ne vri se nikakva provera da li niska s sadri
dovoljno prostora da prihvati proitani sadraj i ovo funkciju gets ini veoma
kt

opasnom za korienje. U sluaju da je ulaz uspeno proitan, gets vraa s, a


inae vraa NULL pokaziva.
le

Biblioteka funkcija fputs:


E

int puts(const char* s);

ispisuje nisku na koju ukazuje s na standardni izlaz, dodajui pri tom oznaku
za kraj reda. Zavrna nula se ne ispisuje. Funkcija puts vraa EOF u sluaju
da je dolo do greke, a nenegativnu vrednost inae.
280 12. Ulaz i izlaz programa

12.1.3 Formatirani izlaz printf


Funkcija printf ve je koriena u prethodnom tekstu. Njen prototip je:
2

int printf(const char *format, arg1, arg2, ...);

Funkcija printf prevodi vrednosti osnovnih tipova podataka u serije ka-


raktera i usmerava ih na standardni izlaz. Funkcija vraa broj odtampanih

5)
karaktera. Sluajevi korienja ove funkcije iz prethodnih glava jesu najuobia-
jeniji, ali svakako nisu potpuni. Pozivi funkcija putchar i printf mogu biti
isprepletani izlaz se vri u istom redosledu u kojem su ove funkcije pozvane.

01
Funkcija printf prevodi, formatira i tampa svoje argumente na stan-
dardni izlaz pod kontrolom date format niske . Format niska sadri dve vrste

(2
objekata: obine karaktere, koji se doslovno prepisuju na standardni izlaz i
specifikacije konverzija od kojih svaka uzrokuje konverziju i tampanje sledeeg
uzastopnog argumenta funkcije printf. Svaka specifikacija konverzije poinje

je
karakterom % i zavrava se karakterima konverzije. Izmeu % i karaktera
konverzije mogue je da se redom nau:
an
Znak minus (-), koji prouzrokuje levo poravnanje konvertovanog argu-
menta.
d
iz

Broj koji specifikuje najmanju irinu polja. Konvertovani argument se


tampa u polju koje je barem ovoliko iroko. Ukoliko je potrebno, polje
se dopunjava razmacima sa leve (odnosno desne strane, ukoliko se trai
o

levo poravnanje).
sk

Taka ., koja odvaja irinu polja od preciznosti.


n

Broj za preciznost, koji specifikuje najvei broj karaktera koje treba tam-
ro

pati iz niske u sluaju tampanja niske ili broj taaka iza decimalne take
u sluaju broja u pokretnom zarezu ili najmanji broj cifara u sluaju
celog broja.
kt

Karakter h, ako ceo broj treba da se tampa kao short, ili l ako ceo broj
le

treba da se tampa kao long.


E

Konverzioni karakteri
Konverzioni karakteri su prikazani u narednoj tabeli. Ukoliko se nakon %
navede pogrean konverzioni karakter, ponaanje je nedefinisano.

2 Funkcija printf je jedna od funkcija iz standardne biblioteke koja ima promenljiv broj
argumenata (tj. broj argumenata u pozivima ne mora uvek da bude isti). Programer moe
definisati svoje funkcije sa promenljivim brojem argumenata koristei funkcije deklarisane u
standardnom zaglavlju stdarg.h.
281 12. Ulaz i izlaz programa

Karakter Tip tampa se kao

d,i int dekadni broj


o int neoznaeni oktalni broj (bez vodeeg simbola 0)
x,X int neoznaeni heksadekadni broj (bez vodeih 0x ili 0X),
korienjem abcdef ili ABCDEF za 10, ...,15
u int neoznaeni dekadni broj
c int pojedinani karakter

5)
s char * tampa karaktere niske do karaktera \0 ili broja
karaktera navedenog u okviru preciznosti.
f double

01
[-]m.dddddd, gde je broj d-ova odreen preciznou
(podrazumevano 6)
e,E double [-]m.dddddde+/-xx ili [-]m.ddddddE+/-xx, gde je

(2
broj d-ova odreen preciznou (podrazumevano 6)
g,G double koristi %e ili %E ako je eksponent manji od -4 ili vei
ili jednak preciznosti; inae koristi %f. Zavrne nule i

je
zavrna decimalna taka se ne tampaju
p void * pokaziva (reprezntacija zavisna od implementacije)
an
% nijedan argument se ne konvertuje; tampa %
d

Za navedene konverzione karaktere postoje i modifikatori duine: h (za


short), l (za celobrojne tipove, za long), L (za brojeve
iz

celobrojne tipove, za
u pokretnom zarezu, za long double). Tako, na primer, sekvenca hd moe se
koristiti za ispisivanje podatka tipa short int, sekvenca ld moe se koristiti
o

za ispisivanje podatka tipa long int, a sekvenca Lf za ispisivanje podatka tipa


sk

long double.
Primetimo da za format float ne postoji zaseban konverzioni karakter. To
je zbog toga to se svi argumenti tipa float implicitno konvertuju u tip double
n

prilikom poziva funkcije printf (jer je printf funkcija sa promenljivim brojem


ro

argumenata kod kojih je ova promocija podrazumevana). Poevi od standarda


C99 pored formata %f doputeno je navoditi i %lf (zanimljivo, kompilator GCC
kt

to i zahteva za tip double).


irina polja ili preciznost se mogu zadati i kao *, i tada se njihova vrednost
le

odreuje na osnovu vrednosti narednog argumenta (koji mora biti int). Na


primer, da se odtampa najvie max karaktera iz niske s, moe se navesti:
E

printf("%.*s", max, s);

Prilikom poziva funkcije printf na osnovu prvog argumenta (format niske)


odreuje se koliko jo argumenata sledi i koji su njihovi tipovi. Ukoliko se ne
navede odgovarajui broj argumenata ili se navedu argumenti neodgovarajuih
tipova, dolazi do greke. Takoe, treba razlikovati naredna dva poziva:

printf(s); /* pogresno ako s sadrzi karakter % */


printf("%s", s); /* bezbedno */
282 12. Ulaz i izlaz programa

Primer prikaza celih brojeva:

int count = -9234;


printf("Celobrojni formati:\n"
"\tDekadni: %d Poravnat: %.6d Neoznacen: %u\n",
count, count, count);
printf("Broj %d prikazan kao:\n\tHex: %Xh C hex: 0x%x Oct: %o\n",

5)
count, count, count, count );

01
Celobrojni formati:
Dekadni: -9234 Poravnat: -009234 Neoznacen: 4294958062
Broj -9234 prikazan kao:

(2
Hex: FFFFDBEEh C hex: 0xffffdbee Oct: 37777755756

je
/* Prikaz konstanti zapisanih u razlicitim osnovama. */
an
printf("Cifre 10 predstavljaju:\n");
printf("\tHex: %i Octal: %i Decimal: %i\n",
0x10, 010, 10);
d
iz

Cifre 10 predstavljaju:
Hex: 16 Octal: 8 Decimal: 10
o
sk

Primer prikaza karaktera:


n

char ch = h;
ro

printf("Karakteri u polju date sirine:\n%10c%c\n", ch, ch);


kt

Karakteri u polju date sirine:


hh
le
E

Primer prikaza brojeva u pokretnom zarezu:

double fp = 251.7366;
printf("Brojevi u pokretnom zarezu:\n\t%f %.2f %e %E\n",
fp, fp, fp, fp);

Realni brojevi:
251.736600 251.74 2.517366e+002 2.517366E+002
283 12. Ulaz i izlaz programa

Primer prikaza niski:


Ovaj primer prikazuje navoenje irine polja, poravnanja i preciznosti prilikom
tampanja niski. U narednoj tabeli dat je efekat razliitih format niski na
tampanje niske "hello, world" (12 karaktera). Dvotake su stavljene radi
boljeg ilustrovanja efekta raznih formata.

:%s: :hello, world:


:%10s: :hello, world:

5)
:%.10s: :hello, wor:
:%-10s: :hello, world:

01
:%.15s: :hello, world:
:%-15s: :hello, world :
:%15.10s: : hello, wor:

(2
:%-15.10s: :hello, wor :

je
12.1.4 Formatirani ulaz scanf
an
Funkcija scanf je ulazni analogon funkcije printf. Funkcija scanf ita
karaktere sa standardnog ulaza, interpretira ih na osnovu specifikacije navedene
format niskom i smeta rezultat na mesta odreena ostalim argumentima:
d

int scanf(const char *format, ...)


iz

Opis format niske dat je u nastavku. Ostali argumenti moraju biti pokazi-
o

vai i odreuju lokacije na koje se smeta konvertovani ulaz. Kao i u sluaju


sk

funkcije printf, ovde e biti dat samo prikaz najeih naina korienja ove
funkcije. Funkcija scanf prestaje sa radom kada iscrpi svoju format nisku ili
n

kada neki deo ulaza ne moe da se uklopi u shemu navedenu format niskom.
Funkcija vraa broj uspeno uklopljenih i dodeljenih izlaznih podataka. U
ro

sluaju kraja datoteke, funkcija vraa EOF. Ova vrednost je razliita od vred-
nosti 0 koja se vraa u sluaju da tekui karakter sa ulaza ne moe da se uklopi
kt

u prvu specifikaciju zadatu format niskom. Svaki naredni poziv funkcije scanf
nastavlja tano od mesta na ulazu na kojem je prethodni poziv stao.
le

Format niska sadri specifikacije konverzija kojima se kontrolie konverzija


teksta proitanog sa ulaza. Format niska moe da sadri:
E

Praznine ili tabulatore koji se ne zanemaruju.

Obine karaktere (ne %), za koje se oekuje da se poklope sa sledeim


ne-belinama sa standardnog ulaza.

Specifikacije konverzija, koje se sastoje od karaktera %, opcionog karak-


tera * koji spreava dodeljivanje, opcionog broja kojim se navodi maksi-
malna irina polja, kao i od konverzionog karaktera.

Specifikacija konverzije odreuje konverziju narednog ulaznog polja. Obino


se rezultat ove konverzije upisuje u promenljivu na koju pokazuje naredni
284 12. Ulaz i izlaz programa

ulazni argument. Ipak, ako se navede karakter *, konvertovana vrednost


se preskae i nigde ne dodeljuje. Ulazno polje se definie kao niska karak-
tera koji nisu beline. Ono se prostire ili do narednog karaktera beline
ili do irine polja, ako je ona eksplicitno zadana. Ovo znai da funkcija
scanf moe da ita ulaz i iz nekoliko razliitih linija sa standardnog ulaza,
jer se prelasci u novi red tretiraju kao beline .
3

Konverzioni karakter upuuje na eljenu interpretaciju ulaznog polja.

5)
U narednoj tabeli navedeni su konverzioni karakteri.

01
Karakter Tip Ulazni podatak

d int* dekadni ceo broj


int*

(2
i ceo broj, moe biti i oktalan (ako ima vodeu 0)
ili heksadekadan (vodei 0x ili 0X)
o int* oktalni ceo broj (sa ili bez vodee nule)
unsigned*

je
u neoznaeni dekadni broj
x int* heksadekadni broj (sa ili bez vodeih 0x ili 0X)
an
c char* karakter, naredni karakter sa ulaza se smeta na
navedenu lokaciju; uobiajeno preskakanje belina
se ne vri u ovom sluaju; Za itanje prvog ne-
d

belog karaktera, koristi se %1s


iz

s char* niska (bez navodnika), ukazuje na niz karaktera


dovoljno dugaak za nisku ukljuujui i termi-
nalnu \0 koja se automatski dopisuje
o

e,f,g double* broj u pokretnom zarezu sa opcionim znakom, op-


sk

cionom decimalnom takom i opcionim eksponen-


tom
n

% doslovno %; ne vri se dodela


ro

printf, ispred karaktera konverzije d, i, o, u, i x


Slino kao kod funkcije
mogue je navesti i karakter h koji ukazuje da se u listi argumenata oekuje
kt

pokaziva na short, karakter l koji ukazuje da se u listi argumenata oekuje


pokaziva na long ili karakter L koji ukazuje da se u listi argumenata oekuje
le

pokaziva na long double.


Kao to je ve reeno, argumenti funkcije scanf moraju biti pokazivai.
E

Ubedljivo najei oblik pogrenog korienja ove funkcije je:

scanf("%d", n);

umesto

scanf("%d", &n);

3 Pod belinama se podrazumevaju razmaci, tabulatori, prelasci u novi redovi (carriage


return i/ili line feed), vertikalni tabulatori i formfeed.
285 12. Ulaz i izlaz programa

Ova greka se obino ne otkriva tokom kompilacije.


Funkcija scanf ignorie beline u okviru format niske. Takoe, preskau
se beline tokom itanja ulaznih vrednosti. Pozivi funkcije scanf mogu biti
pomeani sa pozivima drugih funkcija koje itaju standardni ulaz. Naredni
poziv bilo koje ulazne funkcije e proitati prvi karakter koji nije proitan
tokom poziva funkcije scanf.

Primeri

5)
U narednom primeru, korienjem funkcije scanf implementiran je jednos-

01
tavni kalkulator:

Program 12.2.

(2
#include <stdio.h>

int main()

je
{
an
double sum, v;

sum = 0.0;
d

while (scanf("%f", &v) == 1)


iz

printf("\t%.2f\n", sum += v);


return 0;
}
o
sk

Datume sa ulaza koji su u formatu 25 Dec 1988, mogue je itati korien-


jem:
n

int day, year;


ro

char monthname[20];
kt

scanf("%d %s %d", &day, monthname, &year);

monthname nema potrebe navesti simbol &, jer je ime niza ve pokazi-
le

Ispred
va. Doslovni karakteri se mogu pojaviti u okviru format niske i u tom sluaju
E

se oni oekuju i na ulazu. Tako, za itanje datuma sa ulaza koji su u formatu


12/25/1988, mogue je koristiti:

int day, month, year;


scanf("%d/%d/%d", &month, &day, &year);

12.2 Ulaz iz niske i izlaz u nisku


Funkcija printf vri ispis formatiranog teksta na standardni izlaz. Stan-
dardna biblioteka jezika C definie funkciju sprintf koja je veoma slina
286 12. Ulaz i izlaz programa

funkciji printf, ali rezultat njenog rada je popunjavanje niske karaktera for-
matiranim tekstom. Prototip funkcije je:

int sprintf(char *string, const char *format, arg1, arg2, ...);

Ova funkcija formatira argumente arg1, arg2, . . . na osnovu format niske,


a rezultat smeta u nisku karaktera prosleenu kao prvi argument, podrazume-
vajui da je ona dovoljno velika da smesti rezultat.

5)
Slino funkciji sprintf koja vri ispis u nisku karaktera umesto na stan-
dardni izlaz, standardna biblioteka jezika C definie i funkciju sscanf koja je

01
analogna funkciji scanf, osim to ulaz ita iz date niske karaktera, umesto sa
standardnog ulaza.

(2
int sscanf(const char* input, char* format, ...)

Navedimo primer korienja ove funkcije. Da bi se proitao ulaz iji format

je
nije fiksiran, najee je dobro proitati celu liniju sa ulaza, a zatim je analizirati
korienjem sscanf. Da bi se proitao datum sa ulaza u nekom od prethodno
an
navedena formata, mogue je koristiti:

while (getline(line, sizeof(line)) > 0) {


d

if (sscanf(line, "%d %s %d", &day, monthname, &year) == 3)


iz

printf("valid: %s\n", line); /* 25 Dec 1988 */


else if (sscanf(line, "%d/%d/%d", &month, &day, &year) == 3)
printf("valid: %s\n", line); /* 12/25/1988 */
o

else
sk

printf("invalid: %s\n", line); /* pogresan oblik */


}
n
ro

12.3 Ulaz iz datoteka i izlaz u datoteke


kt

Do sada opisane funkcije za ulaz i izlaz su uglavnom itale sa standardnog


ulaza i pisale na standardni izlaz, koji su automatski definisani i od strane
le

operativnog sistema i kojima program automatski ima pristup. Iako je, me-
E

hanizmima preusmeravanja, bilo mogue izvesti programski pristup lokalnim


datotekama, mehanizam preusmeravanja je veoma ogranien jer ne daje mogu-
nost istovremenog itanja (ili pisanja) datoteke i standardnog ulaza kao ni mo-
gunost istovremenog itanja (ili pisanja) vie datoteka. Zbog toga, jezik C nudi
direktnu podrku za rad sa lokalnim datotekama, bez potrebe za korienjem
usluga preusmeravanja operativnog sistema. Sve potrebne deklaracije i za rad
sa datotekama nalaze u zaglavlju <stdio.h>.
287 12. Ulaz i izlaz programa

12.3.1 Tekstualne i binarne datoteke

Iako se svaka datoteka moe razmatrati kao niz bajtova, obino razlikujemo
datoteke koje sadre tekstualni sadraj od datoteka koje sadre binarni sadraj.
U zavisnosti od toga da li se u datoteci nalazi tekstualni ili binarni sadraj,
razlikuju se dva razliita naina pristupa.
Prvi nain je prilagoen tekstualnim datotekama iji sadraj u principu
ine vidljivi karakteri, sa dodatkom oznake kraja reda i horizontalnog tabula-

5)
tora. Ovakve datoteke se obino obrauju liniju po liniju, to je karakteristino
za tekst. Na primer, iz datoteke se ita linija po linija ili se u datoteku up-

01
isuje linija po linija. Oigledno, u ovom sluaju oznaka za kraj linije je veoma
relevantna i znaajna. Meutim, na razliitim sistemima tekst se kodira na ra-
zliite naine i na nekim sistemima kraj reda u tekstualnoj datoteci se zapisuje

(2
sa dva karaktera (na primer, sa \r\n na sistemima Windows), a na nekim
sa samo jednim karakterom (na primer, sa \n na sistemu Linux). Da bi se
ovakvi detalji sakrili od programera i kako programer ne bi morao da se stara

je
o ovakvim specifinostima, C jezik nudi tekstualni mod rada sa datotekama.
Ukoliko se datoteka otvori u tekstualnom modu, prilikom svakog itanja i up-
an
isa u datoteku vri se konverzija iz podrazumevanog formata oznaavanja kraja
reda u jedinstven karakter \n. Tako e na Windows sistemima dva karaktera
\r\n \n
d

na kraju reda biti proitana samo kao i, obratno, kada se u datoteku


upisuje \n bie upisana dva karaktera \r\n. Na ovaj nain pojednostavljuje se
iz

i olakava rad sa datotekama koje ine tekstualni podaci. Da bi interpretiranje


sadraja tekstualne datoteke bilo uvek ispravno treba izbegavati, na primer,
o

pravljenje tekstualnih datoteka koja na kraju poslednjeg reda nemaju oznaku


sk

kraja reda, izbegavati razmake u linijama nakon oznake za kraj reda, izbega-
vati da tekstualne datoteke sadre karaktere koji nisu vidljivi karakteri, oznake
kraja reda i tabulatora i slino.
n

Drugi nain pristupa datotekama prilagoen je datotekama iji sadraj ne


ro

predstavlja tekst i u kojima se mogu nai bajtovi svih vrednosti od 0 do 255


(na primer, jpg ili zip datoteke). Za obradu ovakvih datoteka, jezik C nudi
kt

binarni mod rada sa datotekama. U ovom sluaju nema nikakve konverzije


i interpretiranja karaktera prilikom upisa i itanja i svaki bajt koji postoji u
le

datoteci se ita doslovno.


Razlika izmeu tekstualnih i binarnih datoteka jo je otrija u jezicima koji
E

podravaju Unicode, jer se u tom sluaju vie bajtova ita i konvertuje u jedan
karakter.

12.3.2 Pristupanje datoteci

Da bi se pristupilo datoteci, bilo za itanje, bilo za pisanje, potrebno je


izvriti odreenu vrstu povezivanja datoteke i programa. Za ovo se koristi
biblioteka funkcija fopen:
FILE* fopen(const char* filename, const char* mode);
288 12. Ulaz i izlaz programa

Funkcija fopen dobija nisku koja sadri ime datoteke (na primer, datoteka.txt)
i uz pomo usluga operativnog sistema (koje nee na ovom mestu biti de-
taljnije opisane) vraa pokaziva na strukturu (najee dinamiki alociranu)
koja predstavlja sponu izmeu lokalne datoteke i programa i koja sadri infor-
macije o datoteci koje e biti koriene prilikom svakog pristupa datoteci. Ove
informacije mogu da ukljue adresu poetka bafera (prihvatnik, eng. buffer)
kroz koji se vri komunikacija sa datotekom, tekuu poziciju u okviru bafera,
informaciju o tome da li se dolo do kraja datoteke, da li je dolo do greke i

5)
slino. Programer ne mora i ne bi trebalo da direktno koristi ove informacije,
ve je dovoljno da uva pokaziva na strukturu FILE i da ga prosleuju svim

01
funkcijama koje treba da pristupaju datoteci. Ime FILE je ime tipa, a ne ime
strukture; ovo ime je uvedeno korienjem typedef (videti poglavlje 6.7.5).

(2
Prvi argument funkcije fopen je niska karaktera koja sadri ime datoteke.
Drugi argument je niska karaktera koja predstavlja nain (mod) otvaranja da-
toteke i koja ukazuje na to kako e se datoteka koristiti. Dozvoljeni modovi

je
ukljuuju itanje (read, "r"), pisanje (write, "w") i dopisivanje (append, "a").
Ako se datoteka koja ne postoji na sistemu otvara za pisanje ili dopisivanje,
an
onda se ona kreira (ukoliko je to mogue). U sluaju da se postojea datoteka
otvara za pisanje, njen stari sadraj se brie, dok se u sluaju otvaranja za
dopisivanje stari sadraj zadrava, a novi sadraj upisuje nakon njega. Ukoliko
d

se pokuava itanje datoteke koja ne postoji dobija se greka. Takoe, greka


iz

se javlja i u sluaju da se pokuava pristup datoteci za koju program nema


odgovarajue dozvole. U sluaju greke funkcija fopen vraa NULL. Modovi
r+, w+ i a+ ukazuju da e rad sa datotekom podrazumevati i itanje i pisanje
o

(ili dopisivanje). U sluaju da se eli otvaranje binarne datoteke, na mod se


sk

dopisuje "b" (na primer, rb, wb, a+b, . . . ).


Kada se C program pokrene, operativni sistem otvara tri toka podataka
n

(standardni ulaz, stadnardni izlaz i standardni izlaz za greke), kao da su da-


ro

toteke i obezbeuje pokazivae kojima im se moe pristupati. Ti pokazivai se


nazivaju:
kt

FILE* stdin;
FILE* stdout;
le

FILE* stderr;
E

Funkcija

int fclose(FILE *fp)

prekida vezu izmeu programa i datoteke koju je funkcija fopen ostvarila.


Pozivom funkcije fclose prazne se baferi koji privremeno uvaju sadraj da-
toteka ime se obezbeuje da sadraj zaista bude upisan na disk. Takoe,
kreirana struktura FILE nije vie potrebna i uklanja se iz memorije. S obzirom
na to da veina operativnih sistema ograniava broj datoteka koje mogu biti
istovremeno otvorene, dobra je praksa zatvarati datoteke im prestanu da budu
289 12. Ulaz i izlaz programa

potrebne. U sluaju da program normalno zavri svaka otvorena datoteka se


automatski zatvara.

12.3.3 Ulaz i izlaz pojedinanih karaktera

Nakon to se otvori datoteka, iz nje se ita ili se u nju pie sadraj. To


mogu biti i pojedinani karakteri.
getc

5)
Funkcija vraa naredni karakter iz datoteke odreene prosleenim
FILE pokazivaem ili EOF u sluaju kraja datoteke ili greke.

01
int getc(FILE *fp)

putc upisuje dati karakter u datoteku odreenu prosleenim

(2
Slino, funkcija
FILE pokazivaem i vraa upisani karakter ili EOF u sluaju greke. Iako su
greke prilikom izlaza retke, one se nekada javljaju (na primer, ako se prepuni

je
hard disk), pa bi trebalo vriti i ovakve provere.

int putc(int c, FILE *fp);


an
Slino kao i getchar i putchar, getc i putc mogu biti definisani kao makroi,
d

a ne funkcije.
ulazna.txt u datoteku izlazna.txt
iz

Naredni primer kopira sadraj datoteke


itajui i piui pritom pojedinane karaktere. Naglasimo da je ovo veoma
neefikasan nain kopiranja datoteka.
o

Program 12.3.
sk

#include <stdio.h>
n

/* filecopy: copy file ifp to file ofp */


ro

void filecopy(FILE *ifp, FILE *ofp)


{
kt

int c;
while ((c = getc(ifp)) != EOF)
le

putc(c, ofp);
}
E

int main()
{
FILE *ulaz, *izlaz;

ulaz = fopen("ulazna.txt","r");
if(ulaz == NULL)
return -1;

izlaz = fopen("izlazna.txt","w");
290 12. Ulaz i izlaz programa

if(izlaz == NULL)
return -1;

filecopy(ulaz,izlaz);

fclose(ulaz);
fclose(izlaz);
return 0;

5)
}
ungetc:

01
Funkcija

int ungetc (int c, FILE *fp);

(2
vraa karakter u datoteku i vraa identifikator pozicije datoteke tako da
naredni poziv operacije itanja nad ovom datotekom vrati upravo vraeni karak-

je
ter. Ovaj karakter moe, ali ne mora biti jednak poslednjem proitanom karak-
teru datoteke. Iako ova funkcije utie na naredne operacije itanja datoteke,
an
ona ne menja fiziki sadraj same datoteke (naime vraeni karakteri se najee
smetaju u memorijski bafer odakle se dalje itaju, a ne u datoteku na disku).
Funkcija ungetc vraa EOF ukoliko je dolo do greke, a vraeni karakter inae.
d

Naredni primer ita karaktere iz datoteke dok su u pitanju cifre i pri tom
iz

gradi dekadni ceo broj. Poto poslednji karakter koji je proitan nije cifra, on
se (uslovno reeno) vraa u tok kako ne bi bio izostavljen prilikom narednih
o

itanja.
sk

#include <stdio.h>

int readint(FILE* fp) {


n

int val = 0, c;
ro

while (isdigit(c = getc(fp)))


val = 10*val + (c - 0);
kt

ungetc(c, fp);
}
le
E

12.3.4 Provera greaka i kraja datoteke

Funkcija feof vraa vrednost tano (ne-nula) ukoliko se dolo do kraja date
datoteke.

int feof(FILE *fp)

Funkcija ferror vraa vrednost tano (ne-nule) ukoliko se dolo do greke


u radu sa datotekom.
291 12. Ulaz i izlaz programa

int ferror(FILE *fp)

12.3.5 Linijski ulaz i izlaz

Standardna biblioteka definie i funkcije za rad sa tekstualnim datotekama


liniju po liniju.
fgets

5)
Funkcija ita jednu liniju iz datoteke.

char *fgets(char *line, int maxline, FILE *fp)

01
Funkcija fgets ita sledeu liniju iz datoteke (ukljuujui oznaku kraja

(2
reda) iz datoteke fp i rezultat smeta u nisku line. Moe da bude proitano
najvie maxline-1 karaktera. Rezultujua niska zavrava se karakterom \0.
U normalnom toku izvravanja, funkcija fgets vraa pokaziva na poetak

je
linije, a u sluaju kraja datoteke ili greke vraa NULL.
Funkcija fputs ispisuje nisku (koja ne mora da sadri oznaku kraja reda)
an
u datoteku:

int fputs(const char *line, FILE *fp)


d

EOF
iz

Ona vraa u sluaju da doe do greke, a neki nenegativan broj inae.


Za razliku od funkcija fgets i fputs, funkcija gets brie zavrni \n, a
funkcija puts ga dodaje.
o

Implementacija bibliotekih funkcija nije znaajno razliita od implementacije


sk

ostalih funkcija, to je ilustrovano implementacijama funkcija fgets i fputs,


u obliku doslovno preuzetom iz jedne realne implementacije C biblioteke:
n

/* fgets: get at most n chars from iop */


ro

char *fgets(char *s, int n, FILE *iop)


{
register int c;
kt

register char *cs;


le

cs = s;
while (--n > 0 && (c = getc(iop)) != EOF)
E

if ((*cs++ = c) == \n)
break;
*cs = \0;
return (c == EOF && cs == s) ? NULL : s;
}

/* fputs: put string s on file iop */


int fputs(char *s, FILE *iop)
{
292 12. Ulaz i izlaz programa

register int c;

while (c = *s++)
putc(c, iop);
return ferror(iop) ? EOF : 0;
}
Korienjem funkcije fgets jednostavno je napraviti funkciju koja ita liniju

5)
po liniju sa standardnog ulaza, vraajui duinu proitane linije, odnosno nulu
kada liniju nije mogue proitati:

01
/* getline: read a line, return length */
int getline(char *line, int max)
{

(2
if (fgets(line, max, stdin) == NULL)
return 0;
else
return strlen(line);
je
an
}
d

12.3.6 Formatirani ulaz i izlaz


iz

Za formatirani ulaz i izlaz mogu se koristiti funkcije fscanf i fprintf.


One su identine funkcijama scanf i printf, osim to je prvi argument FILE
o

pokaziva koji ukazuje na datoteku iz koje se ita, odnosno u koju se pie.


sk

Format niska je u ovom sluaju drugi argument.

int fscanf(FILE *fp, const char *format, ...)


n

int fprintf(FILE *fp, const char *format, ...)


ro
kt

12.3.7 Rad sa binarnim datotekama


le

Standardna biblioteka nudi funkcije za direktno itanje i pisanje bajtova


u binarne datoteke. Progameru su na raspolaganju i funkcije za pozicionira-
E

nje u okviru datoteka, pa se binarne datoteke mogu koristiti i kao neke vrste
memorije sa slobodnim pristupom.
Funkcija fread se koristi za itanje niza slogova iz binarne datoteke, a
funkcija fwrite za pisanje niza slogova u binarnu datoteku.
size_t fread(void *ptr, size_t size,
size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size,
size_t nmemb, FILE *stream);
293 12. Ulaz i izlaz programa

Za ove funkcije, prvi argument je adresa na koju se smetaju proitani slogovi


iz datoteke, odnosno u kojoj su smeteni slogovi koji e biti upisani u da-
toteku. Drugi argument predstavlja veliina jednog sloga, trei broj slogova, a
etvrti pokaziva povezan datoteke. Funkcije vraaju broj uspeno proitanih,
odnosno upisanih slogova.
Funkcija fseek slui za pozicioniranje na mesto u datoteci sa koga e biti
proitan ili na koje e biti upisan sledei podatak. Funkcija ftell vraa
trenutnu poziciju u datoteci (u obliku pomeraja od poetka izraenog u broju

5)
bajtova). Iako primena ovih funkcija nije striktno ograniena na binarne da-
toteke, one se najee koriste sa binarnim datotekama.

01
int fseek (FILE * stream, long int offset, int origin);
long int ftell (FILE * stream);

(2
Prvi argument obe funkcije je pokaziva na datoteku. Funkcija fseek kao drugi

je
argument dobija pomeraj izraen u broju bajtova, dok su za trei argument
dozvoljene vrednosti SEEK_SET koja oznaava da se pomeraj rauna u odnosu
SEEK_CUR koji oznaava da se pomeraj rauna u
an
na poetak datoteke, odnosu
na tekuu poziciju i SEEK_END koji oznaava da se pomeraj rauna u odnosu
na kraj datoteke.
d

Sledei primer ilustruje primenu ovih funkcija.


iz

Program 12.4.
#include <stdio.h>
o
sk

int main() {
struct S {
n

int broj;
int kvadrat;
ro

} s;
kt

FILE* f;
int i;
le

if ((f = fopen("junk", "r+b")) == NULL) {


E

fprintf(stderr, "Greska prilikom otvaranja datoteke");


return 1;
}

for (i = 1; i <= 5; i++) {


s.broj = i; s.kvadrat = i*i;
fwrite(&s, sizeof(struct S), 1, f);
}

printf("Upisano je %ld bajtova\n", ftell(f));


294 12. Ulaz i izlaz programa

for (i = 5; i > 0; i--) {


fseek(f, (i-1)*sizeof(struct S), SEEK_SET);
fread(&s, sizeof(struct S), 1, f);
printf("%3d %3d\n", s.broj, s.kvadrat);
}

fclose(f);
}

5)
01
12.4 Argumenti komandne linije programa

(2
Jedan nain da se odreeni podaci proslede programu je i da se navedu
u komandnoj liniji prilikom njegovog pokretanja. Argumenti koji su tako
navedeni prenose se programu kao argumenti funkcije main. Prvi argument
argc,

je
(koji se obino naziva od engleskog argument count ) je broj argume-
nata komandne linije (ukljuujui i sm naziv programa) navedenih prilikom
an
pokretanja programa. Drugi argument (koji se obino naziva argv, od en-
gleskog argument vector ) je niz niski karaktera koje sadre argumente svaka
niska direktno odgovara jednom argumentu. Nazivu programa odgovara niska
d

argv[0]. Ako je argc tano 1, onda nisu navedeni dodatni argumenti nakon
iz

imena programa. Dakle, argv[0] je ime programa, argv[1] do argv[argc-1]


su tekstovi argumenata programa, a element argv[argc] sadri vrednost NULL.
Identifikatori argc i argv su proizvoljni i funkcija main moe biti deklarisana i
o

na sledei nain:
sk

int main (int br_argumenata, char* argumenti[]);


n

Naredni jednostavan program tampa broj argumenata kao i sadraj vektora


ro

argumenata.

Program 12.5.
kt

#include <stdio.h>
le

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


int i;
E

printf("argc = %d\n", argc);

/* Nulti argument uvek je ime programa (na primer, a.out) */


for (i = 0; i < argc; i++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
}

Ukoliko se program prevede sa gcc -o echoargs echoargs.c i pozove sa


./echoargs -U zdravo svima "dobar dan", ispisae
295 12. Ulaz i izlaz programa

argc = 5
argv[0] = ./echoargs
argv[1] = -U
argv[2] = zdravo
argv[3] = svima
argv[4] = dobar dan

5)
Jedna od moguih upotreba argumenata komandne linije je da se programu
navedu razliite opcije. Obiaj je da se opcije kodiraju pojedinanim karak-
-. Pri tome, esto je predvieno da se iza

01
terima i da se navode iza karaktera
jednog simbola - moe navesti vie karaktera koji odreuju opcije. Na primer,
pozivom:

(2
./program -a -bcd 134 -ef zdravo

a, b, c, d e i f 134 i zdravo.

je
programu se zadaju opcije i argumenti
Naredni program ispisuje sve opcije pronaene u komandnoj liniji:
an
Program 12.6.
#include <stdio.h>
d
iz

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


/* Za svaki argument komande linije, pocevsi od argv[1]
(preskace se ime programa) */
o

int i;
sk

for (i = 1; i < argc; i++) {


/* Ukoliko i-ti argument pocinje crticom */
n

if (argv[i][0] == -) {
/* Ispisuju se sva njegova slova od pozicije 1 */
ro

int j;
for (j = 1; argv[i][j] != \0; j++)
kt

printf("Prisutna je opcija : %c\n", argv[i][j]);


}
le

}
return 0;
E

Krae, ali dosta kompleksnije reenje se intenzivno zasniva na pokazivakoj


aritmetici i prioritetu operatora.

Program 12.7.
#include <stdio.h>

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


char c;
296 12. Ulaz i izlaz programa

/* Dok jos ima argumenata i dok je na poziciji 0 crtica */


while(--argc>0 && (*++argv)[0]==-)
/* Dok se ne dodje do kraja tekuce niske */
while (c=*++argv[0])
printf("Prisutna je opcija : %c\n",c);
return 0;
}

5)
Pitanja i zadaci za vebu

01
Pitanje 12.1.
stderr?

(2
ta je to

Pitanje 12.2. U kojoj datoteci zaglavlja se nalazi deklaracija funkcije printf?


Navesti njen prototip. Koju vrednost vraa ova funkcija?

Pitanje 12.3.
je
U kojoj datoteci zaglavlja se nalazi deklaracija funkcije scanf?
an
Navesti njen prototip. Koju vrednost vraa ova funkcija?

Pitanje 12.4. ta, u format niski koja je argument funkcije printf, specifikuje
d

opcioni broj koji se navodi iza opcione take za argument koji je niska (na
iz

primer: printf("%.2s", "zdravo");)?


ta specifikuje opcioni broj koji se navodi iza opcione take za argument koji
realan broj (na primer: printf("%.2f", 3.2453);)?
o

Pitanje 12.5.
sk

printf("#%6.*f#", 2, 3.14159);?
ta je rezultat poziva
ta je rezultat poziva printf("%6.1f",1/7);?
n

Pitanje 12.6. Navesti prototip funkcije iz standardne biblioteke koja omoguava


ro

formatirani ispis u nisku. Navesti primer korienja. O emu treba voditi


rauna prilikom pozivanja ove funkcije?
kt

Pitanje 12.7. Na to jednostavniji nain u nisku n (deklarisanu sa char n[8];)


upisati vrednosti 1/7 i 2/7 sa po pet cifara iza decimalnog zareza i razdvojene
le

razmakom.
E

Pitanje 12.8. Koju vrednost vraa sscanf("1 2","%i%i%i", &a, &b, &c);
Pitanje 12.9. Navesti prototip funkcije gets. Navesti prototip funkcije puts.
Kakva je razlika izmeu funkcija gets i fgets? Kakva je razlika izmeu
funkcija puts i fputs?
Pitanje 12.10. U emu se razlikuju tekstualne i binarne datoteke?

Pitanje 12.11. Navesti prototip funkcije fopen. Koju vrednost vraa ova
funkcija ukoliko se datoteka ne moe otvoriti?
297 12. Ulaz i izlaz programa

Pitanje 12.12. Navesti prototip standardne biblioteke funkcije za zatvaranje


datoteke. Koju vrednost ona vraa? Navesti bar dva razloga zbog kojih je pre-
poruljivo zatvoriti datoteku im se zavri njeno korienje.

Pitanje 12.13. Moe se desiti da nakon prekida rada programa u toku njegovog
izvravanja, datoteka u koju su u okviru prorama upisani neki podaci funkcijom
fprintf ipak bude prazna. Zato se to moe desiti?

Pitanje 12.14. Navesti prototip funkcije kojom se proverava da li dostignut

5)
kraj datoteke sa datotekim pokazivaem p?
Pitanje 12.15.

01
Ukratko opisati funkcije za odreivanje mesta u datoteci.

Pitanje 12.16. emu slue funkcije fseek i fsetpos? Da li one isto rade za

(2
tekstualne i binarne datoteke?

Pitanje 12.17. ta moe biti vrednost argumenta offset u pozivu funkcije


fseek (int fseek(FILE *stream, long offset, int origin)) za tekstua-

je
lnu datoteku?
an
Pitanje 12.18. Navesti prototip funkcije main sa argumentima.

Pitanje 12.19. Kako se, u okviru funkcije main, moe ispisati ime programa
d

koji se izvrava?
iz

Zadatak 12.1. Napisati funkciju main koja izraunava i ispisuje zbir svih
argumenata navedenih u komandnoj liniji (pretpostaviti da e svi argumenti

int).
o

biti celi brojevi koji se mogu zapisati u okviru tipa


sk

Zadatak 12.2. Napisati program koji iz datoteke, ije se ime zadaje kao prvi
argument komandne linije, ita prvo dimenziju kvadratne matrice , a za-
n

tim elemente matrice (pretpostavljamo da se u datoteci nalaze brojevi pravilno


. ni, odnosno da za dato , sledi elemenata matrice). Matricu
rasporee
ro

dinamiki alocirati. Nakon toga, na standardni izlaz ispisati redni broj kolone
koja ima najvei zbir elemenata. Na primer, za datoteka sa sadrajem:
kt

3
1 2 3
le

7 3 4
5 3 1
E

program treba da ispie redni broj 0 (jer je suma elemenata u nultoj koloni

1 + 7 + 5 = 13, u prvoj 2 + 3 + 3 = 8, u drugoj 3 + 4 + 1 = 8).
Zadatak 12.3. Data je datoteka apsolventi.txt. U svakom redu datoteke
nalaze se podaci o apsolventu: ime (ne vee od 20 karaktera), prezime (ne vee
od 20 karaktera), broj preostalih ispita. Datoteka je dobro formatirana i broj
redova datoteke nije poznat. Potrebno je uitati podatke iz datoteke, odrediti
prosean broj zaostalih ispita i potom ispisati imena i prezimena studenta koji
imaju vei broj zaostalih ispita od prosenog u datoteku ije ime je zadato kao
argument komadne linije. NAPOMENA: koristiti strukturu
298 12. Ulaz i izlaz programa

typedef struct {
char ime[20];
char prezime[20];
unsigned br_ispita;
} APSOLVENT;

Zadatak 12.4.

5)
Napisati funkciju
unsigned btoi(char s[], unsigned char b);
s b.

01
koja odreuje vrednost zapisa datog neoznaenog broja u datoj osnovi Na-
pisati zatim i funkciju
void itob(unsigned n, unsigned char b, char s[]);

(2
n zapisuje u datoj osnovi b i smeta rezultat
koja datu vrednost u nisku s.
Napisati zatim program koji ita liniju po liniju datoteke koja se zadaje kao
prvi argument komandne linije i obrauje ih sve dok ne naie na praznu liniju.

je
Svaka linija sadri jedan dekadni, oktalni ili heksadekadni broj (zapisan kako
se zapisuju konstante u programskom jeziku C). Program za svaki uneti broj
an
u datoteku koja se zadaje kao drugi argument komandne linije ispisuje njegov
binarni zapis. Pretpostaviti da e svi uneti brojevi biti u opsegu tipa unsigned
.
d
iz
o
sk
n
ro
kt
le
E
Dodatak A

5)
Tabela prioriteta operatora

01
(2
je
an
d
iz
o
sk
n
ro
kt
le
E

299
300 A. Tabela prioriteta operatora

Operator Opis Asocijativnost


() poziv funkcije sleva na desno
[] indeksni pristup elementu niza
. pristup lanu strukture/unije
-> pristup lanu strukture/unije preko pokazivaa
++ -- postfiksni inkrement/dekrement
++ -- prefiksni inkrement/dekrement zdesna na levo

5)
+ - unarni plus/minus
! ~ logika negacija/bitski komplement

01
(type) eksplicitna konverzija tipa (cast)
* dereferenciranje
&

(2
referenciranje (adresa)
sizeof veliina u bajtovima
* / % mnoenje, deljenje, ostatak sleva na desno
+ -

je
sabiranje i oduzimanje sleva na desno
<< >> bitsko pomeranje ulevo i udesno
an
< <= relacije manje, manje jednako sleva na desno
> >= relacije vee, vee jednako
== != relacija jednako, razliito sleva na desno
d

& bitska konjunkcija sleva na desno


iz

^ bitska ekskluzivna disjunkcija sleva na desno


| bitska disjunkcija sleva na desno
&&
o

logika konjunkcija sleva na desno


||
sk

logika disjunkcija sleva na desno


?: ternarni uslovni zdesna na levo
= dodele zdesna na levo
n

+= -=
ro

*= /= %=
&= ^= |=
<<= >>=
kt

, spajanje izraza u jedan sleva na desno


le
E
Dodatak B

5)
Reenja zadataka

01
(2
je
Softver savremenih raunara
an
Reenje zadatka 1.4:
d

Reenje se zasniva na algoritmu opisanom u primeru 3.3. Na raspolaganju


ax uva vrednost 1 , bx vrednost 2 , dok registar
iz

imamo samo 3 registra. Neka


cx privremeno uva ostale potrebne vrednosti. Neka se na memorijskoj lokaciji
200 uva rezultujua vrednost .
o
sk

mov [200], 0
mov ax, 0
mov bx, 1
n

petlja:
ro

cmp ax, bx
je uvecanje
mov cx, [100]
kt

cmp cx, ax
je kraj
le

add ax, 1
jmp petlja
E

uvecanje:
mov cx, [200]
add cx, 1
mov [200], cx
add bx, cx
add bx, cx
jmp petlja
kraj:

301
302 B. Reenja zadataka

Reprezentacija podataka u raunarima

Reenje zadatka 2.1:


(a) 185 (b) 964 (c) 476

Reenje zadatka 2.2:

5)
(11111110)2 , (376)8 , ( )16 ,

01
Reenje zadatka 2.3:
Prevoenjem svake etvorke binarnih cifara zasebno, dobija se

(2
(48 5566293)16 .
Prevoenjem svake heksadekadne cifre zasebno dobija se
(1010 0011 1011 1111 0100 0110 0001 1100 1000 1001 1011 1110 0010 0011 1101 0111)2

Reenje zadatka 2.4:


je
an
(a) rgb(53, 167, 220) (b) #FFFF00 (c) Svaki par heksadekadnih cifara je isti
(npr. #262626 ili #A0A0A0).
d
iz

Reenje zadatka 2.5:


Najmanji broj je 0 (00 . . . 00), a najvei 2 1 (11 . . . 11). Dakle, (a) od 0 do
o

2 1 = 15, (b) od 0 do 28 1 = 255, (c) od 0 do 216 1 = 65535 = 64 , (d)


4
sk

24
od 0 do 2 1 = 16 777 216 = 16 , (e) 232 1 = 4 294 967 296 = 4.

Reenje zadatka 2.6:


n
ro

(a) 00001100, (b) 01111011, (c) 11111111, (d) ne moe da se zapie

Reenje zadatka 2.7:


kt

Najmanji broj je 21 (10 . . . 00), a najvei je 21 1 (011 . . . 11). Dakle,


le

(a) od -8 do 7, (b) od -128 do 127, (c) -32768 do 32767, (d) od -8388608 do


8388607 i (e) od -2147483648 do 2147483647.
E

Reenje zadatka 2.8:


(a) 00001100, (b) 10000101, (c) 00000000, (d) 11101110, (e) 10000000 (f ) ne
moe da se zapie

Reenje zadatka 2.9:


Broj 5 se zapisuje kao 000101, broj kao 111011, zbir kao 000000, a proizvod
kao 100111.
303 B. Reenja zadataka

Reenje zadatka 2.10:


(a) 38, (b) 83, (c) 128, (d) 1, (e) 127, (f ) 0

Reenje zadatka 2.11:


Sa bitova mogue je kodirati ukupno 2 razliitih karaktera. Dakle, najmanji
broj bitova je 5.

5)
Reenje zadatka 2.12:

01
46 41 4b 55 4c 54 45 54, DISKRETNE

(2
Reenje zadatka 2.13:

je
Heksadekadno: 22 50 72 6f 67 72 61 6d 69 72 61 6e 6a 65 20 31 22,
Binarno: 00100010 01010000 01110010 01101111 01100111 01110010 01100001
an
01001101 01101001 01110010 01100001 01101110 01101010 01100101 00100000
00110001 00100010
d

Oktalno: 042 120 162 157 147 162 141 115 151 162 141 156 152 145 040 061
042
iz

Dekadno: 34 80 114 111 103 114 97 109 105 114 97 110 106 101 32 49 34

Reenje zadatka 2.14:


o
sk

ASCII Windows-1250 ISO-8859-5 ISO-8859-2 Unicode (UCS-2) UTF-8


raunarstvo - 11 - 11 22 12
n

informatika 11 11 11 11 22 11

Reenje zadatka 2.15:


ro

UCS-2 : 006b 0072 0075 017e 0069 0107 UTF-8 : 6b 72 75 c5be 69 c487
kt

Izraunljivost
le
E

Reenje zadatka 3.1:


Predloeni algoritam se zasniva na sledeoj osobini:



= + (1 + . . . + 1) + . . . + (1 + . . . + 1)

Odgovarajui urm program podrazumeva sledeu poetnu konfiguraciju:


1 2 3 . . .
... ...
304 B. Reenja zadataka

i sledeu radnu konfiguraciju:


1 2 3 4 5 ...
...
gde dobija redom vrednosti 0, 1, . . . , , a za svaku od ovih vrednosti dobija
redom vrednosti 0, 1, . . . , .
1. (4) =0
2. (1, 10, 100) ako je = 0, onda kraj
3. (2, 10, 14) = 0?

5)
4. (1, 3) :=
5. (4) := + 1

01
6. (4, 2, 12) = ?
7. (5) := 0

(2
8. (5) := + 1
9. (3) := + 1
10. (5, 1, 5)

je
11. (1, 1, 8)
12. (3, 1) 1
an
13. (1, 1, 100)
14. (1) 0 1
d


iz

:= 0
o


=0
sk



=0
n


0 1
ro

:=


kt

:= + 1


le

=

1
E

:= 0


:= + 1
:= + 1

Reenje zadatka 3.5:


305 B. Reenja zadataka

Predloeni algoritam se zasniva na sledeoj osobini:

= =+
Odgovarajui urm program podrazumeva sledeu poetnu konfiguraciju:
1 2 3 . . .
... ...
i sledeu radnu konfiguraciju:
1 2 3 4 ...

5)
...

01

:= 0

(2
:= 0

je

=
an

>
=

d

:= + 1
:= + 1 := + 1
iz


=
o


sk

1
n


ro

1. (3) =0
2. (4) =0
3. (1, 3, 11) = ?
kt

4. (2, 3, 7) = ?
5. (3) := + 1
le

6. (1, 1, 3)
7. (3) := + 1
E

8. (4) := + 1
9. (3, 1, 11) = ?
10. (1, 1, 7)
11. (4, 1) 1

Jezik C - uvod

Reenje zadatka 5.2.1:


306 B. Reenja zadataka

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

int main() {
double r;
printf("Unesi poluprecnik kruga: ");
scanf("%lf", &r);

5)
assert(r > 0);
printf("Obim kruga je: %lf\n", 2*r*M_PI);
printf("Povrsina kruga je: %lf\n", r*r*M_PI);

01
return 0;
}

(2
Reenje zadatka 5.2.2:

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

int main() {
d

/* Koordinate tacaka A, B, C */
iz

double xa, ya, xb, yb, xc, yc;


/* Duzine stranica BC, AC, AB */
double a, b, c;
o

/* Poluobim i povrsina trougla ABC */


sk

double s, P;

printf("Unesi koordinate tacke A: \n");


n

scanf("%lf%lf", &xa, &ya);


ro

printf("Unesi koordinate tacke B: \n");


scanf("%lf%lf", &xb, &yb);
printf("Unesi koordinate tacke C: \n");
kt

scanf("%lf%lf", &xc, &yc);


le

/* Izracunavaju se duzine stranice trougla ABC i


njegova povrsina Heronovim obrascem */
E

a = sqrt((xb - xc)*(xb - xc) + (yb - yc)*(yb - yc));


b = sqrt((xa - xc)*(xa - xc) + (ya - yc)*(ya - yc));
c = sqrt((xa - xb)*(xa - xb) + (ya - yb)*(ya - yb));

s = (a + b + c) / 2;
P = sqrt(s*(s - a)*(s - b)*(s - c));

printf("Povrsina trougla je: %lf\n", P);

return 0;
}
307 B. Reenja zadataka

Reenje zadatka 5.2.3:

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

5)
int main() {
/* Duzine stranica trougla */
double a, b, c;

01
/* Velicine uglova trougla */
double alpha, beta, gamma;

(2
printf("Unesi duzinu stranice a: ");
scanf("%lf", &a); assert(a > 0);
printf("Unesi duzinu stranice b: ");

je
scanf("%lf", &b); assert(b > 0);
printf("Unesi ugao gama (u stepenima): ");
an
scanf("%lf", &gamma);
d

/* Kosinusnom teoremom izracunavamo duzinu trece stranice i


preostala dva ugla. Pri tom se vrsi konverzija stepena
iz

u radijane i obratno. */
c = sqrt(a*a + b*b - 2*a*b*cos(gamma*M_PI/180.0));
o

alpha = acos((b*b + c*c - a*a) / (2*b*c)) / M_PI * 180.0;


sk

beta = acos((a*a + c*c - b*b) / (2*a*c)) / M_PI * 180.0;


n

printf("Duzina stranice c je: %lf\n", c);


printf("Velicina ugla alfa (u stepenima) je: %lf\n", alpha);
ro

printf("Velicina ugla beta (u stepenima) je: %lf\n", beta);


kt

return 0;
}
le

Reenje zadatka 5.2.4:


E

#include <stdio.h>

int main() {
double kmh;
printf("Unesi brzinu u km/h: ");
scanf("%lf", &kmh);
printf("Brzina u ms/s je: %lf\n", kmh * 1000.0 / 3600.0);
return 0;
}
308 B. Reenja zadataka

Reenje zadatka 5.2.5:

#include <stdio.h>
#include <assert.h>

int main() {

5)
/* Pocetna brzina u m/s, ubrzanje u m/s^2 i vreme u s*/
double v0, a, t;

01
printf("Unesi pocetnu brzinu (u m/s): ");
scanf("%lf", &v0);

(2
printf("Unesi ubrzanje (u m/s^2): ");
scanf("%lf", &a);
printf("Unesi vreme: ");
scanf("%lf", &t); assert(t >= 0);

je
printf("Trenutna brzina (u m/s) je : %lf\n", v0+a*t);
an
printf("Predjeni put (u m) je : %lf\n", v0*t+a*t*t/2);
return 0;
d

}
iz

Reenje zadatka 5.2.6:


o
sk

#include <stdio.h>
n

int main() {
double a1, a2, a3, a4, a5, a6, a7, a8, a9;
ro

scanf("%lf%lf%lf", &a1, &a2, &a3);


scanf("%lf%lf%lf", &a4, &a5, &a6);
kt

scanf("%lf%lf%lf", &a7, &a8, &a9);


/* Determinantu ra\v cunamo primenom Sarusovog pravila:
a1 a2 a3 a1 a2
le

a4 a5 a6 a4 a5
a7 a8 a9 a7 a8
E

- glavne dijagonale pozitivno, sporedne negativno


*/
printf("%lf\n", a1*a5*a9 + a2*a6*a7 + a3*a4*a8
- a3*a5*a7 - a1*a6*a8 - a2*a4*a9);
return 0;
}

Reenje zadatka 5.2.7:


309 B. Reenja zadataka

#include <stdio.h>

int main() {
int a1, a2, a3; /* brojevi */
int m; /* maksimum brojeva */
printf("Unesi tri broja: ");
scanf("%d %d %d", &a1, &a2, &a3);
m = a1;

5)
if (a2 > m)
m = a2;
if (a3 > m)

01
m = a3;
printf("Maksimum je: %d\n", m);

(2
return 0;
}

je
Reenje zadatka 5.2.8:
an
#include <stdio.h>
d

int main() {
iz

int a, b, i;
scanf("%d%d", &a, &b);
for (i = a; i <= b; i++)
o

printf("%d\n", i*i*i);
sk

return 0;
}
n

Jezik C - izrazi
ro
kt

Reenje zadatka 6.4.1:


le

#include <stdio.h>
E

int main() {
int h, m, s, h1, m1, s1;
scanf("%d:%d:%d", &h, &m, &s);
if (h < 0 || h > 23) {
printf("Neispravno unet sat\n");
return 1;
}
if (m < 0 || m > 59) {
printf("Neispravno unet minut\n");
return 2;
310 B. Reenja zadataka

}
if (s < 0 || s > 59) {
printf("Neispravno unet sekund\n");
return 3;
}

s1 = 60 - s;
m1 = 59 - m;

5)
h1 = 23 - h;
if (s1 == 60) {
s1 = 0;

01
m1++;
}

(2
if (m1 == 60) {
m1 = 0;
h1++;
}

printf("%d:%d:%d\n", h1, m1, s1);


je
an
return 0;
}
d

Reenje zadatka 6.4.2:


iz
o

#include <stdio.h>
sk

int main() {
double x, y, z;
n

scanf("%lf%lf%lf", &x, &y, &z);


ro

if (x > 0 && y > 0 && z > 0 &&


x + y > z && x + z > y && y + z > x)
printf("Trougao postoji\n");
kt

else
printf("Trougao ne postoji\n");
le

return 0;
}
E

Reenje zadatka 6.4.3:

#include <stdio.h>

int main() {
/* Broj cija se suma cifara racuna */
int n;
/* Cifre broja*/
311 B. Reenja zadataka

int c0, c1, c2, c3;

printf("Unesi cetvorocifreni broj: ");


scanf("%d", &n);
if (n < 1000 || n >= 10000) {
printf("Broj nije cetvorocifren\n");
return 1;
}

5)
c0 = n % 10;
c1 = (n / 10) % 10;

01
c2 = (n / 100) % 10;
c3 = (n / 1000) % 10;

(2
printf("Suma cifara je: %d\n", c0 + c1 + c2 + c3);
return 0;
}

je
an
Reenje zadatka 6.4.4:
d

#include <stdio.h>
iz

int main() {
unsigned n, c0, c1; /* broj, poslednja i pretposlednja cifra */
o

printf("Unesi broj: ");


sk

scanf("%d", &n);
c0 = n % 10;
c1 = (n / 10) % 10;
n

printf("Zamenjene poslednje dve cifre: %u\n",


ro

(n / 100)*100 + c0*10 + c1);


return 0;
}
kt
le

Reenje zadatka 6.4.5:


E

#include <stdio.h>

int main() {
/*
(0, 0) -> 1
(0, 1) -> 2 (1, 0) -> 3
(2, 0) -> 4 (1, 1) -> 5 (0, 2) -> 6
(0, 3) -> 7 (1, 2) -> 8 (2, 1) -> 9 (3, 0) -> 10
...
*/
312 B. Reenja zadataka

unsigned x, y; /* koordinate */
unsigned z; /* pomocna promenljiva */
unsigned n; /* redni broj u cik-cak nabrajanju */
printf("Unesi x i y koordinatu: ");
scanf("%d%d", &x, &y);
z = x + y;
if (z % 2)
n = z*(z + 1)/2 + x + 1;

5)
else
n = z*(z + 1)/2 + y + 1;
printf("Redni broj u cik-cak nabrajanju je: %u\n", n);

01
return 0;
}

(2
Reenje zadatka 6.4.6:

#include <stdio.h>
je
an
int main() {
double A, B;
d

printf("Unesi koeficijente A i B jednacine A*X + B = 0: ");


iz

scanf("%lf%lf", &A, &B);


if (A != 0)
printf("Jednacina ima jedinstveno resenje: %lf\n", -B/A);
o

else if (B == 0)
sk

printf("Svaki realan broj zadovoljava jednacinu\n");


else
printf("Jednacina nema resenja\n");
n

return 0;
ro

}
kt

Reenje zadatka 6.4.7:


le

#include <stdio.h>
E

int main() {
double a1, b1, c1, a2, b2, c2; /* koeficijenti sistema */
double Dx, Dy, D; /* determinante */
scanf("%lf%lf%lf", &a1, &b1, &c1);
scanf("%lf%lf%lf", &a2, &b2, &c2);
/* Sistem resavamo primenom Kramerovog pravila */
/* Determinanta sistema:
a1 b1
a2 b2
*/
313 B. Reenja zadataka

D = a1*b2 - b1*a2;
/* Determinanta promenljive x:
c1 b1
c2 b2
*/
Dx = c1*b2 - b1*c2;
/* Determinanta promenljive y:
a1 c1

5)
a2 c2
*/
Dy = a1*c2 - c1*a2;

01
if (D != 0)
printf("x = %lf\ny = %lf\n", Dx / D, Dy / D);

(2
else if (Dx == 0 && Dy == 0)
printf("Sistem ima beskonacno mnogo resenja\n");
else
printf("Sistem nema resenja\n");

return 0;
je
an
}
d

Reenje zadatka 6.4.8:


iz

#include <stdio.h>
o

#include <math.h>
sk

#include <assert.h>

int main() {
n

float a, b, c; /* koeficijenti */
ro

float D; /* diskriminanta */
printf("Unesi koeficijente a, b, c"
" kvadratne jednacine ax^2 + bx + c = 0: ");
kt

scanf("%f%f%f", &a, &b, &c);


assert(a != 0);
le

D = b*b - 4*a*c;
if (D > 0) {
E

float sqrtD = sqrt(D);


printf("Realna resenja su: %f i %f\n",
(-b + sqrtD) / (2*a), (-b - sqrtD) / (2*a));
} else if (D == 0) {
printf("Jedinstveno realno resenje je: %f\n",
-b/(2*a));
} else {
printf("Jednacina nema realnih resenja\n");
}
return 0;
}
314 B. Reenja zadataka

Reenje zadatka 6.4.9:

#include <stdio.h>

int main() {
unsigned n; /* broj koji se ispituje */

5)
unsigned d; /* kandidat za delioca */
scanf("%u", &n);
for (d = 1; d <= n; d++)

01
if (n % d == 0)
printf("%u\n", d);

(2
return 0;
}

je
Reenje zadatka 6.4.10:
an
#include <stdio.h>
d

#include <ctype.h>
iz

int main() {
int c;
while ((c = getchar()) != . && c != , && c != EOF)
o

putchar(toupper(c));
sk

}
n

Jezik C - nizovi
ro

Reenje zadatka 6.6.1:


kt
le

#include <stdio.h>
E

#define MAX 1000

int main() {
int a[MAX];
int n, i, max, zbir;
scanf("%d", &n);
if (n >= MAX) {
printf("Previse elemenata\n");
return 1;
}
/* Ucitavanje niza */
315 B. Reenja zadataka

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


scanf("%d", &a[i]);

/* Ispisivanje niza unatrag */


for (i = n - 1; i >= 0; i--)
printf("%d ", a[i]);
printf("\n");

5)
/* Odredjivanje i ispis zbira */
zbir = 0;
for (i = 0; i < n; i++)

01
zbir += a[i];
printf("zbir = %d\n", zbir);

(2
/* Odredjivanje i ispis maksimuma */
max = a[0];
for (i = 1; i < n; i++)

je
if (a[i] > max)
max = a[i];
an
printf("max = %d\n", max);

return 0;
d

}
iz

Reenje zadatka 6.6.2:


o
sk

#include <stdio.h>
n

int main() {
ro

int b[10], i;

for (i = 0; i < 10; i++)


kt

b[i] = 0;
le

while ((c = getchar()) != .)


if (0<=c && c<=9)
E

b[c - 0]++;

for (i = 0; i < 10; i++)


printf("%d ", b[i]);

return 0;
}

Reenje zadatka 6.6.3:


316 B. Reenja zadataka

#include <stdio.h>
#include <assert.h>

/* Pretprocesorska direktiva kojom se proverava da li je godina


prestupna. Godina je prestupna ako je deljiva sa 4, a ne i sa 100
ili je deljiva sa 400. */
#define prestupna(g) (((g)%4 == 0 && (g)%100 != 0) || ((g)%400 == 0))

5)
int main() {
int d, m, g, b, M;
/* Broj dana po mesecima. Niz je napravljen tako da se broj dana za

01
mesec m moze ocitati sa br_dana[m].
*/

(2
int br_dana[] = {-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
printf("Unesi datum u formatu d/m/g: ");
assert(scanf("%d/%d/%d", &d, &m, &g) == 3);
/* Provera ispravnosti datuma */

je
if (g < 0) {
printf("Pogresna godina\n");
an
return 1;
}
if (m < 1 || m > 12) {
d

printf("Pogresan mesec\n");
iz

return 1;
}
if (d < 1 || d > (m == 2 && prestupna(g) ? 29 : br_dana[m])) {
o

printf("Pogresan dan\n");
sk

return 1;
}
n

b = 0;
ro

/* Broj dana u prethodnim mesecima */


for (M = 1; M < m; M++)
kt

b += br_dana[M];
/* Broj dana u tekucem mesecu */
b += d;
le

/* Eventualno dodati i 29. 2. */


if (prestupna(g) && m > 2)
E

b++;
printf("%d\n", b);

return 0;
}

Reenje zadatka 6.6.4:

#include <stdio.h>
317 B. Reenja zadataka

#include <ctype.h>

int main() {
int a[100], n, k, m;
/* Ucitavamo broj redova trougla */
scanf("%d", &m);
for (n = 0; n < m; n++) {
/* Azuriramo tekuci red trougla */

5)
/* (n n) = 1 */
a[n] = 1;
for (k = n-1; k > 0; k--)

01
/* (n k) = (n-1 k) + (n-1 k-1) */
a[k] = a[k] + a[k-1];

(2
/* (n 0) = 1 */
/* a[0] = 1; -- ovo vec vazi */

/* Ispisujemo tekuci red */

je
for (k = 0; k <= n; k++)
printf("%d ", a[k]);
an
printf("\n");
}
d

return 0;
iz

}
o

Reenje zadatka 6.6.5:


sk

#include <stdio.h>
n

#include <assert.h>
ro

int main() {
int n, A[10][10], i, j, s;
kt

/* ucitavamo matricu */
scanf("%d", &n); assert(0 < n && n <= 10);
le

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


for (j = 0; j < n; j++)
E

scanf("%d", &A[i][j]);

/* sumiramo elemente glavne dijagonale */


s = 0;
for (i = 0; i < n; i++)
s += A[i][i];
printf("Suma elemenata glavne dijagonale je: %d\n", s);

/* sumiramo elemente iznad sporedne dijagonale */


s = 0;
for (i = 0; i < n; i++)
318 B. Reenja zadataka

for (j = 0; i + j + 1 < n; j++)


s += A[i][j];
printf("Suma elemenata iznad sporedne dijagonale je: %d\n", s);

return 0;
}

Reenje zadatka 6.6.6:

5)
01
#include <stdio.h>
#include <assert.h>

(2
int main() {
int n, A[100][100], i, j, dt;
/* ucitavamo matricu */

je
scanf("%d", &n); assert(0 < n && n <= 100);
for (i = 0; i < n; i++)
an
for (j = 0; j < n; j++)
scanf("%d", &A[i][j]);
/* proveravamo da li je matrica donjetrougaona */
d

dt = 1;
iz

for (i = 0; i < n && dt; i++)


for (j = i+1; j < n && dt; j++)
if (A[i][j] != 0)
o

dt = 0;
sk

printf("Matrica %s donjetrougaona\n", dt ? "je" : "nije");


return 0;
}
n
ro

Jezik C - korisniki definisani tipovi


kt

Reenje zadatka 6.7.1:


le
E

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

/* Struktura za reprezentovanje kompleksnog broja */


typedef struct complex {
double Re, Im;
} COMPLEX;

int main() {
/* Najveci broj */
COMPLEX max_z;
319 B. Reenja zadataka

/* Najveci moduo */
double max_m = 0.0;

int i;
for (i = 0; i < 10; i++) {
/* Tekuci broj */
COMPLEX z;
/* Moduo tekuceg broja */

5)
double z_m;
/* Ucitavanje */
scanf("%lf%lf", &z.Re, &z.Im);

01
/* Racunanje modula */
z_m = sqrt(z.Re*z.Re + z.Im*z.Im);

(2
/* Azuriranje najveceg */
if (z_m > max_m) {
max_z = z;
max_m = z_m;

je
}
}
an
printf("%lf + i*%lf\n", max_z.Re, max_z.Im);
return 0;
}
d
iz

Reenje zadatka 6.7.2:


o
sk

#include <stdio.h>
#include <assert.h>
n

int main() {
ro

enum dani {PON = 1, UTO, SRE, CET, PET, SUB, NED};


int dan;
scanf("%d", &dan);
kt

assert(1 <= dan && dan <= 7);


if (dan == SUB || dan == NED)
le

printf("Vikend\n");
else
E

printf("Radni dan\n");
return 0;
}

Jezik C - naredbe

Reenje zadatka 7.1:

#include <stdio.h>
320 B. Reenja zadataka

int main() {
int n, i;
printf("Unesi gornju granicu: ");
scanf("%d", &n);
for (i = 1; i < n; i += 2)
printf("%d ", i);
printf("\n");

5)
return 0;
}

01
Reenje zadatka 7.2:

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

int main() {
je
an
int N = 100;
double l = 0.0, d = 2*M_PI;
double h = (d - l) / (N - 1);
d

double x;
iz

printf(" x sin(x)\n");
for (x = l; x <= d; x += h)
printf("%4.2lf %7.4lf\n", x, sin(x));
o

return 0;
sk

}
n

Reenje zadatka 7.3:


ro

#include <stdio.h>
kt

int main() {
le

double x, s;
unsigned n, i;
E

printf("Unesi broj x: ");


scanf("%lf", &x);
printf("Unesi izlozilac n: ");
scanf("%d", &n);
for (s = 1.0, i = 0; i < n; i++)
s *= x;
printf("x^n = %lf\n", s);
return 0;
}
321 B. Reenja zadataka

Reenje zadatka 7.4:

#include <stdio.h>

#define N 4

5)
int main() {
int i, j;

01
/* Deo (a): */

(2
for (i = 0; i < N; i++) {
for (j = 0; j < N; j++)
putchar(*);
putchar(\n);

je
}
an
printf("--------------\n");
d

/* Deo (b): */
for (i = 0; i < N; i++) {
iz

for (j = 0; j < N - i; j++)


putchar(*);
putchar(\n);
o

}
sk

printf("--------------\n");
n

/* Deo (c): */
ro

for (i = 0; i < N; i++) {


for (j = 0; j < i + 1; j++)
kt

putchar(*);
putchar(\n);
}
le

printf("--------------\n");
E

/* Deo (d): */
for (i = 0; i < N; i++) {
for (j = 0; j < i; j++)
putchar( );
for (j = 0; j < N - i; j++)
putchar(*);
putchar(\n);
}

printf("--------------\n");
322 B. Reenja zadataka

/* Deo (e): */
for (i = 0; i < N; i++) {
for (j = 0; j < N - i - 1; j++)
putchar( );
for (j = 0; j < i + 1; j++)
putchar(*);
putchar(\n);

5)
}

printf("--------------\n");

01
/* Deo (f): */

(2
for (i = 0; i < N; i++) {
for (j = 0; j < i; j++)
putchar( );
for (j = 0; j < N - i - 1; j++) {

je
putchar(*); putchar( );
}
an
putchar(*);
putchar(\n);
}
d
iz

printf("--------------\n");

/* Deo (g): */
o

for (i = 0; i < N; i++) {


sk

for (j = 0; j < N - i - 1; j++)


putchar( );
for (j = 0; j < i; j++) {
n

putchar(*); putchar( );
ro

}
putchar(*);
kt

putchar(\n);
}
le

return 0;
E

Reenje zadatka 7.5:

#include <stdio.h>
/* Delioci se dodaju u parovima: ako je n deljiv sa d, deljiv je i
sa n/d. Pretraga se vrsi do korena iz n.
Ako je n potpun kvadrat, koren se sabira samo jednom.
Napomena: za jako velike vrednosti n, d*d moze da prekoraci.
323 B. Reenja zadataka

*/
int main() {
unsigned n; /* broj koji se ispituje */
unsigned s; /* suma delioca */
unsigned d; /* kandidat za delioca */
scanf("%u", &n);
for (s = 0, d = 1; d*d < n; d++)
if (n % d == 0)

5)
s += d + n / d;
if (d * d == n)
s += d;

01
printf("Suma: %u\n", s);
return 0;

(2
}

Reenje zadatka 7.6:

je
an
#include <stdio.h>
#include <assert.h>
d

/* Teorema: Ako broj ima pravog delioca koji je veci od korena iz n,


iz

onda ima delioca koji je manji od korena iz n.


Dokaz: Ako je d <= sqrt(n) delilac broja n, onda je n/d >= sqrt(n)
takodje delilac.
o

Zato je dovoljno proveravati delioce samo od 2 do sqrt(n).


sk

Uslov d <= sqrt(n) menjamo sa d*d <= n, da bismo izbegli rad sa


realnim brojevima (za jako velike vrednosti n, ovo moze da dovede do
prekoracenja)
n

*/
ro

int main() {
unsigned n, d;
int prost;
kt

scanf("%d", &n);
assert(n > 1); /* Brojevi <= 1 nisu ni prosti ni slozeni */
le

for (prost = 1, d = 2; prost && d*d <= n; d++)


if (n % d == 0)
E

prost = 0;
printf("Broj je %s\n", prost ? "prost" : "slozen");
return 0;
}

Reenje zadatka 7.7:

#include <stdio.h>
324 B. Reenja zadataka

int main() {
unsigned n, d;
/* Ucitavamo broj */
scanf("%u", &n);

/* Prvi kandidat za prost cinioc je 2 */


d = 2;
/* Broj n delimo sa jednim po jednim njegovim ciniocem. Postupak se

5)
zavrsava kada trenutni broj postane prost */
while (d*d <= n) {
/* Broj je deljiv sa d - dakle, d je prost cinioc. Zaista, ako d

01
ne bi bio prost, bio bi deljiv sa nekim svojim ciniocem d koji
je manji od njega. Time bi i n bio deljiv sa d. No, kada se d

(2
uvecavalo (a moralo je biti uvecano da bi se stiglo do d) n
nije bio deljiv njime, sto je kontradikcija. */
if (n % d == 0) {
printf("%u\n", d);

je
n /= d;
} else
an
/* Broj nije deljiv sa d, pa prelazimo na narednog kandidata */
d++;
}
d

/* poslednji preostali cinilac */


iz

if (n > 1)
printf("%u\n", n);
return 0;
o

}
sk

Reenje zadatka 7.8:


n
ro

#include <stdio.h>
#include <limits.h>
kt

#include <math.h>
le

int main() {
int a; /* broj koji se unosi */
E

int n = 0; /* broj unetih brojeva */


int s = 0; /* zbir unetih brojeva */
int p = 1; /* proizvod unetih brojeva */
int min = INT_MAX; /* minimum unetih brojeva */
int max = INT_MIN; /* maksimum unetih brojeva */
double sr = 0; /* zbir reciprocnih vrednosti */
while (1) {
scanf("%d", &a);
if (a == 0) break;
n++;
s += a;
325 B. Reenja zadataka

p *= a;
if (a < min) min = a;
if (a > max) max = a;
sr += 1.0/a;
}
if (n == 0) {
printf("Nije unet nijedan broj\n");
return 1;

5)
}
printf("broj: %d\n", n);
printf("zbir: %d\n", s);

01
printf("proizvod: %d\n", p);
printf("minimum: %d\n", min);

(2
printf("maksimum: %d\n", max);
printf("aritmeticka sredina: %f\n", (double)s / (double)n);
printf("geometrijska sredina: %f\n", pow(p, 1.0/n));
printf("harmonijska sredina: %f\n", n / sr);

return 0;
je
an
}
d

Reenje zadatka 7.9:


iz

#include <stdio.h>
o
sk

int main() {
int ts = 0; /* Tekuca serija */
int ns = 0; /* Najduza serija */
n

int pb, tb; /* Prethodni i tekuci broj */


ro

scanf("%d", &pb);
if (pb != 0) {
kt

ts = ns = 1;
while(1) {
le

scanf("%d", &tb);
if (tb == 0) break; /* Prekidamo kada je uneta 0 */
E

/* Da li je tekuci broj jednak prethodnom? */


if (tb == pb)
/* Ako jeste, nastavlja se tekuca serija */
ts++;
else
/* Inace, krenula je nova serija */
ts = 1;

/* Azuriranje najduze serije */


if (ts > ns)
326 B. Reenja zadataka

ns = ts;

/* Azuriranje prethodnog broja */


pb = tb;
}
}
/* Ispis rezultata */
printf("%d\n", ns);

5)
return 0;
}

01
Reenje zadatka 7.10:

(2
#include <stdio.h>

je
int main() {
/* Izracunava se ceo deo korena unetog broja x. Trazi se interval
an
oblika [n^2, (n+1)^2] = [N1, N2] tako da mu x pripada i tada je n
ceo deo korena iz x.
*/
d

unsigned x;
iz

unsigned N1, N2, n;

/* Ucitava se broj x */
o

scanf("%d", &x);
sk

/* Krece se od intervala [0, 1] */


n = 0; N1 = 0; N2 = 1;
n

while (x != N1) {
ro

N1++;
if (N1 == N2) {
/* Prelazi se na sledeci interval */
kt

unsigned l;
/* Uvecava se malo n */
le

n++;
/* N2 je potrebno uvecati za 2*n+1 */
E

N2++;
l = 0;
do {
l++;
N2 += 2;
} while (l != n);
}
}
/* Ispis rezultata */
printf("%d\n", n);
}
327 B. Reenja zadataka

Reenje zadatka 7.11:

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

#define EPS 0.0001

5)
int main() {
double xp, x, a;
printf("Unesite broj: ");

01
scanf("%lf", &a);
x = 1.0;

(2
do {
xp = x;
x = xp - (xp * xp - a) / (2.0 * xp);
printf("%lf\n", x);

je
} while (fabs(x - xp) >= EPS);
printf("%lf\n", x);
an
return 0;
}
d
iz

Reenje zadatka 7.12:


o

#include <stdio.h>
sk

int main() {
n

unsigned fpp = 1, fp = 1, k;
scanf("%d", &k);
ro

if (k == 0) return 0;
printf("%d\n", fpp);
kt

if (k == 1) return 0;
printf("%d\n", fp);
k -= 2;
le

while (k > 0) {
unsigned f = fpp + fp;
E

printf("%d\n", f);
fpp = fp; fp = f;
k--;
}
return 0;
}

Reenje zadatka 7.13:


328 B. Reenja zadataka

#include <stdio.h>

int main() {
unsigned n;
printf("Unesi broj: ");
scanf("%u", &n);
do {
/* Poslednja cifra broja */

5)
printf("%u\n", n % 10);
/* Brisemo poslednju cifru broja */
n /= 10;

01
} while (n > 0);
return 0;

(2
}

#include <stdio.h>

je
int main() {
unsigned n, suma = 0;
an
printf("Unesi broj: ");
scanf("%u", &n);
do {
d

suma += n % 10;
iz

n /= 10;
} while (n > 0);
printf("Suma cifara broja je: %u\n", suma);
o

return 0;
sk

}
n

Reenje zadatka 7.14:


ro
kt

#include <stdio.h>

int main() {
le

unsigned n1, n2, tmp;


scanf("%d", &n1);
E

scanf("%d", &n2);

/* Da bi se izvrsilo nadovezivanje, n1 se mnozi sa 10^k,


gde je k broj cifara broja n2 i zatim se dodaje n2 */

/* posto ce nam n2 kasnije trebati, kopiramo ga u pomocnu promenljivu */


tmp = n2;
/* Dok ima cifara broja tmp */
while (tmp > 0) {
/* Mnozimo n1 sa 10 */
n1 *= 10;
329 B. Reenja zadataka

/* Uklanjamo poslednju cifru broja tmp */


tmp /= 10;
}
/* Uvecavamo n1 za n2 */
n1 += n2;
printf("%d\n", n1);
return 0;
}

5)
Reenje zadatka 7.15:

01
#include <stdio.h>

(2
int main() {
unsigned n; /* polazni broj */

je
unsigned o = 0; /* obrnuti broj */
scanf("%d", &n);
an
do {
/* poslednja cifra broja n se uklanja iz broja n i
dodaje na kraj broja o */
d

o = 10*o + n % 10;
iz

n /= 10;
} while (n > 0);
printf("%d\n", o);
o

return 0;
sk

}
n

Reenje zadatka 7.16:


ro

#include <stdio.h>
kt

int main() {
le

/* Broj koji se obradjuje */


unsigned n;
E

/* Rezultat i 10^k gde je k broj cifara rezultata (ovaj stepen je


potreban zbog dodavanja cifara na pocetak rezutlata) */
unsigned r = 0, s = 1;
/* Ucitavamo broj */
scanf("%u", &n);
while (n > 0) {
/* Uklanjamo mu poslednju cifru */
unsigned c = n % 10;
n /= 10;
if (c % 2 == 0) {
/* Ako je parna, dodajemo je kao prvu cifru rezultata */
330 B. Reenja zadataka

r = c * s + r;
s *= 10;
}
}
/* Ispis rezultata */
printf("%u\n", r);
return 0;
}

5)
Reenje zadatka 7.17:

01
#include <stdio.h>

(2
int main() {
unsigned n, s = 1;
unsigned char p, c;

je
scanf("%u%hhu%hhu", &n, &p, &c);
an
/* s = 10^p */
while (p > 0) {
s *= 10;
d

p--;
iz

/* Broj se razdvaja na prvih p cifara (n/s) i poslednjih p cifara


o

(n%s). Prve cifre se pomeraju za jedno mesto ulevo (n/s)*s*10,


sk

umece se cifra (c*s) i dodaju poslednje cifre (n%s). */


printf("%d\n", (n/s)*s*10 + c*s + n%s);
return 0;
n

}
ro

Reenje zadatka 7.18:


kt
le

/* Razmena prve i poslednje cifre */


#include <stdio.h>
E

int main() {
unsigned n, a, tmp, s, poc, sred, kraj;
scanf("%u", &n);

/* Jednocifreni brojevi se posebno obradjuju */


if (n < 10) {
printf("%u\n", n);
return 0;
}
331 B. Reenja zadataka

/* Odredjuje se s = 10^k gde je k+1 broj cifara broja n. Koristi se


pomocna promenljiva tmp kako bi se ocuvala vrednost n.
*/
tmp = n; s = 1;
while (tmp >= 10) {
s *= 10;
tmp /= 10;
}

5)
/* Prva cifra */
poc = n / s;

01
/* Cifre izmedju prve i poslednje */
sred = (n % s) / 10;

(2
/* Poslednja cifra */
kraj = n % 10;

/* Gradi se i stampa rezultat */

je
printf("%u\n", s*kraj + 10 * sred + poc);
an
return 0;

}
d
iz

/* Rotiranje ulevo */
#include <stdio.h>
o

int main() {
sk

unsigned n, tmp, s;
scanf("%u", &n);
n

/* Odredjuje se s = 10^k gde je k+1 broj cifara broja n. Koristi se


ro

pomocna promenljiva tmp kako bi se ocuvala vrednost n.


*/
tmp = n; s = 1;
kt

while (tmp >= 10) {


s *= 10;
le

tmp /= 10;
}
E

/* Gradi se i ispisuje rezultat tako sto se poslednja cifra broja n


stavlja ispred ostalih cifara broja n. */
printf("%u\n", s * (n % 10) + n / 10);

return 0;
}

/* Rotiranje udesno */
#include <stdio.h>
332 B. Reenja zadataka

int main() {
unsigned n, tmp, s;
scanf("%u", &n);

/* Odredjuje se s = 10^k gde je k+1 broj cifara broja n. Koristi se


pomocna promenljiva tmp kako bi se ocuvala vrednost n.
*/
tmp = n; s = 1;

5)
while (tmp >= 10) {
s *= 10;
tmp /= 10;

01
}

(2
/* Gradi se i ispisuje rezultat tako sto se prva cifra broja n
stavlja iza ostalih cifara broja n. */
printf("%u\n", n % s * 10 + n / s);

je
return 0;
}
an
Reenje zadatka 7.19:
d
iz

#include <stdio.h>
#include <assert.h>
o
sk

int main() {
int d, m, g, dm;
n

/* Ucitavamo datum i proveravamo da je ispravno ucitan */


ro

assert(scanf("%d/%d/%d", &d, &m, &g) == 3);


kt

/* Vrsimo analizu meseca */


switch(m) {
/* Meseci sa 31 danom */
le

case 1: case 3: case 5: case 7: case 8: case 10: case 12:


dm = 31;
E

break;
/* Meseci sa 30 dana */
case 4: case 6: case 9: case 11:
dm = 30;
break;
/* Februar */
case 2:
/* Proverava se da li je godina prestupna */
dm = (g % 4 == 0 && g % 100 != 0 || g % 400 == 0) ? 29 : 28;
break;
default:
333 B. Reenja zadataka

printf("Pogresan mesec\n");
return 1;
}

/* Provera dana */
if (d < 1 || d > dm) {
printf("Pogresan dan\n");
return 1;

5)
}
/* Provera godine */
if (g < 0) {

01
printf("Pogresna godina\n");
return 1;

(2
}
printf("Uneti datum je korektan\n");
return 0;
}

je
an
Reenje zadatka 7.20:
d

#include <stdio.h>
iz

#define BROJ_PREDMETA 12

int main() {
o

/* Nabrojivi tip */
sk

enum Uspeh {NEDOVOLJAN, DOVOLJAN, DOBAR, VRLO_DOBAR, ODLICAN, GRESKA};

/* Broj jedinica i prosecna ocena ucenika */


n

int broj_jedinica;
ro

float prosek;

/* Uspeh ucenika - odredjuje se automatski na osnovu


kt

prosecne ocene i broja jedinica */


enum Uspeh uspeh;
le

/* Unos podataka */
E

printf("Unesi broj jedinica: ");


scanf("%d", &broj_jedinica);
printf("Unesi prosek: ");
scanf("%f", &prosek);

/* Odredjivanje uspeha */
if (broj_jedinica > BROJ_PREDMETA || prosek < 2.0 || prosek > 5.0)
uspeh = GRESKA;
else if (broj_jedinica > 0)
uspeh = NEDOVOLJAN;
else if (prosek < 2.5)
334 B. Reenja zadataka

uspeh = DOVOLJAN;
else if (prosek < 3.5)
uspeh = DOBAR;
else if (prosek < 4.5)
uspeh = VRLO_DOBAR;
else
uspeh = ODLICAN;

5)
/* Prijavljivanje rezultata */
switch(uspeh) {

01
case NEDOVOLJAN:
printf("Nedovoljan uspeh\n");

(2
break;
case DOVOLJAN:
printf("Dovoljan uspeh\n");
break;

je
case DOBAR:
printf("Dobar uspeh\n");
an
break;
case VRLO_DOBAR:
printf("Vrlo dobar uspeh\n");
d

break;
iz

case ODLICAN:
printf("Odlican uspeh. Cestitamo!\n");
break;
o

case GRESKA:
sk

printf("Deluje da su podaci neispravni\n");


break;
}
n
ro

return 0;
}
kt

Reenje zadatka 7.21:


le
E

#include <stdio.h>
#include <limits.h>

int main() {
int x; /* tekuci broj */
int m; /* najmanji prethodno unet broj */
int n; /* broj unetih brojeva */
int s; /* suma unetih brojeva */

/* Prvi uneti broj mora biti specificno obradjen jer nema


prethodnih brojeva kako bi se odredio njihov minimum. Po uslovu
335 B. Reenja zadataka

zadatka, uvek ga ispisujemo.


*/
scanf("%d", &x);
printf("manji od minimuma: %d\n", x);
m = x;
/* Za sredinu prethodnih se uzima da je 0.0 */
if (x > 0.0)
printf("Veci od proseka prethodnih: %d\n", x);

5)
/* Azuriramo prosek prethodnih */
s = x; n = 1;

01
/* Obradjujemo ostale brojeve dok se ne pojavi 0 */
while (x != 0) {

(2
scanf("%d", &x);
if (x < m) {
printf("Manji od minimuma: %d\n", x);
/* Azuriramo vrednost minimuma */

je
m = x;
}
an
if (x > s / n)
printf("Veci od proseka prethodnih: %d\n", x);
/* Azuriramo prosek prethodnih */
d

s += x; n++;
iz

return 0;
o

}
sk

Reenje zadatka 7.22:


n
ro

#include <stdio.h>
kt

int main() {
unsigned a[1000], n, i, j;
le

scanf("%d", &n);
E

/* Gradimo niz sve do pozicije n */


a[0] = 0;
for (i = 1; i < n; i = j)
/* Kopiramo prefiks uvecavajuci elemente za 1, vodeci racuna da ne
predjemo granicu */
for (j = i; j < i+i && j < n; j++)
a[j] = a[j - i] + 1;

/* Stampamo niz */
for (i = 0; i < n; i++)
336 B. Reenja zadataka

printf("%d ", a[i]);


printf("\n");

return 0;
}

Reenje zadatka 7.23:

5)
#include <stdio.h>

01
#include <assert.h>

int main() {

(2
int a[100], b[100]; /* Ulazni nizovi */
int na, nb; /* Njihov broj elemenata */
int p[100], u[200], r[100]; /* Presek, unija, razlika */

je
int np, nu, nr; /* Njihov broj elemenata */
int i, j, k; /* Pomocne promenljive */
an
/* Ucitavamo niz a proveravajuci da se uneti elementi ne ponavljaju */
printf("Unesi broj elemenata niza a: ");
d

scanf("%d", &na);
iz

for (i = 0; i < na; i++) {


scanf("%d", &a[i]);
for (j = 0; j < i; j++)
o

assert(a[i] != a[j]);
sk

/* Ucitavamo niz b proveravajuci da se uneti elementi ne ponavljaju */


n

printf("Unesi broj elemenata niza b: ");


ro

scanf("%d", &nb);
for (i = 0; i < nb; i++) {
scanf("%d", &b[i]);
kt

for (j = 0; j < i; j++)


assert(b[i] != b[j]);
le

}
E

/* ------------------------------------------------------- */
/* presek - u niz p kopiramo sve elemente niza a koji se javljaju u
nizu b */
np = 0;
for (i = 0; i < na; i++) {
for (j = 0; j < nb; j++) {
/* Ako je element a[i] pronadjen u nizu b dodajemo ga u presek p
i prekidamo pretragu */
if (a[i] == b[j]) {
p[np++] = a[i];
break;
337 B. Reenja zadataka

}
}
}
/* Ispisujemo elemente preseka */
printf("Presek: ");
for (i = 0; i < np; i++)
printf("%d ", p[i]);
printf("\n");

5)
/* ------------------------------------------------------- */

01
/* unija - u niz u kopiramo sve elemente niza a i za njima sve
elemente niza b koji se ne javljaju u a */

(2
/* Kopiramo niz a u niz u */
nu = 0;
for (i = 0; i < na; i++)
u[nu++] = a[i];

je
for (i = 0; i < nb; i++) {
/* Trazimo b[i] u nizu a */
an
for (j = 0; j < na; j++)
if (b[i] == a[j])
break;
d

/* Ako je petlja stigla do kraja, znaci da niz a ne sadrzi b[i] pa


iz

ga dodajemo u uniju u */
if (j == na)
u[nu++] = b[i];
o

}
sk

/* Ispisujemo elemente unije */


printf("Unija: ");
for (i = 0; i < nu; i++)
n

printf("%d ", u[i]);


ro

printf("\n");
kt

/* ------------------------------------------------------- */
/* razlika - u niz r kopiramo sve elemente niza a koji se ne
le

javljaju u nizu b */
nr = 0;
E

for (i = 0; i < na; i++) {


/* Trazimo a[i] u nizu b */
for (j = 0; j < nb; j++)
if (a[i] == b[j])
break;
/* Ako je petlja stigla do kraja, znaci da niz b ne sadrzi a[i] pa
ga dodajemo u razliku r */
if (j == nb)
r[nr++] = a[i];
}
/* Ispisujemo elemente razlike */
338 B. Reenja zadataka

printf("Razlika: ");
for (i = 0; i < nr; i++)
printf("%d ", r[i]);
printf("\n");

return 0;
}

5)
Reenje zadatka 7.24:

01
#include <stdio.h>
#include <string.h>

(2
int main() {
/* Rec koja se proverava */

je
char s[] = "anavolimilovana";
/* Bulovska promenljiva koja cuva rezultat */
an
int palindrom = 1;

/* Obilazimo nisku paralelno sa dva kraja sve dok se pozicije ne


d

mimoidju ili dok ne naidjemo na prvu razliku */


iz

int i, j;
for (i = 0, j = strlen(s) - 1; i < j && palindrom; i++, j--)
if (s[i] != s[j])
o

palindrom = 0;
sk

/* Ispisujemo rezultat */
if (palindrom)
n

printf("Rec %s je palindrom\n", s);


ro

else
printf("Rec %s nije palindrom\n", s);
}
kt
le

Reenje zadatka 7.25:


E

#include <stdio.h>
#include <string.h>

int main() {
/* Niska koja se obrce */
char s[] = "Zdravo";

/* Nisku obilazimo paralelno sa dva kraja sve dok se pozicije ne


susretnu, razmenjujuci karaktere */
int i, j;
339 B. Reenja zadataka

for (i = 0, j = strlen(s)-1; i<j; i++, j--) {


int tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}

/* Ispisujemo obrnutu nisku */


printf("%s\n", s);

5)
}

01
Reenje zadatka 7.26:

(2
#include <stdio.h>
#include <assert.h>

je
#define MAX 100
an
int main() {
unsigned n, i, j, k;
int m[MAX][MAX];
d
iz

/* Ucitavanje matrice */
scanf("%u", &n);
assert(n < MAX);
o

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


sk

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


scanf("%d", &m[i][j]);
n

/* Ispisujemo dijagonale - na svakoj je i+j konstantno */


ro

for (k = 0; k <= 2*n-2; k++) {


for (i = 0; i <= k; i++)
if (i < n && k-i < n)
kt

printf("%d ", m[i][k-i]);


putchar(\n);
le

}
E

return 0;
}

Reenje zadatka 7.27:

#include <stdio.h>
#include <assert.h>

#define MAX 100


340 B. Reenja zadataka

int main() {
unsigned m, n, i, j;
int a[MAX][MAX];

/* Ucitavamo matricu */
scanf("%d%d", &m, &n);
assert(m < MAX && n < MAX);
for (i = 0; i < m; i++)

5)
for (j = 0; j < n; j++)
scanf("%d", &a[i][j]);

01
/* Proveravamo sva polja */
for (i = 0; i < m; i++)

(2
for (j = 0; j < n; j++) {
/* Odredjujemo zbir susednih elemenata polja (i, j) */
int k, l;
int s = 0;

je
for (k = -1; k <= 1; k++)
for (l = -1; l <= 1; l++) {
an
if (k == 0 && l == 0)
continue;
if (0 <= i+k && i+k < m && 0 <= j+l && j+l < n)
d

s += a[i+k][j+l];
iz

/* Proveravamo da li je zbir jednak vrednosti na polju (i, j) */


o

if (s == a[i][j])
sk

printf("%u %u: %d\n", i, j, a[i][j]);


}
n

return 0;
ro

}
kt

Jezik C - funkcije
le

Reenje zadatka 8.1:


E

#include <stdio.h>

int prost(unsigned n) {
unsigned d;
if (n <= 1) return 0; /* najmanji prost broj je 2 */
/* Proveravaju se delioci do korena i prekida se ako se
pronadje delioc (moze da prekoraci za jako veliko n) */
for (d = 2; d * d <= n; d++)
if (n % d == 0)
341 B. Reenja zadataka

return 0;
/* Ako delioc nije pronadjen, broj je prost */
return 1;
}

int main() {
/* Ucitava se broj i proverava da li je prost */
unsigned n;

5)
scanf("%u", &n);
printf(prost(n) ? "Prost\n" : "Nije prost\n");
}

01
Reenje zadatka 8.2:

(2
#include <stdio.h>

je
#include <math.h>
an
double rastojanje(double x1, double y1, double x2, double y2) {
double dx = x2 - x1, dy = y2 - y1;
return sqrt(dx*dx + dy*dy);
d

}
iz

int main() {
double xa, ya, xb, yb, xc, yc;
o

scanf("%lf%lf", &xa, &ya);


sk

scanf("%lf%lf", &xb, &yb);


scanf("%lf%lf", &xc, &yc);
double a = rastojanje(xb, yb, xc, yc);
n

double b = rastojanje(xa, ya, xc, yc);


ro

double c = rastojanje(xa, ya, xb, yb);


double s = (a + b + c) / 2.0;
double P = sqrt(s * (s - a) * (s - b) * (s - c));
kt

printf("%lf\n", P);
return 0;
le

}
E

Reenje zadatka 8.3:

#include <stdio.h>

unsigned suma_delilaca(unsigned n) {
unsigned s = 1, d;
for (d = 2; d*d < n; d++)
if (n % d == 0)
s += d + n / d;
342 B. Reenja zadataka

if (d * d == n)
s += d;
return s;
}

int savrsen(unsigned n) {
return n == suma_delilaca(n);
}

5)
int main() {
unsigned n;

01
for (n = 1; n <= 10000; n++)
if (savrsen(n))

(2
printf("%u\n", n);
return 0;
}

Reenje zadatka 8.4:


je
an
#include <stdio.h>
d

#include <assert.h>
iz

/* Struktura razlomak */
struct razlomak {
o

int brojilac, imenilac;


sk

};

/* a/b < c/d je ekvivalentno sa


n

a*d < c*b ako je a*d > 0, tj. sa


ro

a*d > c*b ako je a*d < 0 */


int poredi_razlomke(struct razlomak a, struct razlomak b) {
assert(a.imenilac != 0 && b.imenilac != 0);
kt

if(a.imenilac*b.imenilac>0) {
if(a.brojilac*b.imenilac > a.imenilac*b.brojilac)
le

return 1;
else if(a.brojilac*b.imenilac == a.imenilac*b.brojilac)
E

return 0;
else
return -1;
} else {
if(a.brojilac*b.imenilac < a.imenilac*b.brojilac)
return 1;
else if(a.brojilac*b.imenilac == a.imenilac*b.brojilac)
return 0;
else
return -1;
}
343 B. Reenja zadataka

/* Ucitavaju se dva razlomka i porede se */


int main() {
struct razlomak a, b;
scanf("%d%d", &a.brojilac, &a.imenilac); assert(a.imenilac != 0);
scanf("%d%d", &b.brojilac, &b.imenilac); assert(b.imenilac != 0);
printf("%d\n", poredi_razlomke(a, b));

5)
}

01
Reenje zadatka 8.5:

(2
#include <stdio.h>

int sadrzi(int a[], int n, int x) {

je
int i;
for (i = 0; i < n; i++)
an
if (a[i] == x)
return 1;
return 0;
d

}
iz

int prva_poz(int a[], int n, int x) {


int i;
o

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


sk

if (a[i] == x)
return i;
return -1;
n

}
ro

int poslednja_poz(int a[], int n, int x) {


int i;
kt

for (i = n - 1; i >= 0; i--)


if (a[i] == x)
le

return i;
return -1;
E

int suma(int a[], int n) {


int i;
int s = 0;
for (i = 0; i < n; i++)
s += a[i];
return s;
}

double prosek(int a[], int n) {


344 B. Reenja zadataka

int i;
int s = 0;
for (i = 0; i < n; i++)
s += a[i];
return (double)s / (double)n;
}

/* Pretpostavlja se da je niz neprazan */

5)
int min(int a[], int n) {
int i, m;
m = a[0];

01
for (i = 1; i < n; i++)
if (a[i] < m)

(2
m = a[i];
return m;
}

je
/* Pretpostavlja se da je niz neprazan */
int max_poz(int a[], int n) {
an
int i, mp;
mp = 0;
for (i = 1; i < n; i++)
d

if (a[i] > a[mp])


iz

mp = i;
return mp;
}
o
sk

int sortiran(int a[], int n) {


int i;
for (i = 0; i + 1 < n; i++)
n

if (a[i] > a[i+1])


ro

return 0;
return 1;
kt

int main() {
le

int a[] = {3, 2, 5, 4, 1, 3, 8, 7, 5, 6};


int n = sizeof(a) / sizeof(int);
E

printf("Sadrzi: %d\n", sadrzi(a, n, 3));


printf("Prva pozicija: %d\n", prva_poz(a, n, 3));
printf("Poslednja pozicija: %d\n", poslednja_poz(a, n, 3));
printf("Suma: %d\n", suma(a, n));
printf("Prosek: %lf\n", prosek(a, n));
printf("Minimum: %d\n", min(a, n));
printf("Pozicija maksimuma: %d\n", max_poz(a, n));
printf("Sortiran: %d\n", sortiran(a, n));
return 0;
}
345 B. Reenja zadataka

Reenje zadatka 8.6:

#include <stdio.h>

int izbaci_poslednji(int a[], int n) {


return n - 1;

5)
}

/* Cuva se redosled elemenata niza */

01
int izbaci_prvi_1(int a[], int n) {
int i;

(2
for (i = 0; i + 1 < n; i++)
a[i] = a[i + 1];
return n - 1;
}

/* Ne cuva se redosled elemenata niza. */


je
an
int izbaci_prvi_2(int a[], int n) {
int i;
d

a[0] = a[n - 1];


return n - 1;
iz

/* Cuva se redosled elemenata niza. */


o

int izbaci_kti(int a[], int n, int k) {


sk

int i;
for (i = k; i + 1 < n; i++)
n

a[i] = a[i + 1];


return n - 1;
ro

}
kt

/* Izbacivanje prvog se moze svesti na izbacivanje k-tog. */


int izbaci_prvi_3(int a[], int n) {
return izbaci_kti(a, n, 0);
le

}
E

/* Pretpostavlja se da ima dovoljno prostora za ubacivanje. */


int ubaci_na_kraj(int a[], int n, int x) {
a[n] = x;
return n + 1;
}

/* Pretpostavlja se da ima dovljno prostora za ubacivanje.


Cuva se redosled elemenata niza. */
int ubaci_na_pocetak_1(int a[], int n, int x) {
int i;
for (i = n; i > 0; i--)
346 B. Reenja zadataka

a[i] = a[i-1];
a[0] = x;
return n + 1;
}

/* Pretpostavlja se da ima dovljno prostora za ubacivanje.


Ne cuva se redosled elemenata niza. */
int ubaci_na_pocetak_2(int a[], int n, int x) {

5)
int i;
a[n] = a[0];
a[0] = x;

01
return n + 1;
}

(2
/* Pretpostavlja se da ima dovljno prostora za ubacivanje.
Cuva se redosled elemenata niza. */
int ubaci_na_poz_k(int a[], int n, int k, int x) {

je
int i;
for (i = n; i > k; i--)
an
a[i] = a[i-1];
a[k] = x;
return n + 1;
d

}
iz

/* Ubacivanje na pocetak se moze svesti na ubacivanje na poziciju k. */


int ubaci_na_pocetak_3(int a[], int n, int x) {
o

return ubaci_na_poz_k(a, n, 0, x);


sk

int izbaci_sve(int a[], int n, int x) {


n

int i, j;
ro

for (j = 0, i = 0; i < n; i++)


if (a[i] != x)
kt

a[j++] = a[i];
return j;
}
le

void ispisi(int a[], int n) {


E

int i;
for (i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
}

int main() {
int a[10] = {1, 2, 3};
int n = 3;
n = ubaci_na_pocetak_1(a, n, 0);
ispisi(a, n);
347 B. Reenja zadataka

n = izbaci_poslednji(a, n);
ispisi(a, n);
n = ubaci_na_poz_k(a, n, 2, 4);
ispisi(a, n);
n = ubaci_na_kraj(a, n, 1);
ispisi(a, n);
n = izbaci_sve(a, n, 1);
ispisi(a, n);

5)
return 0;
}

01
Reenje zadatka 8.7:

(2
#include <stdio.h>

je
/* Napomena: odredjen broj funkcija u ovom zadatku se moze
implementirati i efikasnije, medjutim, uz prilicno komplikovaniji
an
kod. */

void ispisi(int a[], int n) {


d

int i;
iz

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


printf("%d ", a[i]);
printf("\n");
o

}
sk

int najduza_serija_jednakih(int a[], int n) {


int i;
n

/* Duzina tekuce serije je 0 ili 1 u zavisnosti od toga da li je niz


ro

prazan ili nije */


int ts = n != 0;
/* Najduza serija je tekuca serija */
kt

int ns = ts;
for (i = 1; i < n; i++) {
le

/* Ako je element jednak prethodnom */


if (a[i] == a[i-1])
E

/* Nastavlja se tekuca serija */


ts++;
else
/* Inace, zapocinjemo novu seriju */
ts = 1;
/* Azuriramo vrednost najduze serije */
if (ts > ns)
ns = ts;
}
/* Vracamo duzinu najduze serije */
return ns;
348 B. Reenja zadataka

/* Ova funkcija se od prethodne razlikuje samo po uslovu poredjenja


dva susedna elementa niza. Kasnije ce biti prikazano kako se to
poredjenje moze parametrizovati. */
int najduza_serija_neopadajucih(int a[], int n) {
int i;
int ts = n != 0;

5)
int ns = ts;
for (i = 1; i < n; i++) {
if (a[i] >= a[i-1])

01
ts++;
else

(2
ts = 1;
if (ts > ns)
ns = ts;
}

je
return ns;
}
an
int podniz_uzastopnih(int a[], int n, int b[], int m) {
int i, j;
d

/* Za svako i takvo da od pozicije i do kraja niza a ima bar onoliko


iz

elemenata koliko i u nizu b*/


for (i = 0; i + m - 1 < n; i++) {
/* Proveravamo da li se niz b nalazi u nizu a pocevsi od
o

pozicije i */
sk

for (j = 0; j < m; j++)


if (a[i + j] != b[j])
break;
n

/* Nismo naisli na razliku, dakle, ceo niz b se nalazi u nizu a


ro

pocevsi od pozicije i */
if (j == m)
kt

return 1;
}
/* Nismo pronasli b unutar a (inace bi funkcija bila prekinuta sa
le

return 1) */
return 0;
E

int podniz(int a[], int n, int b[], int m) {


int i, j;
/* Prolazimo kroz nisku a, trazeci slova niske b. */
for (i = 0, j = 0; i < n && j < m; i++)
/* Kada pronadjemo tekuce slovo niske b, prelazimo na sledece */
if (a[i] == b[j])
j++;
/* Ako smo iscrpli sve karaktere niske b, onda jeste podniz, inace
nije */
349 B. Reenja zadataka

return j == m;
}

void rotiraj_desno(int a[], int n, int k) {


int i, j;
/* k puta rotiramo za po jednu poziciju */
for (j = 0; j < k; j++) {
/* Upamtimo poslednji */

5)
int tmp = a[n-1];
/* Sve elemente pomerimo za jedno mesto na desno */
for (i = n-1; i > 0; i--)

01
a[i] = a[i-1];
/* Raniji poslednji stavimo na pocetak */

(2
a[0] = tmp;
}
}

je
void rotiraj_levo(int a[], int n, int k) {
int i, j;
an
/* k puta rotiramo za po jednu poziciju */
for (j = 0; j < k; j++) {
/* upamtimo prvi */
d

int tmp = a[0];


iz

/* Sve elemente pomerimo za jedno mesto u levo */


for (i = 0; i + 1 < n; i++)
a[i] = a[i+1];
o

/* Raniji prvi postavimo na kraj */


sk

a[n - 1] = tmp;
}
}
n
ro

int izbaci_duplikate(int a[], int n) {


int i, j, k;
kt

/* za svaki element niza a */


for (i = 0; i < n; i++) {
/* izbacujemo element a[i] iz ostatka niza a tako sto se elementi
le

a[j] razliciti od a[i] prepisuju u niz a (ali na poziciju k


koja moze biti i manja od j). */
E

for (k = i + 1, j = i + 1; j < n; j++)


if (a[j] != a[i])
a[k++] = a[j];
n = k;
}
return n;
}

/* Pretpostavlja se da u niz c moze da se smesti bar n + m elemenata */


int spoji(int a[], int n, int b[], int m, int c[]) {
int i, j, k;
350 B. Reenja zadataka

/* Tekuca pozicija u prvom, drugom nizu i u rezultatu */


i = 0, j = 0, k = 0;
/* Dok postoje elementi i u jednom i u drugom nizu */
while (i < n && j < m)
/* U rezultat prepisujemo manji element */
c[k++] = a[i] < b[j] ? a[i++] : b[j++];
/* Ono sto je preostalo u nizovima prepisujemo u rezultat (jedna od
naredne dve petlje je uvek prazna) */

5)
while (i < n)
c[k++] = a[i++];
while (j < m)

01
c[k++] = b[j++];
return k;

(2
}

void obrni(int a[], int n) {


int i, j;

je
/* Prolazimo niz paralelno sa dva kraja dok se pozicije ne
mimoidju */
an
for (i = 0, j = n-1; i < j; i++, j--) {
/* Vrsimo razmenu elemenata */
int tmp = a[i];
d

a[i] = a[j];
iz

a[j] = tmp;
}
}
o
sk

/* Testiramo napisane funkcije */


int main() {
int a[] = {3, 4, 8, 8, 9, 12}, b[] = {10, 9, 7, 7, 7, 6, 5, 5, 2},
n

c[] = {4, 8, 8, 9}, d[] = {3, 4, 9, 12}, e[20];


ro

int na = sizeof(a)/sizeof(int), nb = sizeof(b)/sizeof(int),


nc = sizeof(c)/sizeof(int), nd = sizeof(d)/sizeof(int), ne;
kt

printf("%d\n", najduza_serija_jednakih(b, nb));


printf("%d\n", najduza_serija_neopadajucih(b, nb));
printf("%d\n", najduza_serija_neopadajucih(a, na));
le

printf("%d\n", podniz_uzastopnih(a, na, c, nc));


printf("%d\n", podniz_uzastopnih(a, na, d, nd));
E

printf("%d\n", podniz(a, na, c, nc));


printf("%d\n", podniz(a, na, d, nd));
printf("%d\n", podniz(b, nb, d, nd));
obrni(b, nb);
ne = spoji(a, na, b, nb, e);
ne = izbaci_duplikate(e, ne);
ispisi(e, ne);
rotiraj_levo(e, ne, 2);
ispisi(e, ne);
rotiraj_desno(e, ne, 5);
ispisi(e, ne);
351 B. Reenja zadataka

return 0;
}

Reenje zadatka 8.8:

#include <stdio.h>

5)
/* Funkcija radi slicno funkciji strcmp iz string.h */
int poredi(char s1[], char s2[]) {

01
/* Petlja tece sve dok ne naidjemo na prvi razliciti karakter */
int i;
for (i = 0; s[i]==t[i]; i++)

(2
if (s[i] == \0) /* Naisli smo na kraj obe niske,
a nismo nasli razliku */
return 0;

je
/* s[i] i t[i] su prvi karakteri u kojima se niske razlikuju.
an
Na osnovu njihovog odnosa, odredjuje se odnos stringova */
return s[i] - t[i];
}
d
iz

int main() {
printf("%d\n", poredi("zdravo", "svima"));
return 0;
o

}
sk

Reenje zadatka 8.9:


n
ro

#include <stdio.h>
kt

/* Proverava da li se niska sub javlja kao podniz niske str (redosled


je bitan, ali karakteri ne moraju da se javljaju uzastopno.
le

Funkcija vraca 1 ako jeste, a 0 ako nije podniz. */


int podniz(char str[], char sub[]) {
E

int i, j = 0;
/* Trazimo svako slovo iz niske sub */
for (i = 0; sub[i] != \0; i++)
/* Petlja traje dok ne nadjeno trazeno slovo */
while (str[j] != sub[i]) {
/* Ako smo usput stigli do kraja niske str, a nismo
nasli trazeno slovo, onda nije podniz. */
if (str[j] == \0)
return 0;
j++;
}
352 B. Reenja zadataka

/* Svako slovo iz sub smo pronasli, onda jeste pondiz */


return 1;
}

/* Proverava da li niska str sadrzi nisku sub kao podnisku (karakteri


moraju da se jave uzastopno). Funkcija radi isto slicno kao
bibliotecka funkcija strstr, ali vraca poziciju na kojoj sub
pocinje, odnosno -1 ukoliko ga nema. Postoje efikasniji algoritmi

5)
za re\v savanje ovog problema, od naivnog algoritma koji je ovde
naveden. */
int podniska(char str[], char sub[]) {

01
int i, j;
/* Proveravamo da li sub pocinje na svakoj poziciji i */

(2
for (i = 0; str[i] != \0; i++)
/* Poredimo sub sa str pocevsi od pozicije i
sve dok ne naidjemo na razliku */
for (j = 0; str[i+j] == sub[j]; j++)

je
/* Nismo naisli na razliku a ispitali smo
sve karaktere niske sub */
an
if (sub[j+1]==\0)
return i;
/* Nije nadjeno */
d

return -1;
iz

int main() {
o

printf("%d\n", podniz("banana", "ann"));


sk

printf("%d\n", podniska("banana", "anan"));


return 0;
}
n
ro

Reenje zadatka 8.10:


kt

#include <stdio.h>
le

/* Dve niske su permutacija jedna druge akko imaju podjednak broj


E

pojavljivanja svih karaktera. Pretpostavljamo da niske sadrze samo


ASCII karaktere. */
int permutacija(char s[], char t[]) {
/* Broj pojavljivanja svakog od 128 ASCII karaktera u niskama s i t */
int ns[128], nt[128], i;
/* Inicijalizujemo brojace na 0 */
for (i = 0; i < 128; i++)
ns[i] = nt[i] = 0;
/* Brojimo karaktere niske s */
for (i = 0; s[i]; i++)
ns[s[i]]++;
353 B. Reenja zadataka

/* Brojimo karaktere niske t */


for (i = 0; t[i]; i++)
nt[t[i]]++;

/* Poredimo brojeve za svaki od 128 ASCII karaktera */


for (i = 0; i < 128; i++)
if (ns[i] != nt[i])
return 0;

5)
/* Nismo naisli na razliku - dakle svi karakteri se javljaju isti
broj puta */
return 1;

01
}

(2
/* Provera funkcije */
int main() {
char s[] = "tom marvolo riddle ", t[] = "i am lord voldemort";
printf("%d\n", permutacija(s, t));

je
return 0;
}
an
d

Reenje zadatka 8.11:


iz

#include <stdio.h>
o

#include <assert.h>
sk

int zbir_vrste(int m[10][10], int n, int i) {


int j;
n

int zbir = 0;
ro

assert(n < 10 && i < 10);


for (j = 0; j < n; j++)
zbir += m[i][j];
kt

return zbir;
}
le

int zbir_kolone(int m[10][10], int n, int j) {


E

int i;
int zbir = 0;
assert(n < 10 && j < 10);
for (i = 0; i < n; i++)
zbir += m[i][j];
return zbir;
}

int zbir_glavne_dijagonale(int m[10][10], int n) {


int i;
int zbir = 0;
354 B. Reenja zadataka

assert(n < 10);


for (i = 0; i < n; i++)
zbir += m[i][i];
return zbir;
}

int zbir_sporedne_dijagonale(int m[10][10], int n) {


int i;

5)
int zbir = 0;
assert(n < 10);
for (i = 0; i < n; i++)

01
zbir += m[i][n-i-1];
return zbir;

(2
}

int ucitaj(int m[10][10]) {


int i, j, n;

je
scanf("%d", &n); assert(1 <= n && n < 10);
for (i = 0; i < n; i++)
an
for (j = 0; j < n; j++)
scanf("%d", &m[i][j]);
return n;
d

}
iz

int magicni(int m[10][10], int n) {


int i, j, zbir;
o

zbir = zbir_vrste(m, n, 0);


sk

for (i = 1; i < n; i++)


if (zbir_vrste(m, n, i) != zbir)
return 0;
n

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


ro

if (zbir_kolone(m, n, j) != zbir)
return 0;
kt

if (zbir_glavne_dijagonale(m, n) != zbir)
return 0;
if (zbir_sporedne_dijagonale(m, n) != zbir)
le

return 0;
return 1;
E

int main() {
int n, m[10][10];
n = ucitaj(m);
printf("%d\n", magicni(m, n));
return 0;
}
355 B. Reenja zadataka

Reenje zadatka 8.12:

#include <stdio.h>
#include <assert.h>

#define MAX 100

5)
typedef struct {
float a[MAX][MAX];

01
unsigned m, n;
} MATRICA;

(2
/* Efikasnije bi bilo prenositi matricu kao argument,
preko pokazivaca, ali to jos nije uvedeno. */
MATRICA ucitaj() {

je
MATRICA m;
unsigned i, j;
an
scanf("%u%u", &m.m, &m.n);
assert(m.n < MAX);
d

for (i = 0; i < m.m; i++)


for (j = 0; j < m.n; j++)
iz

scanf("%f", &m.a[i][j]);
return m;
}
o
sk

MATRICA saberi(MATRICA m1, MATRICA m2) {


MATRICA m;
n

unsigned i, j;
assert(m1.m == m2.m && m1.n == m2.n);
ro

m.m = m1.m; m.n = m1.n;


for (i = 0; i < m.n; i++)
kt

for (j = 0; j < m.n; j++)


m.a[i][j] = m1.a[i][j] + m2.a[i][j];
return m;
le

}
E

MATRICA pomnozi(MATRICA m1, MATRICA m2) {


MATRICA m;
unsigned i, j, k;
assert(m2.m == m1.n);
m.m = m1.m; m.n = m2.n;
for (i = 0; i < m.m; i++)
for (j = 0; j < m.n; j++) {
m.a[i][j] = 0;
for (k = 0; k < m1.n; k++)
m.a[i][j] += m1.a[i][k]*m2.a[k][j];
}
356 B. Reenja zadataka

return m;
}

void ispisi(MATRICA m) {
unsigned i, j;
for (i = 0; i < m.m; i++) {
for (j = 0; j < m.n; j++)
printf("%g ", m.a[i][j]);

5)
putchar(\n);
}
}

01
int main() {

(2
MATRICA m1, m2, m;
m1 = ucitaj(); m2 = ucitaj();
if (m1.m == m2.m && m1.n == m2.n) {
m = saberi(m1, m2);

je
ispisi(m);
}
an
if (m1.n = m2.m) {
m = pomnozi(m1, m2);
ispisi(m);
d

}
iz

return 0;
}
o

Reenje zadatka 8.13:


sk
n

#include <stdio.h>
ro

#include <assert.h>

#define MAX 100


kt

/* Veliki broj je predstavljen nizom od n cifara. Cifre se zapisuju


le

"naopako" kako bi se cifra uz 10^i nalazila na poziciji i */


typedef struct {
E

unsigned char c[MAX];


unsigned n;
} BROJ;

BROJ ucitaj() {
BROJ rez;
rez.n = 0;
int c, i, j;
while(isdigit(c = getchar())) {
rez.c[rez.n++] = c - 0;
assert(rez.n <= MAX);
357 B. Reenja zadataka

}
/* Obrcemo zapis */
for (i = 0, j = rez.n-1; i < j; i++, j--) {
unsigned char tmp = rez.c[i];
rez.c[i] = rez.c[j];
rez.c[j] = tmp;
}
return rez;

5)
}

void ispisi(BROJ b) {

01
int i;
for (i = b.n-1; i >= 0; i--)

(2
putchar(0 + b.c[i]);
}

BROJ saberi(BROJ a, BROJ b) {

je
BROJ rez;
unsigned i;
an
/* Prenos */
unsigned char p = 0;
for (i = 0; i < a.n || i < b.n; i++) {
d

/* Cifra prvog broja ili 0 ako su sve cifre iscrpljene */


iz

unsigned char c1 = i < a.n ? a.c[i] : 0;


/* Cifra drugog broja ili 0 ako su sve cifre iscrpljene */
unsigned char c2 = i < b.n ? b.c[i] : 0;
o

/* Zbir cifara + prenos sa prethodne pozicije*/


sk

unsigned char c = c1 + c2 + p;
/* Nova cifra */
rez.c[i] = c % 10;
n

/* Prenos na sledecu poziciju */


ro

p = c / 10;
}
kt

/* Broj cifara rezultata */


rez.n = i;
/* Ako postoji prenos, treba dodati jos jednu cifru */
le

if (p > 0) {
assert(rez.n < MAX);
E

rez.c[rez.n] = p;
rez.n++;
}
return rez;
}

BROJ pomnozi(BROJ a, BROJ b) {


BROJ rez; /* rezultat */
unsigned i, j;
unsigned char p; /* prenos */
358 B. Reenja zadataka

/* Maksimalni broj cifara rezultata */


rez.n = a.n + b.n;
assert(rez.n <= MAX);
/* Inicijalizujemo sve cifre rezultata */
for (i = 0; i < rez.n; i++)
rez.c[i] = 0;
/* Mnozimo brojeve ne vrseci nikakav prenos */
for (i = 0; i < a.n; i++)

5)
for (j = 0; j < b.n; j++)
rez.c[i+j] += a.c[i]*b.c[j];

01
/* Normalizujemo rezultat */
p = 0;

(2
for (i = 0; i < rez.n; i++) {
unsigned char c = rez.c[i] + p;
rez.c[i] = c % 10;
p = c / 10;

je
}
/* Ako je prva cifra 0, smanjujemo broj cifara */
an
if (rez.c[rez.n-1] == 0)
rez.n--;
return rez;
d

}
iz

int main() {
BROJ a = ucitaj(), b = ucitaj(), c;
o

c = saberi(a, b);
sk

ispisi(c);
putchar(\n);
c = pomnozi(a, b);
n

ispisi(c);
ro

putchar(\n);
}
kt

Jezik C - pokazivai
le

Reenje zadatka 10.2.1:


E

#include <stdio.h>
#include <assert.h>

/* Funkcija vraca 3 vrednosti preko pokazivaca */


void od_ponoci(unsigned n, unsigned* h, unsigned *m, unsigned *s) {
*h = n / 3600;
n %= 3600;
*m = n / 60;
359 B. Reenja zadataka

n %= 60;
*s = n;
}

int main() {
unsigned n, h, m, s;
scanf("%d", &n); assert(n < 60*60*24);
od_ponoci(n, &h, &m, &s);

5)
printf("%d:%d:%d\n", h, m, s);
return 0;
}

01
Reenje zadatka 10.8.1:

(2
#include <stdio.h>

int paran(int x) {
je
an
return x % 2 == 0;
}
d

int pozitivan(int x) {
iz

return x > 0;
}
o

/* Kriterijum se prosledjuje u obliku pokazivaca na funkciju */


sk

int najduza_serija(int a[], int n, int (*f) (int)) {


int i;
/* Duzina tekuce serije je 0 ili 1 u zavisnosti od toga da li je niz
n

prazan ili nije */


ro

int ts = 0;
/* Najduza serija je tekuca serija */
int ns = ts;
kt

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


/* Ako element zadovoljava kriterijum */
le

if ((*f)(a[i]))
/* Nastavlja se tekuca serija */
E

ts++;
else
/* Inace, prekidamo seriju */
ts = 0;
/* Azuriramo vrednost najduze serije */
if (ts > ns)
ns = ts;
}
/* Vracamo duzinu najduze serije */
return ns;
}
360 B. Reenja zadataka

/* Testiramo napisane funkcije */


int main() {
int a[] = {1, 2, 4, -6, 5, 3, -3, 2, 5, 7, 9, 11},
n = sizeof(a)/sizeof(int);
printf("%d %d\n", najduza_serija(a, n, &paran),
najduza_serija(a, n, &pozitivan));
}

5)
Reenje zadatka 10.5.1:

01
#include <stdio.h>

(2
size_t my_strlen(const char* s) {
const char* t;

je
for (t = s; *t; t++)
;
an
return t - s;
}
d

size_t my_strcpy(char* dest, const char* src) {


iz

while(*dest++ = *src++)
;
}
o
sk

int my_strcmp(char* s, char *t) {


while (*s && *s == *t)
s++, t++;
n

return *s - *t;
ro

void my_strrev(char* s) {
kt

char *t = s;
/* Dovodimo s ispred terminalne nule */
le

while(*s)
s++;
E

s--;
/* Obrcemo karaktere */
for (; t < s; t++, s--) {
char tmp = *s; *s = *t; *t = tmp;
}
}

const char* my_strchr(char x, const char* s) {


while(*s) {
if (*s == x) return s;
s++;
361 B. Reenja zadataka

}
return NULL;
}

const char* my_strstr(const char* str, const char* sub) {


if (str == NULL || sub == NULL)
return NULL;

5)
for ( ; *str; str++) {
const char *s, *t;
for (s = str, t = sub; *s && (*s == *t); s++, t++)

01
;
if (*t == \0)

(2
return str;
}
return NULL;
}

int main() {
je
an
char s[] = "abc", t[] = "def", r[4];
printf("%lu %lu\n", my_strlen(s), my_strlen(t));
my_strcpy(r, s);
d

printf("%s\n", r);
iz

my_strrev(s);
printf("%s\n", s);
printf("%d\n", my_strcmp(s, t));
o

printf("%c\n", *my_strchr(x, "abcxyz"));


sk

printf("%s\n", my_strstr("abcdefghi", "def"));


return 0;
}
n
ro

Reenje zadatka 10.9.1:


kt

#include <stdio.h>
le

#include <assert.h>
#include <stdlib.h>
E

int main() {
unsigned n, i, min;
int *a, x;

/* Ucitavamo niz */
scanf("%u", &n);
assert(n > 0);
a = malloc(n*sizeof(int)); /* Dinamicka alokacija */
assert(a != NULL);
for (i = 0; i < n; i++)
362 B. Reenja zadataka

scanf("%d", &a[i]);

/* Ucitavamo broj koji se trazi */


scanf("%d", &x);

/* Trazimo element niza a najblizi broju x */


min = 0;
for (i = 1; i < n; i++)

5)
if (abs(a[i] - x) < abs(a[min] - x))
min = i;

01
/* Ispisujemo rezultat */
printf("a[%d] = %d\n", min, a[min]);

(2
/* Oslobadjamo niz */
free(a);
return 0;

je
}
an
Reenje zadatka 10.9.2:
d
iz

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
o
sk

#define KORAK 32

int main() {
n

unsigned n = 0, aloc = 0, i, m, nm;


ro

int *a = NULL;

/* Ucitavamo niz, dinamicki ga realocirajuci */


kt

do {
if (aloc <= n) {
le

int* a_novo;
aloc += KORAK;
E

a_novo = realloc(a, aloc*sizeof(int));


assert(a_novo);
a = a_novo;
}
scanf("%d", &a[n]);
} while(a[n++] != 0);

/* Trazenje najcesceg elementa - ovaj algoritam je neefikasan, ali


se jednostavno implementira */
/* Broj pojavljivanja najcesceg elementa */
nm = 0;
363 B. Reenja zadataka

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


/* Brojimo koliko puta se javlja element a[i] */
int ni = 0, j;
for (j = 0; j < n; j++)
if (a[i] == a[j])
ni++;
/* Ako se javlja cesce od do tada najcesceg, azuriramo broj
pojavljivanja najcesceg elementa i vrednost najcesceg */

5)
if (ni > nm) {
nm = ni;
m = a[i];

01
}
}

(2
/* Ispisujemo najcesci element i njegov broj pojavljivanja */
printf("%d - %d\n", m, nm);

je
/* Oslobadjamo niz */
free(a);
an
return 0;
}
d
iz

Reenje zadatka 10.9.3:


o
sk

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

int main() {
ro

int n, i, j, lx, ux, ly, uy, **A;

/* Ucitavamo dimenziju matrice */


kt

scanf("%d", &n);
le

/* Dinamicki alociramo prostor */


A = malloc(n*sizeof(int*));
E

assert(A);
for (i = 0; i < n; i++) {
A[i] = malloc(n*sizeof(int));
assert(A[i]);
}

/* Ucitavamo elemente matrice */


for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
scanf("%d", &A[i][j]);
364 B. Reenja zadataka

/* Spiralno ispisujemo raspon [lx, ux] * [ly, uy] */


lx = 0; ux = n-1; ly = 0; uy = n-1;
while (lx <= ux && ly <= uy) {
for (i = lx; i <= ux; i++)
printf("%d ", A[i][ly]);
for (j = ly+1; j <= uy; j++)
printf("%d ", A[ux][j]);
for (i = ux-1; i >= lx; i--)

5)
printf("%d ", A[i][uy]);
for (j = uy-1; j >= ly+1; j--)
printf("%d ", A[lx][j]);

01
lx++; ux--; ly++; uy--;
}

(2
/* Oslobadjamo matricu */
for (i = 0; i < n; i++)
free(A[i]);

je
free(A);
an
return 0;
}
d

Reenje zadatka 10.9.4:


iz
o

#include <stdio.h>
sk

#include <assert.h>
#include <stdlib.h>
n

int skalarni_proizvod(int *a, int *b, int n) {


ro

int suma = 0, i;
for (i = 0; i < n; i++)
suma += a[i]*b[i];
kt

return suma;
}
le

int ortonormirana(int** A, int n) {


E

int i, j;
for (i = 0; i < n; i++) {
if (skalarni_proizvod(A[i], A[i], n) != 1)
return 0;
for (j = i+1; j < n; j++)
if (skalarni_proizvod(A[i], A[j], n) != 0)
return 0;
}
return 1;
}
365 B. Reenja zadataka

int main() {
int n, i, j, **A;

/* Ucitavamo dimenziju matrice */


scanf("%d", &n);

/* Dinamicki alociramo prostor */


A = malloc(n*sizeof(int*));

5)
assert(A);
for (i = 0; i < n; i++) {
A[i] = malloc(n*sizeof(int));

01
assert(A[i]);
}

(2
/* Ucitavamo elemente matrice */
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)

je
scanf("%d", &A[i][j]);
an
/* Ispitujemo da li je matrica ortonormirana */
printf("%d\n", ortonormirana(A, n));
d

/* Oslobadjamo matricu */
iz

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


free(A[i]);
free(A);
o
sk

return 0;
}
n

Jezik C - ulaz/izlaz
ro
kt

Reenje zadatka 12.1:


le

#include <stdio.h>
E

#include <stdlib.h>

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


int zbir = 0;
for (i = 0; i < argc; i++)
zbir += atoi(argv[i]);
printf("%d\n", zbir);
return 0;
}
366 B. Reenja zadataka

Reenje zadatka 12.2:

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

5)
/* Racuna se zbir kolone j matrice A dimenzije n */
int zbir_kolone(int** A, unsigned n, unsigned j) {
int i, zbir = 0;

01
for (i = 0; i < n; i++)
zbir += A[i][j];

(2
return zbir;
}

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

je
unsigned n, i, j, max_j;
int **A, max;
an
FILE* dat;
d

if (argc < 2) {
fprintf(stderr, "Greska: nedostaje ime ulazne datoteke\n");
iz

return -1;
}
o

dat = fopen(argv[1], "r");


sk

if (dat == NULL) {
fprintf(stderr,
n

"Greska: ucitavanje datoteke %s nije uspelo\n",


argv[1]);
ro

return -1;
}
kt

/* Ucitavamo dimenziju matrice */


assert(fscanf(dat, "%u", &n) == 1);
le

assert(n >= 1);


E

/* Dinamicki alociramo prostor */


A = malloc(n*sizeof(int*));
assert(A);
for (i = 0; i < n; i++) {
A[i] = malloc(n*sizeof(int));
assert(A[i]);
}

/* Ucitavamo matricu */
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
367 B. Reenja zadataka

assert(fscanf(dat, "%d", &A[i][j]) == 1);

/* Odredjujemo maksimum */
max_j = 0; max = zbir_kolone(A, n, 0);
for (j = 1; j < n; j++) {
int m = zbir_kolone(A, n, j);
if (m > max) {
max_j = j;

5)
max = m;
}
}

01
/* Prijavljujemo rezultat */

(2
printf("Kolona: %u Zbir: %d\n", max_j, max);

/* Oslobadjamo matricu */
for (i = 0; i < n; i++)

je
free(A[i]);
free(A);
an
/* Zatvaramo datoteku */
fclose(dat);
d
iz

return 0;
}
o

Reenje zadatka 12.3:


sk
n

#include <stdio.h>
ro

#include <stdlib.h>
#include <assert.h>
kt

#define KORAK 64
le

typedef struct {
char ime[20];
E

char prezime[20];
unsigned br_ispita;
} APSOLVENT;

int main() {
FILE* dat;
APSOLVENT* apsolventi = NULL, apsolvent;
unsigned aloc = 0, n = 0, i, zbir;
float prosek;

dat = fopen("apsolventi.txt", "r");


368 B. Reenja zadataka

if (dat == NULL) {
fprintf(stderr,
"Greska pri otvaranju datoteke apsolventi.txt\n");
return 1;
}

/* Ucitavanje apsolvenata uz dinamicku realokaciju niza */


while(fscanf(dat, "%s%s%d",

5)
apsolvent.ime, apsolvent.prezime, &apsolvent.br_ispita)
== 3) {
if (n >= aloc) {

01
aloc += KORAK;
APSOLVENT* tmp = realloc(apsolventi, aloc*sizeof(APSOLVENT));

(2
assert(tmp);
apsolventi = tmp;
}
apsolventi[n++] = apsolvent;

je
}
an
/* Zatvaramo datoteku */
fclose(dat);
d

/* Izracunavanje prosecnog broja ispita */


iz

zbir = 0;
for (i = 0; i < n; i++)
zbir += apsolventi[i].br_ispita;
o

prosek = (float)zbir / n;
sk

/* Ispisujemo apsolvente sa natprosecnim brojem ispita */


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

if (apsolventi[i].br_ispita > prosek)


ro

printf("%s %s: %d\n", apsolventi[i].ime,


apsolventi[i].prezime,
kt

apsolventi[i].br_ispita);

/* Oslobadjamo memoriju */
le

free(apsolventi);
E

return 0;
}

Reenje zadatka 12.4:

#include <stdio.h>
#include <ctype.h>
#include <assert.h>
369 B. Reenja zadataka

/* Maksimalan broj cifara u zapisu */


#define MAX_DIGITS 100

/* Vrednost cifre predstavljene ASCII karakterom.


Npr. 0->0, ..., 9->9,
a->10, ..., f -> 15,
A -> 10, ..., F -> 15,
Za karakter koji nije alfanumericki vraca -1 */

5)
signed char vrednost_cifre(unsigned char c) {
if (isdigit(c)) /* Cifre */
return c-0;

01
if (isalpha(c) && islower(c)) /* Mala slova */
return c-a + 10;

(2
if (isalpha(c) && isupper(c)) /* Velika slova */
return c-A + 10;
return -1;
}

je
/* Cita zapis niske s kao broja u osnovi b. Citanje se vrsi dok se
an
javljaju validne cifre u osnovi b. Koristi se Hornerova shema. */
unsigned btoi(char s[], unsigned char b) {
unsigned rez = 0, i;
d

for (i = 0; s[i]; i++) {


iz

signed char c = vrednost_cifre(s[i]);


/* Karakter ne predstavlja validnu cifru u osnovi b */
if (c == -1 || c >= b) break;
o

rez = rez * b;
sk

rez += c;
}
return rez;
n

}
ro

/* Odredjuje zapis cifre c kao ASCII karakter */


kt

unsigned char zapis_cifre(unsigned c) {


assert(c <= 36);
if (0 <= c && c <= 9)
le

return 0 + c;
else if (10 <= c && c <= 36)
E

return a + c;
}

/* Zapisuje broj n u osnovi b.


Funkcija pretpostavlja da niska s sadrzi dovoljno karaktera za
zapis. */
void itob(unsigned n, unsigned char b, char s[]) {
/* Formira se niz cifara */
unsigned i = 0, j;
do {
s[i++] = zapis_cifre(n % b);
370 B. Reenja zadataka

n /= b;
} while (n > 0);
s[i] = \0;
/* Obrce se niz cifara */
for (j = 0, --i; j < i; j++, i--) {
char tmp = s[i];
s[i] = s[j];
s[j] = tmp;

5)
}
}

01
int main(int argc, char* argv[]) {
/* Broj u osnovi b koji se ucitava (niska cifara i vrednost) */

(2
char s[MAX_DIGITS];
unsigned n;
/* Preveden broj u osnovu 2 */
char bs[MAX_DIGITS];

je
/* Datoteke iz kojih se ucitava i u koje se upisuje */
FILE *ulaz, *izlaz;
an
/* Otvaranje datoteka */
if (argc < 3) {
d

fprintf(stderr, "Upotreba: %s <ulaz> <izlaz>\n", argv[0]);


iz

return 1;
}
ulaz = fopen(argv[1], "r");
o

if (ulaz == NULL) {
sk

fprintf(stderr, "Greska prilikom otvaranja %s\n", argv[1]);


return 1;
}
n

izlaz = fopen(argv[2], "w");


ro

if (izlaz == NULL) {
fprintf(stderr, "Greska prilikom otvaranja %s\n", argv[2]);
kt

return 1;
}
le

/* Citanje i obrada brojeva */


while (fgets(s, MAX_DIGITS, ulaz) != NULL) {
E

char *t = s;
/* Ako je uneta prazna linija, prekida se postupak */
if (*t == \0 || *t == \n)
break;
if (*t == 0) {
t++;
if (*t == x || *t == X) {
/* Heksadekadni broj pocinje sa 0x ili 0X */
t++;
n = btoi(t, 16);
printf("%u\n", n);
371 B. Reenja zadataka

itob(n, 2, bs);
fputs(bs, izlaz); fputc(\n, izlaz);
} else {
/* Oktalni broj pocinje sa 0 */
n = btoi(t, 8);
printf("%u\n", n);
itob(n, 2, bs);
fputs(bs, izlaz); fputc(\n, izlaz);

5)
}
} else {
/* Dekadni broj */

01
n = btoi(t, 10);
printf("%u\n", n);

(2
itob(n, 2, bs);
fputs(bs, izlaz); fputc(\n, izlaz);
}
}

/* Zatvaramo datoteke */
je
an
fclose(ulaz);
fclose(izlaz);
return 0;
d

}
iz
o
sk
n
ro
kt
le
E
372
E
le
kt
ro
n
sk
o
iz
d
an
je
(2
01
B. Reenja zadataka

5)
5)
Indeks

01
(2
32-bitni i 64-bitni sistem, 105, 219 getc, 289
getchar, 278

je
abakus, 12 gets, 279
ABC (raunar), 15 isalnum, 274
an
algoritam, 61 isalpha, 107, 274
analitika maina, 13 isdigit, 107
d

analogni zapis, 39 isdigit, 274


API, 35, 269 islower, 274
iz

argument funkcije, 86 isupper, 274


argumenti komandne linije programa, malloc, 258, 272
o

294 matematika (sin, cos, log2,


exp, . . . ), 275
sk

asembler, 29
automatska promenljiva, 200 printf, 93, 280
putc, 289
n

bajt, 11, 104


putchar, 278
ro

biblioteka funkcija
rand, 272
assert, 276 realloc, 260, 272
calloc, 258, 272
kt

scanf, 95, 283


exit, 272 sprintf, 285
fclose, 288 sqrt, 96, 275
le

feof, 290 srand, 272


ferror, 290
E

sscanf, 286
fgets, 291 strcat, 269
fopen, 287 strchr, 269
fprintf, 292 strcmp, 269
fputs, 279, 291 strcpy, 136, 248, 269
fread, 292 strlen, 135, 247, 269
free, 259, 272 strstr, 269
fscanf, 292 system, 272
fseek, 293 tolower, 107, 274
ftell, 293 toupper, 107, 274
fwrite, 292

373
374 B. Reenja zadataka

ungetc, 290 Fon Nojmanova arhitektura rau-


biblioteka programskog jezika, 35 nara, 16
bit, 11, 12, 44, 104 font, 48
blok, 97, 152, 198 format niska, 95, 105107, 280, 283
blok dijagram, 62 fragmentisanje memorije, 264
brojevni sistem, 40 funkcija, 85, 167
binarni, 11, 38, 40, 44 argument, 170
heksadekadni, 41, 44, 109 definicija, 168, 169

5)
oktalni, 41, 109 deklaracija, 94, 168, 169
buena kartica, 13, 14 main, 93, 167, 190, 294

01
parametar, 170
C (programski jezik), 18 poziv, 168

(2
ANSI/ISO C, 92 prenos argumenata, 171, 223
C11, 92 prenos argumenata po adresi,
C99, 92 239

je
curenje memorije, 262 prenos argumenata po vrednosti,
171
an
er-Tjuringova teza, 63
prenos niza, 174, 242
prototip, vidi funkcija (deklaracija)
datoteka, 286
sa promenljivim brojem argu-
d

binarna, 287
menata, 179
iz

tekstualna, 287
void, 171
datoteka zaglavlja, 94, 186, 194, 269
funkcionalna dekompozicija programa,
<assert.h>, 276
192
o

<ctype.h>, 106, 274


<math.h>, 96, 275
sk

generacije raunara, 16
<stdio.h>, 94, 277 generisanje i optimizacija koda, 187
<stdlib.h>, 258, 272
n

glif, 48
<string.h>, 269
ro

globalna promenljiva, 103, 169, 199,


debager, 190
201
definicija, 169, 204
greka u programu, 94
kt

naelna, 204
izvravanje, 210, 230, 235, 262
stvarna, 204
kompilacija, 209
le

deklaracija, 101, 133, 169, 204


povezivanje, 209
deljenje vremena, 18
pretprocesiranje, 209
E

diferencijska maina, 13
digitalni zapis, 38, 39 halting problem, 72
dinamika alokacija memorije, 258 hard disk, 26
doseg identifikatora, 83, 103, 192, hardver, 11, 16, 22
198 hip, vidi segment memorije (hip)
Holeritova maina, 13
EDVAC raunar, 16
Hornerova ema, 42
elektromehanike maine, 13
ENIAC (raunar), 15 identifikator, 102, 169
enigma maina, 15 IEEE 754 standard, 47, 107, 127
375 B. Reenja zadataka

integrisano kolo, 17 celobrojna promocija, 129


integrisano razvojno okruenje, 189 democija, 127
interpretator, 78, 87 eksplicitna, 126
izraunljiva funkcija, 63 implicitna, 126, 241
izraz, 85, 101, 112 promocija, 126
izvorna datoteka, 192 uobiajena aritmetika, 129
izvorni program, 78, 87, 94, 185, kvalifikator
192 auto, 193, 200

5)
izvrni program, 79, 87, 94, 185, const, 103, 171, 174, 235
218 extern, 193, 205

01
long, 104
jedinica prevoenja, 186, 192, 194, long long, 104

(2
203 register, 193
jeziki procesor, vidi programski pre- short, 104
vodilac signed, 104, 106

je
static, 193, 200, 202, 205, 207
karakter, 48
unsigned, 104, 106
an
kardinalnost, 69
kastovanje, vidi konverzija tipova (ek- l-vrednost, 114, 115, 134, 241
splicitna) Lajbnicova maina, 12
d

klizni lenjir, 12 leksema, 81, 186


iz

kodiranje karaktera, 48 leksika analiza, 186


ASCII, 49, 106 leksiki analizator, 81, 87
ISO-10646, 52 leksika programskog jezika, 80
o

ISO-8859, 51 lenjo izraunavanje, 118, 122


sk

kdna strana, 48 linker, vidi poveziva


UCS-2, 54 lokalna funkcija, 199
n

Unicode, 52 lokalna promenljiva, 103, 169, 171,


UTF-8, 54
ro

199, 200
windows-125x/ANSI, 51
YUSCII, 50 magistrala, 23
kt

Kolos (raunar), 15 magnetni dobo, 17


komentar, 94 make (program), 189
le

kompilacija, 186 makro, vidi pretprocesorska direk-


odvojena, 189 tiva define
E

kompilator, 78, 87, 186 Manesterska Beba (raunar), 17


konflikt identifikatora, 200 Mark I (raunar)
konstanta, 101, 109 harvardski, 14
celobrojna, 109 manesterski, 17
karakterska, 110 matina ploa, 23
u pokretnom zarezu, 110 matrica, vidi niz (dvodimenzioni)
konstanti izraz, 111 memorija
kontroler, 23 glavna, 16, 2325
konverzija tipova, 84, 110, 114, 115, ke, 25
126, 173 RAM, 24, 26
376 B. Reenja zadataka

ROM, 25 arnost, 112


spoljanja, 16, 24, 26 asocijativnost, 113
mikroip, 17 bitovski, 119
mikroprocesor, 19 dekrementiranje, 115
mini raunari, 18 dereferenciranja, 234, 244
model boja, 55 inkrementiranje, 115
CMYK, 56 logiki, 117
RGB, 55 postfiksni, 112, 115

5)
prefiksni, 112, 115
naredba, 85 prioritet, 113

01
break, 155, 159 referenciranja, 234, 244
continue, 160 relacijski, 104, 107, 117
do-while, 99, 158

(2
sizeof, 123, 134, 142, 241
for, 98, 157 sloene dodele, 121
goto, 151 uslovni, 122
if, 97, 152

je
zarez, 122
izraza, 151
an
return, 94, 168, 174 parametar funkcije, 86
switch, 155 prenos po adresi, 86
while, 98, 156 prenos po vrednosti, 86
d

naredba dodele, 85, 113 Paskalina, 12


iz

nazubljivanje koda, 94 pokaziva, 233


neodluiv problem, 71 na funkciju, 254
niska, 134 pokazivai i nizovi, 241
o

doslovna niska, 246 pokazivaka aritmetika, 241, 244


sk

zavrna nula, 135 polje bitova, 145


niz, 132 poluprovodniki elementi, 17
n

deklaracija, 133 potprogram, 85


dvodimenzioni, 137, 250
ro

povezanost identifikatora, 192, 203


indeks, 133, 241 poveziva, 187
inicijalizacija, 133, 135 povezivanje, 186, 187
kt

niz pokazivaa, 250 dinamiko, 188, 218


prenos u funkciju, 174 statiko, 188
le

viedimenzioni, 137 pragmatika programskog jezika, 83


NULL, 235 prebrojiv skup, 69
E

prekoraenje, 105
objektni modul, 187 prekoraenje bafera, 264
objektno-orijentisano programiranje, prelazak u novi red, 50
79 pretprocesiranje, 186, 193
odbirak, 39, 57 pretprocesor, 186, 193
operativni sistem, 34, 218 pretprocesorska direktiva, 193
operator, 101, 112 define, 98, 194
->, 253 if-elif-endif, 197
adresni, 234 ifdef/ifndef, 197
aritmetiki, 104, 107, 114 include, 94, 193
377 B. Reenja zadataka

undef, 196 sempl, vidi odbirak


preusmeravanje (redirekcija) ulaza sintaksa programskog jezika, 80, 81
i izlaza, 277 sintaksika analiza, 186
procesor, 16, 22, 23 sintaksiki analizator, 82, 87
procesorska instrukcija, 29 sintaksiko stablo, 81
programiranje, 11 sloenost izraunavanja, 73
programska paradigma, 80 softver, 11, 16, 34
programski jezik aplikativni, 34

5)
asemblerski, 28 sistemski, 34, 91
mainski, 27, 28, 33, 87 standardna biblioteka programskog

01
mainski zavisan, 26 jezika, 86, 93, 188, 269
vii, 17, 27, 79, 87 standardni izlaz, 93, 277

(2
programski prevodilac, 78, 87, 94 standardni izlaz za greke, 208, 277
gcc, 94, 96, 187189, 210, 220 standardni ulaz, 95, 277
promenljiva, 83, 101 statika promenljiva, 201, 202

je
deklaracija, 95, 101, 102 stek, vidi segment memorije (stek)
inicijalizacija, 99, 102 string, vidi niska
an
propratni efekat, 114, 115 struktura, 140
punilac, 190 definicija, 141
inicijalizacija, 142
d

raunar, 11 prenos u funkciju, 177


iz

elektromehaniki, 14 strukturno programiranje, 79, 85


elektronski, 14
fon Nojmanove arhitekture, 16, terminal, 18
o

78 tip podataka, 83, 101, 104


sk

lini, 19 bool, 108


sa skladitenim programom, 16 char, 106
n

raunarstvo i informatika, 11 double, 96, 107


oblasti, 21 float, 107
ro

rantajm biblioteka, 35, 86, 218 int, 93, 104


rasterska grafika, 55 korisniki definisan, 140
kt

rekurzija, 178, 225 logiki, 108


rezolucija, 56 long double, 107
le

nabrojivi (enum), 140, 146


sakuplja otpada, 86 opseg, 105
E

segment memorije, 220 pokazivaki, 233


hip, 258, 265 polje bitova, 145
segment kda, 220 size_t, 123
segment podataka, 221 struktura (struct), 140
stek, 221 typedef, 147
sekvenciona taka, 116 unija (union), 140, 144
semantika analiza, 187 void*, 235
semantiki analizator, 87 Tjuringova maina, 62
semantika programskog jezika, 80, tok, 277
82 token, 81
378 B. Reenja zadataka

tranzistor, 17

ulazno-izlazni ureaji, 16, 23, 26


unija, 144
Unix (operativni sistem), 18, 91
upozorenje prevodioca, 95, 210
UR maina, 62, 64

5)
vakuumska cev, 17
vektorska grafika, 55

01
Z3 (raunar), 14
zapis

(2
fiksni zarez, 47
neoznaeni brojevi, 41
oznaena apsolutna vrednost, 45

je
oznaeni brojevi, 45
an
pokretni zarez, 47, 107
potpuni komplement, 45
realni brojevi, 46
d

slika, 55
iz

tekst, 48
zvuk, 56
o

akardov razboj, 13
sk

ivotni vek promenljive, 83, 192, 200


n
ro
kt
le
E

You might also like