Professional Documents
Culture Documents
p1 PDF
p1 PDF
ek
ko
tro
ns
izd
20
15
an
je
(
El
ek
ko
tro
ns
izd
20
15
an
je
(
Predrag Janii
an
je
(
20
15
Filip Mari
PROGRAMIRANJE 1
El
ek
tro
ns
ko
izd
Beograd
2015.
Autori:
dr Filip Mari, docent na Matematikom fakultetu u Beogradu
dr Predrag Janii, redovni profesor na Matematikom fakultetu u Beogradu
PROGRAMIRANJE 1
20
15
El
ek
tro
ns
ko
izd
an
je
(
Recenzenti:
dr Gordana Pavlovi-Laeti, redovni profesor na Matematikom fakultetu u
Beogradu
dr Miodrag ivkovi, redovni profesor na Matematikom fakultetu u Beogradu
dr Dragan Uroevi, nauni savetnik na Matematikom institutu SANU
ISBN 978-86-7589-100-0
c
2015.
Filip Mari i Predrag Janii
Ovo delo zatieno je licencom Creative Commons CC BY-NC-ND 4.0 (AttributionNonCommercial-NoDerivatives 4.0 International License). Detalji licence mogu se
videti na veb-adresi http://creativecommons.org/licenses/by-nc-nd/4.0/. Dozvoljeno 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.
)
20
15
an
je
(
Sadraj
Sadraj
izd
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
12
16
21
22
26
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
38
38
40
48
54
3 Algoritmi i izraunljivost
3.1 Formalizacije pojma algoritma . . . . . . . . .
3.2 er-Tjuringova teza . . . . . . . . . . . . . . .
3.3 ur maine . . . . . . . . . . . . . . . . . . . . .
3.4 Enumeracija urm programa . . . . . . . . . . .
3.5 Neizraunljivost i neodluivost . . . . . . . . .
3.6 Vremenska i prostorna sloenost izraunavanja
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
61
61
63
64
69
71
73
programski jezici
Kratki pregled istorije programskih jezika . . . . . . . . . . . .
Klasifikacije programskih jezika . . . . . . . . . . . . . . . . . .
Leksika, sintaksa, semantika programskih jezika . . . . . . . . .
78
79
80
80
El
ek
tro
ns
ko
10
4 Vii
4.1
4.2
4.3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4.4
83
90
91
91
93
nad
. . .
. . .
. . .
. . .
. . .
. . .
. . .
njima
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
101
101
104
109
112
126
132
140
an
je
(
20
15
II Jezik C
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
151
151
152
152
156
8 Funkcije
8.1 Primeri definisanja i pozivanja funkcije . . . .
8.2 Deklaracija i definicija funkcije . . . . . . . .
8.3 Parametri funkcije . . . . . . . . . . . . . . .
8.4 Prenos argumenata . . . . . . . . . . . . . . .
8.5 Konverzije tipova argumenata funkcije . . . .
8.6 Povratna vrednost funkcije . . . . . . . . . .
8.7 Nizovi i funkcije . . . . . . . . . . . . . . . . .
8.8 Korisniki definisani tipovi i funkcije . . . . .
8.9 Rekurzija . . . . . . . . . . . . . . . . . . . .
8.10 Funkcije sa promenljivim brojem argumenata
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
167
167
169
170
171
173
174
174
177
178
179
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
El
ek
tro
ns
ko
izd
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
233
233
238
241
244
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
El
ek
tro
ns
ko
izd
Indeks
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
246
250
253
254
258
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
269
269
272
274
275
276
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
277
277
285
286
294
an
je
(
.
.
.
.
.
B Reenja zadataka
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Pokazivai i niske . . . . . . . . . . . . . .
Nizovi pokazivaa i viedimenzioni nizovi .
Pokazivai i strukture . . . . . . . . . . .
Pokazivai na funkcije . . . . . . . . . . .
Dinamika alokacija memorije . . . . . . .
20
15
10.5
10.6
10.7
10.8
10.9
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
300
301
373
)
20
15
an
je
(
Predgovor
El
ek
tro
ns
ko
izd
ko
izd
an
je
(
20
15
El
ek
tro
ns
Autori
)
20
15
an
je
(
Deo I
El
ek
tro
ns
ko
izd
10
20
15
Glava
an
je
(
El
ek
tro
ns
ko
izd
Raunarstvo i informatika predstavljaju jednu od najatraktivnijih i najvanijih oblasti dananjice. ivot u savremenom drutvu ne moe se zamisliti bez
korienja razliitih raunarskih sistema: stonih i prenosnih raunara, tableta,
pametnih telefona, ali i raunara integrisanih u razliite maine (automobile,
avione, industrijske maine, itd). Definicija raunarskog sistema je prilino
iroka. Moe se rei da se danas pod digitalnim raunarskim sistemom (raunarom) podrazumeva maina koja moe da se programira da izvrava razliite
zadatke svoenjem na elementarne operacije nad brojevima. Brojevi se, u savremenim raunarima, zapisuju u binarnom sistemu, kao nizovi nula i jedinica
tj. binarnih cifara, tj. bitova (engl. bit, od binary digit). Koristei 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
Raunarstvo se bavi izuavanjem raunara, ali i optije, izuavanjem teorije i prakse procesa raunanja i primene raunara u raznim oblastima nauke,
tehnike i svakodnevnog ivota2 .
Raunari u dananjem smislu nastali su polovinom XX veka, ali koreni raunarstva su mnogo stariji od prvih raunara. Vekovima su ljudi stvarali mehanike i elektromehanike naprave koje su mogle da reavaju neke numerike
zadatke. Dananji raunari su programabilni, tj. mogu da se isprogramiraju da
vre razliite zadatke. Stoga je oblast programiranja, kojom se ova knjiga bavi,
jedna od najznaajnijih oblasti raunarstva. Za funkcionisanje modernih raunara neophodni su i hardver i softver. Hardver (tehniki sistem raunara) ine
opipljive, fizike komponente raunara: procesor, memorija, matina ploa,
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
hard disk, DVD ureaj, itd. Softver (programski sistem raunara) ine raunarski programi i pratei podaci koji odreuju izraunavanja koja vri raunar.
Raunarstvo je danas veoma iroka i dobro utemeljena nauna disciplina sa
mnotvom podoblasti.
1.1
El
ek
tro
ns
ko
izd
an
je
(
20
15
Programiranje u savremenom smislu postalo je praktino mogue tek krajem Drugog svetskog rata, ali je njegova istorija znatno starija. Prvi precizni
postupci i sprave za reavanje matematikih problema postojali su jo u vreme
antikih civilizacija. Na primer, kao pomo pri izvoenju osnovnih matematikih operacija koriene su raunaljke zvane abakus. U IX veku persijski matematiar Al Horezmi 3 precizno je opisao postupke raunanja u indo-arapskom
dekadnom brojevnom sistemu (koji i danas predstavlja najkorieniji brojevni
sistem). U XIII veku Leonardo Fibonai 4 doneo je ovaj nain zapisivanja brojeva iz Azije u Evropu i to je bio jedan od kljunih preduslova za razvoj matematike i tehnikih disciplina tokom renesanse. Otkrie logaritma omoguilo
je svoenje mnoenja na sabiranje, dodatno olakano raznovrsnim analognih
spravama (npr. klizni lenjir iber )5 . Prve mehanike sprave koje su mogle
da potpuno automatski izvode aritmetike operacije i pomau u reavanju matematikih zadataka su napravljene u XVII veku. Blez Paskal 6 konstruisao 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 operacije
(sabiranje, oduzimanje, mnoenje i deljenje) nad celim brojevima. Ova maina bila je zasnovana na dekadnom brojevnom sistemu, ali Lajbnic je prvi
predlagao i korienje binarnog brojevnog sistema u raunanju.
13
tro
ns
ko
izd
an
je
(
20
15
El
ek
20
15
14
an
je
(
El
ek
tro
ns
ko
izd
obrada rezultata iz 1890. godine trajala vie od 10 godina, to je bilo neprihvatljivo mnogo. Holerit je sproveo ideju da se podaci prilikom popisa zapisuju na
mainski itljivom medijumu (na buenim karticama), a da se kasnije obrauju
njegovom mainom. Koristei ovaj pristup obrada rezultata popisa uspeno
je zavrena za godinu dana. Od Holeritove male kompanije kasnije je 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
ne i uslovni skokovi), te se esto smatra i prvim realizovanim programabilnim
raunarom14 . Cuzeove maine tokom Drugog svetskog rata naile su samo na
ograniene primene. Cuzeova kompanija proizvela je oko 250 razliitih tipova
raunara do kraja ezdesetih godina, kada je postala deo kompanije Simens
(nem. Siemens).
U okviru saradnje kompanije IBM i univerziteta Harvard, tim Hauarda
Aikena 15 zavrio je 1944. godine mainu Harvard Mark I . Ova maina itala je
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
elektromehanikih prekidaa) 72 broja od po 23 dekadne cifre. Sabiranje i
oduzimanje dva broja trajalo je treinu, mnoenje est, a deljenje petnaest
sekundi.
Elektronski raunari. Elektronski raunari koriste se od kraja 1930-ih do
danas.
Jedan od prvih elektronskih raunara ABC (specijalne namene reavanje
13 Konrad
an
je
(
20
15
15
izd
El
ek
tro
ns
ko
sistema linearnih jednaina) napravili su 1939. godine Atanasov 16 i Beri 17 . Maina je prva koristila binarni brojevni sistem i elektrine kondenzatore (engl.
capacitor) za skladitenje bitova sistem koji se u svojim savremenim varijantama koristi i danas u okviru tzv. DRAM memorije. Maina nije bila
programabilna.
Krajem Drugog svetskog rada, u Engleskoj, u Bleli parku (engl. Bletchley Park) u kojem je radio i Alan Tjuring 18 , konstruisan je raunar Kolos
(engl. Colossus) namenjen deifrovanju nemakih poruka. Raunar je omoguio razbijanje nemake ifre zasnovane na maini Enigma, zahvaljujui emu su
saveznici bili u stanju da prate komunikaciju nemake podmornike flote, to
je znaajno uticalo na ishod Drugog svetskog rata.
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 konstruisan je prvi elektronski raunar opte namene ENIAC (Electronic Numerical Integrator and Calculator). Imao je 1700 vakuumskih cevi, duinu
30m i masu 30t. Raunske operacije izvravao je hiljadu puta bre od elektromehanikih 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
16
1.2
El
ek
tro
ns
ko
izd
an
je
(
20
15
17
20
15
turi.
an
je
(
tro
ns
ko
izd
I generacija raunara (od kraja 1930-ih do kraja 1950-ih) koristila je vakuumske cevi kao logika kola i magnetne doboe (a delom i magnetne trake)
za memoriju. Za programiranje su korieni mainski jezik i asembler a glavne
primene su bile vojne i naune. Raunari su uglavnom bili unikatni (tj. za
veinu nije postojala serijska proizvodnja). Prvi realizovani raunari fon Nojmanove arhitekture bili su Manesterska Beba (engl. Manchester Baby)
eksperimentalna maina, razvijena 1949. na Univerzitetu u Manesteru, na
kojoj je testirana tehnologija vakuumskih cevi i njen naslednik Manesterski
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
Pensilvaniji. Tvorci raunara EDVAC, poeli su 1951. godine proizvodnju prvog komercijalnog raunara UNIVAC UNIVersal Automatic Computer koji
je prodat u, za to doba neverovatnih, 46 primeraka.
El
ek
18
20
15
Prvi raunar koji je koristio ovu tehnologiju bio je IBM 360, napravljen 1964. godine.
an
je
(
El
ek
tro
ns
ko
izd
Nova tehnologija omoguila je poslovnu primenu raunara u mnogim oblastima. U ovoj eri dominirali su mejnfrejm (engl. mainframe)mejnfrejm raunari raunari koji su bili izrazito moni za to doba, ija se brzina merila
milionima 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 obrade velike koliine podataka te su korieni od strane vlada
i velikih korporacija za popise, statistike obrade i slino. Kod raunara ove
generacije uveden je sistem deljenja vremena (engl. timesharing) koji dragoceno procesorsko vreme raspodeljuje i daje na uslugu razliitim korisnicima
koji istovremeno rade na raunaru i komuniciraju sa njim putem specijalizovanih terminala. U ovo vreme uvedeni su prvi standardi za jezike vieg nivoa
(npr. ANSI FORTRAN). Korieni su razliiti operativni sistemi, uglavnom razvijeni u okviru kompanije IBM. Sa udelom od 90%, kompanija IBM je imala
apsolutnu dominaciju na tritu ovih raunara.
Pored mejnfrejm raunara, u ovom periodu iroko su korieni i mini raunari (engl. minicomputers) koji se mogu smatrati prvim oblikom linih (personalnih) 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
kompanija DEC Digital Equipment Corporation sa svojim serijama raunara
poput PDP-8 i VAX. Za ove raunare, obino se vezuje operativni sistem Unix
i programski jezik C razvijeni u Belovim laboratorijama (engl. Bell Laboratories), a esto i hakerska 22 kultura nastala na univerzitetu MIT (engl. Massachusetts Institute of Technology).
IV generacija raunara (od ranih 1970-ih) zasnovana je na visoko integrisanim kolima kod kojih je na hiljade kola smeeno na jedan silikonski ip.
22 Termin haker se obino koristi za osobe koje neovlaeno pristupaju raunarskim sistemima, 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
20
15
Slika 1.6: Mejnfrejm raunar: IBM 7094. Mini raunar: DEC PDP 7
El
ek
tro
ns
ko
izd
an
je
(
an
je
(
20
15
20
Slika 1.7: Prvi mikroprocesor: Intel 4004. Naslovna strana asopisa Popular
electronics sa Altair 8800. Commodore 64. IBM PC 5150.
El
ek
tro
ns
ko
izd
raunara koji nisu proizvedeni u okviru kompanije IBM, ali koji su kompatibilni sa IBM PC raunarima. PC arhitektura vremenom je postala standard
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
proizvoae. I naslednici originalnog IBM PC raunara (IBM PC/XT, IBM
PC/AT, . . . ) bili su zasnovani na Intelovim mikroprocesorima, pre svega na
x86 seriji (Intel 80286, 80386, 80486) i zatim na seriji Intel Pentium. Operativni sistem koji se tradicionalno vezuju uz PC raunare dolaze iz kompanije
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
tritu (pre svega u SAD) je serija raunara Macintosh kompanije Apple. Macintosh, koji se pojavio 1984., je prvi komercijalni kuni raunar sa grafikim
korisnikim interfejsom i miem. Operativni sistem koji se i danas koristi na
Apple raunarima je Mac OS.
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 godina. Danas se veliki obim poslovanja izvrava u internet okruenju, a domen
korienja raunara je veoma irok. Dolo je do svojevrsne informatike revolucije koja je promenila savremeno drutvo i svakodnevni ivot. Na primer,
tokom prve decenije XXI veka dolo je do pojave drutvenih mrea (engl. social
networks) koje postepeno preuzimaju ulogu osnovnog medijuma za komunikaciju.
Tritem dananjih raunara dominiraju raunari zasnovani na PC arhi-
an
je
(
20
15
21
izd
Slika 1.8: Stoni raunar. Prenosni raunar: IBM ThinkPad. Tablet: Apple
Ipad 2. Pametni telefon: Samsung Galaxy S2.
El
ek
1.3
tro
ns
ko
tekturi i Apple Mac raunari. Pored stonih (engl. desktop) raunara popularni su i prenosni (engl. notebook ili laptop) raunari. U najnovije vreme,
javlja se trend tehnoloke konvergencije koja podrazumeva stapanje razliitih
ureaja 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.
Pored linih raunara u IV generaciji se i dalje koriste mejnfrejm raunari
(na primer, IBM Z serija) i superraunari (zasnovani na hiljadama procesora).
Na primer, kineski superraunar Tianhe-2 radi brzinom od preko 30 petaflopsa
(dok proseni lini raunar radi brzinom reda 10 gigaflopsa).23
Savremeno raunarstvo ima mnogo podoblasti, kako praktinih, tako i teorijskih. Zbog njihove isprepletenosti nije jednostavno sve te oblasti sistematizovati i klasifikovati. U nastavku je dat spisak nekih od oblasti savremenog
raunarstva (u skladu sa klasifikacijom amerike asocijacije ACM Association for Computing Machinery, jedne od najveih i najuticajnijih raunarskih
zajednica):
Algoritmika (procesi izraunavanja i njihova sloenost);
23 Flops je mera raunarskih performansi, posebno pogodna za izraunavanja nad brojevima 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
20
15
Prevoenje programskih jezika (efikasno prevoenje viih programskih jezika, obino na mainski jezik);
an
je
(
izd
ko
tro
ns
El
ek
Teorijsko raunarstvo (teorijske osnove izraunavanja, raunarska matematika, verifikacija softvera, itd).
1.4
Hardver ine opipljive, fizike komponente raunara. Iako je u osnovi savremenih raunarskih sistema i dalje fon Nojmanova maina (procesor i memorija), 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 znaajnija je podela na osnovu koje raunar ine:
procesor tj. centralna procesorska jedinica (engl. Central Processing Unit,
CPU), koja obrauje podatke;
23
20
15
izd
an
je
(
El
ek
tro
ns
ko
Procesori. Procesor je jedna od dve centralne komponente svakog raunarskog 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) koja
upravlja njegovim radom i aritmetiko-logike jedinice (engl. Arithmetic Logic
Unit) koja je zaduena za izvoenje aritmetikih operacija (sabiranje, oduzimanje, mnoenje, poreenje, . . . ) i logikih operacija (konjunkcija, negacija,
. . . ) nad brojevima. Procesor sadri i odreeni, manji broj, registara koji privremeno mogu da uvaju podatke. Registri su obino fiksirane irine (8 bitova,
16 bitova, 32 bita, 64 bita). Komunikacija sa memorijom se ranije vrila iskljuivo preko specijalizovanog registra koji se nazivao akumulator. Aritmetiko
logika jedinica sprovodi operacije nad podacima koji su smeteni u registrima
i rezultate ponovo smeta u registre. Kontrolna jedinica procesora ita instrukciju po instrukciju programa zapisanog u memoriji i na osnovu njih odreuje
sledeu akciju sistema (na primer, izvri prenos podataka iz procesora 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. Million Instructions
Per Second, MIPS) tj. poto su operacije u pokretnom zarezu najzahtevnije, u
broju operacija u pokretnom zarezu u sekundi (engl. FLoating 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 istovremeno izvravaju
instrukcije i time omoguuju tzv. paralelno izvravanje.
24
Aritmetiko
logika jedinica
Ulazni
ureaji
Izlazni
ureaji
an
je
(
Memorija
20
15
magistrala
registri
izd
El
ek
tro
ns
ko
Memorijska hijerarhija. Druga centralna komponenta fon Nojmanove arhitekture je glavna memorija u koju se skladite podaci i programi. Memorija je
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 i memorija sa slobodnim pristupom (engl. random access memory, RAM).
Osnovni parametri memorija su kapacitet (danas obino meren gigabajtima
(GB)), vreme pristupa koje izraava vreme potrebno da se memorija pripremi
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).
U savremenim raunarskim sistemima, uz glavnu memoriju uspostavlja se
itava hijerarhija memorija koje slue da unaprede funkcionisanje sistema. Memorije neposredno vezane za procesor koje se koriste iskljuivo dok je raunar
ukljuen nazivaju se unutranje memorije, dok se memorije koje se koriste 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 unutranjih), ve
se pre upotrebe svi podaci prebacuju iz spoljnih u unutranju memoriju.
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
pod napajanjem
Ke memorija
USB diskovi
hard diskovi
bez napajanja
ROM/BIOS
20
15
RAM
an
je
(
CPU
registri
izd
El
ek
tro
ns
ko
26
an
je
(
20
15
ko
izd
El
ek
tro
ns
1.5
Softver ine raunarski programi i pratei podaci koji odreuju izraunavanja 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 1950ih nastali su prvi jezici vieg nivoa i oni su drastino olakali programiranje.
27
1.5.1
20
15
tro
ns
ko
izd
an
je
(
El
ek
28
20
15
s := 1, i := 0
dok je i < n radi sledee:
s := sx, i := i+1
1.5.2
ko
izd
an
je
(
Mainski programi
El
ek
tro
ns
29
an
je
(
20
15
izd
tro
ns
ko
El
ek
30
20
15
izd
an
je
(
Instrukcija mov oznaava operaciju prenosa podataka i ima dva parametra prvi odreuje gde se podaci prenose, a drugi koji odreuje koji se
podaci prenose. Parametar moe biti ime registra (to oznaava da se
pristupa podacima u odreenom registru), broj u zagradama (to oznaava da se pristupa podacima u memoriji i to na adresi odreenoj brojem
u zagradama) ili samo broj (to oznaava da je podatak ba taj navedeni
broj). Na primer, instrukcija mov ax bx oznaava da se sadraj registra bx prepisuje u registar ax, instrukcija mov ax, [10] oznaava da
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 instrukcija oznaava mov [10], ax da se sadraj registra ax upisuje u memoriju
na adresu 10.
ax, [10]
bx, 2
ax, bx
bx, 3
ax, bx
[11], ax
El
ek
mov
mov
mul
mov
add
mov
tro
ns
ko
31
broj na adresi 11, dok rezultat treba smestiti na adresu 12. Program (niz
instrukcija) kojima moe da se odredi maksimum je sledei:
an
je
(
20
15
El
ek
tro
ns
ko
izd
32
Ilustrujmo izvravanje ovog programa na izraunavanju vrednosti 32 . Inicijalna 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:
ax: ?
bx: ?
cx: ?
20
15
10: 3
11: 2
12: ?
Nakon izvravanja prve dve instrukcije (mov ax, 1 i mov bx, 0), postavlja
se vrednost registara ax i bx i prelazi se u sledeu konfiguraciju:
ax: 1
bx: 0
cx: ?
an
je
(
10: 3
11: 2
12: ?
Sledea instrukcija (mov cx, [11]) kopira vrednost 2 sa adrese 11 u registar cx:
ax: 1
bx: 0
cx: 2
izd
10: 3
11: 2
12: ?
tro
ns
ko
Vri se poreenje sa registrom bx (cmp bx, cx) i kako uslov skoka (jge kraj)
nije ispunjen (vrednost 0 u bx nije vea ili jednaka od vrednosti 2 u cx), nastavlja se dalje. Nakon kopiranja vrednosti 3 sa adrese 10 u registar cx (instrukcijom mov cx, [10]), vri se mnoenje vrednosti u registrima ax i cx
(instrukcijom mul ax, cx) i dolazi se u sledeu konfiguraciju:
ax: 3
bx: 0
cx: 3
El
ek
10: 3
11: 2
12: ?
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
registru bx uveava za 1.
10: 3
11: 2
12: ?
ax: 3
bx: 1
cx: 1
33
10: 3
11: 2
12: ?
ax: 9
bx: 2
cx: 1
ax: 9
bx: 2
cx: 1
an
je
(
10: 3
11: 2
12: 9
20
15
El
ek
tro
ns
ko
izd
Mainski jezik. Fon Nojmanova arhitektura podrazumeva da se i sam program (niz instrukcija) nalazi u glavnoj memoriji prilikom njegovog izvravanja.
Potrebno je svaki program (poput tri navedena) predstaviti nizom nula i jedinica, na nain razumljiv procesoru na mainskim jeziku. Na primer,
mogue je da su binarni kdovi za instrukcije uvedeni na sledei nain:
mov 001
add 010
mul 011
cmp 100
jge 101
jmp 110
Takoe, poto neke instrukcije primaju podatke razliite vrste (neposredno
navedeni brojevi, registri, apsolutne memorijske adrese), uvedeni su posebni
kdovi za svaki od razliitih vidova adresiranja. Na primer:
neposredno 00
registarsko 01
apsolutno
10
Pretpostavimo da registar ax ima oznaku 00, registar bx ima oznaku 01,
a registar cx oznaku 10. Pretpostavimo i da su sve adrese osmobitne. Pod
navedenim 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
predstavlja 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
001 01 00 01 00000010
011 00 01
34
001 01 00 01 00000011
010 00 01
001 10 01 00010001 00
// mov bx, 3
// add ax, bx
// mov [11], ax
1.5.3
an
je
(
20
15
Izmeu prikazanog asemblerskog i mainskog programa postoji veoma direktna 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
jednu instrukciju i izvrava akciju zadatu tom instrukcijom. Kod realnih procesora, broj instrukcija i naini adresiranja su mnogo bogatiji a prilikom pisanja
programa potrebno je uzeti u obzir mnoge aspekte na koje se u navedenim
jednostavnim primerima nije obraala panja. Ipak, mainske i asemblerske
instrukcije stvarnih procesora veoma su sline navedenim hipotetikim instrukcijama.
El
ek
tro
ns
ko
izd
35
El
ek
tro
ns
ko
izd
an
je
(
20
15
36
Pitanja za vebu
Pitanje 1.1. Nabrojati osnovne periode u razvoju raunara i navesti njihove
osnovne karakteristike i predstavnike.
20
15
an
je
(
Pitanje 1.4. Kakva je veza izmeu tkakih razboja i raunara s poetka XIX
veka?
Pitanje 1.5. Koji je znaaj arlsa Bebida za razvoj raunarstva i programiranja? U kom veku je on dizajnirao svoje raunske maine? Kako se one
zovu i koja od njih je trebalo da bude programabilna? Ko se smatra prvim
programerom?
izd
ko
tro
ns
El
ek
Pitanje 1.9. Koje su osnovne komponente raunara fon Nojmanove arhitekture? ta se skladiti u memoriju raunara fon Nojmanove arhitekture? Gde se
vri obrada podataka u okviru raunara fon Nojmanove arhitekture? Od kada
su raunari zasnovani na fon Nojmanovoj arhitekturi?
Pitanje 1.10. ta su to raunari sa skladitenim programom? ta je to hardver a ta softver?
Pitanje 1.11. ta su procesorske instrukcije? Navesti nekoliko primera.
37
Pitanje 1.15. Koja serija Intelovih procesora je bila dominantna u PC raunarima 1980-ih i 1990-ih godina?
20
15
an
je
(
izd
ko
Zadatak 1.1. Na opisanom asemblerskom jeziku opisati izraunavanje vrednosti izraza x := x*y + y + 3. Generisati i mainski kd za napisani program.
tro
ns
El
ek
ako je (x < 0)
y := 3*x;
inace
x := 3*y;
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?
20
15
Glava
an
je
(
Reprezentacija podataka u
raunarima
El
ek
tro
ns
ko
izd
2.1
Kontinualna priroda signala. Veina podataka koje raunari koriste nastaje 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 predstaviti 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
ko
izd
an
je
(
20
15
El
ek
tro
ns
Digitalni zapis. Osnovna tehnika koja se koristi kod digitalnog zapisa podataka je da se vrednost signala izmeri u odreenim vremenskim trenucima ili
odreenim takama prostora i da se onda na medijumu zapiu izmerene vrednosti. Ovim je svaki digitalno zapisani signal predstavljen nizom brojeva koji
se nazivaju odbirci ili semplovi (engl. sample). Svaki od brojeva predstavlja
vrednost signala u jednoj taki diskretizovanog domena. S obzirom na to da
izmerene vrednosti takoe pripadaju kontinualnoj skali, neophodno je izvriti i
diskretizaciju kodomena, odnosno dopustiti zapisivanje samo odreenog broja
nivoa razliitih vrednosti.
Digitalni zapis predstavlja diskretnu aproksimaciju polaznog signala. Vano pitanje je koliko esto je potrebno vriti merenje da bi se polazni kontinualni 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
visokog kvaliteta zapisa potrebno imati medijume visokog kvaliteta, kvalitet
reprodukcije digitalnog zapisa ne zavisi od toga kakav je kvalitet medija na
kome su podaci zapisani, sve dok je medijum dovoljnog kvaliteta da se zapisani
brojevi 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 zapis
20
15
40
an
je
(
El
ek
tro
ns
ko
izd
brojeva koji predstavljaju vrednosti boja u takama digitalno zapisane fotografije, injenica da papir uti ne bi predstavljala problem dok god se brojevi
mogu razaznati.
Digitalni zapis omoguava kreiranje apsolutno identinih kopija to dalje
omoguava prenos podataka na daljinu. Na primer, ukoliko izvrimo fotokopiranje fotografije, napravljena fotokopija je daleko loijeg kvaliteta od originala.
Meutim, ukoliko umnoimo CD na kojem su zapisani brojevi koji ine zapis neke fotografije, kvalitet slike ostaje apsolutno isti. Ukoliko bi se dva CD-a
pregledala pod mikroskopom, oni bi izgledali delimino razliito, ali to ne predstavlja problem sve dok se brojevi koji su na njima zapisani mogu razaznati.
Obrada digitalno zapisanih podataka se svodi na matematiku manipulaciju
brojevima i ne zahteva (za razliku od analognih podataka) korienje specijalizovanih maina.
Osnovni problem implementacije digitalnog zapisa predstavlja injenica da
je neophodno imati veoma razvijenu tehnologiju da bi se uopte stiglo do iole
upotrebljivog zapisa. Na primer, izuzetno je komplikovano napraviti ureaj
koji je u stanju da 40 hiljada puta izvri merenje intenziteta zvuka. Jedna
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
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 brojevima. Kako se svi podaci u raunarima reprezentuju na taj nain brojevima, neophodno je precizno definisati zapisivanje razliitih vrsta brojeva.
Osnovu digitalnih raunara, u skladu sa njihovom tehnolokom osnovom, predstavlja binarni brojevni sistem (sistem sa osnovom 2) . U raunarstvu se koriste
41
2.2.1
tro
ns
ko
izd
an
je
(
20
15
Neoznaeni brojevi
El
ek
= + 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
42
:= 0
za svako od 0 do
:= +
20
15
tro
ns
ko
:= 0
:= 1
za svako od 1 do
:=
:= +
izd
an
je
(
Za izraunavanje vrednosti nekog ( + 1)-tocifrenog zapisa drugim navedenim postupkom potrebno je sabiranja i + ( 1) + . . . + 1 = (+1)
2
mnoenja. Zaista, da bi se izraunalo potrebno je mnoenja, da bi
se izraunalo 1 1 potrebno je 1 mnoenja, itd. Meutim, ovo izraunavanje moe da se izvri i efikasnije. Ukoliko se za izraunavanje lana
iskoristi ve izraunata vrednost 1 , broj mnoenja se moe svesti na 2.
Ovaj nain izraunavanja primenjen je u sledeem postupku:
El
ek
( 1 . . . 1 0 ) = (. . . (( + 1 ) + 2 ) . . . + 1 ) + 0
Korienjem ove sheme, dolazi se do sledeeg postupka za odreivanje vrednosti broja zapisanog u nekoj brojevnoj osnovi:
:= 0
za svako od unazad do 0
:= +
Naredni primer ilustruje primenu Hornerovog postupka na zapis (9876)10 .
3
9
0 10 + 9 = 9
2
8
9 10 + 8 = 98
1
7
98 10 + 7 = 987
0
6
987 10 + 6 = 9876
43
Meurezultati dobijeni u ovom raunu direktno odgovaraju prefiksima zapisa 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 .
2
2
3 8 + 2 = 26
1
4
26 8 + 4 = 212
0
5
212 8 + 5 = 1701
3
3
08+3=3
20
15
2
26
4
212
5
1701
an
je
(
izd
El
ek
tro
ns
ko
0
1701
1701
1
1701 div 8 = 212
1701 mod 8 = 5
2
212 div 8 = 26
212 mod 8 = 4
3
26 div 8 = 3
26 mod 8 = 2
3 div 8 = 0
3 mod 8 = 3
44
212
4
26
2
3
3
20
15
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.
Ovaj algoritam i Hornerov algoritam su meusobno simetrini u smislu da
se svi meurezultati poklapaju.
heksa
4
5
6
7
binarno
0100
0101
0110
0111
heksa
8
9
A
B
izd
binarno
0000
0001
0010
0011
ko
heksa
0
1
2
3
an
je
(
heksa
C
D
E
F
binarno
1100
1101
1110
1111
tro
ns
El
ek
Zapisi fiksirane duine U raunarima se obino koristi fiksirani broj binarnih cifara (sauvanih u pojedinanim bitovima) za zapis svakog broja. Takve
zapise oznaavamo sa (. . .) , ako se koristi cifara. Ukoliko je broj cifara potrebnih za zapis broja krai od zadate duine zapisa, onda se broj proiruje
vodeim nulama. Na primer, 55 = (0011 0111)82 . Ograniavanjem broja cifara
ograniava se i raspon brojeva koje je mogue zapisati (u binarnom sistemu)
i to na raspon od 0 do 2 1. U sledeoj tabeli su dati rasponi za najee
koriene duine zapisa:
broj bitova
8
16
32
raspon
od 0 do 255
od 0 do 65535
od 0 do 4294967295
45
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.
an
je
(
20
15
izd
Potpuni komplement. Zapis u potpunom komplementu (engl. twos complement) oznaenih brojeva zadovoljava sledee uslove:
ko
tro
ns
El
ek
(1 00 . . . 00)+1
=
(11
.
.
.
11)
+
1,
zapis
broja
()
je
mogue
odrediti
tako to
2
2
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
tro
ns
2.2.3
ko
izd
an
je
(
20
15
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 potpunom komplementu. Zapis broja (100 . . . 00)2 je sam sebi komplementaran, a
poto poinje cifrom 1, uzima se da on predstavlja zapis najmanjeg zapisivog
negativnog broja, tj. broja 21 . Zahvaljujui ovoj konvenciji, u zapisu potpunog komplementa (. . .)2 mogue je zapisati brojeve od 21 do 21 1. U
sledeoj tabeli su dati rasponi za najee koriene duine zapise u potpunom
komplementu:
broj bita
raspon
8
od 128 do +127
16
od 32768 do +32767
32
od 2147483648 do +2147483647
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
biti upisane u raspoloiv broj mesta). To ne vai samo za sabiranje, ve i za oduzimanje i mnoenje. Ovo pogodno svojstvo vai i kada je jedan od argumenata
broj 21 (koji se je jedini izuzetak u optem postupku odreivanja zapisa).
Sve ovo su vani razlozi za korienje potpunog komplementa u raunarima.
El
ek
47
El
ek
tro
ns
ko
izd
an
je
(
20
15
vrednost
0,01 104
0,02 104
0,98 104
0,99 104
= 0,000001
= 0,000002
= 0,000098
= 0,000099
zapis
011
021
981
991
vrednost
0,01 103
0,02 103
0,98 103
0,99 103
= 0,00001
= 0,00002
= 0,00098
= 0,00099
...
...
...
...
...
zapis
018
028
988
998
vrednost
0,01 104
0,02 104
0,98 104
0,99 104
= 100,0
= 200, 0
= 9800,0
= 9900,0
zapis
019
029
989
999
vrednost
0,01 105
0,02 105
0,98 105
0,99 105
= 1 000,0
= 2 000,0
= 98 000,0
= 99 000,0
48
2.3
an
je
(
20
15
Raspon je prilino irok (od 0, 000001 do 99 000, 0 tj. od oko 106 do oko
105 ) 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
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
neki od ova dva broja). Ovo nije preveliki problem, jer nam obino kod velikih
brojeva preciznost nije toliko bitna koliko kod malih (matematiki gledano,
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
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
cifre) i on je skoro potpuno odreeno irinom mantise.
Zapis teksta
El
ek
tro
ns
ko
izd
Meunarodna organizacija za standardizaciju, ISO (engl. International Standard Organization) definie tekst (ili dokument) kao informaciju namenjenu
ljudskom sporazumevanju koja moe biti prikazana u dvodimenzionalnom obliku.
Tekst se sastoji od grafikih elemenata kao to su karakteri, geometrijski ili fotografski elementi ili njihove kombinacije, koji ine sadraj dokumenta. Iako
se tekst obino zamilja kao dvodimenzioni objekat, u raunarima se tekst predstavlja kao jednodimenzioni (linearni) niz karaktera koji pripadaju odreenom
unapred fiksiranom skupu karaktera. U zapisu teksta, koriste se specijalni
karakteri koji oznaavaju prelazak u novi red, tabulator, kraj teksta i slino.
Osnovna ideja koja omoguava zapis teksta u raunarima je da se svakom
karakteru pridrui odreen (neoznaeni) ceo broj (a koji se interno u raunarima zapisuje binarno) i to na unapred dogovoreni nain. Ovi brojevi se
nazivaju kdovima karaktera (engl. character codes). Tehnika ogranienja ranih raunara kao i neravnomeran razvoj raunarstva izmeu razliitih zemalja,
doveli su do toga da postoji vie razliitih standardnih tabela koje dodeljuju
numerike kdove karakterima. U zavisnosti od broja bitova potrebnih za kodiranje karaktera, razlikuju se 7-bitni kdovi, 8-bitni kdovi, 16-bitni kdovi,
32-bitni kdovi, kao i kodiranja promenljive duine koja razliitim karakterima 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
pojedinih karaktera nazivaju se glifovi (engl. glyph), a skupovi glifova nazivaju
se fontovi (engl. font). Korespondencija izmeu karaktera i glifova ne mora biti
jednoznana. Naime, softver koji prikazuje tekst moe vie karaktera predstaviti 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
49
20
15
sadre glifove za odreene karaktere i u tom sluaju se tekst ne prikazuje na eljeni 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
u evropskim jezicima, dok je za ispravan prikaz, na primer, kineskih karaktera,
potrebno instalirati dodatne fontove. Specijalnim karakterima se najee ne
pridruuju zasebni grafiki likovi.
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 potrebno predstaviti sledee karaktere:
an
je
(
Interpunkcijske znake: , . : ; + * - _ ( ) [ ] { } . . .
izd
ko
EBCDIC - IBM-ov standard, korien uglavnom na mainframe raunarima, pogodan za buene kartice.
tro
ns
El
ek
50
NUL
STX
SOT
ETX
EOT
ENQ
ACK
BEL
BS
HT
LF
VT
FF
CR
SO
SI
DLE
DC1
DC2
DC3
DC4
NAK
SYN
ETB
CAN
EM
SUB
ESC
FS
GS
RS
US
"
&
'
2
0
<
>
DEL
20
15
an
je
(
ko
izd
Razliiti operativni sistemi predviaju razliito kodiranje oznake za prelazak u novi red. Tako operativni sistem Windows podrazumeva da se prelazak
u novi red kodira sa dva kontrolna karaktera i to (carriage return) predstavljen kdom (0)16 i (line feed) predstavljen kodom (0)16 , operativni
sistem Unix i njegovi derivati (pre svega Linux) podrazumevaju da se koristi
samo karakter , dok MacOS podrazumeva korienje samo karaktera .
El
ek
tro
ns
Nacionalne varijante ASCII tablice i ISO 646. Tokom 1980-ih, Jugoslovenski zavod za standarde definisao je standard YU-ASCII (YUSCII, JUS
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
i odreene interpunkcijske znakove) dodeljeni naim dijakriticima:
YUSCII ASCII
kd
YUSCII ASCII
kd
@
(40)16
(60)16
[
(5)16
{
(7)16
\
(5)16
|
(7)16
]
(5)16
}
(7)16
^
(5)16
~
(7)16
51
0
8
9
tro
ns
ko
izd
an
je
(
20
15
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 standardizacija ovako kreiranih kdnih strana i to od strane ISO
(International Standard Organization) i od strane znaajnih korporacija, pre
svega kompanije Microsoft.
ISO je definisao familiju 8-bitnih kdnih strana koje nose zajedniku oznaku
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):
ISO-8859-1 Latin 1
veina zapadno-evropskih jezika
ISO-8859-2 Latin 2
centralno i istono-evropski jezici
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
ISO-8859-6 Latin/Arabic
najee korieni arapski
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
kao Windows-125x (ove strane se nekada nazivaju i ANSI ). Za srpski jezik,
znaajne su kdne strane:
Windows-1250 centralno i istono-evropski jezici
Windows-1251 irilica veine slovenskih jezika
Windows-1252 (esto se neispravno naziva i ANSI) zapadnoevropski jezici
1
NBSP
SHY
El
ek
A
C
52
NBSP
SHY
izd
an
je
(
0
8
20
15
NBSP
SHY
El
ek
tro
ns
ko
0
8
od jednog bajta. Kasnih 1980-ih, dve velike organizacije zapoele su standardizaciju 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 proizvodnjom viejezikog softvera (Xerox Parc, Apple, Sun Microsystems, Microsoft,
. . . ).
ISO 10646 zamiljen je kao etvorobajtni standard. Prvih 65536 karaktera 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:
53
20
15
an
je
(
El
ek
tro
ns
ko
izd
ASCII printable
Latin-1
Latin extended A (osnovno proirenje latinice,
sadri sve nae dijakritike)
Latin extended B
grki alfabet
irilica
specijalni karakteri
CJK (Chinese-Japanese-Korean) simboli
54
UCS-2. ISO definie UCS-2 standard koji svaki Unicode karakter osnovne
viejezike ravni jednostavno zapisuje sa odgovarajua dva bajta.
izd
ko
raspon
0000-007F
0080-07FF
0800-FFFF
an
je
(
20
15
UTF-8. Latinini tekstovi kodirani korienjem UCS-2 standarda sadre veliki broj nula. Ne samo to te nule zauzimaju dosta prostora, ve zbog njih
softver koji je razvijen za rad sa dokumentima u ASCII formatu ne moe da
radi bez izmena nad dokumentima kodiranim korienjem UCS-2 standarda.
Unicode Transformation Format (UTF-8) je algoritam koji svakom dvobajtnom Unicode karakteru dodeljuje odreeni niz bajtova ija duina varira od 1
do najvie 3. UTF je ASCII kompatibilan, to znai da se ASCII karakteri zapisuju pomou jednog bajta, na standardni nain. Konverzija se vri na osnovu
sledeih pravila:
El
ek
tro
ns
2.4
55
2.4.1
Zapis slika
izd
an
je
(
20
15
Slike se u raunaru zapisuju koristei vektorski zapis, rasterski zapis ili kombinovani zapis..
U vektorskom obliku, zapis slike sastoji se od konanog broja geometrijskih
figura (taaka, linija, krivih, poligona), pri emu se svaka figura predstavlja
koncizno svojim koordinatama ili jednainom u Dekartovoj ravni. Slike 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
da se objekti mogu nezavisno jedan od drugoga pomerati, menjati, dodavati i
uklanjati.
tro
ns
ko
U rasterskom zapisu, slika je predstavljena pravougaonom matricom komponenti 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.
El
ek
56
an
je
(
20
15
(monitori, projektori, . . . ). Ukoliko se za informaciju o svakoj komponenti koristi po jedan bajt, ukupan broj bajtova za zapis informacije o boji je 3, te je
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
kombinovanjem svetlosti tri osnovne komponente, u tampi se koristi subtraktivni CMY (Cyan-Magenta-Yellow) model boje kod kojeg se boje dobijaju kombinovanjem obojenih pigmenata na belom papiru. Kako se potpuno crna teko
dobija meanjem drugih pigmenata, a i kako je njena upotreba obino daleko
najea, obino se prilikom tampanja uz CMY pigmente koristi i crni pigment
ime se dobija model CMYK.
Za obradu slika pogodni su HSL ili HSV (poznat i kao HSB) model boja.
U ovom modelu, svaka boja se reprezentuje Hue (H) komponentom (koja predstavlja ton boje), Saturation (S) komponentom (koja predstavlja zasienost
boje odnosno njenu jarkost) i Lightness (L), Value (V) ili Brightness (B)
komponentom (koja predstavlja osvetljenost).
El
ek
tro
ns
ko
izd
Formati zapisa rasterskih slika. Rasterske slike su reprezentovane matricom piksela, pri emu se za svaki piksel uva informacija o njegovoj boji.
Dimenzije ove matrice predstavljaju tzv. apsolutnu rezoluciju slike. Apsolutna
rezolucija i model boja koji se koristi odreuju broj bajtova potrebnih za zapis 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) kompresije 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).
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 merenjem
i zapisivanjem vazdunog pritiska u kratkim vremenskim intervalima. Osnovni
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 odabiranja 40KHz, tj. dovoljno je izvriti odabiranje oko 40 000 puta u sekundi.
Na primer, AudioCD standard koji se koristi prilikom snimanja obinih audio
kompakt diskova, propisuje frekvenciju odabiranja 44.1KHz. Za postizanje jo
veeg kvaliteta, neki standardi (miniDV, DVD, digital TV) propisuju odabiranje na frekvenciji 48KHz. Ukoliko se snima ili prenosi samo ljudski govor (na
primer, u mobilnoj telefoniji), frekvencije odabiranja mogu biti i znatno ma-
57
an
je
(
20
15
nje. Drugi vaan parametar digitalizacije je broj bita kojim se zapisuje svaki
pojedinani odbirak. Najee se koristi 2 bajta po odbirku (16 bita), ime se
dobija mogunost zapisa 216 = 65536 razliitih nivoa amplitude.
Da bi se dobio prostorni oseaj zvuka, primenjuje se tehnika viekanalnog
snimanja zvuka. U ovom sluaju, svaki kanal se nezavisno snima posebnim
mikrofonom i reprodukuje na posebnom zvuniku. Stereo zvuk podrazumeva
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
izdvaja za specijalno snimanje niskofrekvencijskih komponenti zvuka (tzv. bas).
Najpoznatiji takvi sistemi su 5+1 gde se koristi 5 regularnih i jedan bas kanal.
Kao i slika, nekomprimovan zvuk zauzima puno prostora. Na primer, jedan
60 2 = 10584000 10.1 . Zbog toga se koriste tehnike kompresije, od kojeg je danas najkorienija tehnika kompresije sa gubitkom MP3
(MPEG-1 Audio-Layer 3). MP3 kompresija se zasniva na tzv. psiho-akustici
koja prouava koje je komponente mogue ukloniti iz zvunog signala, a da
pritom ljudsko uho ne oseti gubitak kvaliteta signala.
izd
Pitanja i zadaci
ko
tro
ns
El
ek
Pitanje 2.9. Nabrojati bar tri kdne sheme u kojima moe da se zapie re
raunarstvo.
Pitanje 2.10. Koliko bitova koristi ASCII tabela karaktera, koliko YUSCII
tabela, koliko ISO-8859-1, a koliko osnovna UNICODE ravan? Koliko razliitih
karaktera ima u ovim tabelama?
58
Pitanje 2.11. Koja kodiranja teksta je mogue koristiti ukoliko se u okviru istog dokumenta eli zapisivanje teksta koji sadri jedan pasus na srpskom (pisan
latinicom), jedan pasus na nemakom i jedan pasus na ruskom (pisan irilicom)?
Pitanje 2.12. U emu je razlika izmeu Unicode i UTF-8 kodiranja?
20
15
Pitanje 2.13. Prilikom prikazivanja filma, neki program prikazuje titlove tipa
"raunari e biti...". Objasniti u emu je problem.
an
je
(
izd
1010101101001000111101010101011001101011101010101110001010010011.
Zapisati ovaj broj u heksadekadnom sistemu.
tro
ns
ko
(b) Registar ima sadraj 3 46189237. Zapisati ovaj sadraj u binarnom sistemu.
El
ek
Zadatak 2.4. Na Vebu se boje obino predstavljaju estocifrenim heksadekadnim 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 izgle
daju kdovi za nijanse sive boje?
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) 4 bita (b) 8 bita (c) 16 bita (d) 24 bita (e) 32 bita
59
Zadatak 2.8. Odrediti zapis narednih brojeva u binarnom potpunom komplementu irine 8 bita:
an
je
(
20
15
brojeva?
izd
ko
tro
ns
(a) ASCII
(b) Windows-1250
(c) ISO-8859-5
(d) ISO-8859-2 (e) Unicode (UCS-2) (f ) UTF-8
El
ek
Zadatak 2.15. Odrediti (heksadekadno predstavljene) kdove kojima se zapisuje tekst krui u UCS-2 i UTF-8 kodiranjima. Rezultat proveriti korienjem
HEX editora.
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, ukoliko editor nema traene mogunosti) kreirati datoteku koja sadri listu imena
10 vaih najomiljenijih filmova (pisano latinicom uz ispravno korienje dijakritika). Datoteka treba da bude kodirana kodiranjem:
(a) Windows-1250 (b) ISO-8859-2 (c) Unicode (UCS-2) (d) UTF-8
Otvoriti zatim datoteku iz nekog pregledaa Veba i prouiti ta se deava kada
se menja kodiranje koje pregleda koristi prilikom tumaenja datoteke (obino
60
El
ek
tro
ns
ko
izd
an
je
(
20
15
20
15
Glava
an
je
(
Algoritmi i izraunljivost
3.1
ko
izd
Neformalno govorei, algoritam 1 je precizan opis postupka za reavanje nekog problema u konanom broju koraka. Algoritmi postoje u svim ljudskim
delatnostima. U matematici, na primer, postoje algoritmi za sabiranje i za
mnoenje prirodnih brojeva. U raunarstvu, postoje algoritmi za odreivanje
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,
potrebno je najpre precizno definisati pojam algoritma.
El
ek
tro
ns
61
62
3. Algoritmi i izraunljivost
an
je
(
20
15
izd
El
ek
tro
ns
ko
Navedeni sistemi nastali su pre savremenih raunara i veina njih podrazumeva 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 precizne opise algoritama i mogue ih je pridruiti gore navedenoj
listi. Znaajna 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 savremeni programski jezici tee da budu to udobniji za programiranje te ukljuuju 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 )
mogu se smatrati poluformalnim nainom opisa algoritama. Oni nee biti razmatrani u teorijskom kontekstu ali e biti korieni u reenjima nekih zadataka,
radi lakeg razumevanja.
Za sistem izraunavanja koji je dovoljno bogat da moe da simulira Tjuringovu mainu i tako da izvri sva izraunavanja koja moe da izvri Tjuringova
3 Kurt
63
3. Algoritmi i izraunljivost
er-Tjuringova teza
izd
3.2
an
je
(
20
15
maina kae se da je Tjuring potpun (engl. Turing complete). Za sistem izraunavanja 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 formalizmi 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.
Zbog toga se, umesto pojmova poput, na primer, Tjuring-izraunljiva ili urmizraunljiva funkcija (i slinih) moe koristiti samo termin izraunljiva funkcija.
Svi navedeni formalizmi za izraunavanje, podrazumevaju u nekom smislu,
pojednostavljeno reeno, beskonau raspoloivu memoriju. Zbog toga savremeni 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
jeziku, moe se opisati i kao program za Tjuringovu mainu ili neki drugi ekvivalentan formalizam.
tro
ns
ko
El
ek
64
3.3
3. Algoritmi i izraunljivost
ur maine
2
2
3
3
...
...
tro
ns
1
1
ko
izd
an
je
(
20
15
El
ek
65
3. Algoritmi i izraunljivost
oznaka
()
()
(, )
(, , )
naziv
nulainstrukcija
instrukcija sledbenik
instrukcija prenosa
instrukcija skoka
efekat
0 (tj. := 0)
+ 1 (tj. := + 1)
(tj. := )
ako je = , idi na -tu;
inae idi na sledeu instrukciju
20
15
izd
an
je
(
ko
tro
ns
+ = + 1 + 1 + ... + 1
El
ek
... ...
i sledeu konfiguraciju u toku rada programa:
1
2 3 . . .
+
...
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 , , . . . , sledi niz nula, tj. da
1 2
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
=
20
15
(3)
(3, 2, 100)
(1)
(3)
(1, 1, 2)
an
je
(
1.
2.
3.
4.
5.
:= + 1
:= + 1
izd
ko
El
ek
tro
ns
...
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
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.
I u programima koji slede, uniformnosti radi, za prekid programa e se takoe koristiti skok
na instrukciju 100.
67
3. Algoritmi i izraunljivost
:= 0
0 1
>
1 1
:= + 1
=0
= ?
= ?
:= + 1
0 1
kraj
an
je
(
(3)
(1, 3, 6)
(2, 3, 8)
(3)
(1, 1, 2)
(1)
(1, 1, 100)
(1)
(1)
izd
1.
2.
3.
4.
5.
6.
7.
8.
9.
20
15
1 1
ko
() = [ ]
tro
ns
= [ ] 2 < ( + 1)2
El
ek
... ...
i sledeu konfiguraciju u toku svog rada:
1 2
3
4
5 . . .
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
na vrednosti kvadrata dva uzastopna broja 2 i ( + 1)2 , 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
imaju vrednost 2 (naravno, za uveano ). Tada je potrebno 2 postaviti na
vrednost ( + 1)2 , tj. potrebno je uveati 2 za ( + 1)2 2 = 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
1 := 1 + 1
an
je
(
1 = 2
:= + 1
2 := 2 + 1
izd
:= 0
ko
2 := 2 + 2
:= + 1
tro
ns
El
ek
(2)
(3)
(4)
(4)
1.
2.
3.
4.
20
15
= 1
2 := 1
:= 0
1 := 0
2 := 1
5.
6.
7.
8.
(1, 3, 17)
(3)
(3, 4, 9)
(1, 1, 5)
= 1 ?
1 := 1 + 1
1 = 2 ?
9.
10.
11.
12.
13.
14.
15.
16.
17.
(2)
(4)
(5)
(4)
(4)
(5)
(5, 2, 5)
(1, 1, 12)
(2, 1)
:= + 1
2 := 2 + 1
:= 0
2 := 2 + 1
2 := 2 + 1
:= + 1
= ?
1
69
3. Algoritmi i izraunljivost
3.4
20
15
izd
an
je
(
tro
ns
ko
Primer 3.5. Skupovi parnih i neparnih brojeva imaju istu kardinalnost jer je
izmeu njih mogue uspostaviti bijektivno preslikavanje () = + 1. Ono to
je na prvi pogled iznenaujue je da su oba ova skupa prebrojiva, odnosno imaju
istu kardinalnost kao i skup prirodnih brojeva, iako su njegovi pravi podskupovi
(npr. () = 2 uspostavlja bijekciju izmeu skupa prirodnih i skupa parnih
brojeva).
Lema 3.1. Ureenih parova prirodnih brojeva ima prebrojivo mnogo.
El
ek
Dokaz: Razmotrimo beskonanu tabelu elemenata koja odgovara svim ureenim parovima prirodnih brojeva. Mogue je izvriti cik-cak obilazak
ove tabele, polazei od gornjeg levog ugla kao to je to pokazano na slici
3.1.
Prilikom obilaska, svakom elementu je mogue dodeliti jedinstven redni
broj, ime je uspostavljena traena bijekcija sa skupom prirodnih brojeva.
70
3. Algoritmi i izraunljivost
1
1 1
2
2
3
6
2 3
3 4
4
7
7
...
7
..
.
..
an
je
(
20
15
izd
El
ek
tro
ns
ko
71
3. Algoritmi i izraunljivost
koje svakom prirodnom broju dodeljuje jedinstven urm program. Zbog toga,
za fiksirano dodeljivanje brojeva programima, moemo da govorimo o prvom,
drugom, treem, . . . , stotom urm programu itd.
3.5
Neizraunljivost i neodluivost
an
je
(
20
15
1. Neka su data dva konana skupa rei. Pitanje je da li je mogue nadovezati nekoliko rei prvog skupa i, nezavisno, nekoliko rei drugog
skupa tako da se dobije ista re. Na primer, za skupove {, , } i
{, , }, 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 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 rei odreuje da li traena nadovezivanja postoje.15
izd
ko
tro
ns
4. Zadatak je utvrditi da li postoji opti algoritam koji za proizvoljni zadati skup aksioma i zadato tvrenje proverava da li je tvrnje posledica
aksioma.18
El
ek
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
an
je
(
20
15
Problem ispitivanja zaustavljanja programa (halting problem). Pitanje zaustavljanja raunarskih programa je jedno od najznaajnijih pitanja
raunarstva i programiranja. esto je veoma vano da li se neki program zaustavlja za neku ulaznu vrednost, da li se zaustavlja za ijednu ulaznu vrednost
i slino. Za mnoge konkretne programe i za mnoge konkretne ulazne vrednosti, na ovo pitanje moe se odgovoriti. No, nije oigledno da li postoji opti
postupak kojim se za proizvoljni dati program i proizvoljne vrednosti ulaznih
argumenata moe proveriti da li se program zaustavlja ako se pokrene sa tim
argumentima.
Iako se problem ispitivanja zaustavljanja moe razmatrati i za programe
u savremenim programskim jezicima, u nastavku emo razmotriti formulaciju
halting problema za urm programe:
Da li postoji urm program koji na ulazu dobija drugi urm program i neki
broj i ispituje da li se program zaustavlja za ulazni parametar ?
tro
ns
ko
izd
El
ek
73
3. Algoritmi i izraunljivost
() 0 ako je ()
() ako je ()
() ako je ()
20
15
() 0 ako je ()
an
je
(
ko
izd
El
ek
tro
ns
3.6
74
3. Algoritmi i izraunljivost
20
15
an
je
(
izd
Pitanje 3.3. Da li postoji algoritam koji opisuje neku funkciju iz skupa prirodnih brojeva u skup prirodnih brojeva i koji moe da se isprogramira u programskom jeziku C i izvri na savremenom raunaru, a ne moe na Tjuringovoj
maini?
ko
tro
ns
Pitanje 3.5. U emu je kljuna razlika izmeu urm maine i bilo kog stvarnog
raunara?
Pitanje 3.6. Opisati efekat urm naredbe (, , ).
El
ek
75
3. Algoritmi i izraunljivost
20
15
Pitanje 3.13. Da li se svakom urm programu moe pridruiti jedinstven prirodan broj (razliit za svaki program)? Da li se svakom prirodnom broju moe
pridruiti jedinstven urm program (razliit za svaki broj)?
an
je
(
izd
2. Da li postoji algoritam koji za drugi zadati urm utvruje da li se zaustavlja posle 100 koraka?
ko
tro
ns
El
ek
Pitanje 3.17. Na primeru korena uporedite URM sa savremenim asemberlskim jezicima. Da li URM ima neke prednosti?
Zadatak
3.1. Napisati urm program koji izraunava funkciju (, ) = .
76
3. Algoritmi i izraunljivost
20
15
an
je
(
izd
ko
tro
ns
El
ek
(, ) =
, inae
77
3. Algoritmi i izraunljivost
20
15
El
ek
tro
ns
ko
izd
an
je
(
20
15
Glava
an
je
(
El
ek
tro
ns
ko
izd
Razvoj programskih jezika, u bliskoj je vezi sa razvojem raunara tj. sa razvojem hardvera. Programiranje u dananjem smislu nastalo je sa pojavom raunara fon Nojmanovog tipa iji se rad kontrolie programima koji su smeteni
u memoriji, zajedno sa podacima nad kojim operiu. Na prvim raunarima
tog tipa moglo je da se programira samo na mainski zavisnim programskim
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 najfinijim detaljima raunara koji se programira. Problemi ovakvog naina programiranja su viestruki. Naime, ukoliko je eleo da programira na novom raunaru, programer je morao da izui sve detalje njegove arhitekture (na primer,
skup instrukcija procesora, broj registara, organizaciju memorije). Programi
napisani za jedan raunar mogli su da se izvravaju iskljuivo na istim takvim
raunarima i prenosivost programa nije bila mogua.
Vii programski jezici namenjeni su ljudima a ne mainama i sakrivaju detalje konkretnih raunara od programera. Specijalizovani programi (tzv. jeziki
procesori, programski prevodioci, kompilatori ili interpretatori ) na osnovu specifikacije zadate na viem (apstraktnijem) nivou mogu automatski da proizvedu
mainski kd za specifian raunar na kojem se programi izvravaju. Ovim se
omoguava prenosivost programa (pri emu, da bi se program mogao izvravati
na nekom konkretnom raunaru, potrebno je da postoji procesor vieg programskog 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 programa 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.1
20
15
El
ek
tro
ns
ko
izd
an
je
(
Prvim viim programskim jezikom najee se smatra jezik FORTRAN, nastao u periodu 1953-1957 godine. Njegov glavni autor je Don Bakus1 , koji je
implementirao i prvi interpretator i prvi kompilator za ovaj jezik. Ime FORTRAN dolazi od The IBM Mathematical FORmula TRANslating System, to
ukazuje na to da je osnovna motivacija bila da se u okviru nauno-tehnikih primena omogui unoenje matematikih formula, dok je sistem taj koji bi unete
matematike formule prevodio u niz instrukcija koje raunar moe da izvrava.
Neposredno nakon pojave Fortrana, Don Mekarti2 je dizajnirao programski jezik LISP (LISt Processing), zasnovan na -raunu. LISP je uveo funkcionalno
programiranje i dugo bio najpopularniji jezik u oblasti vetake inteligencije.
Krajem 1950-ih godina, nastao je i jezik COBOL (COmmon Business-Oriented
Language), ije su osnovne primene u oblasti poslovanja. Zanimljivo je da puno
starih COBOL programa i danas uspeno radi u velikim poslovnim sistemima
kod nas i u svetu.
Rani programski jezici, nastali pod uticajem asemblerskih jezika, intenzivno
koriste skokove u programima (tzv. GOTO naredbu) to dovodi do programa
koje je teko razumeti i odravati. Kao odgovor na softversku krizu 1970-ih
godina (period kada zbog loe prakse programiranja softver nije mogao da dostigne mogunosti hardvera), nastala je praksa strukturnog programiranja u kojoj se insistira na disciplinovanom pristupu programiranju, bez nekontrolisanih
skokova i uz korienje samo malog broja naredbi za kontrolu toka programa.
Krajem 1950-ih godina zapoet je razvoj programskog jezika ALGOL 60
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.
Kao odgovor na jo jednu softversku krizu prelazi se na korienje tzv. objektno-orijentisanih jezika koji olakavaju izradu velikih programa i podelu posla
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.2
20
15
El
ek
tro
ns
ko
izd
an
je
(
4.3
81
20
15
Leksika. Osnovni leksiki elementi prirodnih jezika su rei, pri emu se razlikuje 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 predstavljeni 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 (, , , ) i
brojevne konstante. Razmotrimo naredni fragment kda u jeziku C:
if (a < 3)
x1 = 3+4*a;
El
ek
tro
ns
ko
izd
an
je
(
82
<
+
*
20
15
3
4
an
je
(
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
3 i proizvoda konstante 4 i vrednosti promenljive a.
Sintaksom programa obino se bavi deo programskog prevodioca koji se
naziva sintaksiki analizator.
El
ek
tro
ns
ko
izd
Semantika. 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 program
opisuje koje je izraunavanje opisano tim programom.
Tako se, na primer, naredbi if (a < 3) x1 = 3+4*a; jezika C moe pridruiti sledee znaenje: Ako je tekua vrednost promenljive a manja od 3,
tada promenljiva x1 treba da dobije vrednost zbira broja 3 i etvorostruke
tekue vrednosti promenljive a.
Postoje sintaksiki ispravne reenice prirodnog jezika kojima nije mogue
dodeliti ispravno znaenje, tj. za njih nije definisana semantika, na primer:
Bezbojne zelene ideje besno spavaju ili Pera je oenjeni neenja. Slino je
i sa programskim jezicima. Neki aspekti semantike korektnosti programa se
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
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.
Naredni C kd nema jasno definisano dinamiko znaenje, pa u fazi izvravanja
dolazi do greke (iako se prevoenje na mainski jezik uspeno izvrava).
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 jezike3 . U ostalim sluajevima, semantika programskog jezika se opisuje neformalno, opisima zadatim korienjem prirodnog jezika. est je sluaj da neki
3 Leksika se obino opisuje regularnim izrazima, sintaksa kontekstno slobodnim gramatikama, dok se semantika formalno zadaje ili aksiomatski (npr. u obliku Horove logike) ili u
vidu denotacione semantike ili u vidu operacione semantike.
83
4.4
20
15
aspekti semantike ostaju nedefinisani standardom jezika i preputa se implementacijama kompilatora da samostalno odrede potpunu semantiku. Tako, na
primer, programski jezik C ne definie kojim se redom vri izraunavanje vrednosti 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).
ko
izd
an
je
(
El
ek
tro
ns
84
. . . Pored ovoga, programski jezici obino nude i mogunost korienja 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),
20
15
skup operacija koje se mogu primeniti nad podacima tog tipa (na primer,
sabiranje, oduzimanje, mnoenje, . . . ),
nain reprezentacije i detalji implementacije (na primer, zapis u obliku binarnog potpunog komplementa irine 8 bita, odakle sledi opseg vrednosti
od -128 do 127).
an
je
(
izd
int x, y;
...
int z = x + y;
El
ek
tro
ns
ko
Prilikom prevoenja i kreiranja mainskog kda, prevodilac, voen tipom promenljivih x, y i z, dodeljuje odreeni broj bitova u memoriji ovim promenljivim
(tj. rezervie odreene memorijske lokacije iskljuivo za smetanje vrednosti
ovih promenljivih) i generie kd (procesorske instrukcije) kojim se tokom izvravanja programa sabiraju vrednosti smetene na dodeljenim memorijskim
lokacijama. Pri tom se vodi rauna o nainu reprezentacije koji se koristi za
zapis. S obzirom da je naglaeno da su promenljive x, y i z, celobrojnog tipa,
prevodilac e najverovatnije podrazumevati zapis u potpunom komplementu i
operacija + e se prevesti u mainsku instrukciju kojom se sabiraju brojevi zapisani u potpunom komplementu (koja je obino direktno podrana u svakom
procesoru). U zavisnosti od tipa operanada, ista operacija se ponekad prevodi
na razliite naine. Tako, da su promenljive bile tipa float, najverovatnije bi
se podrazumevao zapis u obliku pokretnog zareza i operacija + bi se prevela u
mainsku instrukciju kojom se sabiraju brojevi zapisani u pokretnom zarezu.
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
20
15
tro
ns
ko
izd
an
je
(
Kontrola toka izvravanja. Osnovi gradivni element imperativnih programa su naredbe. Osnovna naredba je naredba dodele kojom se vrednost neke
promenljive postavlja na vrednost nekog izraza definisanog nad konstantama i
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 specijalne vrste naredbi kojima se vri kontrola toka izvravanja programa (tzv. kontrolne strukture). Ovim se postie da se u zavisnosti od tekuih 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
(goto). Za naredbu skoka (goto) je pokazano da nije neophodna, tj. svaki program se moe zameniti programom koji daje iste rezultate a pri tome koristi
samo sekvencijalno nizanje naredbi, naredbu izbora (if-then-else) i jednu
vrstu petlje (na primer, do-while).4 Ovaj vaan teorijski rezultat ima svoju
punu praktinu primenu i u dananjem, strukturnom, programiranju naredba
skoka se gotovo uopte ne koristi jer dovodi do nerazumljivih (tzv. pageti)
programa.
El
ek
86
an
je
(
20
15
El
ek
tro
ns
ko
izd
Modularnost. Modularnost podrazumeva razbijanje veeg programa na nezavisne celine. Celine, koje sadre definicije srodnih podataka i funkcija, obino
se nazivaju biblioteke (engl. library) i smetaju se u posebne datoteke. Ovim se
postie lake odravanje kompleksnih sistema, kao i mogunost viestruke upotrebe pojedinih modula u okviru razliitih programa. Celine se obino zasebno
prevode i kasnije povezuju u jedinstven program.
Programski jezici obino imaju svoje standardne biblioteke (engl. standard
library) koje sadre funkcije esto potrebne programeru (npr. funkciju za izraunavanje duine niske karaktera). esto se kd funkcija standardne biblioteke
statiki povezuju sa programom (tj. ukljuuje se u izvrni kd programa).
Osim standardne biblioteke, programske jezike esto karakterie i rantajm
biblioteka (engl. runtime library) koja predstavlja sponu izmeu kompilatora
(tj. izvrnih programa koje on generie) i operativnog sistema. Funkcije rantajm biblioteke se ne ukljuuju u izvrni kd programa ve se dinamiki pozivaju tokom izvravanja programa (i zato, da bi program mogao da se izvri
na nekom operativnom sistemu, neophodno je da na njemu bude instalirana
rantajm biblioteka za taj programski jezik).
Upravljanje memorijom. Neki programski jezici (na primer C, C++) zahtevaju od programera da eksplicitno rukuje memorijom tj. da od sistema zahteva memoriju u trenutku kada je ona potrebna i da tu memoriju eksplicitno
oslobaa, tj. vraa sistemu kada ona programu vie nije potrebna. Drugi programski 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 vea sloboda, ali i mnogo vea mogunost pravljenja greaka. Sa druge
87
4.4.1
Jeziki procesori
an
je
(
20
15
El
ek
tro
ns
ko
izd
Kompilatori. Kompilatori (ili kompajleri) su programski prevodioci kod kojih su faza prevoenja i faza izvravanja programa potpuno razdvojene.
Nakon analize izvornog kda programa vieg programskog jezika, kompilatori generiu izvrni (mainski) kd i dodatno ga optimizuju, a zatim
uvaju u vidu izvrnih (binarnih) datoteka. Jednom sauvani mainski
kd mogue je izvravati neogranien broj puta, bez potrebe za ponovnim
prevoenjem. Krajnjim korisnicima nije neophodno dostavljati izvorni
kd programa na viem programskom jeziku, ve je dovoljno distribuirati izvrni mainski kd5 . Jedan od problema u radu sa kompilatorima
je da se prevoenjem gubi svaka veza izmeu izvrnog i izvornog kda
programa. Svaka (i najmanja) izmena u izvornom kdu programa zahteva ponovno prevoenje programa ili njegovih delova. S druge strane,
kompilirani programi su obino veoma efikasni.
Interpretatori. Interpretatori su programski prevodioci kod kojih su faza prevoenja i faza izvravanja programa isprepletane. Interpretatori analiziraju 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 neophodno iznova vriti analizu izvornog kda. Zbog ovoga, programi koji
se interpretiraju se obino izvravaju znatno sporije nego u sluaju kompilacije. S druge strane, razvojni ciklus programa je eto krai ukoliko
se koriste interpretatori. Naime, prilikom malih izmena programa nije
potrebno iznova vriti analizu celokupnog kda.
88
20
15
Za neke programske jezike postoje i interpretatori i kompilatori. Intepretator 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 interpretatora. Naime, kd sa vieg programskog jezika se prvo kompilira u neki precizno
definisan meujezik niskog nivoa (ovo je obino jezik neke apstraktne virtuelne
maine), a zatim se vri interpretacija ovog meujezika i njegovo izvravanje
na konkretnom raunaru. Ovaj pristup primenjen je kod programskog jezika
Java, kod .Net jezika (C#, VB), itd.
an
je
(
Pitanje 4.1. Ukoliko je raspoloiv kd nekog programa na nekom viem programskom jeziku (na primer, na jeziku C), da li je mogue jednoznano konstruisati odgovarajui mainski kd? Ukoliko je raspoloiv mainski kd nekog
programa, da li je mogue jednoznano konstruisati odgovarajui kd na jeziku
C?
ko
izd
Pitanje 4.2. Za koji programski jezik su izgraeni prvi interpretator i kompilator i ko je bio njihov autor? Koji su najkorieniji programski jezici 1960-ih,
koji 1970-ih, koji 1980-ih i 1990-ih, a koji danas? ta je to strukturno, a ta
objektno-orijentisano programiranje?
tro
ns
El
ek
Pitanje 4.5. Navesti primer leksiki ispravnog, ali sintaksiki neispravnog dela
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?
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?
89
20
15
Pitanje 4.11. ta je kompilator, a ta interpretator? Koje su osnovne prednosti, a koje su mane korienja kompilatora u odnosu na korienje interpretatora?
El
ek
tro
ns
ko
izd
an
je
(
Pitanje 4.12. Ako se koristi kompilator, da li je za izvravanje programa korisniku neophodno dostaviti izvorni kd programa? Ako se koristi interpretator,
da li je za izvravanje programa korisniku neophodno dostaviti izvorni kd programa?
El
ek
ko
tro
ns
izd
20
15
an
je
(
Deo II
Jezik C
90
20
15
Glava
an
je
(
El
ek
tro
ns
ko
izd
5.1
Standardizacija jezika
91
92
20
15
K&R C. Brajan Kerningen2 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, godinama 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 prenosivosti, jer su konstrukte opisane u prvoj verziji K&R knjige uglavnom
podravali svi C prevodioci.
izd
an
je
(
ANSI C i ISO C. Tokom 1980-ih godina, C se proirio na veliki broj heterogenih platformi i time se javila potreba za zvaninom standardizacijom jezika. Godine 1989. ameriki nacionalni institut za standardizaciju
(ANSI) objavio je standard pod zvaninim nazivom ANSI X3.159-1989
Programming Language C. Ova verzija jezika C se obino jednostavnije naziva ANSI C ili C89. Godine 1990. Meunarodna organizacija
za standardizaciju (ISO) usvojila je ovaj dokument (uz sitnije izmene)
pod oznakom ISO/IEC 9899:1990 ime je briga o standardizaciji jezika
C prela 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 podrane od strane velikog broja prevodilaca.
tro
ns
ko
El
ek
C11 (nekada C1X). Godine 2007. zapoet je rad na novom standardu jezika C koji je zavren 2011. godine i objavljen kao ISO/IEC 9899:2011.
Ovaj, 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
izraunavanja u programu odvija paralelno).
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
93
5.2
Prvi programi
20
15
Jezik C deli mnoga svojstva sa drugim programskim jezicima, ali ima i svoje
specifinosti. U nastavku teksta jezik C bie prezentovan postupno, koncept
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.
an
je
(
Program Zdravo!
izd
#include <stdio.h>
ko
int main() {
printf("Zdravo!\n"); /* ispisuje tekst */
return 0;
}
El
ek
tro
ns
94
El
ek
tro
ns
ko
izd
an
je
(
20
15
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 saoptava da
je neophodno da ukljui sadraj datoteke stdio.h je pretprocesorska direktiva.
To nije naredba C jezika, ve instrukcija C pretprocesoru koji predstavlja nultu
fazu kompilacije i koji pre kompilacije program priprema tako to umesto prve
linije upisuje celokupan sadraj datoteke stdio.h.
Naredba return 0; prekida izvravanje funkcije main i, kao njen rezultat,
vraa vrednost 0. Vrednost funkcije vraa se u okruenje iz kojeg je ona pozvana, u ovom sluaju u okruenje iz kojeg je pozvan sm program. Uobiajena
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.
Tekst naveden izmeu simbola /* i simbola */ predstavlja komentare. Oni
su korisni samo programerima, doprinose itljivosti i razumljivosti samog programa. Njih C prevodilac ignorie i oni ne utiu na izvrnu verziju programa.
Primetimo da je svaka naredba u prikazanom programu pisana u zasebnom
redu, pri emu su neki redovi uvueni u odnosu na druge. Naglasimo da sa
stanovita C prevodioca ovo nije neophodno (u ekstremnom sluaju, doputeno
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 sintaksikom strukturom nezaobilazna praksa. Naglasimo i da postoje razliiti stilovi
nazubljivanja (na primer, da li otvorena vitiasta zagrada poinje u istom ili
sledeem redu). Meutim, obino se smatra da nije bitno koji se stil koristi
dok god se koristi na uniforman nain.
Ukoliko se, na primer, koristi GCC prevodilac (to je est sluaj pod operativnim 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:
gcc -o ime_izvrsnog_koda ime_izvornog_koda
Na primer, ako je izvorni kd sauvan u datoteci zdravo.c, a eljeno ime
izvrnog programa je zdravo, potrebno je uneti tekst:
gcc -o zdravo zdravo.c
U tom sluaju, izvrni program pokree se sa ./zdravo. Ako se ime izvrnog
programa izostavi, podrazumeva se ime a.out3 .
Nakon pokretanja programa dobija se sledei izlaz.
Zdravo!
95
20
15
an
je
(
Prethodni program nije uzimao u obzir nikakve podatke koje bi uneo korisnik, 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>
tro
ns
ko
izd
int main() {
int a;
printf("Unesite ceo broj: ");
scanf("%i", &a);
printf("Kvadrat unetog broja je: %i", a*a);
return 0;
}
El
ek
Prva linija funkcije main je takozvana deklaracija promenljive. Ovom deklaracijom se uvodi promenljiva a celobrojnog tipa tipa int. Naredna naredba
(poziv funkcije printf) na standardni izlaz ispisuje tekst Unesite ceo broj:,
a naredba nakon nje (poziv funkcije scanf) omoguava uitavanje vrednosti
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
%i. Nakon formata, zapisuje se ime promenljive u koju treba upisati proitanu vrednost. Simbol & govori da e promenljiva a biti promenjena u funkciji
scanf, tj. da e proitani broj biti smeten na adresu promenljive a. Korienjem 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
20
15
an
je
(
Sledei primer prikazuje program koji rauna rastojanje izmeu dve take
u dekartovskoj ravni. Osnovna razlika u odnosu na prethodne programe je to
se koriste razlomljeni brojevi, tj. brojevi u pokretnom zarezu. Dakle, u ovom
programu, umesto promenljivih tipa int koriste se promenljive tipa double,
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 deklarisana u zaglavlju math.h.
Program 5.3.
izd
#include <stdio.h>
#include <math.h>
El
ek
tro
ns
ko
int main() {
double x1, y1, x2, y2;
printf("Unesi koordinate prve tacke: ");
scanf("%lf%lf", &x1, &y1);
printf("Unesi koordinate druge tacke: ");
scanf("%lf%lf", &x2, &y2);
printf("Rastojanje je: %lf\n",
sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)));
return 0;
}
Nakon
unosa podataka, traeno rastojanje se rauna primenom Pitagorine
teoreme (2 1 )2 + (2 1 )2 . Poto u jeziku C ne postoji operator stepenovanja, kvadriranje se sprovodi mnoenjem.
Za prevoenje programa koji koriste matematike funkcije, prilikom korienja GCC prevodioca potrebno je navesti argument -lm (kao poslednji argument). 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
20
15
an
je
(
int main() {
int a;
printf("Unesi broj: ");
scanf("%d", &a);
if (a % 2 == 0)
printf("Broj %d je paran\n", a);
else
printf("Broj %d je neparan\n", a);
return 0;
}
#include <stdio.h>
El
ek
tro
ns
ko
izd
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
vri operatorom ==. Operator ispitanja jednakosti (==) i operator dodele (=) se
sutinski razlikuju. Naredba if 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
;. U sluaju da je uslov ispunjen, izvrava se prva naredba navedena nakon
uslova (takozvana then grana), dok se u sluaju da uslov nije ispunjen izvrava,
ukoliko postoji, naredba navedena nakon rei else (takozvana else grana). I
then i else grana mogu da sadre samo jednu naredbu ili vie naredbi. Ukoliko
sadre vie naredbi, onda te naredbe ine blok iji se poetak i kraj moraju
oznaiti zagradama { i }. Grana else ne mora da postoji.
Program 5.5.
#include <stdio.h>
#include <math.h>
#define N 100
int main() {
int i;
for (i = 1; i <= N; i++)
98
izd
an
je
(
20
15
ko
tro
ns
Naredni program ispisuje prvi prirodni broj za koji je zbir svih prirodnih
brojeva do tog broja vei od 100.
Program 5.6.
#include <stdio.h>
El
ek
#define N 100
int main() {
int i = 1;
int s = 1;
while(s <= N) {
i++;
s = s+i;
}
printf("%d\n", i);
return 0;
}
99
20
15
ko
izd
an
je
(
#include <stdio.h>
#include <ctype.h>
int main() {
int c;
printf("Otkucaj recenicu (zavrsi je znakom .): ");
do {
c = getchar();
putchar(toupper(c));
} while (c != .);
putchar(\n);
return 0;
}
tro
ns
El
ek
U programu se koristi funkcija getchar koja uitava karakter sa standardnog 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
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
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
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.
100
20
15
an
je
(
izd
Zadatak 5.2.3. Napisati program koji za unete dve duine stranica trougla
i ( > 0, > 0) i ugla izmeu njih ( > 0 je zadat u stepenima) izraunava
duinu tree stranice i veliine dva preostala ugla i . Napomena: koristiti
sinusnu i/ili kosinusnu teoremu i funkcije sin() i cos() iz zaglavlja math.h.
ispisuje odgova
ko
tro
ns
ravnomerno ubrzano.
El
ek
1 2 3
4 5 6
7 8 9
Zadatak 5.2.7. Napisati program koji ispisuje maksimum tri uneta broja.
Zadatak 5.2.8. Napisati program koji uitava cele brojeve i i zatim ispisuje
20
15
Glava
an
je
(
Predstavljanje podataka i
operacije nad njima
izd
ko
tro
ns
El
ek
6.1
Promenljive i deklaracije
Promenljive su osnovni objekti koji se koriste u programima. Svaka promenljiva mora biti deklarisane pre korienja.
6.1.1
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.1.2
tro
ns
ko
izd
an
je
(
20
15
Imena promenljivih (ali i funkcija, struktura, itd.) odreena su identifikatorima. 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
da malim slovima poinju imena promenljivih i funkcija, a velikim imena simbolikih konstanti (vidi poglavlja o nabrojivim tipovima 6.7.4 i pretprocesoru
9.2.1), vrednosti koje se ne menjaju u toku programa.
Imena promenljivih i funkcija, u principu, treba da oslikavaju njihovo znaenje i ulogu u programu, ali za promenljive kao to su indeksi u petljama
se obino koriste kratka, jednoslovna imena (na primer i). Ako ime promenljive sadri vie rei, onda se, radi bolje itljivosti, te rei razdvajaju simbolom _ (na primer, broj_studenata) ili poetnim velikim slovima (na primer,
brojStudenata) ovo drugo je takozvana kamilja notacija (CamelCase). Postoje 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
Iako je dozvoljeno, ne preporuuje se korienje identifikatora koji poinju
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.
Ukoliko dve promenljive imaju vie od 63 poetna znaka ista, onda se ne garantuje da e te dve promenljive biti razlikovane.2
Deklaracije
El
ek
103
int vrednost = 5;
/* deklaracija sa inicijalizacijom */
int a = 3, b, c = 5; /* deklaracije sa inicijalizacijom */
20
15
an
je
(
El
ek
tro
ns
ko
izd
Izmeu ova dva naina navoenja kvalifikatora razlike postoje samo kod
sloenijih tipova (pre svega pokazivaa) i o tome e biti vie rei u narednim
glavama.
Vrednost tipa const T moe biti dodeljena promenljivoj tipa T, ali promenljivoj tipa const T ne moe biti dodeljena vrednost (osim prilikom inicijalizacije) pokuaj menjanja vrednosti konstantne promenljive (kao i svakog
drugog konstantog sadraja) dovodi do greke prilikom prevoenja programa.
Deklaracije promenljivih mogu se navoditi na razliitim mestima u programu. Najee se navode na poetku funkcije (u dosada navedenim primerima to je bila funkcija main). Ukoliko je promenljiva deklarisana u nekoj
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 imena.
Promenljive deklarisane van svih funkcija su globalne i mogu se koristiti u vie
funkcija. Vidljivost tj. oblast vaenja identifikatora (i njima uvedenih promenljivih) odreena je pravilima dosega identifikatora o emu e vie rei biti u
poglavlju 9.2.2.
104
6.2
6.2.1
an
je
(
20
15
Tip int
El
ek
tro
ns
ko
izd
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 koristei potpuni komplement. Nad podacima ovog tipa mogu se koristiti
aritmetike operacije (na primer, +, -, *, /, %), relacije (na primer, <, >=) i
druge. Standardom nije propisano koliko bitova koriste podaci tipa int, ali je
propisano da se koristi najmanje esnaest bita (tj. dva bajta). Veliina tipa
int je obino prilagoena konkretnoj maini, tj. njenom procesoru. Na dananjim 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. Ovi kvalifikatori uvode cele brojeve potencijalno razliitih
duina u odnosu na int. Nije propisano koliko tipovi short int, long int i
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
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
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.
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
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
dugi
(long int)
veoma dugi
(long long int)
od C99
20
15
neoznaeni (unsigned)
1B = 8b
[0, 28 -1] =
[0, 255]
2B = 16b
[0, 64K-1] =
[0, 216 -1] =
[0, 65535]
4B = 32b
[0, 4G-1] =
[0, 232 -1] =
[0, 4294967295]
8B = 64b
[0, 264 -1] =
[0, 1.841019 ]
an
je
(
kratki
(short int)
oznaeni (signed)
1B = 8b
[-27 , 27 -1] =
[-128, 127]
2B = 16b
[-32K, 32K-1] =
[-215 , 215 -1] =
[-32768, 32767]
4B = 32b
[-2G, 2G-1] =
[-231 , 231 -1] =
[-2147483648,2147483647]
8B = 64b
[-263 , 263 -1] =
[-9.21018 , 9.21018 ]
izd
karakteri
(char)
ko
El
ek
tro
ns
106
20
15
#include <stdio.h>
an
je
(
int main() {
unsigned int a = 2000000000, b = 2000000000;
printf("Zbir brojeva %u i %u je: %u\n", a, b, a + b);
return 0;
}
Zbir brojeva 2000000000 i 2000000000 je: 4000000000
Tip char
izd
6.2.2
El
ek
tro
ns
ko
Male cele brojeve opisuje tip char (od engleskog character karakter,
simbol, znak). I nad podacima ovog tipa mogu se primenjivati aritmetike
operacije i relacije. Podatak tipa char zauzima tano jedan bajt. Standard
ne propisuje da li se podatak tipa char smatra oznaenim 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
unsigned i signed. Kvalifikator unsigned obezbeuje da se vrednost tretira
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.
Kvalifikator signed obezbeuje da se vrednost tretira kao oznaen broj i za
njegovu reprezentaciju se najee koristi zapis u potpunom komplementu, a
skup njegovih moguih vrednosti je interval od 128 do 127.
Iako je char opti brojevni tip podataka i moe da se koristi za predstavljanje vrednosti malih celih brojeva, u jeziku C se ovaj tip obino koristi za
brojeve koji predstavljaju kdove karaktera (otuda i ime char). Standard ne
propisuje koje kodiranje se koristi, ali na savremenim PC raunarima i C implementacijama, najee se koristi ASCII kodiranje karaktera. U tom sluaju,
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 karakterskim tipom i one su deklarisane u datoteci zaglavlja <ctype.h>. Najkorienije
107
su isalpha, isdigit i sline kojima se proverava da li je karakter slovo abecede 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
El
ek
tro
ns
ko
izd
an
je
(
20
15
108
6.2.4
an
je
(
20
15
izd
Pitanje 6.2.4. Koliko bajtova zauzima podatak tipa signed char? Koja je
najmanja a koja najvea vrednost koju moe da ima?
ko
Pitanje 6.2.5. Koliko bajtova zauzima podatak tipa unsigned char? Koja je
najmanja a koja najvea vrednost koju moe da ima?
tro
ns
Pitanje 6.2.6. Koja ogranienja vai za duine u bajtovima tipova short int,
int, long int?
Pitanje 6.2.7. Ako je opseg tipa int oko etiri milijarde, koliko je sizeof(int)?
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?
El
ek
Pitanje 6.2.9. U kojoj datoteci zaglavlja se nalaze podaci o opsezima celobrojevnih tipova za konkretnu implementaciju?
Pitanje 6.2.10. a 32-bitnom sistemu, koliko bajtova zauzima podatak tipa:
(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?
109
6.3
6.3.1
Celobrojne konstante
an
je
(
20
15
izd
ko
tro
ns
2147483647 -2147483648
El
ek
jer se 2147483647 tumai kao konstanta tipa int, dok se 2147483648 tumai
kao konstanta tipa unsigned long, to ne odgovara formatu %d.
Ukoliko se eli da se neka celobrojna konstanta tretira kao da ima tip
unsigned, onda se na njenom kraju zapisuje slovo u ili U. Tip takve konstante bi unsigned long, ako ona moe da bude reprezentovana tim tipom,
a unsigned int inae. Ukoliko se eli eksplicitno naglasiti da se neka celobrojna konstanta tretira kao da je tipa long, onda se na njenom kraju zapisuje
slovo l ili L. Na primer, 12345l je tipa long, 12345 je tipa int, a 12345ul 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
110
-123 vrednost predstavljena ovim izrazom jeste minus stodvadesettri, ali izraz
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
6.3.3
Karakterske konstante
an
je
(
20
15
tro
ns
ko
izd
Iako se tip char koristi i za predstavljanje malih celih brojeva, on se prevashodno koristi za predstavljanje kdova karaktera (najee ASCII kdova).
Direktno specifikovanje karaktera korienjem numerikih kdova nije preporuljivo. Umesto toga, preporuuje se korienje karakterskih konstanti. Karakterske 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, karakterska konstanta 0 predstavlja vrednost 48 (koja nema veze sa numerikom
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 ilustrovano sledeim primerom.
El
ek
char c = a;
char c = 97; /* ekvivalentno prethodnom (na ASCII masinama),
ali se ne preporucuje zbog toga sto smanjuje
citljivost i prenosivost programa */
111
Konstantni izrazi
tro
ns
6.3.4
ko
izd
an
je
(
20
15
\a
alert (bell) character
\b
backspace
\f
formfeed
\n
newline
\r
carriage return
\t
horizontal tab
\v
vertical tab
\\
backslash
\?
question mark
\
single quote
\"
double quote
\ooo (npr. \012) octal number
\xhh (npr. \x12) hexadecimal number
Karakterska konstanta \0 predstavlja karakter ija je vrednost nula. Ovaj
karakter ima specijalnu ulogu u programskom jeziku C jer se koristi za oznaavanje 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
umesto 0 da bi se istakla karakterska priroda ovog izraza.
Poto se karakterske konstante identifikuju sa njihovim numerikim vrednostima, one predstavljaju podatke tipa int i mogu ravnopravno da uestvuju
u aritmetikim izrazima (o izrazima e vie biti reeno u nastavku ove glave).
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.
El
ek
Konstantni izraz je izraz koji sadri samo konstante (na primer, 4 + 3*5).
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)./
112
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
20
15
6.4
an
je
(
Operatori i izrazi
El
ek
tro
ns
ko
izd
Izrazi se u programskom jeziku C grade od konstanti i promenjivih primenom irokog spektra operatora. Osim od konstanti i promenljivih, elementarni
izrazi se mogu dobiti i kao rezultat poziva funkcija, operacija pristupa elementima nizova, struktura i slino.
Operatorima su predstavljene osnovne operacije i relacije koje se mogu vriti
nad podacima osnovnih tipova u jeziku C. Za celobrojne tipove, te operacije
ukljuuju aritmetike, relacijske i logike operacije. Podrane su i operacije
koje se primenjuju nad pojedinanim bitovima celobrojnih vrednosti.
Operatori se dele na osnovu svoje arnosti tj. broja operanada na koje se primenjuju. Unarni operatori deluju samo na jedan operand i mogu biti prefiksni
kada se navode pre operanda i postfiksni kada se navode nakon svog operanda.
Binarni operatori imaju dva operanda i obino su infiksni tj. navode se izmeu
svojih operanda. U jeziku C postoji jedan ternarni operator koji se primenjuje
na tri operanda.
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 implementatorima prevodilaca da u potpunosti preciziraju semantiku na nain koji dovodi
do efikasnije implementacije. Tako, na primer, standardi ne definiu kojim se
redom izraunavaju operandi operatora + pre njihovog sabiranja, pa nije propisano koja od funkcija f i g e biti prva pozvana prilikom izraunavanja izraza
f() + g().
6.4.1
113
20
15
an
je
(
El
ek
tro
ns
ko
izd
Druga vana konvencija je asocijativnost operatora koja definie kojim redosledom e se izraunavati dva ista operatora ili operatora istog prioriteta
kada se nau uzastopno u istom, nezagraenom izrazu. Obino se razlikuju
leva asocijativnost, kada se izraz izraunava sleva na desno i desna asocijativnost, kada se izraz izraunava zdesna na levo. Neki jezici razlikuju i neasocijativnost, kada je zabranjeno da se isti operator dva puta uzastopno ponovi u
istom izrazu. Matematiki, neki operatori su asocijativni, to znai da vrednost izraza ne zavisi od redosleda primene uzastopno ponovljenog operatora.
Na primer, u matematici uvek vai + ( + ) = ( + ) + . Zbog naina
reprezentacije podataka u raunaru mnogi operatori (ukljuujui i +) nisu uvek
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,
ali dolazi do prekoraenja7 . Ovakvo ponaanje je posledica injenice da je u
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
bio sluaj da ima desnu asocijativnost, iz ega je jasno da - nije asocijativan
operator). Veina operatora ima levu asocijativnost (najznaajniji izuzeci su
prefiksni unarni operatori i operatori dodele). Asocijativnost operatora data je
u tabeli u dodatku A.
6.4.2
Operator dodele
Operatorom dodele se neka vrednost pridruuje datoj promenljivoj. Operator 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
broj_studenata = 80;
broj_grupa
= 2;
izd
an
je
(
20
15
Aritmetiki operatori
tro
ns
6.4.3
ko
x = y = z = 0;
El
ek
115
an
je
(
20
15
izd
Inkrementiranje i dekrementiranje. Inkrementiranje generalno znai postepeno uveavanje ili uveavanje za neku konkretnu vrednost. U programiranju se pod inkrementiranjem obino podrazumeva uveavanje za 1, a pod
dekrementiranjem umanjivanje za 1. U jeziku C, operator inkrementiranja
(uveavanja za 1) zapisuje se sa ++, a operator dekrementiranja (umanjivanja
za 1) zapisuje se sa --:
++ (prefiksno i postfiksno) inkrementiranje;
ko
El
ek
tro
ns
116
ispisuje
20
15
a = 4, b = 4, x = 3, y = 4
an
je
(
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,
int a = 3, b = 3;
a++; ++b;
printf("a = %d, b = %d\n", a, b);
izd
prethodni kd ispisuje
a = 4, b = 4
tro
ns
ko
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, izmeu
svake dve susedne promenljive koje se deklariu (mesto oznaenom simbolom
,) sekvenciona taka, kao i kraj naredbe i deklaracije (mesto oznaeno sa ;),
dok operator + to esto nije9 . Efekat moe biti izvren i pre sekvencione take,
ali za to ne postoji garancija, tako neto zavisi od konkretne implementacije
(od kompilatora) i na to se ne treba oslanjati. Na primer, naredni kd
El
ek
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
117
an
je
(
6.4.4
20
15
vrednost promenljive a (vrednost 4). Drugo uveanje promenljive a (na vrednost 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 oznaava 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 komplikovana i stoga se ne savetuje korienje sloenijih izraza sa ovim operatorima,
koji se oslanjaju na fine detalje semantike.
izd
> vee;
>= vee ili jednako;
< manje;
ko
El
ek
tro
ns
Relacijski operatori poretka <, <=, > i >= imaju isti prioritet i to vii od operatora jednakosti == i razliitosti != i svi imaju levu asocijativnost. Rezultat
relacionog operatora primenjenog nad dva broja je vrednost 0 (koja odgovara
istinitosnoj vrednosti netano) ili vrednost 1 (koja odgovara istinitosnoj vrednosti 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,
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
ima vrednost 0, a zatim izraz 0 < 5 ima vrednost 1. Kako u ovakvim sluajevima kompilator ne prijavljuje greku, a u fazi izvravanja se dobija rezultat
neoekivan za poetnike, ovo je opasna greka programera koji su navikli na uobiajenu 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
20
15
Logiki operatori. Logiki operatori primenjuju se nad brojevnim vrednostima 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:
! logika negacija ne;
&& logika konjunkcija i;
an
je
(
Operator && ima vii prioritet u odnosu na operator ||, a oba su levo
asocijativna. Binarni logiki operatori imaju nii prioritet u odnosu na binarne
relacijske i logike operatore. Operator !, kao unarni operator, ima vii prioritet
u odnosu na bilo koji binarni operator i desno je asocijativan. Na primer,
izd
ko
tro
ns
El
ek
119
nakon izraunavanja vrednosti navedenog izraza. S druge strane, nakon izraunavanja vrednosti izraza
a++ && 2<1
20
15
vrednost promenljive a e biti promenjena (uveana za 1). U izrazima u kojima se javlja logiko i, ukoliko je vrednost prvog operanda jednaka 1, onda se
izraunava i vrednost drugog operanda.
U izrazu u kojem se javlja logiko ili, ukoliko je vrednost prvog operanda
jednaka 1, onda se ne izraunava vrednost drugog operanda. Ukoliko je vrednost prvog operanda jednaka 0, onda se izraunava i vrednost drugog operanda.
Na primer, nakon
an
je
(
1<2 || a++_
6.4.5
Bitovski operatori
izd
tro
ns
bitovska negacija;
ko
El
ek
120
broj 74 se binarno zapisuje kao 01001010, broj 87 kao 01010111, i konjunkcija njihovih pojedinanih bitova daje 01000010, to predstavlja zapis 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.
20
15
| bitovsko ili primenom ovog operatora vri se (obina) disjunkcija pojedinanih bitova dva navedena argumenta. Za brojeve iz tekueg primera, vrednost izraza x1 | x2 je 01011111, tj. 95, tj. 0x5F.
an
je
(
bitovsko ekskluzivno ili primenom ovog operatora vri se ekskluzivna disjunkcija pojedinanih bitova dva argumenta. Za brojeve iz tekueg primera, vrednost izraza x1 ^ x2 je 00011101, tj. 29, tj. 0x1D.
jedinini komplement primenom ovog operatora vri se komplementiranje (invertovanje) svakog bita argumenta. Na primer, vrednost izraza
~x1 u tekuem primeru je 10110101, tj. B5, tj. 181.
tro
ns
ko
izd
El
ek
desno pomeranje (iftovanje) primenom ovog operatora bitovi prvog argumenta se pomeraju u desno za broj pozicija naveden kao drugi
argument. Krajnji (desni) bitovi prvog argumenta se zanemaruju, a to
se tie poetnih (levih) bitova rezultata, postoji mogunost da se oni
popunjavaju uvek nulama (tzv. logiko pomeranje) ili da se oni popune
vodeim bitom (koji predstavlja znak) prvog argumenta (tzv. aritmetiko
pomeranje). Osnovna motivacija aritmetikog pomeranja je da desno pomeranje broja za jednu poziciju odgovara celobrojnom deljenju sa dva.
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 argument 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
20
15
6.4.6
an
je
(
Operatori dodele imaju nii prioritet od svih ostalih operatora i desnu asocijativnost.
Izraunavanje vrednosti izraza
izd
ko
El
ek
tro
ns
10 Na primer, prilikom izraunavanja izraza a[i++] += 1 (a[i] oznaava i-ti element niza
a), promenljiva inkrementira se samo jednom, dok se u sluaju a[i++] = a[i++] + 1 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
}
Izlaz programa:
20
15
6.4.7
c = 255
c = 0
Operator uslova
an
je
(
izd
ko
m = (a > b) ? a : b
tro
ns
El
ek
6.4.8
Operator zarez
Binarni operator zarez (,) je operator kao i svaki drugi (najnieg je prioriteta 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 vrednost se esto
123
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; */
6.4.9
an
je
(
20
15
Operator sizeof
tro
ns
ko
izd
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 najee
4.
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
zahteva samo da vrednosti ovog tipa imaju barem dva bajta).
El
ek
124
20
15
izd
Pitanje
(a) 3
(e) 3
(k) 3
(j) 3
an
je
(
Pitanje 6.4.11. Postaviti zagrade u naredne izraze u skladu sa podrazumevanim 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
(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
tro
ns
ko
El
ek
int a = 1, b = 1, x, y;
x = a++; y = ++b;
125
Pitanje 6.4.18.
20
15
int a = 3, b = 0, c, d;
c = a % 2 || b++;
d = a % 3 || b++;
printf("%d %d %d %d", a, b, c, d);
an
je
(
4. Napisati izraz koji je, na ASCII sistemu, taan ako je karakter c malo
slovo.
5. Napisati izraz koji je taan ako je karakter c samoglasnik.
izd
7. Napisati izraz koji ako je c malo slovo ima vrednost odgovarajueg velikog
slova, a inae ima vrednost c.
ko
Zadatak 6.4.1. Napisati program koji za zadati sat, minut i sekund izraunava
koliko je vremena (sati, minuta i sekundi) ostalo do ponoi. Program trebai
da izvri proveru da li su uneti podaci korektni.
tro
ns
Zadatak 6.4.2. Napisati program koji za uneta tri pozitivna broja u pokretnom
zarezu ispituje da li postoji trougao ije su duine stranica upravo ti brojevi
El
ek
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.
Napisati program koji ciklino u levo rotira poslednje tri cifre unetog prirodnog
broja. Na primer, za uneti broj 12345 program treba da ispie 12453.
Zadatak 6.4.5. Napisati program koji za uneti par prirodnih brojeva izraunava 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
126
reenja.
20
15
jednaine 2 + + = 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).
Konverzije tipova
izd
6.5
an
je
(
putchar)
El
ek
tro
ns
ko
6.5.1
Vrste konverzija
127
float a=4;
16777216.00000
20
15
float f = 16777217;
printf("%f\n", f);
ko
izd
an
je
(
jer vrednost 16777217.0 ne moe da se zapie u okviru tipa float ako on koristi
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
celobrojne vrednosti do 224 ali samo neke vee od 224 . Broj 16777217 jednak
je 224 + 1 i binarno se zapisuje kao 1000000000000000000000001, pa prilikom
konverzije u float (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
ponekad se naziva democija (ili nazadovanje). Prilikom ovog oblika konverzije,
mogue je da doe do gubitka informacije (u sluaju da se polazna vrednost
ne moe predstaviti u okviru novog tipa). U takvim sluajevima, kompilator
obino izdaje upozorenje, ali ipak vri konverziju.
tro
ns
El
ek
Prilikom konverzije iz brojeva u pokretnom zarezu u celobrojne tipove podataka i obratno potrebno je potpuno izmeniti interni zapis podataka (na primer,
iz IEEE754 zapisa brojeva u pokretnom zarezu u zapis u obliku potpunog komplementa). Ovo se najee vri uz odsecanje decimala, a ne 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 (razliitog broja
bajtova), vri se odsecanje vodeih bitova zapisa (u sluaju konverzija u ui tip)
ili proirivanje zapisa dodavanjem vodeih bitova (u sluaju 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 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.
Navedeni primer int c = 7.7f; ilustruje i zato vrednost izraza dodele nije
jednaka desnoj strani (u ovom sluaju 7.7f), nego vrednosti koja je dodeljena
objektu na levoj strani operatora dodele (u ovom sluaju 7).
128
6.5.2
Eksplicitne konverzije
an
je
(
20
15
izd
Operator eksplicitne konverzije tipa ili operator kastovanja (engl. type cast
operator) se navodi tako to se ime rezultujueg tipa navodi u malim zagradama
ispred izraza koji se konvertuje:
ko
(tip)izraz
El
ek
tro
ns
3.250000
Prilikom prvog poziva funkcije printf upotrebljen je format %d, zbog toga
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.
129
6.5.3
Implicitne konverzije
20
15
int a;
double b = (a = 3.5);
ko
izd
an
je
(
tro
ns
El
ek
130
1. Ako je bar jedan od operanada tipa long double, onda se drugi konvertuje u long double;
2. inae, ako je jedan od operanada tipa double, onda se drugi konvertuje
u double;
20
15
an
je
(
izd
ko
3. inae, oba operanda se konvertuju u neoznaeni tip koji odgovara oznaenom tipu.
El
ek
tro
ns
131
20
15
an
je
(
Pitanje 6.5.3. Navesti pravila koja se primenjuju prilikom primene aritmetikih operatora.
izd
tro
ns
Pitanje 6.5.6.
ko
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
konvertuju argumenti tipa char i short int?
2. Ako promenljiva c ima tip char, koji tip e ona imati nakon dodele
c = 1.5 * c;?
El
ek
132
6.6
Nizovi i niske
20
15
6.6.1
esto je u programima potrebno korienje velikog broja srodnih promenljivih. Umesto velikog broja pojedinano deklarisanih promenljivih, mogue je
koristiti nizove. Obrada elemenata nizova se onda obino vri na uniforman
nain, korienjem petlji.
an
je
(
Razmotrimo, kao ilustraciju, program koji uitava 10 brojeva sa standardnog ulaza, a zatim ih ispisuje u obratnom poretku. Bez nizova, neophodno je
koristiti deset promenljivih. Da se u zadatku trailo ispisivanje unatrag 1000
brojeva, program bi bio izrazito mukotrpan za pisanje i nepregledan.
Program 6.4.
#include <stdio.h>
tro
ns
ko
izd
int main() {
int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9;
scanf("%d%d%d%d%d%d%d%d%d%d",
&b0, &b1, &b2, &b3, &b4, &b5, &b6, &b7, &b8, &b9);
printf("%d %d %d %d %d %d %d %d %d %d",
b9, b8, b7, b6, b5, b4, b3, b2, b1, b0);
}
1 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1
El
ek
133
6.6.2
Deklaracija niza
20
15
int a[10];
uvodi niz a od 10 celih brojeva. Prvi element niza ima indeks 0, pa su elementi
niza:
an
je
(
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]
Indeksi niza su obino nenegativni celi brojevi (mada je u nekim prilikama kada
se nizovi koriste u kombinaciji sa pokazivaima doputeno koristiti i negativne
indekse, o emu e vie biti rei u poglavlju 10.3).
Prilikom deklaracije moe se izvriti i inicijalizacija:
izd
int a[5] = { 1, 2, 3, 4, 5 };
ko
tro
ns
Veliina memorijskog prostora potrebnog za niz odreuje se u fazi prevoenja programa, pa broj elemenata niza (koji se navodi u deklaraciji) mora biti
konstantan izraz. U narednom primeru, deklaracija niza a je ispravna, dok su
deklaracije nizova b i c neispravne14 :
El
ek
int x;
char a[100+10];
int b[];
float c[x];
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
int b[] = { 1, 2, 3 };
sadraj niza b jednak je:
1
20
15
an
je
(
tro
ns
ko
izd
El
ek
6.6.3
Niske
Posebno mesto u programskom jeziku C zauzimaju nizovi koji sadre karaktere 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 memorije 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
20
15
"prvi red\ndrugi red"). Niske su interno reprezentovane kao nizovi karaktera 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.
an
je
(
tro
ns
ko
izd
El
ek
je ekvivaletno sa
printf("Zdravo, svima");
Standardna biblioteka definie veliki broj funkcija za rad sa niskama. Prototipovi ovih funkcija se nalaze u zaglavlju string.h. 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. Pniske).
136
20
15
an
je
(
ili oblika:
izd
ko
Izuzetno neefikasan kd dobija se pozivanjem funkcije strlen za izraunavanje duine niske u svakom koraku iteracije:
tro
ns
El
ek
137
6.6.4
Viedimenzioni nizovi
20
15
tro
ns
ko
char a[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
izd
an
je
(
Kao i u sluaju jednodimenzionih nizova, ako je naveden inicijalizator, vrednost prvog indeksa mogue je i izostaviti (jer se on u fazi kompilacije moe
odrediti na osnovu broja inicijalizatora):
El
ek
char a[][3] = {
{1, 2, 3},
{4, 5, 6}
};
138
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:
20
15
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}
};
an
je
(
izd
ko
tro
ns
El
ek
Pitanje 6.6.3. Ukoliko je niz a deklarisan kao float a[10];, koja je vrednost
izraza sizeof(a)?
Pitanje 6.6.4. Ako je niz a tipa float inicijalizovan u okviru deklaracije i
njegova dimenzija nije navedena, kako se ona moe izraunati?
Pitanje 6.6.5. Kada je u deklaraciji niza dozvoljeno izostaviti njegovu dimenziju?
Pitanje 6.6.6. 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
20
15
1 2
3 4 .
5 6
an
je
(
Zadatak 6.6.1. Napisati program koji unosi prirodan broj ( < 1000), a
zatim i elemenata niza celih brojeva, nakon ega:
izd
Zadatak 6.6.2. Sa standardnog ulaza se uitava tekst sve dok se ne unese taka
(tj. karakter .). Napisati program koji u unetom tekstu prebrojava pojavljivanje
El
ek
tro
ns
ko
Zadatak 6.6.3. Napisati program koji za uneti datum u obliku (dan, mesec,
godina) odreuje koji je to po redu dan u godini. Proveriti i koretknost unetog
datuma. U obzir uzeti i prestupne godine. Koristiti niz koji sadri broj dana
1
2
3
4
5
1
3 1
6 4 1
10 10 5 1
( )
( )
(primetite da su ivice trougla 1 tj. da vai 0 = = 1, kao da se svaki
unutranji lan trougla (moe
zbir
) (dobiti
) kao
(1
) odgovarajua dva lana prethodne
140
4
1
5
9
4
2
6
1
5
3
7
2
6
4
8
3
7
20
15
Program izraunava i ispisuje sumu elemenata glavne dijagonale i sumu elemenata iznad sporedne dijagonale matrice. Suma elemenata glavne dijagonale
matrice navedene u primeru je 1 + 6 + 2 + 7 = 16, a iznad sporedne dijagonale
je 1 + 2 + 3 + 5 + 6 + 9 = 26
ko
6.7
izd
an
je
(
Zadatak 6.6.6. Napisati program koji sa standardnog ulaza unosi prvo dimenziju matrice ( < 100) pa zatim elemente matrice i zatim proverava da li je
matrica donje-trougaona. Matrica je donje-trougaona ako se u gornjem trouglu
(iznad glavne dijagonale, ne ukljuujui je) nalaze sve nule. Na primer:
1 0 0 0
5 6 0 0
9 1 2 0
4 5 6 7
El
ek
tro
ns
6.7.1
Strukture
Osnovni tipovi jezika C esto nisu dovoljni za pogodno opisivanje svih podataka 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 odravanje. Umesto toga, pogodnije je koristiti strukture. Za razliku od nizova koji
objedinjuju jednu ili vie promenljivh istog tipa, struktura objedinjuje jednu ili
vie promenljivih, ne nuno istih tipova. Definisanjem strukture uvodi se novi
141
20
15
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
definisati na sledei nain:
an
je
(
struct razlomak {
int brojilac;
int imenilac;
};
tro
ns
struct student {
char ime[50];
float prosek;
};
ko
izd
Strukture mogu biti ugnjedene, tj. lanovi struktura mogu biti druge strukture. Na primer:
El
ek
struct dvojni_razlomak {
struct razlomak gore;
struct razlomak dole;
};
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
Definicijom strukture je opisano da se razlomci sastoje od brojioca i imenioca, 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 };
an
je
(
20
15
izd
struct razlomak {
int brojilac;
int imenilac;
} a = {1, 2}, b, c;
a.imenilac
ko
tro
ns
El
ek
Od ranije prikazanih operacija, nad promenljivama tipa strukture dozvoljene su operacije dodele a nisu dozvoljeni aritmetiki i relacijski operatori.
Operator sizeof se moe primeniti i na ime strukturnog tipa i na promenljive 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
Nizovi struktura
20
15
an
je
(
struct opis_meseca {
char ime[10];
int broj_dana;
};
izd
tro
ns
ko
El
ek
144
Unije
6.7.2
ko
union vreme {
int obicno;
float precizno;
};
izd
an
je
(
20
15
tro
ns
El
ek
Unije se esto koriste i kao lanovi struktura. Neka se, na primer, u programu uvaju i obrauju informacije o studetima i zaposlenima na nekom fakultetu. 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:
struct akademac {
char ime_i_prezime[50];
char jmbg[14];
char vrsta;
union {
double plata;
char indeks[7];
} dodatno;
};
145
20
15
int main() {
struct akademac pera = {"Pera Peric", "0101970810001", z};
pera.dodatno.plata = 56789.5;
printf("%f\n", pera.dodatno.plata);
an
je
(
izd
Pokuaj promene polja pera.dodatno.indeks bi naruio podatak o koeficijentu plate, dok bi pokuaj promene polja ana.dodatno.koeficijent naruio
podatak o broju indeksa.
Navedimo, kao ilustraciju, kako se korienjem unije moe otkriti binarni
zapis broja u pokretnom zarezu:
6.7.3
tro
ns
ko
Polja bitova
El
ek
: 1;
: 3;
: 2;
146
an
je
(
20
15
6.7.4
izd
ko
U nekim sluajevima korisno je definisati tip podataka koji ima mali skup
doputenih vrednosti. Ovakvi tipovi se nazivaju nabrojivi tipovi . U jeziku C
nabrojivi tipove se definiu korienjem kljune rei enum. Na primer:
El
ek
tro
ns
enum znak_karte {
KARO,
PIK,
HERC,
TREF
};
147
TREF = 8
};
an
je
(
20
15
izd
struct karta {
unsigned char broj;
enum znak_karte znak;
} mala_dvojka = {2, TREF};
tro
ns
ko
El
ek
enum return_type {
OK,
FileError,
MemoryError,
TimeOut
};
6.7.5
Typedef
148
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;
izd
struct point {
int x, y;
};
typedef struct point Point;
an
je
(
20
15
Novo ime tipa se navodi kao poslednje, na poziciji na kojoj se u deklaracijama obino navodi ime promenljive, a ne neposredno nakon kljune rei
typedef. Obino se novouvedena imena tipova piu velikim poetnim slovima
da bi se istakla.
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 strukturama da bi se izbeglo pisanje kljune rei struct pri svakom korienju imenu
strukturnog tipa. Na primer:
Point a, b;
ko
tro
ns
El
ek
Point a, b;
149
20
15
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
razumeti nego dugo ime strukture.
an
je
(
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. Da li je nad vrednostima koje su istog tipa strukture dozvoljeno
koristiti operator dodele i koje je njegovo dejstvo?
izd
tro
ns
struct tacka {
int a, b;
char naziv[5];
}
ko
El
ek
Ova struktura opisuje taku sa koordinatama (a,b) u ravni kojoj je dodeljeno ime naziv. Za dve promenljive tipa struct tacka, napisati naredbe koje
kopiraju sadraj prve promenljive u drugu promenljivu.
Pitanje 6.7.6. Kakva je veza izmeu nabrojivog (enum) i celobrojnog tipa u
C-u?
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
El
ek
tro
ns
ko
izd
an
je
(
20
15
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
20
15
Glava
an
je
(
7.1
tro
ns
ko
izd
Naredba izraza
El
ek
151
152
20
15
7.2
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).
Naredbe grananja
tro
ns
7.3
ko
izd
an
je
(
Naredba if-else
El
ek
7.3.1
if (izraz)
naredba1
else
naredba2
Naredba naredba1 i naredba2 su ili pojedinane naredbe (kada se zavravaju 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
je taan (tj. da je uslov ispunjen) ako ima ne-nula vrednost, a inae se smatra
da je netaan. Na primer, nakon naredbe
an
je
(
if (7)
a = 1;
else
a = 2;
20
15
if (5 > 7)
a = 1;
else
a = 2;
izd
tro
ns
ko
a = 3;
if (a = 0)
printf("a je nula\n");
else
printf("a nije nula\n");
El
ek
154
7.3.2
20
15
if (izraz1) {
if (izraz2)
naredba1
} else
naredba2
Konstrukcija else-if
izd
an
je
(
if (izraz1)
naredba1
else if (izraz2)
naredba2
else if (izraz3)
naredba3
else
naredba4
tro
ns
ko
El
ek
if (a > 0)
printf("A je veci od nule\n");
else if (a < 0)
printf("A je manji od nule\n");
else /* if (a == 0) */
printf("A je nula\n");
7.3.3
Naredna naredba
if (a > b)
x = a;
else
x = b;
odreuje i smeta u promenljivu x veu od vrednosti a i b. Naredba ovakvog
oblika se moe zapisati krae korienjem ternarnog operatora uslova ?: (koji
155
7.3.4
Naredba switch
20
15
an
je
(
switch (izraz) {
case konstantan_izraz1: naredbe1
case konstantan_izraz2: naredbe2
...
default:
naredbe_n
}
El
ek
tro
ns
ko
izd
156
20
15
int main() {
int n;
scanf("%i",&n);
an
je
(
switch (n % 3) {
case 1:
case 2:
printf("Uneti broj nije deljiv sa 3");
break;
default: printf("Uneti broj je deljiv sa 3");
}
return 0;
izd
7.4
tro
ns
ko
Petlje
El
ek
7.4.1
Petlja while
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
Ukoliko iza while sledi samo jedna naredba, onda, kao i oblino, nema
potrebe za vitiastim zagradama. Na primer:
while (i < j)
i++;
Petlja for
an
je
(
7.4.2
20
15
while (1)
i++;
El
ek
tro
ns
ko
izd
158
20
15
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
koja prekida izvravanje, na primer, break ili return):
for (;;)
...
an
je
(
i=0, j=10
i=1, j=9
izd
Prethodni kd ispisuje
i=2, j=8
i=3, j=7
i=4, j=6
Program 7.2.
ko
tro
ns
#include<stdio.h>
El
ek
int main() {
int i, j, n=3;
for(i = 1; i <= n; i++) {
for(j = 1; j <= n; j++)
printf("%i * %i = %i\t", i, j, i*j);
printf("\n");
}
return 0;
}
1 * 1 = 1
2 * 1 = 2
3 * 1 = 3
7.4.3
1 * 2 = 2
2 * 2 = 4
3 * 2 = 6
1 * 3 = 3
2 * 3 = 6
3 * 3 = 9
Petlja do-while
159
do {
naredbe
} while(izraz)
Program 7.3.
tro
ns
ko
izd
#include <stdio.h>
int main() {
unsigned n;
printf("Unesi broj: ");
scanf("%u", &n);
do {
printf("%u ", n % 10);
n /= 10;
} while (n > 0);
return 0;
}
an
je
(
20
15
El
ek
7.4.4
U nekim situacijama pogodno je napustiti petlju ne zbog toga to nije ispunjen 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:
for(i = 1; i < n; i++) {
if(i > 10)
break;
...
1 Naredbom
160
20
15
an
je
(
izd
tro
ns
ko
El
ek
ispisuje
0 0
0 1
0 2
1 0
1 1
2 0
161
x += 2;
x *= 2;
ako je pre navedenog kda imala vrednost 0, a koju ako je imala vrednost 5?
Pitanje 7.2. Koju vrednost ima promenljiva x nakon izvravanja kda
20
15
int x = 0;
if (x > 3);
x++;
an
je
(
Pitanje 7.4. Kako se, u okviru naredbe switch, oznaava sluaj koji pokriva
sve sluajeve koji nisu ve pokriveni?
ko
i=2; j=5;
switch(j/i)
{
case 1: i++;
break;
case 2: i += ++j;
default: j += ++i;
}
izd
Pitanje 7.5. Ako su promenljive i i j tipa int odrediti njihovu vrednost nakon
naredbi:
tro
ns
Pitanje 7.6. Ako su promenljive i i j tipa int odrediti njihovu vrednost nakon
naredbi:
El
ek
162
20
15
an
je
(
izd
ko
Pitanje 7.11. Ukoliko se u naredbi for (e1; e2; e3) ... izostavi izraz e2,
koja e biti njegova podrazumevana vrednost?
tro
ns
El
ek
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
while (izraz)
naredba
Pitanje 7.16. Transformisati naredni kd tako da ne sadri break:
163
while(A) {
if(B) break;
C;
}
Pitanje 7.17. Transformisati naredni kd tako da ne sadri continue:
20
15
for(;A;) {
if(B) continue;
C;
}
an
je
(
izd
ko
El
ek
tro
ns
broj i izraunava .
Zadatak 7.4. Napisati programe koji ispisuje naredne dijagrame, pri emu se
dimenzija n unosi. Svi primeri su za = 4:
a) **** b) ****
****
***
****
**
****
*
c) *
**
***
****
d) ****
***
**
*
e)
*
**
***
****
f) * * * *
* * *
* *
*
g)
*
* *
* * *
* * * *
164
Zadatak 7.5. Napisati program koji odreuje sumu svih delilaca broja (koristiti
prost.
20
15
Zadatak 7.7. Napisati program koji ispisuje sve proste inioce unetog neoznaenog celog broja (na primer, za unos 24 traeni prosti inioci su 2, 2, 2, 3).
an
je
(
Zadatak 7.8. Napisati program koji uitava cele brojeve sve dok se ne unese
0, a onda ispisuje:
1. broj unetih brojeva;
2. zbir unetih brojeva;
3. proizvod unetih brojeva;
4. minimum unetih brojeva;
izd
);
6. aritmetiku sredinu unetih brojeva ( 1 +...+
+...+ 1
);
tro
ns
ko
Zadatak 7.9. Napisati program koji uitava cele brojeve sve dok se ne unese
El
ek
+1 =
2
2
niza).
165
Zadatak 7.13. Napisati program koji ispisuje sve cifre unetog neoznaenog
celog broja n, poevi od cifre jedinica. Napisati i program koji izraunava
20
15
Zadatak 7.14. Napisati program koji nadovezuje dva uneta neoznaena cela
broja (na primer, za unos 123 i 456 program ispisuje 123456). Ako je jedan od
brojeva 0, rezultat treba da bude jednak drugom broju. Pretpostaviti da rezultat
unetog celog broja (na primer, za unos 1234 program ispisuje broj 4321).
an
je
(
Zadatak 7.16. Napisati program koji izbacuje sve neparne cifre iz zapisa unetog neoznaenog celog broja (na primer, za uneti broj 123456 program ispisuje
246).
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
izd
Zadatak 7.18.
1. Napisati program koji razmenjuje prvu i poslednju cifru unetog neoznaenog celog broja (na primer, za unos 1234 program ispisuje 4231).
ko
tro
ns
El
ek
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
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.
166
Zadatak 7.22. Napisati program koji ispisuje prvih (najvie 100000) elemenata niza koji se dobija tako to se kree od 0, a zatim prethodni niz ponovi
20
15
Zadatak 7.23. Sa standardnog ulaza se unose dva niza brojeva (svaki sadri
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).
an
je
(
izd
Zadatak 7.26. Napisati program koji sa standardnog ulaza unosi prvo dimenziju matrice ( < 100) pa zatim elemente matrice i zatim ispisuje sve dijagonale
matrice paralelne sa sporednom dijagonalom. Na primer, unos:
3 1 2 3 4 5 6 7 8 9
opisuje matricu
ko
1
4
7
2
5
8
3
6
9
El
ek
1
2 4
3 5 7
6 8
9
tro
ns
Zadatak 7.27. Napisati program koji sa standardnog ulaza uitava prvo dimenzije 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:
1
0
1
0
1
8
1
3
2
1
1
0
1
9
0
2
3
0
0
2
an
je
(
Funkcije
20
15
Glava
8.1
tro
ns
ko
izd
El
ek
Naredni programi ilustruju jednostavne primere korienja funkcija. Pojmovi koji su u okviru ovog primera samo ukratko objanjeni bie detaljnije
objanjeni u nastavku teksta.
Program 8.1.
#include <stdio.h>
int kvadrat(int n);
int main() {
1U
167
168
8. Funkcije
printf("Kvadrat broja %i je %i\n", 5, kvadrat(5));
printf("Kvadrat broja %i je %i\n", 9, kvadrat(9));
return 0;
20
15
int kvadrat(int n) {
return n*n;
}
Program 8.2.
ko
izd
an
je
(
Linija int kvadrat(int n); deklarie funkciju kvadrat koja e biti definisana kasnije u kdu3 . Iz deklaracije se vidi da funkcija kvadrat ima jedan
parametar tipa int i vraa rezultat tipa int. U funkciji main, u okviru poziva 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 moe ravnopravno dalje uestvovati u irim izrazima) iji je tip povratni
tip funkcije, a vrednost povratna vrednost funkcije. Na mestu poziva, tok
izvravanja programa prelazi na poetak funkcije koja je pozvana, a kada se
zavri izvravanje funkcije tok izvravanja programa vraa se na mesto poziva.
Definicija funkcije kvadrat je jednostavna: ona kao rezultat, korienjem naredbe return vraa kvadrat celobrojne vrednosti n ija se vrednost postavlja
na osnovu vrednosti koja je prosleena prilikom poziva.
U narednom programu, funkcija stepen izraunava celobrojni stepen realne
promenljive.
tro
ns
#include <stdio.h>
int max = 10;
El
ek
int main() {
unsigned i;
for(i = 0; i < max; i++)
printf("%d %f %f\n", i, stepen(2.0f,i), stepen(3.0f,i));
return 0;
}
float stepen(float x, unsigned n) {
unsigned i;
float s = 1.0f;
169
8. Funkcije
for(i = 1; i<=n; i++)
s *= x;
return s;
an
je
(
8.2
20
15
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
promenljive i njih je mogue koristiti samo u okviru funkcije u kojoj su definisane, dok promenljive deklarisana van svih funkcija nazivamo obino globalne
promenljive i one su zajednike za vie funkcija. O ovoj temi bie vie rei u
poglavlju 9.2.2.
izd
tro
ns
ko
tip ime_funkcije(niz_deklaracija_parametara) {
deklaracije
naredbe
}
El
ek
a definicija
int kvadrat(int n) {
return n*n;
}
Definicija funkcija mora da bude u skladu sa navedenim prototipom, tj. moraju da se podudaraju tipovi povratne vrednosti i tipovi parametara. Deklaracija 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
170
8. Funkcije
funkcije nepoznata u trenutku te provere). Poto prototip slui samo za proveravanje tipova u pozivima, nije neophodno navoditi imena parametara, ve
je dovoljno navesti njihove tipove. U navedenom primeru, dakle, prototip je
mogao da bude i
int kvadrat(int);
El
ek
tro
ns
ko
izd
an
je
(
20
15
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
171
8. Funkcije
8.4
tro
ns
ko
izd
an
je
(
20
15
Prenos argumenata
El
ek
172
8. Funkcije
an
je
(
int main() {
int x = 3, y = 5;
swap(x, y);
printf("x = %d, y = %d\n", x, y);
}
20
15
izd
ko
x = 3, y = 5
tro
ns
#include <stdio.h>
El
ek
void f(int a) {
a = 3;
printf("f: a = %d\n", a);
}
int main() {
int a = 5;
f(a);
printf("main: a = %d\n", a);
}
173
8. Funkcije
f: a = 3
main: a = 5
an
je
(
8.5
20
15
Program 8.5.
ko
#include <stdio.h>
izd
Prilikom poziva funkcije, ukoliko je poznata njena deklaracija, vri se implicitna konverzija tipova argumenata u tipove parametara (ako se oni razlikuju).
Slino, prilikom vraanja vrednosti funkcije (putem return naredbe) vri se
konverzija vrednosti koja se vraa u tip povratne vrednosti funkcije.
U sledeem primeru, prilikom poziva funkcije f vri se konverzija double
vrednosti u celobrojnu vrednost i program ispisuje 3:
tro
ns
void f(int a) {
printf("%d\n", a);
}
El
ek
int main() {
f(3.5);
}
174
8. 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).
8.6
8.7
tro
ns
ko
izd
an
je
(
20
15
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 izvravanje. Ako funkcija koja treba da vrati vrednost ne sadri return naredbu,
kompilator moe da prijavi upozorenje, a u fazi izvravanja rezultat poziva e
biti nedefinisana vrednost (to je obino pogreno). Ako funkcija ne treba da
vraa rezultat, onda se kao tip povratne vrednosti navodi specijalan tip void
i tada naredba return nema argumenata (tj. navodi se return;). tavie, u
tom sluaju nije ni neophodno navoditi naredbu return iza poslednje naredbe
u funkciji.
Funkcija koja je pozvala neku drugu funkciju moe da ignorie, tj. da ne
koristi vrednost koju je ova vratila.
Kvalifikator const moe se primeniti i na tip povratne vrednosti funkcije.
To nema mnogo smisla, osim u kombinaciji sa pokazivaima (videti glavu 10)
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
kao povratnu vrednost oekuje tip int).
Nizovi i funkcije
El
ek
175
8. Funkcije
an
je
(
20
15
Program 8.6.
ko
izd
Naredni primer ilustruje injenicu da se u funkciju ne prenosi ceo niz. Ukoliko se program pokrene na maini na kojoj su tip int i adresni tip reprezentovani sa 4 bajta) program ispisuje, u okviru funkcije main, broj 20 (5 elemenata
tipa int ija je veliina 4 bajta)4 i, u okviru funkcije f, broj 4 (veliina adrese
koja je preneta u funkciju). Dakle, funkcija f nema informaciju o broju elemenata niza a.
tro
ns
#include <stdio.h>
El
ek
int main() {
int a[] = {1, 2, 3, 4, 5};
printf("main: %d\n", sizeof(a));
f(a);
return 0;
}
main: 20
f: 4
4 sizeof je operator, a ne funkcija, te prilikom poziva sizeof(a) nema prenosa argumenata u kojem bi bila izgubljena informacija o broju elemenata niza a.
176
8. Funkcije
20
15
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.
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.
void ucitaj_broj(int a) {
printf("Ucitaj broj: ");
scanf("%d", &a);
}
an
je
(
#include <stdio.h>
ko
izd
El
ek
tro
ns
int main() {
int x = 0;
int y[3] = {0, 0, 0};
ucitaj_broj(x);
ucitaj_niz(y, 3);
printf("x = %d\n", x);
printf("y = %d %d %d\n", y[0], y[1], y[2]);
}
Unesi broj: 5
Unesi niz: 1 2 3
x = 0
y = 1, 2, 3
Prenos niski u funkciju se vri kao i prenos bilo kog drugog niza. Jedina
razlika je to nije neophodno funkciji prosleivati duinu niske, jer se ona moe
odrediti i u samoj funkciji zahvaljujui postojanju terminalne nule.5 Na pri5 Naravno, duina niske ne odreuje duinu niza karaktera u koji je smetena, ve samo
broj karaktera do zavrne nule.
177
8. Funkcije
return -1;
an
je
(
20
15
ko
8.8
izd
tro
ns
El
ek
178
8. Funkcije
struct razlomak c;
c.brojilac = a.brojilac*b.imenilac + a.imenilac*b.brojilac;
c.imenilac = a.imenilac*b.imenilac;
return c;
izd
an
je
(
20
15
Rekurzija
tro
ns
8.9
ko
El
ek
179
8. Funkcije
8.10
an
je
(
20
15
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 podataka va_list kojim se predstavlja lista argumenata prosleenih funkciji, 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).
#include <stdio.h>
#include <stdarg.h>
tro
ns
ko
izd
El
ek
int main() {
printf("%d\n", sumof(5, 1, 2, 3, 4, 5));
return 0;
}
180
8. Funkcije
20
15
an
je
(
int f(int x) {
x = x+2;
return x+4;
}
izd
int main() {
int x = 1, y = 2;
x = f(x+y);
printf("%d\n", x);
}
ko
tro
ns
int main() {
int n = 4, k = 5;
f(n, k);
printf("%d %d\n", n, k);
}
El
ek
181
8. Funkcije
20
15
an
je
(
izd
int main() {
int i;
int a[] = {1, 2, 3, 4};
f(a);
for(i = 0; i < sizeof(a)/sizeof(int); i++)
printf("%d ", a[i]);
}
ko
tro
ns
El
ek
int main() {
f(2);
return 0;
}
main.
Zadatak 8.2. Napisati program koji za tri take Dekartove ravni zadate parovima svojih koordinata (tipa double) izraunava povrinu trougla koji obrazuju
(koristiti
Heronov obrazac, na osnovu kojeg je povrina trougla jednaka vredno
sti ( )( )( ), gde su , i duine stranica, a njegov poluobim;
napisati i koristiti funkciju koja rauna rastojanje izmeu dve take Dekartove
ravni).
182
8. Funkcije
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
20
15
2. pronalazi indeks prve pozicije na kojoj se u nizu nalazi dati broj (-1 ako
niz ne sadri broj).
an
je
(
3. pronalazi indeks poslednje pozicije na kojoj se u nizu nalazi dati broj (-1
ako niz ne sadri broj).
4. izraunava zbir svih elemenata datog niza brojeva;
izd
7. odreuje poziciju najveeg elementa u nizu brojeva (u sluaju vie pojavljivanja najveeg elementa, vratiti najmanju poziciju);
8. proverava da li je dati niz brojeva ureen neopadajue.
ko
tro
ns
El
ek
Napomena: funkcija kao argument prima niz i broj njegovih trenutno popunjenih elemenata, a vraa broj popunjenih elemenata nakon izvoenja zahtevane
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;
183
8. Funkcije
20
15
an
je
(
9. spaja dva niza brojeva koji su sortirani neopadajui u trei niz brojeva
koji je sortiran neopadajui.
izd
Zadatak 8.8. Napisati funkciju koja poredi dve niske leksikografski (kao u
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
prvoj.
Zadatak 8.9. Napisati funkciju koja za dve niske proverava da li je:
ko
El
ek
tro
ns
184
8. Funkcije
Primer, matrica:
7 12
2 13
16 3
9
6
1 14
8 11
10 5
15 4
je magini kvadrat.
20
15
Zadatak 8.12. Definisati strukturu kojom se moe predstaviti matrica dimenzija najvie 100100 (struktura sadri dvodimenzionalni niz brojeva tipa float
i dimenzije).
1. Napisati funkciju koja uitava matricu sa standardnog ulaza.
an
je
(
El
ek
tro
ns
ko
izd
Zadatak 8.13. 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 od 1000
cifara.
20
15
Glava
an
je
(
El
ek
tro
ns
ko
izd
U prethodnim glavama ve je naglaeno da se izvorni program pie najee na viim programskim jezicima i predstavlja sredstvo komunikacije izmeu
programera i raunara, ali i izmeu programera smih. Specijalizovani programi, prevodioci (kompilatori), prevode izvorni u izvrni program, napisan na
mainskom jeziku raunara, tj. na jeziku neposredno podranom arhitekturom
procesora.
Postoji mnotvo pisanih pravila (tj. pravila koja proistiu iz standarda
jezika koji se koristi) i nepisanih pravila (razne konvencije i obiaji) za organizovanje kda izvornog programa. Ta pravila olakavaju programiranje i
omoguuju programeru kreiranje razumljivih programa koji se efikasno mogu
prevoditi i izvravati. Svi programi u prethodnim poglavljima bili su jednostavni, imali su svega nekoliko funkcija i nije bilo potrebno previe obraati
panju na pomenuta pravila. Meutim, sa uslonjavanjem programa, potovanje kodeksa postaje kljuno jer u protivnom pisanje i odravanje kda postaje
veoma komplikovano1 . Takoe, postoje pravila za organizovanje kda izvrnog
programa koja omoguavaju njegovo efikasno izvravanje na odreenom raunaru, u sklopu odreenog operativnog sistema, to podrazumeva i nesmetano
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.
185
186
an
je
(
9.1
20
15
sistem nasilno prekida njegovo izvravanje), ali mogu da budu takve da se program 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 programa u tom, dubljem smislu zahteva formalnu analizu i ispitivanje te vrste
ispravnosti najee je van moi automatskih alata. Ipak, sredstva za prijavljivanje greaka tokom prevoenja i izvravanja programa znatno olakavaju
proces programiranja i esto ukazuju i na sutinske propuste koji naruavaju
ispravnost programa.
izd
Iako proces prevoenja jednostavnih programa poetnicima izgleda kao jedan, nedeljiv proces, on se sastoji od vie faza i podfaza (a od kojih se svaka i
sama sastoji od vie koraka). U sluaju jezika C, ti koraci su obino pretprocesiranje (engl. preprocessing), kompilacija (engl. compilation) i povezivanje
(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
na osnovu vrste greke).
El
ek
tro
ns
ko
187
20
15
ko
izd
an
je
(
Kompilacijom se od svake jedinice prevoenja gradi zasebni objektni modul (engl. object module)2 . Objektni moduli sadre programe (mainski kd
funkcija) i podatke (memorijski prostor rezervisan za promenljive). Iako su u
mainskom obliku, objektni moduli se ne mogu izvravati.
Na primer, rezultat rada GCC kompilatora moe se dobiti navoenjem opcije -c (na primer, gcc -c program.c). Ovim se dobija datoteka program.o
objektni modul koji je na Linux sistemu u ELF formatu (engl. Executable and
Linkable Format), formatu u kome je i izvrna datoteka, pri emu objektni 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
navede opcija gcc -O3, vri se najopsenija optimizacija kda to uzrokuje da
se rezultujui kd (esto znatno) bre izvrava. Ukoliko se navede opcija -Wall
navode se sva upozorenja na mogue greke tokom kompilacije.
El
ek
tro
ns
188
El
ek
tro
ns
ko
izd
an
je
(
20
15
189
u proces povezivanja ukljuuje i standardnu biblioteku libm koja nije podrazumevano obuhvaena povezivanjem.
an
je
(
20
15
Odvojena kompilacija i povezivanje. Korak kompilacije i korak povezivanja mogue je razdvojiti tj. mogue je prvo prevesti datoteku sa izvornim
programom i eksplicitno napraviti objektni modul (u ovom sluaju bi se zvao
program.o), a tek zatim ovako napravljeni objektni modul povezati (ukljuujui i objektni kd standardne biblioteke) i dobiti izvrni program. U sluaju
da se koristi GCC, to se postie sa:
gcc -c program.c
gcc -o program program.o
ko
izd
tro
ns
El
ek
gcc -c program1.c
gcc -c program2.c
gcc -o program program1.o program2.o
Razdvajanjem kompilacije i povezivanja u dve faze omogueno je da se moduli koji se ne menjaju ne prevode iznova svaki put kada je potrebno napraviti
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 environment, IDE) u kojima programer na interaktivni nain specifikuje proces
kompilacije.
190
20
15
Prilikom korienja programa make, korisnik u datoteci koja obino ima ime
Makefile specifikuje nain dobijanja novih datoteka od starih. Specifikuju 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
an
je
(
tro
ns
ko
izd
El
ek
Izvravanje programa. Nakon uspenog prevoenja moe da sledi faza izvravanja programa.
Izvrni programi smeteni su u datotekama na disku i pre pokretanja uitavaju se u glavnu memoriju. Za ovo je zaduen program koji se naziva punilac
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
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
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 dodatne 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
191
20
15
omoguavaju izvravanje programa korak po korak (ili pauziranje njegovog izvravanja u nekim karakteristinim takama), uz prikaz meuvrednosti promenljivih. 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).
an
je
(
Pitanje 9.1.3. Kako se korienjem GCC prevodioca moe izvriti samo faza
pretprocesiranja kda i prikazati rezultat? Kako se moe izvriti samo faza
kompilacije, bez povezivanja objektnih modula?
izd
ko
tro
ns
Pitanje 9.1.6. 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?
Pitanje 9.1.7. ta znai da u objektnim modulima pre povezivanja adrese nisu
korektno razreene?
El
ek
192
9.2
El
ek
tro
ns
ko
izd
an
je
(
20
15
193
se na osnovu mesta i naina deklarisanja tj. definisanja objekata, ali i primenom kvalifikatora 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.
Pretprocesor
20
15
9.2.1
tro
ns
ko
izd
an
je
(
El
ek
Veliki programi obino su podeljeni u vie datoteka, radi preglednosti i lakeg odravanja. esto je potrebno da se iste promenljive, funkcije i korisniki
definisani tipovi koriste u vie datoteka i neophodno je da kompilator prilikom
prevoenja svake od njih poznaje deklaracije5 promenljivih, funkcija i tipova
podataka koji se u njoj koriste. ak i kada programer pie samo jednu datoteku, program po pravilu koristi i funkcionalnost koju mu prua standardna
biblioteka i da bi ona mogla da se koristi, neophodno je da kompilator 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.
194
datoteke koje se onda ukljuuju gde god je potrebno. Takve datoteke zovu se
datoteke zaglavlja (engl. header files). Osnovni primer datoteka zaglavlja su zaglavlja 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"
20
15
i linija oblika
#include <ime_datoteke>
tro
ns
ko
izd
an
je
(
El
ek
Makro zamene
195
#define
max(A, B)
an
je
(
20
15
primeru, MAX_LEN je samo simboliko ime i nikako ga ne treba meati sa 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 kompilatoru, 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
reda koji se nastavlja. Direktive zamene mogu da koriste direktive koje im
prethode.
Zamene se ne vre u konstantnim niskama niti u okviru drugih simbolikih
imen a6 . Na primer, gore navedena direktiva #define MAX_LEN 80 nee uticati
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
((A) > (B) ? (A) : (B))
El
ek
tro
ns
ko
izd
definie tekst zamene za max(A, B) koji zavisi od argumenata. Ukoliko je u nastavku datoteke, u nekoj naredbi navedeno max(2, 3), to e, pre prevoenja,
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)
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
primer, ukoliko je negde u programu navedeno max(a++, b++), na osnovu date
definicije, ovaj tekst bie zamenjen tekstom
((a++) > (b++) ? (a++) : (b++)),
to e dovesti do toga da se vea od vrednosti a i b inkrementira dva puta (to
moda nije planirano).
Vano je voditi rauna i o zagradama u tekstu zamene, da bi bio ouvan
poredak primene operacija. Na primer, ukoliko se definicija
#define kvadrat(x)
x*x
(x)*(x)
196
#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; }
#define
dprint(expr)
an
je
(
20
15
koji definie zamenjivanje vrednosti dve promenljive tipa t. Celobrojnim promenljivama a i b se, zahvaljujui ovom makrou, mogu razmeniti vrednosti sa
swap(int, a, b).
Ukoliko se u direktivi #define, u novom tekstu, ispred imena parametra navede simbol #, kombinacija e biti zamenjena vrednou parametra navedenog
izmeu dvostrukih navodnika. Ovo moe da se kombinuje sa nadovezivanjem
niski. Na primer, naredni makro moe da poslui za otkrivanje greaka:
printf(#expr " = %g\n", expr)
Tako e tekst:
izd
dprint(x/y);
ko
tro
ns
Poto se niske automatski nadovezuju, efekat ove naredbe je isti kao efekat
naredbe:
printf("x/y = %g\n", x/y);
El
ek
197
#undef originalni_tekst
an
je
(
20
15
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 neoekivanog rada programa. Argument makroa u zamenjenoj verziji moe biti
naveden vie puta to moe da utie na izvravanje programa, a izostavljanje
zagrada u tekstu zamene moe da utie na redosled izraunavanja operacija. S
druge strane, kod poziva makroa nema prenosa argumenata te se oni izvravaju
bre nego odgovarajue funkcije. Ipak, moderni kompilatori kratke funkcije
obino umeu (engl. inline) itavo telo funkcije na mesto poziva, starajui se
o argumentima i tada nema gubitaka u vremenskoj ili prostornoj efikasnosti
u odnosu 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
biti, alternativno, implementirane i kao makroi (na primer, getchar koji koristi
funkciju getc, printf koji koristi funkciju fprintf, itd.).
izd
Uslovno prevoenje
El
ek
tro
ns
ko
198
9.2.2
20
15
ispisuje tekst na srpskom jeziku, a izostavljanjem direktive iz prvog reda ispisivao 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
koristi C jezik (a ne pretprocesor), program sadri kd za sve mogue grane.
Doseg identifikatora
tro
ns
ko
izd
an
je
(
Jedna od glavnih karakteristika dobrih programa je da se promenljive veinom definiu u funkcijama (ili ak nekim uim blokovima) i upotreba promenljive je uglavnom ograniena na funkciju (ili blok) u kojoj je deklarisana. Ovim
se smanjuje zavisnost izmeu funkcija i ponaanje funkcije odreeno je samo
njenim ulaznim parametrima, a ne nekim globalnim stanjem programa. Time
se omoguava i da se analiza rada programa zasniva na analizi pojedinanih
funkcija, nezavisnoj od konteksta celog programa. Ipak, u nekim sluajevima
prihvatljivo je da funkcije meusobno komuniciraju korienjem zajednikih
promenljivih. Doseg, tj. vidljivost identifikatora (engl. scope of identifiers)
odreena je nainom tj. mestom u izvornom kdu na kojem su uvedeni.
Doseg identifikatora odreuje deo teksta programa u kojem je mogue koristiti odreeni identifikator i u kojem taj identifikator identifikuje odreeni
objekat (na primer, promenljivu ili funkciju). Svaki identifikator ima neki doseg. 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 vrste dosega:
El
ek
doseg nivoa datoteke (engl. file level scope) koji podrazumeva da ime vai
od take uvoenja do kraja datoteke;
doseg nivoa bloka (engl. block level scope) koji podrazumeva da ime vai
od take uvoenja do kraja bloka u kojem je uvedeno;
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 podrazumeva 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
199
an
je
(
20
15
Najznaajni nivoi dosega su doseg nivoa datoteke i doseg nivoa bloka. Identifikatori koji imaju doseg nivoa datoteke najee se nazivaju spoljanji ili
globalni , 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.
Imajui u vidu podelu na globalne i lokalne objekte, C program se esto definie
kao skup globalnih objekata (promenljivih, funkcija, tipova podataka itd.).
U ranim verzijama jezika C nije bilo mogue definisati funkciju u okviru
definicije druge funkcije, tj. funkciju sa dosegom nivoa bloka, pa nije bilo lokalnih funkcija, dok je od standarda C99 i takva mogunost predviena. Doseg
globalnih funkcija je doseg nivoa datoteke i protee se od mesta deklaracije pa
do kraja datoteke. U nastavku su dati primeri razliitih dosega:
int a;
/* a je globalna promenljiva - doseg nivoa datoteke */
ko
izd
tro
ns
El
ek
}
/* 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.
200
an
je
(
20
15
void f() {
int a = 3, i;
for (i = 0; i < 4; i++) {
int a = 5;
printf("%d ", a);
}
}
5 5 5 5
9.2.3
izd
tro
ns
ko
El
ek
201
tro
ns
ko
izd
an
je
(
20
15
deklaracije do kraja bloka i nije ih mogue koristiti van bloka u kojem su deklarisane. 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
automatskih promenljivih nije odreena i zavisi od ranijeg sadraja memorijske
lokacije koja je pridruena promenljivoj, pa se smatra nasuminom.
Automatske promenljive postoje samo tokom izvravanja funkcije u kojoj
su deklarisane i prostor za njih je rezervisan u stek okviru te funkcije (videti
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 funkciju ue rekurzivno, kreira se prostor za novu promenljivu, potpuno
nezavisan od prostora za prethodnu promenljivu istog imena.
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 i kvalifikator register ime se kompilatoru sugerie da se ove promenljive
uvaju 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 kda veoma dobro mogu da odrede koje promenljive ima smisla
uvati u registrima, ovaj kvalifikator se sve ree koristi.
Globalne statike promenljive i funkcije
El
ek
202
tro
ns
ko
izd
an
je
(
20
15
#include <stdio.h>
El
ek
void f() {
int a = 0;
printf("f: %d ", a);
a = a + 1;
}
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.
203
20
15
an
je
(
9.2.4
int main() {
f(); f();
g(); g();
return 0;
}
El
ek
tro
ns
ko
izd
Ne treba meati spoljanju i unutranju povezanost sa spoljanjim i unutranjim dosegom (otuda i korienje alternativnih termina globalni i lokalni
za doseg) povezanost (i unutranja i spoljanja) se odnosi iskljuivo na globalne 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
204
El
ek
tro
ns
ko
izd
an
je
(
20
15
205
struct i { int a; }
20
15
int g1(int i) {
static int j;
}
int g2() {
static int i, j;
...
}
an
je
(
int f() {
int i;
...
}
El
ek
tro
ns
ko
izd
206
an
je
(
20
15
int g() {
extern int b;
printf("b=%d\n", b);
f();
}
El
ek
tro
ns
ko
void f() {
printf("a=%d\n", a);
}
#include <stdio.h>
extern int a;
void f();
izd
#include <stdio.h>
int a;
int b = 3;
int main() {
a = 1;
/* b = 2; */
g();
return 0;
}
b=3
a=0
207
an
je
(
20
15
El
ek
tro
ns
ko
izd
Globalni objekat ima unutranju povezanost ako se kvalifikuje kvalifikatorom static.10 Unutranja povezanost povezuje sva pojavljivanja istog identifikatora, na nivou tano jedne jedinice prevoenja. Ukoliko se u istoj jedinici prevoenja, u istom dosegu naie na vie deklaracija identifikatora sa
unutranjom povezanou sve one se odnose na isti objekat i za taj objekat
mora postojati tano jedna definicija u toj jedinici prevoenja. S druge strane,
kvalifikator static 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 navedena deklaracija na koju je primenjen kvalifikator extern. Ukoliko neka druga
jedinica prevoenja sadri deklaraciju istog identifikatora neophodno je da postoji definicija odgovarajueg objekta, razliitog od objekta iz prve jedinice
prevoenja.
Osnovna uloga unutranje povezanosti obino je u tome da neki deo kda
uini zatvorenim, u smislu da je nemogue menjanje nekih njegovih globalnih
promenljivih ili pozivanje nekih njegovih funkcija iz drugih datoteka. Time se
taj deo kda enkapsulira i iz drugih jedinica prevoenja mu se moe pristupiti samo kroz preostale take komunikacije (funkcije i globalne promenljive sa
spoljanjom povezanou). Cilj programera, dakle, nije da onemogui druge
programere da koriste deo njegovog kda, ve da im omogui jasan interfejs
ijim korienjem se onemoguuju nehotine greke. Ukoliko je u jednoj jedinici 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.
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).
208
#include <stdio.h>
int main() {
printf("main: a=%d, b=%d\n",
a, b);
g(); /* f() */
return 0;
}
an
je
(
int g() {
f();
}
20
15
Program ispisuje
izd
El
ek
tro
ns
ko
U prvoj jedinici prevoenja promenljiva a i funkcija f imaju unutranju povezanost (dok promenljiva b i funkcija g imaju spoljanju povezanost (o kojoj
e biti rei u nastavku poglavlja). U drugoj jedinici prevoenja promenljive
a, b i funkcije g i main imaju spoljanju povezanost. Ime a u dve razliite
jedinice prevoenja odnosi se na dve razliite promenljive. Promenljiva a, definisana u prvoj jedinici prevoenja, kljunom reju static sakrivena je i nije
joj mogue pristupiti iz ostalih jedinica prevoenja. Slino je i sa funkcijom f
(uklanjanje komentara u drugoj jedinici prevoenja dovelo bi do greke prilikom
povezivanja jer je definicija funkcije f skrivena u prvoj jedinici prevoenja). Sa
druge strane, sva pojavljivanja imena b odnose se na istu promenljivu i sva
pojavljivanja imena g odnose se na istu funkciju.
Poto u prvoj jedinici prevoenja postoji definicija promenljive b, u drugoj jedinici prevoenja int b; je samo njena deklaracija. Poto je definicija
promenljive a iz prve jedinice prevoenja skrivena, prilikom povezivanja druge
jedinice prevoenja nije dostupna definicija promenljive a, onda se int a; u
drugoj jedinici prevoenja smatra njenom definicijom (uz podrazumevanu vrednost 0).
9.2.5
209
stoji greka koja tada moe biti otkrivena. Razmotrimo jednostavan (vetaki,
bez konkretne svrhe) program naveden u nastavku i greke do kojih moe doi
malim izmenama:
Program 9.5.
an
je
(
20
15
#include<stdio.h>
int main() {
int a = 9;
if (a == 9)
printf("9");
return 0;
}
izd
ko
Leksika analiza: Na primer, ukoliko u programu pie int a = 09; umesto int a = 9;, bie detektovana neispravna oktalna konstanta 09, tj. neispravna leksema 09 i bie prijavljena greka:
tro
ns
El
ek
210
izd
an
je
(
20
15
ko
tro
ns
Ukoliko navedeni program, umesto naredbe if (a == 9) ... sadri naredbu if (9 == 9) ..., prevodilac moe izdati upozorenje:
primer.c:3: warning: unused variable a
El
ek
Ukoliko navedeni program, umesto naredbe if (a == 9) ... sadri naredbu if (a / 0) ..., prevodilac moe izdati upozorenje:
primer.c:4: warning: division by zero
211
9.2.6
izd
#ifndef __RAZLOMAK_H_
#define __RAZLOMAK_H_
an
je
(
20
15
Kao primer programa sastavljenog iz vie datoteka i vie jedinica prevoenja, 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 bila drugaija).
Biblioteka prua definiciju tipa za reprezentovanje razlomaka (RAZLOMAK), funkcije za kreiranje razlomka na osnovu brojioca i imenioca, za 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 imenilac 0). Ovim ishodima odgovaraju lanovi OK i IMENILAC_0 nabrojivog tipa RAZLOMAK_GRESKA.
Kako im nisu eksplicitno pridruene vrednosti ovim 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.
Datoteka razlomak.h je javni interfejs biblioteke i sadri deklaracije funkcija i promenljivih koje su na raspolaganju njenim korisnicima.
tro
ns
ko
El
ek
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.
Datoteka zaglavlja razlomak.h poinje i zavrava se pretprocesorskim direktivama. 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
212
puta u neku jedinicu prevoenja (to se esto dogaa preko posrednog ukljuivanja), 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
bi se spreilo viestruko ukljuivanje, svaka datoteka zaglavlja u tom sluaju
treba da ima sledei opti oblik:
20
15
#ifndef IME
#define IME
...
an
je
(
#endif
#include "razlomak.h"
izd
gde je tekst IME karakteristian za tu datoteku (na primer njeno ime obogaeno nekim specijalnim simbolima). Prilikom prvog ukljuivajna ove datoteke,
definie se simboliko ime IME i zbog toga naredna ukljuivanja (iz iste datoteke) ignoriu celokupan njen sadraj. Ovaj mehanizam olakava odravanje
datoteka zaglavlja i primenjen je i u navedenom primeru.
Implementacija biblioteke sadrana je u datoteci razlomak.c.
tro
ns
ko
char* razlomak_poruka[] = {
"Operacija uspesno sprovedena",
"Greska: imenilac je nula!"
};
static const RAZLOMAK NULA = {0, 0};
RAZLOMAK_GRESKA razlomak_greska;
El
ek
20
15
213
izd
an
je
(
El
ek
tro
ns
ko
214
20
15
an
je
(
izd
r = saberi_razlomke(r1, r2);
if (razlomak_greska != OK)
printf("%s\n", razlomak_poruka[razlomak_greska]);
printf("Rezultat je: %u/%u\n", r.brojilac, r.imenilac);
return 0;
ko
tro
ns
Program uitava dva razlomka, sabira ih i ispisuje rezultat, proveravajui u pojedinim koracima vrednost promenljive razlomak_greska. U sluaju
da je dolo do greke, opis greke se ispisuje na standardni izlaz. Datoteka
razlomak.h nesmetano je ukljuena u obe jedinice prevoenja.
Odgovarajua Makefile datoteka moe da bude:
El
ek
215
20
15
Pitanje 9.2.4. Kako se od neke take u programu ponitava dejstvo pretprocesorske direktive
#define x(y) y*y+y?
Pitanje 9.2.5. Kako se moe proveriti da li je neko simboliko ime definisano
u trenutnoj jedinici prevoenja?
an
je
(
Pitanje 9.2.6. Objasniti razliku izmeu makroa i funkcija. U kojoj fazi se vri
razreavanje poziva makroa, a u kojoj poziva funkcija? Objasniti ta znai da
kod makroa nema prenosa argumenata, a kod funkcija ima.
Pitanje 9.2.7. Kojoj operaciji u tekstualnom editoru je slian efekat direktive
#define, a kojoj efekat direktive #include?
izd
Pitanje 9.2.8. Kojim parametrom se GCC navodi da izvri samo pretprocesiranje i prikae rezultat faze pretprocesiranja?
ko
Pitanje 9.2.9. Za svaki od programa koji slede, navesti ta je rezultat pretprocesiranja programa i ta se ispisuje kada se program prevede i izvri.
tro
ns
1. #include <stdio.h>
#define x(y) y*y+y
int main() { printf("%d", x(3 + 5)); /* ispis */ }
El
ek
2. #include <stdio.h>
#define A(x,y) (x > 2) ? x*y : x-y
int main() { printf("%d %d", A(4,3+2), A(4,3*2); }
3. #include <stdio.h>
#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)); }
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)); }
216
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
definiciji #define kvadrat(x) ((x)*(x)) daje ispravan rezultat.
2. Navesti primer poziva makroa dvostruko za koji pri definiciji
20
15
an
je
(
izd
ko
tro
ns
El
ek
217
20
15
an
je
(
Pitanje 9.2.24. Koje vrste ivotnog veka postoje u jeziku C? Koji ivotni vek
imaju lokalne promenljive (ako na njih nije primenjen kvalifikator static)?
Koji ivotni vek imaju globalne promenljive?
Pitanje 9.2.25. Ukoliko je za neku lokalnu promenljivu naveden kvalifikator
static, ta e biti sa njenom vrednou nakon kraja izvravanja funkcije u
kojoj je deklarisana?
izd
ko
El
ek
tro
ns
int i = 2;
int f() { static int i; return ++i; }
int g() { return ++i; }
int main() {
int i;
for(i=0; i<3; i++)
printf("%d", f()+g());
}
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();
}
218
9.3
9.3.1
an
je
(
20
15
Ukoliko je od izvornog programa uspeno generisana izvrna verzija, ta, generisana verzija moe biti izvrena. Zahtev za izvravanje se izdaje operativnom
sistemu (npr. navoenjem imena programa u komandnoj liniji). Program koji
treba da bude izvren najpre se uitava sa spoljne memorije u radnu memoriju
raunara. Operativni sistem mu stavlja odreenu memoriju na raspolaganje,
vri se dinamiko povezivanje poziva rutina niskog nivoa rantajm bibliotekom,
vre se potrebne inicijalizacije i onda izvravanje moe da pone. U fazi izvravanja mogue je da doe do greaka koje nije bilo mogue detektovati u fazi
prevoenja i povezivanja.
Izvrni program generisan za jedan operativni sistem moe se izvravati
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,
64-bitna aplikacija ne moe da se izvrava na 32-bitnom sistemu).
El
ek
tro
ns
ko
izd
Kao to je ranije reeno, izvrni program koji je dobijen prevoenjem i povezivanjem ne moe da se izvrava autonomno. Naime, taj program sadri
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 realizovane kao niz zahteva operativnom sistemu (najee zahteva koji se
odnose na ulaz/izlaz i na memoriju). Kada se program uita u memoriju, vri
se takozvano dinamiko povezivanje (engl. dynamic linking) i pozivi funkcija i
promenljivih iz izvrnog programa se povezuju sa funkcijama i promenljivama
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 definie ta je implementirano u vidu funkcija standardne biblioteke a ta u vidu
funkcija rantajm biblioteke nije specifikovana niti kruta i zavisi od konkretnog
kompilatora. Funkcije rantajm biblioteke su raspoloive programima generisanim od strane kompilatora ali obino nisu neposredno raspoloive aplikativnom
programeru. Ako se izvrava vie programa kompiliranih istim kompilatorom,
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
219
9.3.2
El
ek
tro
ns
ko
izd
an
je
(
20
15
220
32
8
16
32
32
64
32
ILP64
8
16
64
64
64
64
LP64
8
16
32
64
64
64
LLP64
8
16
32
32
64
64
20
15
tip
char
short
int
long
long long
pointer
9.3.3
Organizacija memorije
an
je
(
tro
ns
ko
izd
El
ek
U nastavku e biti opisana prva tri, dok e o hip segmentu biti vie rei u
poglavlju 10.9, posveenom dinamikoj alokaciji memorije.
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 podaci i programi. Dok su ostala tri segmenta predviena za uvanje podataka,
u segmentu kda se nalazi sm izvrni kd programa njegov mainski kd
221
koji ukljuuje mainski kd svih funkcija programa (ukljuujui kd svih korienih 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.
20
15
Segment podataka
an
je
(
U segmentu podataka uvaju se odreene vrste promenljivih koje su zajednike za ceo program (one koje imaju statiki ivotni vek, najee globalne
promenljive), kao i konstantni podaci (najee konstantne niske). Ukoliko
se istovremeno izvrava vie instanci istog programa, svaka instanca ima svoj
zaseban segment podataka. Na primer, u programu
ko
#include <stdio.h>
int a;
int main() {
int b;
static double c;
printf("Zdravo\n");
return 0;
}
izd
Program 9.7.
El
ek
tro
ns
222
izd
an
je
(
20
15
ko
Vrh steka
f(),
njeni argumenti,
lokalne promenljive,
...
main(),
njeni argumenti,
lokalne promenljive,
...
El
ek
tro
ns
Vrh steka
main(),
njeni argumenti,
lokalne promenljive,
...
Vrh steka
main(),
njeni argumenti,
lokalne promenljive,
...
Veliina stek segmenta obino je ograniena. Zbog toga je poeljno izbegavati 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.
223
sa desne strane niz biti smeten u segment podataka i sve e tei oekivano.
Predefinisana veliina steka C prevodioca se moe promeniti zadavanjem odgovarajue opcije.
int a[1000000];
int main() {
...
}
20
15
int main() {
int a[1000000];
...
}
Program 9.8.
ko
izd
an
je
(
Prenos argumenata u funkciju preko steka. Ilustrujmo prenos argumenata u funkciju na sledeem jednostavnom primeru.
El
ek
tro
ns
int main() {
int a[] = {1, 2, 3};
int s;
s = f(a, sizeof(a)/sizeof(int));
printf("s = %d, a = {%d, %d, %d}\n",
s, a[0], a[1], a[2]);
return 0;
}
U funkciji main deklarisane su dve lokalne promenljive: niz a i ceo broj s.
Na poetku izvravanja funkcije main na steku se nalazi samo jedan stek okvir
sledeeg oblika13 :
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.
224
El
ek
tro
ns
ko
izd
an
je
(
20
15
argumenta). Argumenti se inicijalizuju na vrednosti koje su prosleene prilikom 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
126: ?
<- i
122: 3
<- n
118: 100 <- a
main: 114: ...
112: ?
<- s
108: 3
104: 2
100: 1
<- a
Nakon izvravanja koraka petlje, stanje steka je sledee.
f: 134: ...
f: 134: ...
130: 2
<- s
130: 5
<- s
126: 0
<- i
126: 1
<- i
122: 3
<- n
122: 3
<- n
118: 100 <- a
118: 100 <- a
main: 114: ...
main: 114: ...
112: ?
<- s
112: ?
<- s
108: 3
108: 3
104: 2
104: 3
100: 2
<- a
100: 2
<- a
f:
main:
134:
130:
126:
122:
118:
114:
112:
108:
104:
100:
...
9
2
3
100
...
?
4
3
2
<<<<-
s
i
n
a
<- s
<- 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:
225
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
svoju promenljivu s. Kada funkcija f zavri svoje izvravanje, stanje steka je:
main: 114: ...
112: 9
<- s
108: 4
104: 3
100: 2
<- a
Nakon toga dolazi do poziva funkcije printf. Na stek okvir funkcije main
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
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.
izd
an
je
(
20
15
f:
tro
ns
Program 9.9.
ko
#include <stdio.h>
El
ek
int faktorijel(int n) {
if (n <= 0)
return 1;
else
return n*faktorijel(n-1);
}
int main() {
int n;
while(scanf("%d",&n) == 1)
printf("%d! = %d\n", n, faktorijel(n));
return 0;
}
226
20
15
9.3.4
an
je
(
U ovom poglavlju emo pokuati da proces kompilacije i povezivanja ogolimo do krajnjih granica tako to emo pokazati kako je mogue videti mainski
kd tj. sadraj objektnih modula i izvrnog programa nastalih kompilacijom
sledeeg jednostavnog programa15 . Detalji ovog primera svakako u velikoj meri
prevazilaze osnove programiranja u programskom jeziku C, ali zainteresovanom
itaocu mogu da poslue da prilino demistifikuje proces prevoenja i izvravanja C programa.
Program 9.10.
izd
#include <stdio.h>
int n = 10, sum;
tro
ns
ko
int main() {
int i;
for (i = 0; i < n; i++)
sum += i;
printf("sum = %d\n", sum);
return 0;
}
El
ek
Value
Class
|00000000|
T
|00000000|
D
|
|
U
|00000004|
C
|
|
|
|
Type
FUNC
OBJECT
NOTYPE
OBJECT
Size
|00000050|
|00000004|
|
|
|00000004|
Section
.text
.data
*UND*
*COM*
227
izd
an
je
(
20
15
ko
<main>:
e5
e4 f0
ec 20
44 24 1c 00 00 00
tro
ns
00000000
0: 55
1: 89
3: 83
6: 83
9: c7
10: 00
11: eb
13: 8b
19: 8b
1d: 01
1f: a3
24: 83
29: a1
2e: 39
32: 7c
34: a1
39: 89
3d: c7
44: e8
49: b8
4e: c9
4f: c3
00 00 00 00
24 1c
El
ek
16
15
44
d0
00
44
00
44
df
00
44
04
fc
00
00
24
00
24
00 00
1c 01
00 00
1c
00
24
24
ff
00
00
04
00
ff
00
00
00 00 00
ff
00
push
mov
and
sub
mov
ebp
ebp,esp
esp,0xfffffff0
esp,0x20
DWORD PTR [esp+0x1c],0x0
jmp
mov
mov
add
mov
add
mov
cmp
jl
mov
mov
mov
call
mov
leave
ret
29 <main+0x29>
edx,DWORD PTR ds:0x0
eax,DWORD PTR [esp+0x1c]
eax,edx
ds:0x0,eax
DWORD PTR [esp+0x1c],0x1
eax,ds:0x0
DWORD PTR [esp+0x1c],eax
13 <main+0x13>
eax,ds:0x0
DWORD PTR [esp+0x4],eax
DWORD PTR [esp],0x0
45 <main+0x45>
eax,0x0
228
ko
izd
an
je
(
20
15
tro
ns
El
ek
229
El
ek
tro
ns
ko
izd
an
je
(
20
15
230
9.3.5
izd
an
je
(
20
15
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
izvrni kd programa, one ine tzv. rantajm biblioteku i pozivaju se dinamiki,
tek u fazi izvravanja (videti poglavlje 9.3.1).
Pretpostavimo da je prethodno opisani objektni modul povezan na sledei
nain: gcc -o program program.o, dajui izvrni program program. Ako se
on pregleda (npr. komandom nm), moe da se vidi da postoji znatno vie imenovanih simbola (npr. _start, printf@@GLIBC_2.0 itd.). Ako se pogleda mainski kd funkcije main (npr. komandom objdump), moe da se vidi da su sporne
adrese uspeno razreene. Meutim, na mestu poziva funkcije printf poziva se
funkcija printf@plt, a ona poziva nedefinisanu funkciju printf@@GLIBC_2.0.
Naime, ova funkcija implementirana je u rantajm biblioteci i njena adresa bie
povezana dinamiki, na poetku izvravanja programa.
El
ek
tro
ns
ko
231
an
je
(
20
15
i program e prekinuti rad. Neposredna odgovornost za ovu greku pripada autoru programa jer je prevideo da moe doi do deljenja nulom i nije u programu
predvideo akciju koja to spreava.
U fazi izvravanja moe doi i do greaka u komunikaciji sa periferijama (na
primer, pokuaj itanja iz nepostojee datoteke na disku) itd.
izd
Pitanje 9.3.2. U kom segmentu memorije se tokom izvravanja programa uvaju: lokalne promenljive;
ko
tro
ns
Pitanje 9.3.4. Navesti barem tri vrste podataka koje se uvaju u svakom stek
okviru.
Pitanje 9.3.5. Kako se u prenose argumenti funkcija? Objasniti kako se taj
proces odvija u memoriji raunara?
El
ek
Pitanje 9.3.6. Ukoliko se na steku poziva istovremeno nae vie instanci neke
funkcije f, kakva mora biti ta funkcija f?
Pitanje 9.3.7. Da li se za svaku instancu rekurzivne funkcije stvara novi stek
okvir? Da li se za svaku instancu rekurzivne funkcije stvara nova kopija funkcije
u kd segmentu?
Pitanje 9.3.8. Da li se tokom izvravanja programa menja sadraj: (a) segmenta 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 programa? U kojim segmentima memorije se uvaju podaci? Kakva je veza izmeu
ivotnog veka promenljivih i segmenata memorije u kojima se uvaju? U kom
232
segmentu memorije se uvaju automatske promenljive, u kom statike promenljive, 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 instrukcije prevedenog C programa?
El
ek
tro
ns
ko
an
je
(
izd
int a;
int f() {
double b; static float c;
}
20
15
Pitanje 9.3.12. Za svaku promenljivu u narednom kdu rei koji joj je nivo
dosega, kakav joj je ivotni vek u kom memorijskom segmentu je smetena i
kakva joj je inicijalna vrednost.
10
20
15
Glava
an
je
(
10.1
ko
izd
Pokazivai i adrese
El
ek
tro
ns
233
234
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 pokazivai koji ukazuju na int dok je p4 tipa int:
20
15
int *p1;
int* p2;
int* p3, p4;
an
je
(
tro
ns
ko
izd
El
ek
235
El
ek
tro
ns
ko
izd
an
je
(
20
15
ove mogunosti treba veoma oprezno koristiti i iskljuivo sa jasnim ciljem (koji
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
podrazumeva da pokaziva koji ima vrednost 0 ne moe da pokazuje ni na ta
smisleno (tj. adresa 0 ne smatra se moguom). Pokaziva koji ima vrednost 0
nije mogue dereferencirati, tj. pokuaj dereferenciranja dovodi do greke tokom izvravanja programa (najee segmentation fault). Umesto konstante 0
obino se koristi simbolika konstanta NULL, definisana u zaglavlju <stdio.h>,
kao jasniji pokazatelj da je u pitanju specijalna pokazivaka vrednost.
Pokazivai se mogu eksplicitno (pa ak i implicitno) konvertovati iz jednog
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 upozorenje. Meutim, prilikom konverzije pokazivaa vri se samo prosta dodela
adrese, bez ikakve konverzije podataka na koje pokaziva ukazuje, to esto
moe dovesti do neeljenih efekata, te stoga ovakve konverzije pokazivaa 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 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
se koristi tip void*. Izraze ovog tipa je mogue eksplicitno konvertovati u
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
mogue odrediti tip takvog izraza kao ni broj bajtova u memoriji koji predstavljaju njegovu vrednost. Pre dereferenciranja, neophodno je konvertovati
vrednost ovog pokazivakog tipa u neki konkretan pokazivaki tip. Takoe,
za razliku od ostalih pokazivakih tipova, nad pokazavaima tipa void* nije
mogue vriti aritmetike operacije (o kojima e vie rei biti u poglavlju 10.4).
I na pokazivae se moe primeniti kljuna re const. Tako const int* p
oznaava pokaziva na konstantnu promenljivu tipa int, int *const p oznaava konstantni pokaziva na promenljivu tipa int, dok const int *const p
oznaava konstantni pokaziva na konstantnu promenljivu tipa int. U prvom
sluaju mogue je menjati pokaziva, ali ne i ono na ta on pokazuje, u drugom
je mogue menjati ono na ta ukazuje pokaziva, ali ne i sam pokaziva, dok
u treem sluaju nije mogue menjati ni jedno ni drugo. Trik koji pomae 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
236
sadraj
originalni pokaziva
sadraj
an
je
(
originalni pokaziva
20
15
kopija pokazivaa
novi pokaziva
kopija sadraja
izd
El
ek
tro
ns
ko
Korienjem pokazivaa mogue je stei uvid u nain smetanja promenljivih u memoriju raunara. esto se zahteva da podaci u memoriji budu poravnati (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
memorije, jer se ne prebacuju pojedinani bajtovi ve ire binarne rei). Poravnanje dodatno moe da otea konverzije pokazivaa (npr. tip char zauzima
uvek jedan bajt i char* moe da ukae na bilo koju adresu, pa njegova eksplicitna konverzija u tip int* moe dovesti do problema na sistemu gde se, na
primer, tip int poravnava na etiri bajta jer tada vrednosti int* moraju biti
deljive sa etiri, dok vrednosti char* ne moraju).
Primetimo da se u primeru int a=10, *p=&a; sadraju iste memorijske
lokacije moe pristupiti na dva razliita naina (i preko promenljive a i preko
dereferenciranja pokazivaa *p). Ta pojava se naziva alijasovanje (preklapanje)
(engl. aliasing). U primeru, *p predstavlja alijas (drugo ime) promenljive a i sadraji te dve promenljive su isti. Programi u kojima se alijasovanje intenzivno
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 alijasovanja 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
237
float f = 3.5f;
printf("%x\n", *( ( unsigned* )&f ));
20
15
an
je
(
Pravi nain da se ovaj zadatak rei je korienje unije, koja vri alijasovanje
promenljivih, ali na standardom doputeni nain jer kompilator prilikom organizovanja unije vodi rauna o pitanjima koja mogu naruiti ispravnost rada
programa (na primer, vodi se rauna o poravnanju elemenata unije).
izd
tro
ns
ko
int a;
void f(double *p) {
a = 1;
*p = 1.234;
g(a);
}
El
ek
Pod pravilima striktnog alijasovanja, moe se pretpostaviti da se izmenom vrednosti *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
1. Ako bi neko pokuao poziv f((double*)&a) koji bi doveo do alijasovanja
ova dva objekta, iako je sintaksno ovakva konverzija pokazivaa doputena,
kompilator bi ga upozorio da time naruava pravila striktnog alijasovanja i
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).
238
Pitanje 10.1.2. Koliko bajtova zauzima podatak tipa unsigned char? Koliko
bajtova zauzima podatak tipa unsigned char*?
20
15
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?
an
je
(
izd
ko
tro
ns
Pitanje 10.1.8. Ako je promenjliva tipa int*, da li joj se moe dodeliti celobrojna vrednost? Ako je promenjliva tipa double*, da li joj se moe dodeliti
celobrojna vrednost?
El
ek
10.2
4 Kao to je reeno, u sluaju nizova, po vrednosti se ne prenosi itav niz, ve samo adresa
njegovog poetka.
239
an
je
(
20
15
tro
ns
ko
izd
Zbog drugaijeg tipa argumenata, funkcija swap vie ne moe biti pozvana
na isti nain kao ranije swap(x, y). Njeni argumenti nisu tipa int, 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
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
vrednosti, kao i uvek. Funkcija se izvrava koristei samo kopije pokazivaa,
ali zahvaljujui njima ona zaista pristupa promenljivama x i y i razmenjuje im
vrednosti. Nakon povratka iz funkcije, vrednosti x i y su razmenjene. Na ovaj
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
vrednosti (preko argumenata). Na primer, naredna funkcija rauna kolinik i
ostatak pri deljenju dva cela broja (ne koristei pritom operatore / i %):
El
ek
Program 10.1.
#include <stdio.h>
void deljenje(unsigned a, unsigned b,
unsigned* pk, unsigned* po) {
*pk = 0; *po = a;
while (*po >= b) {
(*pk)++;
*po -= b;
}
}
int main() {
unsigned k, o;
240
izd
1. int a = 1, b = 2;
void f(int* p) {
p = &b;
}
int main() {
int *p = &a
f(p);
printf("%d\n", *p);
}
an
je
(
20
15
ko
2. #include<stdio.h>
void f(int* p) { p++; p += 2; p--; p = p + 1; }
tro
ns
int main() {
int a[] = {1, 2, 3, 4, 5}, *p = a, *q = a;
f(p); printf("%d\n", *p);
q++; q += 2; q--; q = q + 1; printf("%d\n", *q);
}
El
ek
241
10.3
Pokazivai i nizovi
El
ek
tro
ns
ko
izd
an
je
(
20
15
242
int a[10];
int *b;
a[3] = 5; b[3] = 8;
an
je
(
20
15
izd
int a[10];
int *b[10];
int (*c)[10];
El
ek
tro
ns
ko
U navedenom primeru, promenljiva a je niz od 10 elemenata tipa char i zauzima 10 bajtova. Promenljiva b je niz 10 pokazivaa na char i zauzima prostor
potreban za smetanje 10 pokazivaa. Promenljiva c je (jedan) pokaziva na
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
c=&a;. Tada je (*c)[0] isto to i a[0]. Ukoliko je promenljiva c deklarisana, na primer, sa char (*c)[5];, u dodeli c=&a; e biti izvrena implicitna
konverzija i ova naredba e proi kompilaciju (uz upozorenje da je izvrena
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
se prenosi samo pokaziva koji ukazuje na poetak niza. Funkcija koja prihvata
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
niza, ali ne i informacija o duini niza.
Kako se kao argument nikada ne prenosi itav niz, ve samo adresa poetka,
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
a ime niza i ako funkcija f ima prototip int f(int x[]); (ili ekvivalentno
int f(int *x);), onda se funkcija moe pozivati i za poetak niza (sa
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
nema informaciju o tome koliko elemenata niza ima nakon prosleene adrese.
243
20
15
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]?
an
je
(
Pitanje 10.3.4. Da li je vrednost a[i] ista to i (odgovoriti za svaku pojedinano) (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],
kako se moe dobiti adresa drugog elementa niza?
izd
ko
tro
ns
*d = b + 2; *c = d[-1];
El
ek
244
10.4
Pokazivaka aritmetika
El
ek
tro
ns
ko
izd
an
je
(
20
15
U prethodnom poglavlju je reeno da nakon deklaracije int a[10];, vrednosti a odgovara pokaziva na prvi element niza, vrednosti a+1 odgovara pokaziva 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 i p se razlikuju
za sizeof(int) bajtova). Na primer, ako je p pokaziva na int koji sadri
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.
Pored dereferenciranja, dodavanja i oduzimanja celih brojeva, i oduzimanja
pokazivaa, nad pokazivaima se (samo ako su istog tipa) mogu primenjivati i
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
memorije) niza od pokazivaa p2.
Unarni operatori & i * imaju vii prioritet nego binarni aritmetiki operatori. Zato je znaenje izraza *p+1 zbir sadraja lokacije na koju ukazuje p i
vrednosti 1 (a ne sadraj na adresi p+1). Unarni operatori &, *, i prefiksni ++
se primenjuju zdesna nalevo, pa naredba ++*p; inkrementira sadraj lokacije
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 primenjuju zdesna nalevo, pa *p++ vraa sadraj na lokaciji p, dok se kao boni
efekat p inkrementira.
Vezu pokazivaa i nizova i pokazivaku aritmetiku ilustruje naredni jednostavni primer.
Program 10.2.
#include <stdio.h>
int main() {
int a[] = {8, 7, 6, 5, 4, 3, 2, 1};
int *p1, *p2, *p3;
/* Ispis elemenata niza */
printf("%d %d %d\n", a[0], a[3], a[5]);
/* Inicijalizacija pokazivaca -
245
an
je
(
3
3
4
1 6
6
ko
5
5
3
6
5
izd
}
8
8
7
7
6
20
15
/* Pokazivacka aritmetika */
p1++; --p2; p3 += 2; /* a++ nije dozvoljeno */
printf("%d %d %d %lu\n", *p1, *p2, *p3, p3 - p1);
tro
ns
El
ek
Pitanje 10.4.2. Ako su promenljive p i q tipa double *, za koliko e se razlikovati vrednosti p i q nakon komande p = q+n (pri emu je n celobrojna
promenljiva)?
Pitanje 10.4.3. Ako je promenljiva p tipa double *, za koliko e se promeniti
njena vrednost (a) nakon komande ++*p;? (b) nakon komande ++(*p);? (c)
nakon komande *p++;? (d) nakon komande (*p)++;?
Da li postoji razlika izmeu komandi (*p)++ i *(p++)?
Pitanje 10.4.4. Koje binarne operacije su dozvoljene nad vrednostima istog
pokazivakog tipa (npr. nad promenljivima p1 i p2 tipa int*)?
Pitanje 10.4.5. Koje binarne aritmetike operacije se mogu primeniti nad
pokazivaem i celim brojem (npr. nad promenljivom p tipa int* i promenljivom
n tipa int)?
246
Pitanje 10.4.6. Ako su p1, p2 pokazivai istog tipa, da li postoji razlika izmeu
vrednosti: (p1-p2)+d i p1-(p2-d) ? Kog su tipa navedene vrednosti?
20
15
an
je
(
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?
izd
ko
Pitanje 10.4.9.
tro
ns
El
ek
10.5
Pokazivai i niske
247
ko
izd
an
je
(
20
15
El
ek
tro
ns
248
20
15
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
char *p;.
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).
an
je
(
ko
izd
Slino, naredna dodela nije doputena, jer nizove nije mogue dodeljivati.
tro
ns
El
ek
249
Inkrementiranje koje se koristi u navedenom kdu, moe se izvriti (u postfiksnom obliku) i u okviru same while petlje:
void strcpy(char *s, const char *t) {
while ((*s++ = *t++) != \0);
}
an
je
(
20
15
Konano, poreenje sa nulom moe biti izostavljeno jer svaka ne-nula vrednost ima istinitosnu vrednost tano:
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.
izd
ko
tro
ns
void f() {
char* a = "Zdravo";
char b[] = "Zdravo";
...
}
El
ek
250
20
15
10.6
an
je
(
Zadatak 10.5.1. Navesti jednu moguu implementaciju funkcije strlen. Navesti jednu moguu implementaciju funkcije strcpy. Navesti jednu moguu
implementaciju funkcije strcmp. Navesti jednu moguu implementaciju funkcije strrev. Navesti jednu moguu implementaciju funkcije strchr. Navesti
jednu moguu implementaciju funkcije strstr. Napomena: sve vreme koristiti
pokazivaku sintaksu.
tro
ns
int a[10][20];
ko
izd
Kao to je reeno u poglavlju 10.3, izuzev u sluaju ako je u pitanju argument sizeof ili & operatora, ime niza tipa T se konvertuje u pokaziva na T. To
vai i za viedimenzione nizove. Ime niza tipa T a[d1][d2]...[dn] se konvertuje u pokaziva na n-1-dimenzioni niz, tj. u pokaziva tipa T (*)[d2]...[dn].
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,
deklaraciju dvodimenzionog niza:
El
ek
Svaki od izraza a[0], a[1], a[2], ... oznaava niz od 20 elemenata tipa int,
te ima tip int [20] (koji se, po potrebi, implicitno konvertuje u tip int*).
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,
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
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
ima tip int (*)[20], a drugi tip int*. Ovi tipovi ponaaju se u skladu sa
optim pravilima pokazivake aritmetike. Tako je, na primer, a+i jednako
a[i], a a[0]+i je jednako &a[0][i].
Razmotrimo razliku izmeu dvodimenzionog niza i niza pokazivaa. Ako
su date deklaracije
int a[10][20];
int *b[10];
251
an
je
(
20
15
ko
izd
char meseci[][10] = {
"Greska", "Januar", "Februar", "Mart", "April",
"Maj", "Jun", "Jul", "Avgust", "Septembar",
"Oktobar", "Novembar", "Decembar"};
tro
ns
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 elemenata niza poto je izvrena inicijalizacija):
El
ek
char *meseci[] = {
"Greska", "Januar", "Februar", "Mart", "April",
"Maj", "Jun", "Jul", "Avgust", "Septembar",
"Oktobar", "Novembar", "Decembar"};
252
20
15
g h i
e f
Stek segment
a a b c d
e f
an
je
(
g h i
[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]
izd
ko
tro
ns
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" };
El
ek
253
20
15
an
je
(
ko
void f() {
char* a = "Zdravo";
char b[] = "Zdravo";
...
}
izd
tro
ns
Pokazivai i strukture
El
ek
10.7
254
20
15
istu adresu (na onu na koju je ukazivao b.p). Iako se takvo plitko kopranje
koristi (pre svega zbog utede memorije), ono je est uzrok greaka i pokazivae 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 podataka, est je obiaj da se umesto struktura u funkcije proslede njihove adrese,
tj. pokazivai na strukture. Na primer,
Pokazivai na funkcije
izd
10.8
an
je
(
Program 10.3.
ko
Funkcije se ne mogu direktno prosleivati kao argumenti drugim funkcijama, vraati kao rezultat funkcija i ne mogu se dodeljivati promenljivima.
Ipak, ove operacije je mogue posredno izvriti ukoliko se koriste pokazivai na
funkcije. Razmotrimo naredni ilustrativni primer.
tro
ns
#include <stdio.h>
El
ek
255
inc1(a, N, b);
mul2(a, N, b);
even0(a, N, b);
print(b, N);
print(b, N);
print(b, N);
return 0;
}
an
je
(
#define N 8
int main() {
int a[N] = {1, 2, 3, 4, 5, 6, 7, 8}, b[N];
20
15
ko
izd
El
ek
tro
ns
256
return 0;
20
15
izd
an
je
(
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
pokazivaa na funkciju se vri tako to se ime promenljive kojem prethodi karakter * 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);
tro
ns
ko
El
ek
a predstavlja niz od 3 pokazivaa na funkcije koje vraaju int, i primaju argument tipa int. Funkcije ije se adrese nalaze u nizu se mogu direktno i pozvati.
Na primer, naredni kd ispisuje vrednost 4:
printf("%d", (*a[0])(3));
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.
257
20
15
an
je
(
izd
ko
tro
ns
El
ek
Zadatak 10.8.1. Napisati funkciju koja u nizu odreuje najduu seriju elemenata koji zadovoljavaju dato svojstvo. Svojstvo dostaviti kao parametar funkcije. Iskoristiti je da bi se nala najdua serija parnih kao i najdua serija
258
10.9
izd
10.9.1
an
je
(
20
15
U veini realnih aplikacija, u trenutku pisanja programa nije mogue precizno predvideti memorijske zahteve programa. Naime, memorijski zahtevi
zavise od interakcije sa korisnikom i tek u fazi izvravanja programa korisnik
svojim akcijama implicitno odreuje potrebne memorijske zahteve (na primer,
koliko elemenata nekog niza e biti korieno). U nekim sluajevima, mogue
je predvideti neko gornje ogranienje, ali ni to nije uvek zadovoljavajue. Ukoliko 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. Reenje ovih problema je dinamika alokacija memorije koja omoguava da program u toku svog rada, u fazi izvravanja, zahteva (od operativnog
sistema) odreenu koliinu memorije. U trenutku kada mu memorija koja je
dinamiki 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.
tro
ns
ko
El
ek
Ona alocira blok memorije (tj. niz uzastopnih bajtova) veliine n bajtova i
vraa adresu alociranog bloka u vidu generikog pokazivaa (tipa void*). U
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,
u principu, nedefinisan (tj. zavisi od podataka koji su ranije bili uvani u tom
delu memorije).
Funkcija malloc oekuje argument tipa size_t. Podsetimo se, ovo je nenegativni celobrojni tip za ije vrednosti je rezervisano najmanje dva bajta. Ovaj
tip se razlikuje od tipa unsigned int, ali su meusobne konverzije mogue i,
zapravo, trivijalne. Tip size_t moe da se koristi za uvanje bilo kog indeksa
niza, a on je i tip povratne vrednosti operatora sizeof.
Funkcija calloc ima sledei prototip:
void *calloc(size_t n, size_t size)
259
an
je
(
20
15
izd
El
ek
tro
ns
ko
Poziv free(p) oslobaa memoriju na koju ukazuje pokaziva p (a ne memorijski prostor koji sadri sm pokaziva p), pri emu je neophodno da p pokazuje
na blok memorije koji je alociran pozivom funkcije malloc ili calloc. Oslobaanje memorije koja nije alocirana na ovaj nain dovodi do nedefinisanog
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. Redosled
oslobaanja memorije ne mora da odgovara redosledu alociranja.
Ukoliko neki dinamiki alociran blok nije osloboen ranije, on e biti osloboen prilikom zavretka rada programa, zajedno sa svom drugom memorijom
260
20
15
Program 10.4.
#include <stdio.h>
#include <stdlib.h>
an
je
(
int main() {
int n, i, *a;
tro
ns
ko
izd
El
ek
Parametar memblock je pokaziva na prethodno alocirani blok memorije, a parametar size je nova veliina u bajtovima. Funkcija realloc vraa pokaziva
tipa void* na realociran blok memorije, a NULL u sluaju da zahtev ne moe
biti ispunjen. Zahtev za smanjivanje veliine alociranog bloka memorije uvek
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
ne postoji dovoljno slobodnog prostora, onda se u memoriji trai drugo mesto
dovoljno da prihvati proireni blok i, ako se nae, sadraj postojeeg bloka se
261
20
15
kopira na to novo mesto i zatim se stari blok memorije oslobaa. Ova operacija
moe biti vremenski zahtevna.
Upotreba funkcije realloc ilustrovana je programom koji uitava cele brojeve 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
je postepeno poveavati skladini prostor tokom rada programa. Kako esta
realokacija moe biti neefikasna, u narednom programu se izbegava realokacija
prilikom unosa svakog sledeeg elementa, ve se vri nakon unoenja svakog
desetog elementa. Naravno, ni ovo nije optimalna strategija u praksi se
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).
an
je
(
Program 10.5.
#include <stdio.h>
#include <stdlib.h>
izd
tro
ns
ko
do {
printf("Unesi ceo broj (-1 za kraj): ");
scanf("%d", &i);
El
ek
262
20
15
an
je
(
if (duzina == alocirano) {
/* Kreira se novi niz */
int* new_a;
alocirano += KORAK;
new_a = malloc(alocirano*sizeof(int));
/* Kopira se sadrzaj starog niza u novi */
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;
}
izd
U ovoj implementaciji, prilikom svake realokacije vri se premetanje memorije, tako da je ona neefikasnija od verzije sa realloc.
U gore navedenom primeru koristi se konstrukcija:
a = realloc(a, alocirano*sizeof(int));
10.9.2
tro
ns
ko
U nekim sluajevima ova konstrukcija moe da bude neadekvatna ili opasna. Naime, ukoliko zahtev za proirenje memorijskog bloka ne uspe, vraa se
vrednost 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).
El
ek
263
p = (char*) malloc(5000);
tro
ns
ko
izd
an
je
(
20
15
Inicijalno je 1000 bajtova dinamiki alocirano i adresa poetka ovog bloka memorije smetena je u pokazivaku promenljivu p. Kasnije je dinamiki alocirano
5000 bajtova i adresa poetka tog bloka memorije je opet smetena u promenljivu p. Meutim, poto originalnih 1000 bajtova nije osloboeno korienjem
funkcije free, a adresa poetka tog bloka memorije je izgubljena promenom
vrednosti pokazivake promenljive p, tih 1000 bajtova biva nepovratno izgubljeno za program.
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 dugtrajnog 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
prebacuje 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 vremenski period (moda i bez prestanka) i da obrauje vee koliine ulaza, curenje
memorije postaje vidljivo, ini program neupotrebljivim i moe da uzrokuje
velike 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 memorije (engl. memory leaks profilers) koji olakavaju otkrivanje uzroka curenja
memorije.
El
ek
264
Oslobaanje neispravnog pokazivaa. Funkciji free(p) doputeno je proslediti iskljuivo adresu vraenu od strane funkcije malloc, calloc ili realloc.
ak i prosleivanje pokazivaa na lokaciju koja pripada alociranom bloku (a
nije njegov poetak) uzrokuje probleme. Na primer,
20
15
an
je
(
10.9.3
tro
ns
ko
izd
Fragmentisanje memorije
El
ek
265
an
je
(
10.9.4
20
15
Postoji nekoliko pristupa za izbegavanje fragmentisanja memorije. Ukoliko je mogue, poeljno je izbegavati dinamiku alokaciju memorije. Naime,
alociranje svih struktura podataka statiki (u sluajevima kada je to mogue)
dovodi do breg i predvidljivijeg rada programa (po cenu veeg utroka memorije). Alternativno, ukoliko se ipak koristi dinamika alokacija memorije,
poeljno je memoriju alocirati u veim blokovima i umesto alokacije jednog
objekta alocirati prostor za nekoliko njih odjednom (kao to je to, na primer,
bilo raeno u primeru datom prilikom opisa funkcije realloc). Na kraju, postoje tehnike efikasnijeg rukovanja memorijom (na primer, memory pooling), u
kojima se programer manje oslanja na sistemsko reenje, a vie na svoje reenje
koje je prilagoeno specifinim potrebama.
El
ek
tro
ns
ko
izd
U poglavlju 9.3.3, reeno je da je memorija dodeljena programu organizovana 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 memorijskom prostoru nisu imenovani, ve im se pristupa iskljuivo preko adresa.
Poto ovi objekti nisu imenovani, oni nemaju definisan doseg (a doseg pokazivaa putem kojih se pristupa dinamikim objektima podlee standardnim
pravilima). 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
trenutku kada se iscrpi memorijski prostor dodeljen programu, hip i stek potencijalno sudaraju, ali operativni sistemi obino to spreavaju i tada obino
dolazi do nasilnog prekida rada programa.
Pitanje 10.9.1.
Navesti prototip, opisati njeno ponaanje i primer korienja funkcije:
malloc;
calloc;
realloc;
free.
266
an
je
(
char *p;
p = (char*)malloc(n);
20
15
izd
tro
ns
ko
El
ek
free(p+1);
267
Pitanje 10.9.10. Koja naredba treba da se, prema dobroj praksi, izvri nakon
free(p);? ta se time postie?
Pitanje 10.9.11. Kako sistem upravlja dinamikom memorijom? Zato se ne
sme dinamiki alociran prostor osloboditi dva puta?
Pitanje 10.9.12. Opisati ukratko bar etiri este greke koje se javljaju u vezi
sa dinamikom alokacijom memorije.
20
15
an
je
(
izd
tro
ns
ko
El
ek
Zadatak 10.9.2. Napisati program koji sa standardnog ulaza uitava cele brojeve 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
pojave brojevi: 2 5 12 4 5 2 3 12 15 5 6 6 5 program treba da vrati broj
268
2. Napisati funkciju int ortonormirana(int** A, int n) kojom se proverava da li je zadata kvadratna matrica A dimenzije ortonormirana.
Za matricu emo rei da je ortonormirana ako je skalarni proizvod svakog
para razliitih vrsta jednak 0, a skalarni proizvod vrste sa samom sobom
1. Funkcija vraa 1 ukoliko je matrica ortonormirana, 0 u suprotnom.
El
ek
tro
ns
ko
izd
an
je
(
20
15
11
20
15
Glava
an
je
(
El
ek
tro
ns
ko
izd
11.1
Zaglavlje string.h
269
270
Od
<assert.h>
<complex.h>
<ctype.h>
C89
C99
C89
<errno.h>
C89
<fenv.h>
C99
<float.h>
C89
<inttypes.h>
<iso646.h>
<limits.h>
C99
NA1
C89
<locale.h>
<math.h>
<setjmp.h>
<signal.h>
<stdalign.h>
<stdarg.h>
C89
C89
C89
C89
C11
C89
<stdatomic.h>
C11
<stdbool.h>
<stddef.h>
C99
C89
<stdint.h>
<stdio.h>
<stdlib.h>
C99
C89
C89
<stdnoreturn.h>
<string.h>
<tgmath.h>
<threads.h>
<time.h>
<uchar.h>
<wchar.h>
<wctype.h>
C11
C89
C99
C11
C89
C11
NA1
NA1
Makro assert.
Funkcije za rad sa kompleksnim brojevima.
Funkcije za klasifikovanje i konvertovanje karaktera,
nezavisno od karakterske tabele koja se koristi (ASCII
ili neke druge);
Podrka za rad sa kdovima greaka koje vraaju biblioteke funkcije.
Funkcije za kontrolu okruenja za brojeve u pokretnom
zarezu.
Konstante koje specifikuju svojstva brojeva u pokretnom zarezu zavisna od implementacije.
Proirena podrka za celobrojne tipovi fiksne irine.
Makroi za opisivanje standardnih tokena.
Konstante koje specifikuju svojstva celih brojeva zavisna od implementacije.
Funkcije za lokalizaciju.
esto korie matematike funkcije.
Makroi setjmp i longjmp.
Funkcije za upravljanje signalima.
Podrka sa ravnanje objekata.
Podrka za funkcije sa promenljivim brojem parametara
Podrka za atomike operacije nad podacima deljenim
izmeu niti.
Tip bool.
Nekoliko raznorodnih tipova i konstanti (ukljuujui
NULL i size_t).
Podrka za celobrojne tipove fiksne irine.
Deklaracije osnovnih ulazno/izlaznih funkcija;
Deklaracije funkcija za konverzije, za generisanje pseudosluajnih brojeva, za dinamiku alokaciju memorije,
prekid programa itd.
Podrka za specifikovanje funkcija bez povratka
Funkcije za obradu niski.
Matematike funkcije generikog tipa.
Funkcije za upravljanje nitima.
Funkcije za rad sa datumima i vremenom
Tipovi i deklaracije za rad sa Unicode karakterima.
Funkcije za rad sa niskama karaktera vee irine.
Funkcije za klasifikovanje i konvertovanje karaktera
vee irine.
El
ek
tro
ns
ko
izd
an
je
(
20
15
Datoteka zaglavlja
size_t strlen
(char* str);
char*
char*
char*
char*
strcpy
strncpy
strcat
strncat
(char*
(char*
(char*
(char*
int
int
char*
char*
char*
source);
source, size_t num);
source);
source, size_t num);
20
15
char*
char*
char*
char*
an
je
(
destination,
destination,
destination,
destination,
271
izd
char*
ko
strlen Funkcija strlen izraunava duinu prosleene niske karaktera (terminalna nula se ne rauna).
El
ek
tro
ns
strcpy Funkcija strcpy kopira drugu navedenu nisku u prvu (pretpostavljajui da u prvoj ima dovoljno mesta za sve karaktere druge, ukljuujui
i terminalnu nulu). Funkcija strncpy takoe vri kopiranje, ali se dodatnim parameterom n kontrolie najvei broj karaktera koji moe biti
kopiran (u sluaju da je niska koja se kopira kraa od broja n ostatak se
popunjava nulama, a ukoliko je dua od broja n terminalna nula se ne
dodaje automatski na kraj). Korienje funkcije strncpy smatra se bezbednijim od strcpy jer je mogue kontrolisati mogunost prekoraenja
bafera.
strcat Funkcija strcat nadovezuje drugu navedenu nisku na prvu, pretpostavljajui da prva niska ima dovoljno prostora da smesti i sve karaktere
druge, 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 karakter nije naen. Funkcija strrchr radi slino, ali vraa pokaziva na
poslednje pojavljivanje karaktera.
272
strpbrk Funkcija strpbrk vraa pokaziva na prvo pojavljivanje nekog karaktera iz druge navedene niske u prvoj navedenoj niski ili NULL pokaziva
ako takvog karaktera nema.
20
15
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.
an
je
(
strtok Niz poziva funkcije strtok slue da podele nisku u tokene koji su niske
uzastopnih karaktera razdeljene karakterima navedenim u drugoj niski.
U prvom pozivu funkcije kao prvi argument navodi se niska koja se deli,
a u drugom navodi se NULL. Na primer, kd
tro
ns
ispisuje
ko
izd
El
ek
Ovo
je
jedna
niska
11.2
Zaglavlje stdlib.h
void
void
void
void
*malloc(size_t n);
*calloc(size_t n, size_t size);
*realloc(void *memblock, size_t size);
free(void* p);
int rand(void);
void srand(unsigned int);
int system(char* command);
void exit(int);
273
an
je
(
20
15
rand Funkcija int rand(void); generie pseudo-sluajne cele brojeve u intervalu od 0 do vrednosti RAND_MAX (koja je takoe definisana u zaglavlju
<stdlib.h>). Termin pseudo-sluajni se koristi da se naglasi da ovako
dobijeni brojevi nisu zaista sluajni, ve su dobijeni specifinim izraunavanjima 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:
Funkcija rand() moe biti jednostavno upotrebljena za generisanje pseudosluajnih brojeva u celobrojnom intervalu [, ]:
izd
n+rand()%(m-n+1)
ko
El
ek
tro
ns
srand Funkcija rand niz pseudo-sluajnih brojeva generie uvek poev od iste
podrazumevane vrednosti (koja je jednaka 1). To znai da e se u svakom
pokretanju programa dobiti isti niz pseudo-sluajnih brojeva. Funkcija
void srand(unsigned); postavlja vrednost koja se u sledeem pozivu
funkcije rand (a time, indirektno, i u svim narednim) koristi u generisanju tog niza. Za bilo koju vrednost funkcije srand, itav niz brojeva
koji vraa funkcija rand je uvek isti. To je pogodno za ponavljanje (na
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
<time.h>) da bi se postiglo da se pri svakom pokretanju programa dobije
razliit niz vrednosti.
274
20
15
isalpha(int
isalnum(int
isupper(int
toupper(int
c);
c);
c);
c);
int
int
int
int
isdigit(int
isspace(int
islower(int
tolower(int
ko
int
int
int
int
Zaglavlje ctype.h
tro
ns
11.3
izd
an
je
(
exit Funkcija void exit(int); zaustavlja rad programa (bez obzira iz koje
funkcije je pozvana) i vraa svoj argument kao povratnu vrednost programa. Obino povratna vrednost nula oznaava uspeno izvrenje programa, a vrednost ne-nula ukazuje na neku greku tokom izvravanja.
esto se koriste i simbolike konstante EXIT_SUCCESS EXIT_FAILURE za
uspeh i za neuspeh. Naredba exit(e); u okviru funkcije main ekvivalentna je naredbi return e;. Funkcija exit automatski poziva fclose
za svaku datoteku otvorenu u okviru programa (vie o funkciji fclose
bie reeno u glavi 12).
c);
c);
c);
c);
El
ek
Zaglavlje ctype.h sadri deklaracije nekoliko funkcija za ispitivanje i konvertovanje karaktera. Sve funkcije navedene u nastavku imaju argument tipa
int i imaju int kao tip povratne vrednosti.
isalpha(c) vraa ne-nula vrednost ako je c slovo, 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
tolower(c) vraa karakter c konvertovan u malo slovo ili, ukoliko je to nemogue sm karakter c;
Zaglavlje math.h
sin(double);
cos(double);
tan(double);
log(double);
pow(double);
double
double
double
double
double
asin(double);
acos(double);
atan(double); double atan2(double);
log10(double); double log2(double);
exp(double); double sqrt(double);
double
double
double
double
double
20
15
11.4
an
je
(
Zaglavlje math.h sadri deklaracije vie od dvadeset esto korienih matematikih funkcija. Svaka od njih ima jedan ili dva argumenta tipa double i
ima double kao tip povratne vrednosti.
sin(x) vraa vrednost funkcije sin(), smatra se da je zadato u radijanima;
cos(x) vraa vrednost funkcije cos(), smatra se da je zadato u radijanima;
izd
ko
tro
ns
log(x) vraa vrednost ln (mora da vai > 0); logaritam log (za , > 0,
= 1) moe se izraunati kao log(b)/log(a).
log10(x) vraa vrednost log10 (mora da vai > 0);
log2(x) vraa vrednost log12 (mora da vai > 0);
El
ek
2 itd.
276
11.5
Zaglavlje assert.h
void assert(int)
20
15
tro
ns
ko
izd
an
je
(
El
ek
12
20
15
Glava
an
je
(
12.1
ko
izd
Standardni tokovi
El
ek
tro
ns
277
278
12.1.1
20
15
an
je
(
El
ek
tro
ns
ko
izd
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
<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)
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.
Ako bi povratni tip funkcije getchar bio char, a povratna vrednost u nekom
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
bila vrednost 255 koja bi pre poreenja sa -1 bila promovisana u tip int).
Funkcija getchar() najee se realizuje tako to karaktere uzima iz privremenog bafera koji se puni itanjem jedne po jedne linije ulaza. Dakle, u
interaktivnom radu sa programom, getchar() nee imati efekta sve dok se ne
unese prelazak u novi red ili oznaka za kraj datoteke.1
Najjednostavniji mehanizam pisanja na standardni izlaz je pisanje jednog
po jednog karaktera korienjem funkcije putchar:
int putchar(int);
279
mala slova.
Program 12.1.
20
15
an
je
(
int main()
{
int c;
while ((c = getchar()) != EOF)
putchar(tolower(c));
return 0;
}
#include <stdio.h>
#include <ctype.h>
ko
12.1.2
izd
tro
ns
El
ek
ita karaktere sa standardnog ulaza do kraja tekue linije ili do kraja datoteke
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
opasnom za korienje. U sluaju da je ulaz uspeno proitan, gets vraa s, a
inae vraa NULL pokaziva.
Biblioteka funkcija fputs:
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.1.3
an
je
(
20
15
Funkcija printf prevodi vrednosti osnovnih tipova podataka u serije karaktera i usmerava ih na standardni izlaz. Funkcija vraa broj odtampanih
karaktera. Sluajevi korienja ove funkcije iz prethodnih glava jesu najuobiajeniji, ali svakako nisu potpuni. Pozivi funkcija putchar i printf mogu biti
isprepletani izlaz se vri u istom redosledu u kojem su ove funkcije pozvane.
Funkcija printf prevodi, formatira i tampa svoje argumente na standardni
izlaz pod kontrolom date format niske. Format niska sadri dve vrste 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 karakterom % i zavrava se karakterima konverzije. Izmeu % i karaktera
konverzije mogue je da se redom nau:
izd
ko
tro
ns
El
ek
Broj za preciznost, koji specifikuje najvei broj karaktera koje treba tampati 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.
Karakter h, ako ceo broj treba da se tampa kao short, ili l ako ceo broj
treba da se tampa kao long.
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
double
e,E
double
g,G
double
p
%
void *
int
int
char *
izd
u
c
s
tampa se kao
dekadni broj
neoznaeni oktalni broj (bez vodeeg simbola 0)
neoznaeni heksadekadni broj (bez vodeih 0x ili 0X),
korienjem abcdef ili ABCDEF za 10, ...,15
neoznaeni dekadni broj
pojedinani karakter
tampa karaktere niske do karaktera \0 ili broja karaktera navedenog u okviru preciznosti.
[-]m.dddddd, gde je broj d-ova odreen preciznou
(podrazumevano 6)
[-]m.dddddde+/-xx ili [-]m.ddddddE+/-xx, gde je
broj d-ova odreen preciznou (podrazumevano 6)
koristi %e ili %E ako je eksponent manji od -4 ili vei
ili jednak preciznosti; inae koristi %f. Zavrne nule i
zavrna decimalna taka se ne tampaju
pokaziva (reprezntacija zavisna od implementacije)
nijedan argument se ne konvertuje; tampa %
20
15
Tip
int
int
int
an
je
(
Karakter
d,i
o
x,X
El
ek
tro
ns
ko
282
20
15
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",
count, count, count, count );
an
je
(
Celobrojni formati:
Dekadni: -9234 Poravnat: -009234 Neoznacen: 4294958062
Broj -9234 prikazan kao:
Hex: FFFFDBEEh C hex: 0xffffdbee Oct: 37777755756
izd
tro
ns
ko
Cifre 10 predstavljaju:
Hex: 16 Octal: 8 Decimal: 10
Primer prikaza karaktera:
El
ek
char ch = h;
printf("Karakteri u polju date sirine:\n%10c%c\n", ch, ch);
Karakteri u polju date sirine:
hh
283
12.1.4
20
15
:hello, world:
:hello, world:
:hello, wor:
:hello, world:
:hello, world:
:hello, world
:
:
hello, wor:
:hello, wor
:
an
je
(
:%s:
:%10s:
:%.10s:
:%-10s:
:%.15s:
:%-15s:
:%15.10s:
:%-15.10s:
izd
El
ek
tro
ns
ko
Opis format niske dat je u nastavku. Ostali argumenti moraju biti pokazivai i odreuju lokacije na koje se smeta konvertovani ulaz. Kao i u sluaju
funkcije printf, ovde e biti dat samo prikaz najeih naina korienja ove
funkcije. Funkcija scanf prestaje sa radom kada iscrpi svoju format nisku ili
kada neki deo ulaza ne moe da se uklopi u shemu navedenu format niskom.
Funkcija vraa broj uspeno uklopljenih i dodeljenih izlaznih podataka. U sluaju kraja datoteke, funkcija vraa EOF. Ova vrednost je razliita od vrednosti
0 koja se vraa u sluaju da tekui karakter sa ulaza ne moe da se uklopi u
prvu specifikaciju zadatu format niskom. Svaki naredni poziv funkcije scanf
nastavlja tano od mesta na ulazu na kojem je prethodni poziv stao.
Format niska sadri specifikacije konverzija kojima se kontrolie konverzija
teksta proitanog sa ulaza. Format niska moe da sadri:
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 karaktera * koji spreava dodeljivanje, opcionog broja kojim se navodi maksimalna 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
20
15
tro
ns
ko
izd
an
je
(
El
ek
285
20
15
Primeri
an
je
(
#include <stdio.h>
int main()
{
double sum, v;
izd
sum = 0.0;
while (scanf("%f", &v) == 1)
printf("\t%.2f\n", sum += v);
return 0;
ko
tro
ns
El
ek
Ispred monthname nema potrebe navesti simbol &, jer je ime niza ve pokaziva. Doslovni karakteri se mogu pojaviti u okviru format niske i u tom
sluaju 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
Funkcija printf vri ispis formatiranog teksta na standardni izlaz. Standardna biblioteka jezika C definie funkciju sprintf koja je veoma slina funk-
286
ciji printf, ali rezultat njenog rada je popunjavanje niske karaktera formatiranim tekstom. Prototip funkcije je:
int sprintf(char *string, const char *format, arg1, arg2, ...);
20
15
an
je
(
izd
El
ek
12.3
tro
ns
ko
287
12.3.1
El
ek
tro
ns
ko
izd
an
je
(
20
15
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 tabulatora.
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 upisuje
linija po linija. Oigledno, u ovom sluaju oznaka za kraj linije je veoma relevantna i znaajna. Meutim, na razliitim sistemima tekst se kodira na razliite
naine i na nekim sistemima kraj reda u tekstualnoj datoteci se zapisuje 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 o ovakvim
specifinostima, C jezik nudi tekstualni mod rada sa datotekama. Ukoliko se
datoteka otvori u tekstualnom modu, prilikom svakog itanja i upisa u datoteku vri se konverzija iz podrazumevanog formata oznaavanja kraja reda u
jedinstven karakter \n. Tako e na Windows sistemima dva karaktera \r\n na
kraju reda biti proitana samo kao \n i, obratno, kada se u datoteku upisuje \n
bie upisana dva karaktera \r\n. Na ovaj nain pojednostavljuje se i olakava
rad sa datotekama koje ine tekstualni podaci. Da bi interpretiranje sadraja
tekstualne datoteke bilo uvek ispravno treba izbegavati, na primer, pravljenje
tekstualnih datoteka koja na kraju poslednjeg reda nemaju oznaku kraja reda,
izbegavati razmake u linijama nakon oznake za kraj reda, izbegavati da tekstualne datoteke sadre karaktere koji nisu vidljivi karakteri, oznake kraja reda i
tabulatora i slino.
Drugi nain pristupa datotekama prilagoen je datotekama iji sadraj ne
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
binarni mod rada sa datotekama. U ovom sluaju nema nikakve konverzije
i interpretiranja karaktera prilikom upisa i itanja i svaki bajt koji postoji u
datoteci se ita doslovno.
Razlika izmeu tekstualnih i binarnih datoteka jo je otrija u jezicima koji
podravaju Unicode, jer se u tom sluaju vie bajtova ita i konvertuje u jedan
karakter.
12.3.2
Pristupanje datoteci
288
tro
ns
ko
izd
an
je
(
20
15
Funkcija fopen dobija nisku koja sadri ime datoteke (na primer, datoteka.txt)
i uz pomo usluga operativnog sistema (koje nee na ovom mestu biti detaljnije opisane) vraa pokaziva na strukturu (najee dinamiki alociranu) koja
predstavlja sponu izmeu lokalne datoteke i programa i koja sadri informacije 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
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
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).
Prvi argument funkcije fopen je niska karaktera koja sadri ime datoteke.
Drugi argument je niska karaktera koja predstavlja nain (mod) otvaranja datoteke i koja ukazuje na to kako e se datoteka koristiti. Dozvoljeni modovi
ukljuuju itanje (read, "r"), pisanje (write, "w") i dopisivanje (append, "a").
Ako se datoteka koja ne postoji na sistemu otvara za pisanje ili dopisivanje,
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
se pokuava itanje datoteke koja ne postoji dobija se greka. Takoe, greka
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
(ili dopisivanje). U sluaju da se eli otvaranje binarne datoteke, na mod se
dopisuje "b" (na primer, rb, wb, a+b, . . . ).
Kada se C program pokrene, operativni sistem otvara tri toka podataka
(standardni ulaz, stadnardni izlaz i standardni izlaz za greke), kao da su datoteke i obezbeuje pokazivae kojima im se moe pristupati. Ti pokazivai se
nazivaju:
El
ek
FILE* stdin;
FILE* stdout;
FILE* stderr;
Funkcija
289
12.3.3
20
15
an
je
(
tro
ns
Program 12.3.
ko
izd
Slino kao i getchar i putchar, getc i putc mogu biti definisani kao makroi,
a ne funkcije.
Naredni primer kopira sadraj datoteke ulazna.txt u datoteku izlazna.txt
itajui i piui pritom pojedinane karaktere. Naglasimo da je ovo veoma neefikasan nain kopiranja datoteka.
#include <stdio.h>
El
ek
290
20
15
fclose(ulaz);
fclose(izlaz);
return 0;
}
an
je
(
Funkcija ungetc:
tro
ns
ko
izd
vraa karakter u datoteku i vraa identifikator pozicije datoteke tako da naredni poziv operacije itanja nad ovom datotekom vrati upravo vraeni karakter. Ovaj karakter moe, ali ne mora biti jednak poslednjem proitanom
karakteru datoteke. Iako ova funkcije utie na naredne operacije itanja datoteke, 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.
Naredni primer ita karaktere iz datoteke dok su u pitanju cifre i pri tom
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
itanja.
#include <stdio.h>
El
ek
12.3.4
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.3.5
20
15
an
je
(
izd
tro
ns
ko
Ona vraa EOF 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.
Implementacija bibliotekih funkcija nije znaajno razliita od implementacije ostalih funkcija, to je ilustrovano implementacijama funkcija fgets i
fputs, u obliku doslovno preuzetom iz jedne realne implementacije C biblioteke:
El
ek
}
/* fputs: put string s on file iop */
int fputs(char *s, FILE *iop)
292
{
register int c;
while (c = *s++)
putc(c, iop);
return ferror(iop) ? EOF : 0;
12.3.6
izd
an
je
(
20
15
tro
ns
ko
El
ek
12.3.7
293
20
15
an
je
(
izd
Prvi argument obe funkcije je pokaziva na datoteku. Funkcija fseek kao drugi
argument dobija pomeraj izraen u broju bajtova, dok su za trei argument
dozvoljene vrednosti SEEK_SET koja oznaava da se pomeraj rauna u odnosu
na poetak datoteke, SEEK_CUR koji oznaava da se pomeraj rauna u odnosu
na tekuu poziciju i SEEK_END koji oznaava da se pomeraj rauna u odnosu
na kraj datoteke.
Sledei primer ilustruje primenu ovih funkcija.
Program 12.4.
El
ek
tro
ns
int main() {
struct S {
int broj;
int kvadrat;
} s;
ko
#include <stdio.h>
FILE* f;
int i;
if ((f = fopen("junk", "r+b")) == NULL) {
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);
}
294
fclose(f);
}
an
je
(
12.4
20
15
tro
ns
ko
izd
El
ek
295
./echoargs
-U
zdravo
svima
dobar dan
20
15
argc = 5
argv[0] =
argv[1] =
argv[2] =
argv[3] =
argv[4] =
an
je
(
Program 12.6.
ko
#include <stdio.h>
izd
El
ek
tro
ns
296
#include <stdio.h>
Pitanje 12.1.
ta je to stderr?
an
je
(
20
15
izd
tro
ns
ko
Pitanje 12.4. ta, u format niski koja je argument funkcije printf, specifikuje
opcioni broj koji se navodi iza opcione take za argument koji je niska (na
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);)?
El
ek
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?
297
Pitanje 12.11. Navesti prototip funkcije fopen. Koju vrednost vraa ova funkcija ukoliko se datoteka ne moe otvoriti?
Pitanje 12.12. Navesti prototip standardne biblioteke funkcije za zatvaranje datoteke. Koju vrednost ona vraa? Navesti bar dva razloga zbog kojih je
preporuljivo zatvoriti datoteku im se zavri njeno korienje.
20
15
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?
an
je
(
Pitanje 12.16. emu slue funkcije fseek i fsetpos? Da li one isto rade za
tekstualne i binarne datoteke?
izd
ko
Pitanje 12.19. Kako se, u okviru funkcije main, moe ispisati ime programa
koji se izvrava?
tro
ns
Zadatak 12.1. Napisati funkciju main koja izraunava i ispisuje zbir svih
argumenata navedenih u komandnoj liniji (pretpostaviti da e svi argumenti
El
ek
Zadatak 12.2. Napisati program koji iz datoteke, ije se ime zadaje kao prvi
argument komandne linije, ita prvo dimenziju kvadratne matrice , a zatim elemente matrice (pretpostavljamo da se u datoteci nalaze brojevi pravilno
rasporee.ni, odnosno da za dato , sledi elemenata matrice). Matricu
dinamiki alocirati. Nakon toga, na standardni izlaz ispisati redni broj kolone
koja ima najvei zbir elemenata. Na primer, za datoteka sa sadrajem:
3
1 2 3
7 3 4
5 3 1
program treba da ispie redni broj 0 (jer je suma elemenata u nultoj koloni
298
an
je
(
20
15
typedef struct {
char ime[20];
char prezime[20];
unsigned br_ispita;
} APSOLVENT;
El
ek
tro
ns
ko
izd
20
15
Dodatak
El
ek
tro
ns
ko
izd
an
je
(
299
El
ek
Asocijativnost
sleva na desno
zdesna na levo
ko
izd
an
je
(
20
15
Opis
poziv funkcije
indeksni pristup elementu niza
pristup lanu strukture/unije
pristup lanu strukture/unije preko pokazivaa
postfiksni inkrement/dekrement
prefiksni inkrement/dekrement
unarni plus/minus
logika negacija/bitski komplement
eksplicitna konverzija tipa (cast)
dereferenciranje
referenciranje (adresa)
veliina u bajtovima
mnoenje, deljenje, ostatak
sabiranje i oduzimanje
bitsko pomeranje ulevo i udesno
relacije manje, manje jednako
relacije vee, vee jednako
relacija jednako, razliito
bitska konjunkcija
bitska ekskluzivna disjunkcija
bitska disjunkcija
logika konjunkcija
logika disjunkcija
ternarni uslovni
dodele
tro
ns
Operator
()
[]
.
->
++ -++ -+ ! ~
(type)
*
&
sizeof
* / %
+ << >>
< <=
> >=
== !=
&
^
|
&&
||
?:
=
+= -=
*= /= %=
&= ^= |=
<<= >>=
,
300
sleva na desno
sleva na desno
sleva na desno
sleva na desno
sleva na desno
sleva na desno
sleva na desno
sleva na desno
sleva na desno
zdesna na levo
zdesna na levo
sleva na desno
20
15
Dodatak
izd
an
je
(
Reenja zadataka
El
ek
tro
ns
mov [200], 0
mov ax, 0
mov bx, 1
petlja:
cmp ax, bx
je uvecanje
mov cx, [100]
cmp cx, ax
je kraj
add ax, 1
jmp petlja
uvecanje:
mov cx, [200]
add cx, 1
mov [200], cx
add bx, cx
add bx, cx
jmp petlja
kraj:
ko
301
302
B. Reenja zadataka
(b) 964
(c) 476
20
15
an
je
(
izd
(a) rgb(53, 167, 220) (b) #FFFF00 (c) Svaki par heksadekadnih cifara je isti
(npr. #262626 ili #A0A0A0).
tro
ns
ko
El
ek
(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
20
15
an
je
(
ko
izd
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
01001101 01101001 01110010 01100001 01101110 01101010 01100101 00100000
00110001 00100010
Oktalno: 042 120 162 157 147 162 141 115 151 162 141 156 152 145 040 061
042
Dekadno: 34 80 114 111 103 114 97 109 105 114 97 110 106 101 32 49 34
Reenje zadatka 2.14:
ASCII
11
Windows-1250
11
11
tro
ns
raunarstvo
informatika
ISO-8859-5
11
ISO-8859-2
11
11
Unicode (UCS-2)
22
22
UTF-8
12
11
El
ek
UCS-2 : 006b 0072 0075 017e 0069 0107 UTF-8 : 6b 72 75 c5be 69 c487
Izraunljivost
Reenje zadatka 3.1:
= + (1 + . . . + 1) + . . . + (1 + . . . + 1)
... ...
304
B. Reenja zadataka
ko
:= 0
izd
an
je
(
20
15
...
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?
4. (1, 3)
:=
5. (4)
:= + 1
6. (4, 2, 12)
= ?
7. (5)
:= 0
8. (5)
:= + 1
9. (3)
:= + 1
10. (5, 1, 5)
11. (1, 1, 8)
12. (3, 1)
1
13. (1, 1, 100)
14. (1)
0 1
tro
ns
=0
=0
0 1
:=
El
ek
:= + 1
=
:= 0
:= + 1
:= + 1
305
B. Reenja zadataka
20
15
... ...
i sledeu radnu konfiguraciju:
1 2 3 4 . . .
...
:= 0
>
izd
an
je
(
:= 0
:= + 1
:= + 1
:= + 1
tro
ns
ko
El
ek
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
(3)
(4)
(1, 3, 11)
(2, 3, 7)
(3)
(1, 1, 3)
(3)
(4)
(3, 1, 11)
(1, 1, 7)
(4, 1)
Jezik C - uvod
Reenje zadatka 5.2.1:
=0
=0
= ?
= ?
:= + 1
:= + 1
:= + 1
= ?
1
306
B. Reenja zadataka
#include <stdio.h>
#include <math.h>
#include <assert.h>
an
je
(
20
15
int main() {
double r;
printf("Unesi poluprecnik kruga: ");
scanf("%lf", &r);
assert(r > 0);
printf("Obim kruga je: %lf\n", 2*r*M_PI);
printf("Povrsina kruga je: %lf\n", r*r*M_PI);
return 0;
}
izd
#include <stdio.h>
#include <math.h>
tro
ns
ko
int main() {
/* Koordinate tacaka A, B, C */
double xa, ya, xb, yb, xc, yc;
/* Duzine stranica BC, AC, AB */
double a, b, c;
/* Poluobim i povrsina trougla ABC */
double s, P;
El
ek
i
yc));
yc));
yb));
307
B. Reenja zadataka
#include <stdio.h>
#include <math.h>
#include <assert.h>
an
je
(
20
15
int main() {
/* Duzine stranica trougla */
double a, b, c;
/* Velicine uglova trougla */
double alpha, beta, gamma;
izd
ko
tro
ns
El
ek
return 0;
#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
an
je
(
20
15
int main() {
/* Pocetna brzina u m/s, ubrzanje u m/s^2 i vreme u s*/
double v0, a, t;
tro
ns
#include <stdio.h>
ko
izd
El
ek
int main() {
double a1, a2, a3, a4, a5, a6, a7, a8, a9;
scanf("%lf%lf%lf", &a1, &a2, &a3);
scanf("%lf%lf%lf", &a4, &a5, &a6);
scanf("%lf%lf%lf", &a7, &a8, &a9);
/* Determinantu ra\v cunamo primenom Sarusovog pravila:
a1 a2 a3 a1 a2
a4 a5 a6 a4 a5
a7 a8 a9 a7 a8
- 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;
}
#include <stdio.h>
#include <assert.h>
309
B. Reenja zadataka
20
15
tro
ns
ko
int main() {
int a, b, i;
scanf("%d%d", &a, &b);
for (i = a; i <= b; i++)
printf("%d\n", i*i*i);
return 0;
}
izd
#include <stdio.h>
an
je
(
int main() {
int a1, a2, a3; /* brojevi */
int m; /* maksimum brojeva */
printf("Unesi tri broja: ");
scanf("%d %d %d", &a1, &a2, &a3);
m = a1;
if (a2 > m)
m = a2;
if (a3 > m)
m = a3;
printf("Maksimum je: %d\n", m);
return 0;
}
#include <stdio.h>
Jezik C - izrazi
El
ek
#include <stdio.h>
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;
}
s;
m;
h;
60) {
20
15
= 60 = 59 = 23 (s1 ==
s1 = 0;
m1++;
}
if (m1 == 60) {
m1 = 0;
h1++;
}
printf("%d:%d:%d\n", h1, m1, s1);
return 0;
tro
ns
#include <stdio.h>
ko
izd
an
je
(
s1
m1
h1
if
El
ek
int main() {
double x, y, z;
scanf("%lf%lf%lf", &x, &y, &z);
if (x > 0 && y > 0 && z > 0 &&
x + y > z && x + z > y && y + z > x)
printf("Trougao postoji\n");
else
printf("Trougao ne postoji\n");
return 0;
}
#include <stdio.h>
int main() {
/* Broj cija se suma cifara racuna */
int n;
/* Cifre broja*/
311
B. Reenja zadataka
20
15
=
=
=
=
n % 10;
(n / 10) % 10;
(n / 100) % 10;
(n / 1000) % 10;
an
je
(
c0
c1
c2
c3
izd
#include <stdio.h>
El
ek
tro
ns
ko
int main() {
unsigned n, c0, c1; /* broj, poslednja i pretposlednja cifra */
printf("Unesi broj: ");
scanf("%d", &n);
c0 = n % 10;
c1 = (n / 10) % 10;
printf("Zamenjene poslednje dve cifre: %u\n",
(n / 100)*100 + c0*10 + c1);
return 0;
}
#include <stdio.h>
int main() {
/*
(0, 0) -> 1
(0, 1) -> 2
(2, 0) -> 4
(0, 3) -> 7
...
*/
(1, 0) -> 3
(1, 1) -> 5
(1, 2) -> 8
(0, 2) -> 6
(2, 1) -> 9
(3, 0) -> 10
B. Reenja zadataka
20
15
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;
else
n = z*(z + 1)/2 + y + 1;
printf("Redni broj u cik-cak nabrajanju je: %u\n", n);
return 0;
312
an
je
(
#include <stdio.h>
tro
ns
ko
izd
int main() {
double A, B;
printf("Unesi koeficijente A i B jednacine A*X + B = 0: ");
scanf("%lf%lf", &A, &B);
if (A != 0)
printf("Jednacina ima jedinstveno resenje: %lf\n", -B/A);
else if (B == 0)
printf("Svaki realan broj zadovoljava jednacinu\n");
else
printf("Jednacina nema resenja\n");
return 0;
}
El
ek
#include <stdio.h>
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
*/
an
je
(
D = a1*b2 - b1*a2;
/* Determinanta promenljive x:
c1 b1
c2 b2
*/
Dx = c1*b2 - b1*c2;
/* Determinanta promenljive y:
a1 c1
a2 c2
*/
Dy = a1*c2 - c1*a2;
if (D != 0)
printf("x = %lf\ny = %lf\n", Dx / D, Dy / D);
else if (Dx == 0 && Dy == 0)
printf("Sistem ima beskonacno mnogo resenja\n");
else
printf("Sistem nema resenja\n");
return 0;
tro
ns
ko
izd
#include <stdio.h>
#include <math.h>
#include <assert.h>
B. Reenja zadataka
20
15
313
El
ek
int main() {
float a, b, c; /* koeficijenti */
float D;
/* diskriminanta */
printf("Unesi koeficijente a, b, c"
" kvadratne jednacine ax^2 + bx + c = 0: ");
scanf("%f%f%f", &a, &b, &c);
assert(a != 0);
D = b*b - 4*a*c;
if (D > 0) {
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
izd
#include <stdio.h>
#include <ctype.h>
20
15
an
je
(
int main() {
unsigned n; /* broj koji se ispituje */
unsigned d; /* kandidat za delioca */
scanf("%u", &n);
for (d = 1; d <= n; d++)
if (n % d == 0)
printf("%u\n", d);
return 0;
}
#include <stdio.h>
tro
ns
ko
int main() {
int c;
while ((c = getchar()) != . && c != , && c != EOF)
putchar(toupper(c));
}
Jezik C - nizovi
El
ek
#include <stdio.h>
#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
an
je
(
20
15
izd
tro
ns
ko
#include <stdio.h>
int main() {
int b[10], i;
El
ek
316
B. Reenja zadataka
#include <stdio.h>
#include <assert.h>
tro
ns
ko
izd
an
je
(
20
15
int main() {
int d, m, g, b, M;
/* Broj dana po mesecima. Niz je napravljen tako da se broj dana za
mesec m moze ocitati sa br_dana[m].
*/
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 */
if (g < 0) {
printf("Pogresna godina\n");
return 1;
}
if (m < 1 || m > 12) {
printf("Pogresan mesec\n");
return 1;
}
if (d < 1 || d > (m == 2 && prestupna(g) ? 29 : br_dana[m])) {
printf("Pogresan dan\n");
return 1;
}
El
ek
b = 0;
/* Broj dana u prethodnim mesecima */
for (M = 1; M < m; M++)
b += br_dana[M];
/* Broj dana u tekucem mesecu */
b += d;
/* Eventualno dodati i 29. 2. */
if (prestupna(g) && m > 2)
b++;
printf("%d\n", b);
return 0;
#include <stdio.h>
317
B. Reenja zadataka
20
15
izd
an
je
(
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 */
/* (n n) = 1 */
a[n] = 1;
for (k = n-1; k > 0; k--)
/* (n k) = (n-1 k) + (n-1 k-1) */
a[k] = a[k] + a[k-1];
/* (n 0) = 1 */
/* a[0] = 1; -- ovo vec vazi */
#include <ctype.h>
return 0;
ko
tro
ns
#include <stdio.h>
#include <assert.h>
El
ek
int main() {
int n, A[10][10], i, j, s;
/* ucitavamo matricu */
scanf("%d", &n); assert(0 < n && n <= 10);
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
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
20
15
#include <stdio.h>
#include <assert.h>
tro
ns
ko
izd
an
je
(
int main() {
int n, A[100][100], i, j, dt;
/* ucitavamo matricu */
scanf("%d", &n); assert(0 < n && n <= 100);
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
scanf("%d", &A[i][j]);
/* proveravamo da li je matrica donjetrougaona */
dt = 1;
for (i = 0; i < n && dt; i++)
for (j = i+1; j < n && dt; j++)
if (A[i][j] != 0)
dt = 0;
printf("Matrica %s donjetrougaona\n", dt ? "je" : "nije");
return 0;
}
El
ek
#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
tro
ns
#include <stdio.h>
#include <assert.h>
ko
izd
20
15
an
je
(
int i;
for (i = 0; i < 10; i++) {
/* Tekuci broj */
COMPLEX z;
/* Moduo tekuceg broja */
double z_m;
/* Ucitavanje */
scanf("%lf%lf", &z.Re, &z.Im);
/* Racunanje modula */
z_m = sqrt(z.Re*z.Re + z.Im*z.Im);
/* Azuriranje najveceg */
if (z_m > max_m) {
max_z = z;
max_m = z_m;
}
}
printf("%lf + i*%lf\n", max_z.Re, max_z.Im);
return 0;
/* Najveci moduo */
double max_m = 0.0;
El
ek
int main() {
enum dani {PON = 1, UTO, SRE, CET, PET, SUB, NED};
int dan;
scanf("%d", &dan);
assert(1 <= dan && dan <= 7);
if (dan == SUB || dan == NED)
printf("Vikend\n");
else
printf("Radni dan\n");
return 0;
}
Jezik C - naredbe
Reenje zadatka 7.1:
#include <stdio.h>
320
B. Reenja zadataka
20
15
int main() {
int n, i;
printf("Unesi gornju granicu: ");
scanf("%d", &n);
for (i = 1; i < n; i += 2)
printf("%d ", i);
printf("\n");
return 0;
}
an
je
(
#include <stdio.h>
#include <math.h>
tro
ns
ko
izd
int main() {
int N = 100;
double l = 0.0, d = 2*M_PI;
double h = (d - l) / (N - 1);
double x;
printf(" x
sin(x)\n");
for (x = l; x <= d; x += h)
printf("%4.2lf
%7.4lf\n", x, sin(x));
return 0;
}
El
ek
#include <stdio.h>
int main() {
double x, s;
unsigned n, i;
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
#include <stdio.h>
#define N 4
20
15
int main() {
int i, j;
/* Deo (a): */
an
je
(
izd
printf("--------------\n");
tro
ns
ko
/* Deo (b): */
for (i = 0; i < N; i++) {
for (j = 0; j < N - i; j++)
putchar(*);
putchar(\n);
}
printf("--------------\n");
El
ek
/* Deo (c): */
for (i = 0; i < N; i++) {
for (j = 0; j < i + 1; j++)
putchar(*);
putchar(\n);
}
printf("--------------\n");
/* 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
20
15
/* 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);
}
printf("--------------\n");
izd
/* Deo (f): */
for (i = 0; i < N; i++) {
for (j = 0; j < i; j++)
putchar( );
for (j = 0; j < N - i - 1; j++) {
putchar(*); putchar( );
}
putchar(*);
putchar(\n);
}
an
je
(
printf("--------------\n");
El
ek
tro
ns
ko
/* Deo (g): */
for (i = 0; i < N; i++) {
for (j = 0; j < N - i - 1; j++)
putchar( );
for (j = 0; j < i; j++) {
putchar(*); putchar( );
}
putchar(*);
putchar(\n);
}
return 0;
#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.
an
je
(
*/
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)
s += d + n / d;
if (d * d == n)
s += d;
printf("Suma: %u\n", s);
return 0;
}
B. Reenja zadataka
20
15
323
#include <stdio.h>
El
ek
tro
ns
ko
izd
#include <stdio.h>
#include <assert.h>
324
B. Reenja zadataka
tro
ns
#include <stdio.h>
ko
izd
an
je
(
20
15
int main() {
unsigned n, d;
/* Ucitavamo broj */
scanf("%u", &n);
El
ek
325
B. Reenja zadataka
}
/* poslednji preostali cinilac */
if (n > 1)
printf("%u\n", n);
return 0;
20
15
an
je
(
#include <stdio.h>
#include <limits.h>
#include <math.h>
El
ek
tro
ns
ko
izd
int main() {
int a; /* broj koji se unosi */
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;
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;
}
printf("broj: %d\n", n);
printf("zbir: %d\n", s);
printf("proizvod: %d\n", p);
printf("minimum: %d\n", min);
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;
}
326
B. Reenja zadataka
20
15
int main() {
int ts = 0; /* Tekuca serija */
int ns = 0; /* Najduza serija */
int pb, tb; /* Prethodni i tekuci broj */
#include <stdio.h>
an
je
(
scanf("%d", &pb);
if (pb != 0) {
ts = ns = 1;
while(1) {
scanf("%d", &tb);
if (tb == 0) break; /* Prekidamo kada je uneta 0 */
ko
izd
tro
ns
El
ek
}
}
/* Ispis rezultata */
printf("%d\n", ns);
return 0;
#include <stdio.h>
int main() {
/* Izracunava se ceo deo korena unetog broja x. Trazi se interval
oblika [n^2, (n+1)^2] = [N1, N2] tako da mu x pripada i tada je n
ceo deo korena iz x.
327
B. Reenja zadataka
*/
unsigned x;
unsigned N1, N2, n;
ko
izd
20
15
an
je
(
/* Ucitava se broj x */
scanf("%d", &x);
tro
ns
El
ek
#include <stdio.h>
#include <math.h>
328
B. Reenja zadataka
20
15
an
je
(
ko
izd
int main() {
unsigned fpp = 1, fp = 1, k;
scanf("%d", &k);
if (k == 0) return 0;
printf("%d\n", fpp);
if (k == 1) return 0;
printf("%d\n", fp);
k -= 2;
while (k > 0) {
unsigned f = fpp + fp;
printf("%d\n", f);
fpp = fp; fp = f;
k--;
}
return 0;
}
#include <stdio.h>
tro
ns
#include <stdio.h>
El
ek
int main() {
unsigned n;
printf("Unesi broj: ");
scanf("%u", &n);
do {
/* Poslednja cifra broja */
printf("%u\n", n % 10);
/* Brisemo poslednju cifru broja */
n /= 10;
} while (n > 0);
return 0;
}
#include <stdio.h>
int main() {
unsigned n, suma = 0;
printf("Unesi broj: ");
329
B. Reenja zadataka
scanf("%u", &n);
do {
suma += n % 10;
n /= 10;
} while (n > 0);
printf("Suma cifara broja je: %u\n", suma);
return 0;
20
15
an
je
(
#include <stdio.h>
int main() {
unsigned n1, n2, tmp;
scanf("%d", &n1);
scanf("%d", &n2);
izd
El
ek
tro
ns
ko
#include <stdio.h>
int main() {
unsigned n;
/* polazni broj */
unsigned o = 0; /* obrnuti broj */
scanf("%d", &n);
do {
330
B. Reenja zadataka
20
15
an
je
(
#include <stdio.h>
El
ek
tro
ns
ko
izd
int main() {
/* Broj koji se obradjuje */
unsigned n;
/* 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 */
r = c * s + r;
s *= 10;
}
}
/* Ispis rezultata */
printf("%u\n", r);
return 0;
}
#include <stdio.h>
int main() {
unsigned n, s = 1;
unsigned char p, c;
scanf("%u%hhu%hhu", &n, &p, &c);
/* s = 10^p */
331
B. Reenja zadataka
while (p > 0) {
s *= 10;
p--;
}
20
15
an
je
(
izd
int main() {
unsigned n, a, tmp, s, poc, sred, kraj;
scanf("%u", &n);
tro
ns
ko
El
ek
332
B. Reenja zadataka
}
/* Rotiranje ulevo */
#include <stdio.h>
20
15
int main() {
unsigned n, tmp, s;
scanf("%u", &n);
an
je
(
izd
ko
tro
ns
/* Rotiranje udesno */
#include <stdio.h>
int main() {
unsigned n, tmp, s;
scanf("%u", &n);
El
ek
333
B. Reenja zadataka
#include <stdio.h>
#include <assert.h>
20
15
int main() {
int d, m, g, dm;
tro
ns
ko
izd
an
je
(
El
ek
/* Provera dana */
if (d < 1 || d > dm) {
printf("Pogresan dan\n");
return 1;
}
/* Provera godine */
if (g < 0) {
printf("Pogresna godina\n");
return 1;
}
printf("Uneti datum je korektan\n");
return 0;
334
B. Reenja zadataka
#include <stdio.h>
#define BROJ_PREDMETA 12
20
15
int main() {
/* Nabrojivi tip */
enum Uspeh {NEDOVOLJAN, DOVOLJAN, DOBAR, VRLO_DOBAR, ODLICAN, GRESKA};
izd
/* Unos podataka */
printf("Unesi broj jedinica: ");
scanf("%d", &broj_jedinica);
printf("Unesi prosek: ");
scanf("%f", &prosek);
an
je
(
El
ek
tro
ns
ko
/* 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)
uspeh = DOVOLJAN;
else if (prosek < 3.5)
uspeh = DOBAR;
else if (prosek < 4.5)
uspeh = VRLO_DOBAR;
else
uspeh = ODLICAN;
/* Prijavljivanje rezultata */
switch(uspeh) {
case NEDOVOLJAN:
printf("Nedovoljan uspeh\n");
break;
case DOVOLJAN:
printf("Dovoljan uspeh\n");
break;
case DOBAR:
printf("Dobar uspeh\n");
break;
case VRLO_DOBAR:
printf("Vrlo dobar uspeh\n");
break;
case ODLICAN:
printf("Odlican uspeh. Cestitamo!\n");
break;
case GRESKA:
printf("Deluje da su podaci neispravni\n");
break;
}
return 0;
an
je
(
B. Reenja zadataka
20
15
335
izd
#include <stdio.h>
#include <limits.h>
tro
ns
ko
int main() {
int x; /* tekuci broj */
int m; /* najmanji prethodno unet broj */
int n; /* broj unetih brojeva */
int s; /* suma unetih brojeva */
El
ek
336
B. Reenja zadataka
if (x > s / n)
printf("Veci od proseka prethodnih: %d\n", x);
/* Azuriramo prosek prethodnih */
s += x; n++;
}
return 0;
20
15
an
je
(
#include <stdio.h>
int main() {
unsigned a[1000], n, i, j;
scanf("%d", &n);
tro
ns
ko
izd
El
ek
return 0;
}
#include <stdio.h>
#include <assert.h>
int main() {
int a[100], b[100]; /* Ulazni nizovi */
int na, nb; /* Njihov broj elemenata */
int p[100], u[200], r[100]; /* Presek, unija, razlika */
int np, nu, nr; /* Njihov broj elemenata */
int i, j, k; /* Pomocne promenljive */
337
B. Reenja zadataka
20
15
an
je
(
El
ek
tro
ns
ko
izd
/* ------------------------------------------------------- */
/* 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;
}
}
}
/* Ispisujemo elemente preseka */
printf("Presek: ");
for (i = 0; i < np; i++)
printf("%d ", p[i]);
printf("\n");
/* ------------------------------------------------------- */
/* unija - u niz u kopiramo sve elemente niza a i za njima sve
elemente niza b koji se ne javljaju u a */
/* Kopiramo niz a u niz u */
nu = 0;
for (i = 0; i < na; i++)
u[nu++] = a[i];
for (i = 0; i < nb; i++) {
/* Trazimo b[i] u nizu a */
338
B. Reenja zadataka
for (j = 0; j < na; j++)
if (b[i] == a[j])
break;
/* Ako je petlja stigla do kraja, znaci da niz a ne sadrzi b[i] pa
ga dodajemo u uniju u */
if (j == na)
u[nu++] = b[i];
an
je
(
20
15
}
/* Ispisujemo elemente unije */
printf("Unija: ");
for (i = 0; i < nu; i++)
printf("%d ", u[i]);
printf("\n");
El
ek
tro
ns
ko
izd
/* ------------------------------------------------------- */
/* razlika - u niz r kopiramo sve elemente niza a koji se ne
javljaju u nizu b */
nr = 0;
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 */
printf("Razlika: ");
for (i = 0; i < nr; i++)
printf("%d ", r[i]);
printf("\n");
return 0;
#include <stdio.h>
#include <string.h>
int main() {
/* Rec koja se proverava */
char s[] = "anavolimilovana";
/* Bulovska promenljiva koja cuva rezultat */
339
B. Reenja zadataka
int palindrom = 1;
an
je
(
/* Ispisujemo rezultat */
if (palindrom)
printf("Rec %s je palindrom\n", s);
else
printf("Rec %s nije palindrom\n", s);
}
ko
int main() {
/* Niska koja se obrce */
char s[] = "Zdravo";
izd
#include <stdio.h>
#include <string.h>
20
15
El
ek
tro
ns
#include <stdio.h>
#include <assert.h>
#define MAX 100
340
B. Reenja zadataka
int main() {
unsigned n, i, j, k;
int m[MAX][MAX];
20
15
/* Ucitavanje matrice */
scanf("%u", &n);
assert(n < MAX);
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
scanf("%d", &m[i][j]);
an
je
(
tro
ns
#include <stdio.h>
#include <assert.h>
ko
izd
El
ek
/* Ucitavamo matricu */
scanf("%d%d", &m, &n);
assert(m < MAX && n < MAX);
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
scanf("%d", &a[i][j]);
/* Proveravamo sva polja */
for (i = 0; i < m; i++)
for (j = 0; j < n; j++) {
/* Odredjujemo zbir susednih elemenata polja (i, j) */
int k, l;
int s = 0;
for (k = -1; k <= 1; k++)
for (l = -1; l <= 1; l++) {
341
B. Reenja zadataka
if (k == 0 && l == 0)
continue;
if (0 <= i+k && i+k < m && 0 <= j+l && j+l < n)
s += a[i+k][j+l];
}
20
15
an
je
(
Jezik C - funkcije
Reenje zadatka 8.1:
izd
#include <stdio.h>
El
ek
tro
ns
ko
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)
return 0;
/* Ako delioc nije pronadjen, broj je prost */
return 1;
}
int main() {
/* Ucitava se broj i proverava da li je prost */
unsigned n;
scanf("%u", &n);
printf(prost(n) ? "Prost\n" : "Nije prost\n");
}
#include <stdio.h>
#include <math.h>
342
B. Reenja zadataka
izd
#include <stdio.h>
El
ek
tro
ns
ko
unsigned suma_delilaca(unsigned n) {
unsigned s = 1, d;
for (d = 2; d*d < n; d++)
if (n % d == 0)
s += d + n / d;
if (d * d == n)
s += d;
return s;
}
int savrsen(unsigned n) {
return n == suma_delilaca(n);
}
int main() {
unsigned n;
for (n = 1; n <= 10000; n++)
if (savrsen(n))
printf("%u\n", n);
return 0;
}
20
15
an
je
(
int main() {
double xa, ya, xb, yb, xc, yc;
scanf("%lf%lf", &xa, &ya);
scanf("%lf%lf", &xb, &yb);
scanf("%lf%lf", &xc, &yc);
double a = rastojanje(xb, yb, xc, yc);
double b = rastojanje(xa, ya, xc, yc);
double c = rastojanje(xa, ya, xb, yb);
double s = (a + b + c) / 2.0;
double P = sqrt(s * (s - a) * (s - b) * (s - c));
printf("%lf\n", P);
return 0;
}
343
B. Reenja zadataka
#include <stdio.h>
#include <assert.h>
20
15
/* Struktura razlomak */
struct razlomak {
int brojilac, imenilac;
};
tro
ns
ko
izd
an
je
(
El
ek
#include <stdio.h>
int sadrzi(int a[], int n, int x) {
int i;
344
B. Reenja zadataka
an
je
(
20
15
tro
ns
ko
izd
El
ek
345
B. Reenja zadataka
an
je
(
20
15
tro
ns
ko
izd
int main() {
int a[] = {3, 2, 5, 4, 1, 3, 8, 7, 5, 6};
int n = sizeof(a) / sizeof(int);
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;
}
El
ek
#include <stdio.h>
int izbaci_poslednji(int a[], int n) {
return n - 1;
}
/* Cuva se redosled elemenata niza */
int izbaci_prvi_1(int a[], int n) {
int i;
for (i = 0; i + 1 < n; i++)
a[i] = a[i + 1];
return n - 1;
}
346
B. Reenja zadataka
20
15
an
je
(
ko
izd
El
ek
tro
ns
347
B. Reenja zadataka
for (i
a[i]
a[k] =
return
= n; i > k; i--)
= a[i-1];
x;
n + 1;
izd
ko
an
je
(
20
15
El
ek
tro
ns
int main() {
int a[10] = {1, 2, 3};
int n = 3;
n = ubaci_na_pocetak_1(a, n, 0);
ispisi(a, n);
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);
return 0;
}
#include <stdio.h>
/* Napomena: odredjen broj funkcija u ovom zadatku se moze
348
B. Reenja zadataka
20
15
tro
ns
ko
izd
an
je
(
El
ek
349
B. Reenja zadataka
izd
an
je
(
20
15
El
ek
tro
ns
ko
int i, j;
/* k puta rotiramo za po jednu poziciju */
for (j = 0; j < k; j++) {
/* upamtimo prvi */
int tmp = a[0];
/* Sve elemente pomerimo za jedno mesto u levo */
for (i = 0; i + 1 < n; i++)
a[i] = a[i+1];
/* Raniji prvi postavimo na kraj */
a[n - 1] = tmp;
}
}
B. Reenja zadataka
20
15
350
tro
ns
ko
izd
an
je
(
El
ek
351
B. Reenja zadataka
mimoidju */
for (i = 0, j = n-1; i < j; i++, j--) {
/* Vrsimo razmenu elemenata */
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
20
15
El
ek
tro
ns
ko
izd
an
je
(
#include <stdio.h>
/* Funkcija radi slicno funkciji strcmp iz string.h */
int poredi(char s1[], char s2[]) {
/* Petlja tece sve dok ne naidjemo na prvi razliciti karakter */
int i;
for (i = 0; s[i]==t[i]; i++)
if (s[i] == \0) /* Naisli smo na kraj obe niske,
a nismo nasli razliku */
return 0;
352
B. Reenja zadataka
an
je
(
#include <stdio.h>
20
15
int main() {
printf("%d\n", poredi("zdravo", "svima"));
return 0;
}
El
ek
tro
ns
ko
izd
353
B. Reenja zadataka
an
je
(
20
15
int main() {
printf("%d\n", podniz("banana", "ann"));
printf("%d\n", podniska("banana", "anan"));
return 0;
}
#include <stdio.h>
El
ek
tro
ns
ko
izd
}
/* Provera funkcije */
int main() {
char s[] = "tom marvolo riddle ", t[] = "i am lord voldemort";
printf("%d\n", permutacija(s, t));
return 0;
354
B. Reenja zadataka
an
je
(
20
15
#include <stdio.h>
#include <assert.h>
tro
ns
ko
izd
El
ek
355
B. Reenja zadataka
izd
20
15
an
je
(
El
ek
tro
ns
ko
int main() {
int n, m[10][10];
n = ucitaj(m);
printf("%d\n", magicni(m, n));
return 0;
}
356
B. Reenja zadataka
#include <stdio.h>
#include <assert.h>
20
15
an
je
(
typedef struct {
float a[MAX][MAX];
unsigned m, n;
} MATRICA;
ko
izd
El
ek
tro
ns
357
B. Reenja zadataka
return m;
}
20
15
an
je
(
ko
izd
int main() {
MATRICA m1, m2, m;
m1 = ucitaj(); m2 = ucitaj();
if (m1.m == m2.m && m1.n == m2.n) {
m = saberi(m1, m2);
ispisi(m);
}
if (m1.n = m2.m) {
m = pomnozi(m1, m2);
ispisi(m);
}
return 0;
}
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]);
putchar(\n);
}
}
tro
ns
#include <stdio.h>
#include <assert.h>
El
ek
358
B. Reenja zadataka
an
je
(
void ispisi(BROJ b) {
int i;
for (i = b.n-1; i >= 0; i--)
putchar(0 + b.c[i]);
}
20
15
}
/* 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;
El
ek
tro
ns
ko
izd
izd
an
je
(
/* Normalizujemo rezultat */
p = 0;
for (i = 0; i < rez.n; i++) {
unsigned char c = rez.c[i] + p;
rez.c[i] = c % 10;
p = c / 10;
}
/* Ako je prva cifra 0, smanjujemo broj cifara */
if (rez.c[rez.n-1] == 0)
rez.n--;
return rez;
B. Reenja zadataka
20
15
359
El
ek
tro
ns
ko
int main() {
BROJ a = ucitaj(), b = ucitaj(), c;
c = saberi(a, b);
ispisi(c);
putchar(\n);
c = pomnozi(a, b);
ispisi(c);
putchar(\n);
}
Jezik C - pokazivai
Reenje zadatka 10.2.1:
#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;
360
B. Reenja zadataka
n %= 60;
*s = n;
an
je
(
20
15
int main() {
unsigned n, h, m, s;
scanf("%d", &n); assert(n < 60*60*24);
od_ponoci(n, &h, &m, &s);
printf("%d:%d:%d\n", h, m, s);
return 0;
}
#include <stdio.h>
ko
int pozitivan(int x) {
return x > 0;
}
izd
int paran(int x) {
return x % 2 == 0;
}
El
ek
tro
ns
B. Reenja zadataka
20
15
361
izd
an
je
(
#include <stdio.h>
ko
tro
ns
El
ek
void my_strrev(char* s) {
char *t = s;
/* Dovodimo s ispred terminalne nule */
while(*s)
s++;
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++;
362
B. Reenja zadataka
}
return NULL;
}
20
15
an
je
(
if (*t == \0)
return str;
}
return NULL;
}
tro
ns
ko
izd
int main() {
char s[] = "abc", t[] = "def", r[4];
printf("%lu %lu\n", my_strlen(s), my_strlen(t));
my_strcpy(r, s);
printf("%s\n", r);
my_strrev(s);
printf("%s\n", s);
printf("%d\n", my_strcmp(s, t));
printf("%c\n", *my_strchr(x, "abcxyz"));
printf("%s\n", my_strstr("abcdefghi", "def"));
return 0;
}
El
ek
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
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++)
363
B. Reenja zadataka
scanf("%d", &a[i]);
an
je
(
/* Ispisujemo rezultat */
printf("a[%d] = %d\n", min, a[min]);
/* Oslobadjamo niz */
free(a);
return 0;
}
ko
tro
ns
#define KORAK 32
izd
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
20
15
int main() {
unsigned n = 0, aloc = 0, i, m, nm;
int *a = NULL;
El
ek
B. Reenja zadataka
an
je
(
20
15
364
ko
izd
return 0;
}
tro
ns
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
int main() {
int n, i, j, lx, ux, ly, uy, **A;
El
ek
B. Reenja zadataka
an
je
(
20
15
365
/* Oslobadjamo matricu */
for (i = 0; i < n; i++)
free(A[i]);
free(A);
return 0;
tro
ns
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
ko
izd
El
ek
366
B. Reenja zadataka
int main() {
int n, i, j, **A;
/* Ucitavamo dimenziju matrice */
scanf("%d", &n);
20
15
an
je
(
tro
ns
return 0;
}
ko
/* Oslobadjamo matricu */
for (i = 0; i < n; i++)
free(A[i]);
free(A);
izd
Jezik C - ulaz/izlaz
El
ek
#include <stdio.h>
#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;
}
367
B. Reenja zadataka
20
15
izd
an
je
(
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
ko
if (argc < 2) {
fprintf(stderr, "Greska: nedostaje ime ulazne datoteke\n");
return -1;
}
El
ek
tro
ns
368
B. Reenja zadataka
an
je
(
/* Prijavljujemo rezultat */
printf("Kolona: %u Zbir: %d\n", max_j, max);
return 0;
ko
izd
/* Oslobadjamo matricu */
for (i = 0; i < n; i++)
free(A[i]);
free(A);
/* Zatvaramo datoteku */
fclose(dat);
20
15
/* 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;
max = m;
}
}
tro
ns
El
ek
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define KORAK 64
typedef struct {
char ime[20];
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");
369
B. Reenja zadataka
if (dat == NULL) {
fprintf(stderr,
"Greska pri otvaranju datoteke apsolventi.txt\n");
return 1;
}
an
je
(
20
15
izd
/* Zatvaramo datoteku */
fclose(dat);
tro
ns
ko
El
ek
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
370
B. Reenja zadataka
an
je
(
20
15
tro
ns
ko
izd
El
ek
371
B. Reenja zadataka
20
15
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;
}
}
an
je
(
El
ek
tro
ns
ko
izd
/* Otvaranje datoteka */
if (argc < 3) {
fprintf(stderr, "Upotreba: %s <ulaz> <izlaz>\n", argv[0]);
return 1;
}
ulaz = fopen(argv[1], "r");
if (ulaz == NULL) {
fprintf(stderr, "Greska prilikom otvaranja %s\n", argv[1]);
return 1;
}
izlaz = fopen(argv[2], "w");
if (izlaz == NULL) {
fprintf(stderr, "Greska prilikom otvaranja %s\n", argv[2]);
return 1;
}
/* Citanje i obrada brojeva */
while (fgets(s, MAX_DIGITS, ulaz) != NULL) {
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);
an
je
(
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);
}
} else {
/* Dekadni broj */
n = btoi(t, 10);
printf("%u\n", n);
itob(n, 2, bs);
fputs(bs, izlaz); fputc(\n, izlaz);
}
El
ek
tro
ns
ko
izd
}
/* Zatvaramo datoteke */
fclose(ulaz);
fclose(izlaz);
return 0;
B. Reenja zadataka
20
15
372
tro
ns
ko
izd
abakus, 12
ABC (raunar), 15
algoritam, 61
analitika maina, 13
analogni zapis, 39
API, 35, 269
argument funkcije, 86
argumenti komandne linije programa,
294
asembler, 29
automatska promenljiva, 200
20
15
)
32-bitni i 64-bitni sistem, 105, 219
an
je
(
Indeks
El
ek
373
getc, 289
getchar, 278
gets, 279
isalnum, 274
isalpha, 107, 274
isdigit, 107
isdigit, 274
islower, 274
isupper, 274
malloc, 258, 272
matematika (sin, cos, log2,
exp, . . . ), 275
printf, 93, 280
putc, 289
putchar, 278
rand, 272
realloc, 260, 272
scanf, 95, 283
sprintf, 285
sqrt, 96, 275
srand, 272
sscanf, 286
strcat, 269
strchr, 269
strcmp, 269
strcpy, 136, 248, 269
strlen, 135, 247, 269
strstr, 269
system, 272
tolower, 107, 274
toupper, 107, 274
374
B. Reenja zadataka
er-Tjuringova teza, 63
El
ek
tro
ns
ko
izd
datoteka, 286
binarna, 287
tekstualna, 287
datoteka zaglavlja, 94, 186, 194, 269
<assert.h>, 276
<ctype.h>, 106, 274
<math.h>, 96, 275
<stdio.h>, 94, 277
<stdlib.h>, 258, 272
<string.h>, 269
debager, 190
definicija, 169, 204
naelna, 204
stvarna, 204
deklaracija, 101, 133, 169, 204
deljenje vremena, 18
diferencijska maina, 13
digitalni zapis, 38, 39
dinamika alokacija memorije, 258
doseg identifikatora, 83, 103, 192,
198
20
15
C (programski jezik), 18
ANSI/ISO C, 92
C11, 92
C99, 92
curenje memorije, 262
an
je
(
ungetc, 290
biblioteka programskog jezika, 35
bit, 11, 12, 44, 104
blok, 97, 152, 198
blok dijagram, 62
brojevni sistem, 40
binarni, 11, 38, 40, 44
heksadekadni, 41, 44, 109
oktalni, 41, 109
buena kartica, 13, 14
EDVAC raunar, 16
elektromehanike maine, 13
ENIAC (raunar), 15
enigma maina, 15
generacije raunara, 16
generisanje i optimizacija koda, 187
glif, 48
globalna promenljiva, 103, 169, 199,
201
greka u programu, 94
izvravanje, 210, 230, 235, 262
kompilacija, 209
povezivanje, 209
pretprocesiranje, 209
halting problem, 72
hard disk, 26
hardver, 11, 16, 22
hip, vidi segment memorije (hip)
Holeritova maina, 13
Hornerova ema, 42
identifikator, 102, 169
375
B. Reenja zadataka
El
ek
tro
ns
ko
izd
karakter, 48
kardinalnost, 69
kastovanje, vidi konverzija tipova (eksplicitna)
klizni lenjir, 12
kodiranje karaktera, 48
ASCII, 49, 106
ISO-10646, 52
ISO-8859, 51
kdna strana, 48
UCS-2, 54
Unicode, 52
UTF-8, 54
windows-125x/ANSI, 51
YUSCII, 50
Kolos (raunar), 15
komentar, 94
kompilacija, 186
odvojena, 189
kompilator, 78, 87, 186
konflikt identifikatora, 200
konstanta, 101, 109
celobrojna, 109
karakterska, 110
u pokretnom zarezu, 110
konstanti izraz, 111
kontroler, 23
20
15
an
je
(
376
B. Reenja zadataka
ke, 25
RAM, 24, 26
ROM, 25
spoljanja, 16, 24, 26
mikroip, 17
mikroprocesor, 19
mini raunari, 18
model boja, 55
CMYK, 56
RGB, 55
20
15
an
je
(
parametar funkcije, 86
prenos po adresi, 86
prenos po vrednosti, 86
Paskalina, 12
pokaziva, 233
na funkciju, 254
pokazivai i nizovi, 241
pokazivaka aritmetika, 241, 244
polje bitova, 145
poluprovodniki elementi, 17
potprogram, 85
povezanost identifikatora, 192, 203
poveziva, 187
povezivanje, 186, 187
dinamiko, 188, 218
statiko, 188
pragmatika programskog jezika, 83
prebrojiv skup, 69
prekoraenje, 105
prekoraenje bafera, 264
prelazak u novi red, 50
pretprocesiranje, 186, 193
pretprocesor, 186, 193
pretprocesorska direktiva, 193
define, 98, 194
if-elif-endif, 197
izd
El
ek
tro
ns
ko
naredba, 85
break, 155, 159
continue, 160
do-while, 99, 158
for, 98, 157
goto, 151
if, 97, 152
izraza, 151
return, 94, 168, 174
switch, 155
while, 98, 156
naredba dodele, 85, 113
nazubljivanje koda, 94
neodluiv problem, 71
niska, 134
doslovna niska, 246
zavrna nula, 135
niz, 132
deklaracija, 133
dvodimenzioni, 137, 250
indeks, 133, 241
inicijalizacija, 133, 135
niz pokazivaa, 250
prenos u funkciju, 174
viedimenzioni, 137
NULL, 235
adresni, 234
aritmetiki, 104, 107, 114
arnost, 112
asocijativnost, 113
bitovski, 119
dekrementiranje, 115
dereferenciranja, 234, 244
inkrementiranje, 115
logiki, 117
postfiksni, 112, 115
prefiksni, 112, 115
prioritet, 113
referenciranja, 234, 244
relacijski, 104, 107, 117
sizeof, 123, 134, 142, 241
sloene dodele, 121
uslovni, 122
zarez, 122
377
B. Reenja zadataka
an
je
(
20
15
izd
ifdef/ifndef, 197
include, 94, 193
undef, 196
preusmeravanje (redirekcija) ulaza
i izlaza, 277
procesor, 16, 22, 23
procesorska instrukcija, 29
programiranje, 11
programska paradigma, 80
programski jezik
asemblerski, 28
mainski, 27, 28, 33, 87
mainski zavisan, 26
vii, 17, 27, 79, 87
programski prevodilac, 78, 87, 94
gcc, 94, 96, 187189, 210, 220
promenljiva, 83, 101
deklaracija, 95, 101, 102
inicijalizacija, 99, 102
propratni efekat, 114, 115
punilac, 190
El
ek
tro
ns
ko
raunar, 11
elektromehaniki, 14
elektronski, 14
fon Nojmanove arhitekture, 16,
78
lini, 19
sa skladitenim programom, 16
raunarstvo i informatika, 11
oblasti, 21
rantajm biblioteka, 35, 86, 218
rasterska grafika, 55
rekurzija, 178, 225
rezolucija, 56
sakuplja otpada, 86
segment memorije, 220
hip, 258, 265
segment kda, 220
segment podataka, 221
stek, 221
sekvenciona taka, 116
semantika analiza, 187
semantiki analizator, 87
terminal, 18
tip podataka, 83, 101, 104
bool, 108
char, 106
double, 96, 107
float, 107
int, 93, 104
korisniki definisan, 140
logiki, 108
long double, 107
nabrojivi (enum), 140, 146
opseg, 105
pokazivaki, 233
polje bitova, 145
size_t, 123
struktura (struct), 140
typedef, 147
unija (union), 140, 144
void*, 235
Tjuringova maina, 62
378
B. Reenja zadataka
tok, 277
token, 81
tranzistor, 17
20
15
ko
izd
Z3 (raunar), 14
zapis
fiksni zarez, 47
neoznaeni brojevi, 41
oznaena apsolutna vrednost, 45
oznaeni brojevi, 45
pokretni zarez, 47, 107
potpuni komplement, 45
realni brojevi, 46
slika, 55
tekst, 48
zvuk, 56
an
je
(
vakuumska cev, 17
vektorska grafika, 55
El
ek
tro
ns
akardov razboj, 13
ivotni vek promenljive, 83, 192, 200