Sadræaj
1
Ukratko o programskom jeziku C 1 1.1 Azbuka programskog jezika C 1 1.2 Leksiåke konstrukcije u programskom jeziku C 2 1.3 Proste leksiåke konstrukcije 2 1.4 Podatak 4 1.5 Promenljive 5 1.6 Prosti tipovi podataka 5 1.7 Definisane operacije 7 1.8 Konverzije 11 1.9 Nabrojivi tip podataka 13 1.10 Sloæeni tipovi podataka 14 1.11 Pokazivaåi 17 1.12 Prioriteti i asocijativnost operatora 19 1.13 Izrazi 21 1.14 Naredbe 21 1.15 Funkcije 25 1.16 Program na jeziku C 26 1.17 Memorijske klase 27 1.18 Sloæene deklaracije u programskom jeziku C 28
2
O algoritmima 31 2.1 Zapis algoritma 31 2.2 Ocena algoritma 33 2.3 Neki poznatiji problemi 35
3
Sortiranje 39 3.1 Sortiranje izborom uzastopnih minimuma 39 3.2 Sortiranje umetanjem elemenata niza na odgovarajuña mesta 40 3.3 Sortiranje poreœenjem parova uzastopnih elemenata 42 3.4 Sortiranje koriãñenjem drveta 43
IV
3.5 Sortiranje zasnovano na zapisu brojeva u pozicionom brojnom sistemu sa zadatom osnovom 47 3.6 Sortiranje deljenjem niza 52 3.7 Sortiranje spajanjem sortiranih nizova 54 3.8 Sortiranje primenom nekog od opisanih postupaka na podnizove obrazovane od elemenata koji se nalaze na konstantnom rastojanju pri åemu se rastojanje smanjuje 57 4
Pretraæivanje 61 4.1 Sekvencijalno pretraæivanje 61 4.2 Binarno pretraæivanje 62 4.3 Modifikacija binarnog pretraæivanja 62 4.4 Pretraæivanje koriãñenjem binarnog drveta 63 4.5 Balansirano binarno drvo 68 4.6 B-drvo 80 4.7 Crveno-crno drvo 92 4.8 Crveno-crno drvo – drugi naåin definisanja 100 4.9 Pretraæivanje zasnovano na zapisu vrednosti u nekom brojnom sistemu 119
5
Grafovi 131 5.1 Predstavljanje grafa na raåunaru 132 5.2 Obilazak grafa (pretraæivanje grafa) 133 5.3 Topoloãko sortiranje grafa 138 5.4 Odreœivanje artikulacionih taåaka 139 5.5 Odreœivanje komponenti dvostruke povezanosti 140 5.6 Odreœivanje mostova 142 5.7 Odreœivanje strogo povezanih komponenti 143 5.8 Najkrañe rastojanje 145 5.9 Minimalno drvo razapinjanja 152
6
Obrada reåi u programskom jeziku C 159 6.1 Sekvencijalno uparivanje stringova 160 6.2 Uparivanje stringova koriãñenjem konaånih deterministiåkih automata 160 6.3 Rabin-Karpov algoritam 163 6.4 Knut-Moris-Pratov algoritam za uparivanje stringova 165 6.5 Bojer-Murov algoritam za uparivanje stringova 167
V
7
Koriãñenje tehnike pretraæivanja sa vrañanjem 173 7.1 Postavljanje kraljica na ãahovsku tablu 173 7.2 Reãenje problema kraljica za tablu dimenizija n × n 176 7.3 Nerekurzivno reãenje problema kraljica 178 7.4 Obilazak ãahovske table skakaåem 179 7.5 Problem stabilnih brakova 181 7.6 Zadatak optimalnog izbora 182 7.7 Odreœivanje najduæe proste marãrute koju moæe napraviti skakaå 184 7.8 Odreœivanje skupova slobodnih za sumu 190 7.9 Zakljuåak 192
8
Koriãñenje tehnike dinamiåkog programiranja 195 8.1 Lanåano mnoæenje matrica 197 8.2 Elementi dinamiåkog programiranja 199 8.3 Zajedniåki podnizovi 199 8.4 Problem trgovaåkog putnika 203 8.5 Optimalno drvo za pretraæivanje 205 8.6 Raåunanje vrednosti Akermanove funkcije 210 8.7 Triangulacija poligona 214 8.8 Jedno pojednostavljenje problema trgovaåkog putnika 215 8.9 Raåunanje rastojanja izmeœu reåi 217 8.10 Primer problema optimalnog izbora 219 8.11 Najduæi neopadajuñi podniz 221
9
Rad 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9
sa velikim brojevima 223 Predstavljanje velikih brojeva 223 Sabiranje neoznaåenih velikih brojeva 225 Oduzimanje neoznaåenih celih brojeva 226 Mnoæenje celih brojeva 227 Rusko mnoæenje celih nenegativnih brojeva 228 Deljenje nenegativnih celih brojeva 230 Joã jedan postupak za deljenje nenegativnih celih brojeva 232 Nalaæenje reciproåne vrednosti nenegativnog broja 236 Nalaæenje najveñeg zajedniåkog delioca 239
9.10 Odreœivanje celobrojnog dela kvadratnog korena nenegativnog celog broja 241 9.11 Ispitivanje da li je broj prost i faktorizacija brojeva 242
VI
10 Programski jezik C i PC raåunari 251 10.1 Microsoft C i BIOS i DOS servisi 251 10.2 Oåitavanje i postavljanje sistemskog vremena 253 10.3 Postavljanje i oåitavanje sistemskog datuma 254 10.4 Postavljanje veliåine kursora 255 10.5 Dvodimenzionalna grafika 255 10.6 Rotiranje kocke 267 10.7 Primer trodimenzionalne grafike 273 10.8 Koriãñenje modema 278 10.9 Crtanje Hilbertovih krivih 283 10.10 Crtanje krivih Serpinskog 285 10.11 Jedan primer grafiåkih datoteka 287 10.12 Analizator deklaracija napisanih na programskom jeziku C 291 Prilog
Joã malo o vremenu 295
Literatura 301 Indeks
303
VII
UVOD
Knjiga Algoritmi u programskom jeziku C sadræi prikaz nekih vrlo poznatih i dosta koriãñenih algoritama i implementaciju tih algoritama u programskom jeziku C. Odabrali smo probleme koji se najåeãñe pojavljuju u praksi i sliånoj literaturi na stranim jezicima. Knjiga ne pretpostavlja dublje poznavanje programskog jezika C. Prvo poglavlje daje kratak prikaz sintakse i semantike jezika C zajedno sa nekim pojedinostima koje se obiåno ne prikazuju u knjigama, ili se prikazuju, ali ne preterano naglaãeno. Pretpostavlja se poznavanje MS-DOS-a i nekog od C prevodilaca rasprostranjenih na PC raåunarima (Microsoft, Borland, Zortech itd.).
Kratak sadræaj knjige U prvom poglavlju je dat kratak prikaz programskog jezika C: • • • • •
proste i sloæene leksiåke konstrukcije; definisani tipovi podataka; definisane operacije nad podacima; skup naredbi; struktura programa.
To je saæeto izlaganje o programskom jeziku, i kada ga proåitate, sigurno ñete napisati svoje prve programe u programskom jeziku C. U drugom poglavlju je opisan pojam algoritam. Definiãe se pojam algoritma i naåin zapisivanja algoritma: • • • •
pomoñu algoritamske ãeme; na prirodnom jeziku; na pseudojeziku (jezik izmeœu prirodnog jezika i programskih jezika) i na nekom programskom jeziku.
Daje se prikaz postupka za ocenjivanje karakteristika nekog algoritma (tzv. sloæenost algoritma).
viii
U treñem poglavlju su opisani postupci za sortiranje. Sortiranje je jedan od problema koji se najåeãñe javlja u programiranju. Kolekciju vrednosti koje se mogu porediti potrebno je urediti u neopadajuñi ili nerastuñi poredak. Prikazani su razliåiti postupci sortiranja: od jednostavnijih za pisanje (koji se duæe izvrãavaju) do sloæenih (koje je teæe razumeti, ali su zato vrlo efikasni). U åetvrtom poglavlju su prikazani postupci za pretraæivanje. Pretraæivanje je drugi problem koji se åesto sreñe u svakodnevnom programiranju. Potrebno je utvrditi da li se u nekoj kolekciji (skupu) vrednosti nalazi odreœena (zadata) vrednost. Kolekcija moæe biti bilo koje veliåine i od njene organizacije zavisi koliko ñe trajati pretraæivanje. Prikazani su razliåiti postupci za predstavljanje kolekcije: • pomoñu niza; • pomoñu drvoidnih struktura: binarno drvo za pretraæivanje, balansirano binarno drvo, crveno-crno drvo, B-drvo, radix drvo (drvo zasnovano na zapisu vrednosti) itd. U petom poglavlju su opisani grafovi. Intuitivno, graf je skup åvorova i skup ivica koje povezuju pojedine parove åvorova (izmeœu bilo koja dva åvora ne mora postojati ivica). Pomoñu grafova se modeliraju neke stvari iz realnog sveta: putna ili æelezniåka mreæa, PTT mreæa, vodovod itd. Izuåavanje grafova ima veliki praktiåan znaåaj. Prikazani su algoritmi za neke poznate probleme: • obilazak grafa; • odreœivanje artikulacionih taåaka grafa (to su åvorovi åijim bi izbacivanjem iz grafa, graf bio razbijen na viãe delova, tj. postojali bi åvorovi koji ne bi bili povezani nekim nizom ivica od kojih svake dve uzastopne imaju zajedniåki åvor); • odreœivanje mostova grafa (ivice åijim bi brisanjem graf bio razbijen); • razbijanje grafa na komponente (takvi maksimalni skupovi åvorova da su svaka dva åvora iz istog skupa povezana); • najkraña rastojanja; • drvo razapinjanja grafa. Ãesto poglavlje govori o obradi reåi u programskom jeziku C. Raspravlja se o pronalaæenju pojavljivanja jedne reåi u okviru druge reåi (uparivanje reåi, engl. string matching). Ovo je joã jedan problem koji se åesto sreñe u svakodnevnom programiranju. Prikazano je nekoliko vrlo efikasnih algoritama za reãavanje ovog problema. U sedmom poglavlju je opisana tehnika programiranja, poznata pod nazivom pretraæivanje sa vrañanjem (engl. backtracking). To je jedna veoma koriãñena tehnika za odreœivanje reãenja nekih problema (najåeãñe su to problemi u bliskoj vezi sa kombinatorikom ili veãtaåkom inteligencijom). U nekoliko vrlo poznatih primera prikazano je koriãñenje ove tehnike: • postavljanje kraljica na ãahovsku tablu tako da se ne tuku; • obilazak ãahovske table pomoñu skakaåa tako da svako polje bude poseñeno taåno jedanput; • obilazak table skakaåem tako da skakaå ne preseåe svoju putanju itd.
UVOD
ix
U osmom poglavlju je opisana tehnika dinamiåkog programiranja. Pod dinamiåkim programiranjem se podrazumeva tehnika u kojoj se ubrzanje raåunanja postiæe memorisanjem odreœenih meœurezultata. U kasnijem toku izraåunavanja, memorisani meœurezultati se koriste (da nisu memorisani, morali bi ponovo da se izraåunaju, ãto je nepotrebno). Prikazano je nekoliko poznatih primera koji su karakteristiåni za ovu tehniku: • lanåano mnoæenje matrica (mnoæenje n matrica åiji su elementi realni brojevi) tako da broj mnoæenja elemenata matrica bude minimalan; • pronalaæenje najduæeg zajedniåkog podniza dva niza; • triangulacija konveksnog poligona (podela poligona na trouglove pomoñu dijagonala tako da zbir obima, ili nekih drugih brojnih karakteristika trouglova, dobijenih trouglova bude najmanji); • jedno reãenje problema trgovaåkog putnika; • formiranje optimalnog drveta za pretraæivanje po kolekciji vrednosti, ukoliko nisu sve vrednosti iz kolekcije ravnopravne (ne pojavljuju se sa jednakom uåestanoãñu kao argumenti funkcije za pretraæivanje). U devetom poglavlju je opisan rad sa velikim celim brojevima. Kao i veñina programskih jezika, i jezik C omoguñava rad sa relativno malim celim brojevima (u najpovoljnijem sluåaju mogu biti registrovani brojevi manji od 2 32 ãto je negde oko 4 milijarde). U praksi se nekad (posebno u teoriji brojeva) radi sa daleko veñim brojevima. Prikazani su: • naåin predstavljanja takvih brojeva u raåunaru; • osnovne aritmetiåke operacije nad takvim brojevima (sabiranje, oduzimanje, mnoæenje, deljenje, odreœivanje kvadratnog korena – celobrojni deo kvadratnog korena i najveñi zajedniåki delilac); • prikazani su neki postupci za faktorisanje velikih brojeva (prikaz broja u obliku proizvoda dva ili viãe celih brojeva). U desetom poglavlju reå je o programiranju na programskom jeziku C na PC raåunarima. Objaãnjavaju se: • povezivanje (sprezanje) delova programa napisanih na programskom jeziku C i na asembleru (simboliåki jezik); • pozivanje nekih DOS-ovih i BIOS-ovih servisa (sistemskih funkcija ili poziva); • rad sa razliåitim video reæimima (tekst, grafika); • primer dvodimenzionalne i trodimenzionalne grafike (razvijena je notacija za zapis funkcije, sliåna notaciji u veñini programskih jezika, kao i funkcija za leksiåku i sintaksnu analizu tako zapisane funkcije; ako je funkcija ispravno zapisana, crta se njen grafik); • programiranje modema.
x
ALGORITMI U PROGRAMSKOM JEZIKU C
Namena knjige Ova knjiga je namenjena: • studentima; • srednjoãkolcima (pogodna je za pripremu takmiåenja iz programiranja); • profesionalnim programerima. Knjiga je nastala na osnovu priprema za takmiåenja i dodatne nastave iz programiranja, koju je autor dræao u Matematiåkoj gimnaziji u Beogradu. Pojedina poglavlja nisu previãe zavisna tako da se delovi knjige mogu åitati i proizvoljnim redosledom. Ako åitalac ne poznaje programski jezik C, onda je najbolje da poåne od prvog poglavlja. Autor
UVOD
xi
1 UKRATKO O PROGRAMSKOM JEZIKU C
Programski jezik C je nastao sedamdesetih godina. Prethodilo mu je nekoliko dosta sliånih programskih jezika, ali se u praksi samo on zadræao. U toku razvoja bilo je postavljeno viãe zahteva, koje je novi jezik trebalo da zadovolji, ali su najbitniji sledeñi: • razviti viãi programski jezik koji raspolaæe barem nekim moguñnostima maãinskog jezika (direktan pristup memoriji, veñi skup definisanih operatora itd.); • razviti programski jezik sa vrlo malim skupom naredbi i relativno velikom bibliotekom funkcija. Jezik koji zadovoljava prvo svojstvo bi bio udoban za rad, ali bi imao izraæajne moguñnosti maãinskog jezika, ãto za veñinu prethodnih viãih programskih jezika nije bilo karakteristiåno. Jezik koji zadovoljava drugo svojstvo ima mali skup naredbi i time je pojednostavljen prevodilac.
1.1 Azbuka programskog jezika C Kao svaki programski jezik, i programski jezik C ima azbuku koja se sastoji od: • • • •
velikih slova engleske abecede; malih slova engleske abecede; cifara dekadnog brojnog sistema; specijalnih znakova: ( ) + –
* ~ ! # % ^ & _ | = ' " : ; > < , . / \ ?
1
1.2 Leksiåke konstrukcije u programskom jeziku C Kombinovanjem znakova iz azbuke po odreœenim sintaksnim pravilima dobijamo leksiåke konstrukcije u programskom jeziku. Te konstrukcije delimo na: • proste i • sloæene.
1.3 Proste leksiåke konstrukcije U proste se ubrajaju: • imena, • konstante, dok sloæene konstrukcije åine: • izrazi, • naredbe, • funkcije.
1.3.1
Imena
Imena åine reåi koje se sastoje od: • slova, • cifara i • specijalnog znaka _ tako da: • prvi znak ne moæe biti cifra i • ime moæe imati najviãe 31 znak. Napomenimo da se mala i velika slova razlikuju. Tako reåi ABC i abc predstavljaju razliåita imena. Imena sluæe da se identifikuju (tj. imenuju): • • • •
2
promenljive, simboliåke konstante, obeleæja, funkcije.
ALGORITMI U PROGRAMSKOM JEZIKU C
1.3.2
Konstante
Konstante se dele na • celobrojne, • meãovite (razlomljene) i • znakovne. 1.3.2.1 Celobrojne konstante Celobrojna konstanta moæe biti zapisana u jednom od tri brojna sistema: • dekadnom (osnova 10), • oktalnom (osnova 8) ili • heksadekadnom (osnova 16). Ispred konstante moæe biti naveden znak – ili + (obiåno konstante u dekadnom sistemu mogu biti oznaåene). Konstanta u dekadnom brojnom sistemu se sastoji od cifara dekadnog brojnog sistema. Konstantu u oktalnom sistemu åini niz cifara iz skupa {0, 1, 2, 3, 4, 5, 6, 7} pri åemu prva cifra mora biti 0 (to je indikator da je konstanta u oktalnom sistemu). Konstanta u heksadekadnom sistemu se sastoji od prefiksa 0x iza koga sledi niz cifara iz skupa {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F}. Znakovi A, B, C, D, E i F odgovaraju ciframa koje imaju, redom, vrednost 10, 11, 12, 13, 14 i 15 u dekadnom sistemu. Umesto velikih slova mogu biti navedena odgovarajuña mala slova (a, b, c, d, e ili f). 1.3.2.2 Meãovite konstante Meãovite brojne konstante se zapisuju u dekadnom brojnom zapisu. Mogu imati znak + ili – (kaæe se da su oznaåene), koji se navodi kao prvi simbol u konstanti. Iza toga sledi meãovita konstanta bez znaka. Meãovita konstanta bez znaka ima dva moguña oblika i javlja se kao: • meãovita konstanta bez eksponenta i • meãovita konstanta sa eksponentom. Meãovita konstanta bez eksponenta se sastoji od niza cifara koji åini celobrojni deo, decimalne taåke (.) i niza cifara koji åini decimalni deo. Meãovita konstanta sa eksponentom se sastoji od meãovite konstante bez eksponenta za kojom slede slovo e (ili E) i eksponent. Eksponent je celobrojna konstanta sa ili bez znaka, zapisana u dekadnom sistemu. Na primer: 1.2e2 ili 0.3e–5.
Vrednost ovakve konstante se raåuna tako ãto se broj ispred simbola e (mantisa meãovite konstante) pomnoæi brojem koji se dobije nakon stepenovanja broja 10 brojnom konstantom iza simbola e, tj. gore zapisane konstante imaju, redom, vrednosti: 1.2*102 i 0.3*10–5.
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
3
1.3.2.3 Znakovne konstante Znakovne konstante su konstrukcije åija je vrednost neki od znakova koje je moguñe proizvesti na datom raåunaru (uåitati ili prikazati na izlaznom ureœaju). To su slova, cifre, interpunkcijski znaci itd. Najjednostavniji oblik zapisivanja takvih konstanti je navoœenjem znaka izmeœu apostrofa: npr. 'a' ili 'Z' ili '!' itd. Meœutim, osim ovakvih znakova koji su, uslovno reåeno, pogodni za zapisivanje postoje i znaci koje je neãto teæe prikazati. Na primer, takav je znak za prelazak u novi red, vrañanje na poåetak reda, vrañanje jedno mesto unazad, itd. To su tzv. neprintabilni znaci i znakovne konstante koje odgovaraju tim znacima moraju biti na drugi naåin zapisane. Tako su u programskom jeziku C uvedene i sledeñe (specijalne) znakovne konstante: '\n' '\t' '\r' '\a' '\b' '\f' '\'' '\"' '\\'
znak za nov red; znak za horizontalno pomeranje; znak za pozicioniranje na poåetak reda; znak åijim se ãtampanjem proizvodi zvuåni efekat (engl. beep); znak za vrañanje jedno mesto unazad (engl. backspace); znak koji proizvodi prelazak na sledeñu stranu (engl. formfeed); jednostruki znak navoda; dvostruki znak navoda; znak \ (engl. backslash).
Ovakav zapis znakovne konstante je vrlo zgodan zato ãto podseña na odgovarajuñi znak. Drugi naåin da se zapiãe neki znak je navoœenjem ASCII koda datog znaka na jedan od sledeña dva naåina: '\ooo' gde pojedinim znakovima o odgovaraju oktalne cifre (iz skupa {0, 1, 2, 3, 4, 5, 6, 7}) tako da trocifreni ceo broj predstavlja ASCII kôd znaka koji æelimo da zapiãemo; '\xhh' gde pojedinim pojavama znaka h odgovaraju cifre iz heksadekadnog brojnog sistema ({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F}). Tako je sa dve cifre zapisan heksadekadni broj koji predstavlja ASCII kôd æeljenog znaka.
1.4 Podatak Podatak je svaki objekat koji se obraœuje (nad kojim se izvrãavaju neke radnje (manipulacije)). Pojedini podaci se razlikuju po tome ãta moæe biti njihova vrednost i ãta sve moæemo s njima da radimo. Tako je jedan podatak potpuno odreœen: • skupom moguñih vrednosti koje moæe imati, i • operacijama (radnjama, manipulacijama) koje moæemo izvrãavati nad njim. Skup vrednosti i operacije odreœuju tip podataka. Svaki tip ima svoje ime.
4
ALGORITMI U PROGRAMSKOM JEZIKU C
1.5 Promenljive Promenljive u programskom jeziku su objekti koji imaju neku vrednost, pri åemu se vrednost moæe menjati u toku izvrãavanja programa. Pri tome to ne moæe biti bilo koja vrednost, veñ samo vrednost jednog tipa. Svaka promenljiva koja se koristi mora biti deklarisana. Deklaracijom se odreœuju: • ime promenljive (to je ime prema sintaksi jezika C), • tip promenljive (odnosno tip kome pripada vrednost date promenljive) i • poåetna vrednost (vrednost u poåetnom trenutku) – neobavezno. Deklaracija promenljive ima sledeñi zapis: imet imep [= vrednost];
gde je imet tip promenljive, imep ime promenljive, a vrednost (koja moæe ali ne mora biti navedena) poåetna vrednost date promenljive. Na primer, deklaracijama: int i, j, n=10; float a, e=2.71, pi=3.14; char c, d='?';
su deklarisane: • promenljive celobrojnog tipa i, j i n; • promenljive realnog tipa a, e i pi; • promenljive znakovnog tipa c i d. Promenljive n, e, pi i d imaju, redom, vrednosti 10, 2.71, 3.14 i '?'.
1.6 Prosti tipovi podataka Programski jezik C raspolaæe tipovima podataka sliånim onima koji postoje u veñini viãih programskih jezika. Moæemo ih podeliti u dve grupe: prosti i sloæeni (komponovani) tipovi podataka. Od prostih tipova podataka postoje: • znakovni tip podataka (char), • celobrojni tip (short, int, long), • meãoviti tip podataka (float, double).
1.6.1
Znakovni tip
Kako veñina ovih tipova postoji u drugim viãim programskim jezicima, nema potrebe da se detaljno opisuju. Promenljiva tipa char moæe imati vrednost iz skupa znakova koje moæemo prikazati na konkretnom raåunaru (velika i mala slova, cifre, specijalni znaci itd.).
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
5
1.6.2
Celobrojni tip
Promenljiva nekog od celobrojnih tipova moæe imati kao vrednost neki ceo broj. Meœutim, opseg u kome se moæe kretati data vrednost je odreœen tipom koji smo koristili (short, int, long), kao i konkretnom implementacijom jezika C. Naime, pojedini od ovih celobrojnih tipova se razlikuju po broju bajtova iskoriãñenih za registrovanje podataka i to je obiåno 1 (ili 2) bajt za podatak tipa short, 2 (ili 4) bajta za podatak tipa int i 4 bajta za podatak tipa long. U zavisnosti od toga koliko je bajtova iskoriãñeno, menja se i opseg u kome se nalazi vrednost podatka, i to je: • od – 128 do 127 ako je iskoriãñen 1 bajt; • od – 32768 do 32767 ako je iskoriãñeno 2 bajta, ili • od – 231 do 231–1 ako se koriste 4 bajta. Svakom od navedenih tipova moæe biti dodat prefiks unsigned. To znaåi da ñe se podatak tog tipa tretirati kao neoznaåen broj (podatak bez znaka ili nenegativan). Time se menja opseg u kome se moæe nalaziti vrednost podatka tog tipa, i to je: • 0 do 255 ako je iskoriãñen 1 bajt; • 0 do 65535 ako su iskoriãñena 2 bajta; • 0 do 232–1 ako su iskoriãñena 4 bajta. Da bi se naglasilo da je neka celobrojna konstanta tipa long, iza niza cifara koji åine tu konstantu, dodaje se sufiks l (ili L). Da bi se naglasilo da je konstanta neoznaåen broj, sliåno se dodaje sufiks u (ili U).
1.6.3
Logiåki tip
Za razliku od nekih viãih programskih jezika, u programskom jeziku C ne postoji logiåki tip podataka (boolean). Promenljive tog tipa bi imale vrednosti iz skupa {taåno (istina, engl. true), netaåno (laæ, engl. false)}. Ipak, kao ãto ñemo videti kasnije, postoje neke logiåke operacije: negacija, konjunkcija, disjunkcija. Naime, podatak bilo kog od navedenih tipova moæe biti interpretiran kao logiåka vrednost (taåno ili netaåno). Uobiåajeno je da se za to koriste celobrojni tipovi podataka. Naime, ako je vrednost nekog podatka tipa int razliåita od nule i ako ga tretiramo kao logiåki podatak (izvodimo logiåke operacije), njegova vrednost ñe biti taåno. Ako je vrednost tog podatka jednaka nuli i ako ga tretiramo kao logiåki podatak, onda ñe imati vrednost netaåno. Ako rezultat odreœenih logiåkih operacija pridruæujemo promenljivoj nekog od navedenih tipova, onda ñe toj promenljivoj biti pridruæena vrednost: • 1 ako je rezultat logiåke operacije taåno (istina); • 0 ako je rezultat logiåke operacije netaåno (laæ).
6
ALGORITMI U PROGRAMSKOM JEZIKU C
1.6.4
Meãoviti tip
Pored navedenih, u proste tipove podataka se ubrajaju i tipovi float i double. To su meãoviti tipovi podataka, tj. podaci tog tipa su realni brojevi. Napomenimo da to ne odgovara matematiåkom poimanju realnih brojeva. Podatak ovog tipa ne moæe imati za vrednost bilo koji realni broj (veñ samo brojeve iz odreœenog podskupa i to je podskup skupa racionalnih brojeva [razlomaka]). Tipovi float i double se razlikuju po veliåini prostora za registrovanje podataka (obiåno su to 4 bajta za podatak tipa float, odnosno 8 bajtova za podatak tipa double). Razlikuju se takoœe po broju znaåajnih cifara koje moæemo izdvojiti (to je obiåno 6–7 cifara za podatak tipa float, odnosno 14–15 cifara za podatak tipa double).
1.7 Definisane operacije U daljem tekstu koristi se termin l-vrednost. Pod l-vrednoãñu se podrazumeva objekat kome moæe biti promenjena vrednost. Za sada su to promenljive a kasnije ñemo videti joã neke objekte sa sliånim svojstvom. Nad navedenim tipovima podataka definisane su neke operacije. Veñina operacija je definisana za sve navedene proste tipove podataka, dok su neke definisane samo nad nekim tipovima.
1.7.1
Aritmetiåki operatori
Prvu grupu operatora åine aritmetiåki operatori: Operator
Naziv
++
Uveñavanje za 1 (inkrementiranje) Umanjivanje za 1 (dekrementiranje) Promena znaka Mnoæenje Deljenje Sabiranje Oduzimanje Ostatak pri deljenju Dodeljivanje
–– – * / + – % =
Tipovi nad kojima je definisana char, int
unarni
char, int
unarni
char, int
Vrsta operatora
unarni binarni binarni binarni binarni binarni binarni
Operatori uveñavanja i umanjivanja se ne pojavljuju u veñini drugih programskih jezika. To su unarni operatori (imaju jedan operand). Operand na koji se
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
7
primenjuju je znakovnog ili celobrojnog tipa i mora biti l-vrednost. Operator moæe biti naveden pre operanda (prefiks operator) ili posle operanda (postfiks operator). U oba sluåaja dovodi do uveñavanja ili umanjivanja vrednosti operanda za 1 (znaåi, izraz x++ [odnosno ++x] je ekvivalentan izrazu x=x+1 i, sliåno, izraz x–– [odnosno ––x] je ekvivalentan izrazu x=x–1). Meœutim, u sluåaju primene prefiks operatora, prvo se izvrãava uveñavanje ili umanjivanje vrednosti operanda i ta nova vrednost se koristi u preostalim operacijama u kojima uåestvuje taj operand. Pri upotrebi postfiks operatora je obratno: trenutna vrednost tog operanda se koristi za ostale operacije u kojima se pojavljuje kao operand, a nakon toga se izvrãi uveñanje ili umanjenje. Na primer: izraz, y=x*++z
je ekvivalentan izrazu, z=z+1, y=x*z,
a izraz, y=x*z++
izrazu, y=x*z, z=z+1.
Moæe se reñi da je vrednost izraza: ++op
jednaka vrednosti izraza op+1, a da je vrednost izraza, op++
jednaka sa op. Meœutim, oba izraza imaju i dodatni efekat, a to je uveñavanje vrednosti objekta op za 1.
1.7.2
Sloæeni operatori dodeljivanja nastali kombinovanjem operatora dodeljivanja i nekog aritmetiåkog operatora
Drugu grupu operatora åine sloæeni operatori dodeljivanja. Ovi operatori su nastali kombinovanjem aritmetiåkih operatora i operatora dodeljivanja. +=
–=
8
Dodaje vrednost operanda sa desne strane operatora vrednosti operanda sa leve strane (levi operand je obavezno l-vrednost) i to postaje nova vrednost levog operanda (npr. x+=3 je ekvivalentno izrazu x=x+3). Oduzima vrednost operanda sa desne strane operatora od vrednosti operanda sa leve strane i to postaje nova vrednost levog operanda (npr. x–=3 je ekvivalentno izrazu x=x–3).
ALGORITMI U PROGRAMSKOM JEZIKU C
*= /= %=
1.7.3
Vrednost izraza sa desne strane mnoæi se vrednoãñu operanda na levoj strani i to je nova vrednost levog operanda. Vrednost operanda sa leve strane se deli vrednoãñu operanda sa desne strane i to je nova vrednost levog operanda. Vrednost operanda sa leve strane se deli vrednoãñu operanda sa desne strane i ostatak tog deljenja je nova vrednost levog operanda.
Relacioni operatori
Treñu grupu operatora åine relacioni operatori: < <= == >= > !=
Manje od. Manje ili jednako. Jednako. Veñe ili jednako. Veñe. Razliåito.
Svi operatori iz ove grupe su binarni. Rezultat njihove primene je logiåkog tipa. Na primer, vrednost izraza x
1.7.4
Logiåki operatori
Åetvrtu grupu åine logiåki operatori: Operator ! && ||
naziv negacija konjunkcija disjunkcija
vrsta unarni binarni binarni
Operandi se interpretiraju kao logiåke vrednosti i nad takvim vrednostima se izvrãava zadata operacija.
1.7.5
Operatori nad bitovima i operatori pomeranja
Petu grupu åine operatori nad bitovima i operatori pomeranja. Za operatore nad bitovima je karakteristiåno da se izvrãavaju nad parovima odgovarajuñih bitova operanada (ako je binarni operand) ili nad pojedinim bitovima operanda (ako je unarni operator).
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
9
<<
>>
~
&
|
^
1.7.6
Pomeranje ulevo. Levi operand je znakovnog ili celobrojnog tipa, a desni ceo broj. Vrednost izraza x<>y je jednaka koliåniku broja x i broja 2y. Na tehniåkom nivou efekat izvrãenja ove operacije je da se u reprezentaciji broja x na raåunaru izvrãi pomeranje svih cifara za y mesta udesno. Nakon tog pomeranja y najteæih cifara ñe biti izjednaåeno sa; • nulom, ako je operand x neoznaåen (unsigned); • najteæim bitom broja x pre izvrãenja operacije, ako je x oznaåen (signed). Negacija nad bitovima. Ovo je unarni operator. Operand je celobrojnog ili znakovnog tipa. Operacija ñe se izvrãiti tako ãto ñe se svaki bit u reprezentaciji tog podatka u raåunaru negirati (tj. ako je vrednost bita jedan (1) promeniñe se u nulu (0) i obratno). Konjukcija nad bitovima. Ovo je binarni operator. Operandi su celobrojnog ili znakovnog tipa. Kao i kod negacije nad bitovima izvrãava se (logiåka) konjunkcija nad parovima odgovarajuñih bitova u operandima, i to ñe biti vrednost odgovarajuñeg bita u rezultatu. Pri tome se 1 tretira kao istina (true), dok se 0 (nula) tretira kao laæ. Na primer: 0x54 & 0x97 = 0x14. Disjunkcija nad bitovima. Ovo je binarni operator. Operandi su celobrojnog ili znakovnog tipa. Izvrãava se disjunkcija nad odgovarajuñim bitovima operanada i to je vrednost odgovarajuñeg bita u rezutatu. Ekskluzivna disjunkcija nad bitovima. Operandi su znakovnog ili celobrojnog tipa. Izvrãava se ekskluzivna disjunkcija nad odgovarajuñim bitovima operanada i to je vrednost odgovarajuñeg bita u rezultatu.
Sloæeni operatori dodeljivanja nastali kombinovanjem operatora dodeljivanja i operatora nad bitovima
Operatori nad bitovima se mogu kombinovati sa operatorom dodeljivanja. Tako se dobija joã jedna grupa sloæenih operatora dodeljivanja: |=
10
Izvrãava se disjunkcija nad odgovarajuñim bitovima operanada sa leve i desne strane operatora i rezultat se dodeljuje levom operandu.
ALGORITMI U PROGRAMSKOM JEZIKU C
&=
<<= >>=
Izvrãava se konjukcija nad bitovima nad odgovarajuñim bitovima operanada sa leve i desne strane operatora i rezultat se dodeljuje levom operandu. Izraz x<<=y je ekvivalentan izrazu x=x<>=y je ekvivalentan izrazu x=x>>y.
Za sve navedene sloæene operatore dodeljivanja levi operand mora biti l-vrednost.
1.7.7
Uslovni operator
Pomenimo, na kraju, i jedan ternarni operator (ima tri operanda). To je takozvani uslovni operator (?:). Prvi operand je uslovni (logiåki ili bulovski) izraz, dok su druga dva operanda proizvoljnog (ali istog tipa). Prvi i drugi operand su razdvojeni znakom pitanja (?), a drugi i treñi dvotaåkom (:). Dejstvo je sledeñe: • izraåunava se vrednost prvog operanda; • ako je vrednost prvog operanda istina, onda se raåuna vrednost drugog operanda i to je vrednost celog izraza; • ako je vrednost prvog operanda laæ, onda se raåuna vrednost treñeg operanda i to je vrednost celog izraza. Na primer: max=(x>y)? x: y; ili min=(x>y)? y: x;
1.7.8
Operator veliåine
Pomenimo i unarni operator sizeof. Operand za ovaj operator se navodi iza reåi sizeof i to je ime promenljive ili tipa. Rezultat primene tog operatora je veliåina memorijskog prostora (izraæena u bajtovima) koji zauzima: • data promenljiva (ako je operand promenljiva) ili • promenljiva datog tipa (ako je operand tip).
1.8 Konverzije Kao i u svim programskim jezicima, tako je i u programskom jeziku C poæeljno da operandi u operacijama budu istog tipa (to se odnosi na binarne operacije, osim na operatore pomeranja). Dozvoljeno je, ipak, da operandi budu razliåitih tipova. U tom sluåaju programski jezik C vrãi konverziju pojedinih podataka. Konverzija podrazumeva promenu tipa podatka, ali tako da podatak zadræi istu vrednost (ako je to moguñe). Postoje situacije kada nije moguñe izvesti konverziju podataka iz jednog tipa u drugi tako da on zadræi vrednost. Naveãñemo par primera konverzija iz jednog tipa u drugi tip.
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
11
1.8.1
Konverzija iz jednog celobrojnog tipa u drugi
Pri konverziji iz oznaåenog u neoznaåeni (ali isti) celobrojni tip, mogu nastupiti dva sluåaja: • ako je oznaåeni broj bio pozitivan, vrednost ostaje ista; • ako je oznaåeni broj bio negativan, onda se konverzijom dobija najmanji pozitivan broj koji ima isti ostatak kao i polazni broj, pri deljenju brojem za jedan veñim od najveñeg neoznaåenog broja tog tipa koji se moæe predstaviti na raåunaru (tj. sa 28 ili 216 ili 232 ako se broj predstavlja pomoñu 1, 2 ili 4 bajta, redom). Ako vrãimo konverziju iz neoznaåenog u oznaåen ceo broj istog tipa, brojevi veñi od najveñeg oznaåenog celog broja koji se moæe predstaviti, biñe negativni posle konverzije. Dobijeni broj ñe imati isti ostatak pri deljenju brojem za jedan veñim od najveñeg neoznaåenog broja tog tipa koji se moæe predstaviti na raåunaru. Na primer, ako je neoznaåen broj 240 predstavljen pomoñu jednog bajta, nakon konverzije u oznaåeni broj dobiñemo –16. To je zato ãto brojevi 240 i –16 imaju isti ostatak pri deljenju sa 256=28. Ako se vrãi konverzija iz celog broja, koji se predstavlja pomoñu veñeg broja memorijskih lokacija, u ceo broj predstavljen sa manje memorijskih lokacija, onda se konverzija izvodi odbacivanjem potrebnog broja znaåajnijih (teæih) cifara. Dobijeni broj ima isti ostatak kao i polazni broj pri deljenju brojem za 1 veñim od najveñeg neoznaåenog broja ciljnog tipa. Pod ciljnim tipom podrazumevamo tip u koji se vrãi konverzija. Ako se vrãi konverzija iz celog broja, koji je predstavljen pomoñu manjeg broja memorijskih lokacija, u ceo broj predstavljen sa viãe memorijskih lokacija, onda konverzija zavisi od toga da li su brojevi oznaåeni ili neoznaåeni: • ako je broj neoznaåen, konverzija se izvodi dopisivanjem potrebnog broja cifara sleva, pri åemu se dopisuju nule (0); • ako je broj oznaåen, konverzija se izvodi dopisivanjem potrebnog broja cifara sleva, pri åemu se dopisuju: 0 (nule, ako je polazni broj bio pozitivan); 1 (jedinice, ako je polazni broj bio negativan). Dopisane cifre biñe, dakle, identiåne najteæoj cifri u predstavljanju datog broja na raåunaru.
1.8.2
Konverzija iz meãovitog u celobrojni tip
Konverzija se izvrãava odbacivanjem dela iza decimalne taåke. Posle odbacivanja, dobijeni ceo broj moæe biti izvan opsega brojeva koji mogu da se predstave. U tom sluåaju efekat je nedefinisan i zavisi od implementacije. Obiåno se dobijeni broj deli brojem za jedan veñim od najveñeg odgovarajuñeg neoznaåenog celog broja koji se moæe predstaviti, a nakon toga se odgovarajuñi neoznaåeni broj konvertuje u oznaåeni (ako se izvodi konverzija u oznaåeni ceo broj).
12
ALGORITMI U PROGRAMSKOM JEZIKU C
1.8.3
Konverzije u toku izraåunavanja vrednosti izraza
Programski jezik C ima linearno ureœenje meœu tipovima, pa je tako: char < unsigned char < short < unsigned short < int < unsigned int < long < unsigned long < float < double.
Ako su operandi nad kojima se obavlja neka operacija razliåitog tipa, tada se vrednost operanda, uslovno reåeno, manjeg tipa konvertuje u odgovarajuñu vrednost koja ñe biti istog tipa kao i drugi operand, posle åega se izvrãava operacija nad tim podacima. Ovakvo ureœenje meœu tipovima dovodi ponekad do neoåekivanih rezultata. Tako ñe rezultat poreœenja –1<1U biti laæ (netaåno) zato ãto je desni operand tipa unsigned int, pa kako je to stariji tip, broj –1 se konvertuje u odgovarajuñi neoznaåen ceo broj (–1 je predstavljen kao 0xFFFF, tj. 65535 i to je vrednost nakon konverzije) koji je veñi od 1. Meœutim, vrednost izraza – 1L<1U je istina jer je levi operand starijeg tipa u hijerarhiji (long) pa se desni operand konvertuje u tip long; i tada poredimo brojeve –1 i 1. Za aritmetiåke operacije je karakteristiåno da je rezultat istog tipa kao i operandi. Tako ñe, na primer, izraz 5/3 imati vrednost 1 zato ãto su operandi celi brojevi. Meœutim, u nekim sluåajevima je neophodno da dobijemo taåan rezultat deljenja 5/3. To postiæemo tako ãto jedan operand konvertujemo u neki drugi tip (recimo float). Konverzija se postiæe koriãñenjem operatora promene tipa ili konverzije (tzv. cast operator). To je unarni operator. Primer koriãñenja tog operatora je: (tip) vrednost, gde je tip ime tipa u koji æelimo da konvertujemo vrednost koja sledi iza operatora (zagrade su obavezne). Na primer: (float) 5/3 proizvodi da se broj 5 konvertuje u odgovarajuñi meãoviti broj (5.0) i nakon toga izvede deljenje brojeva 5.0 i 3. Kako je jedan operand tipa float i tip float je veñi od tipa int (tip drugog operanda), to ñe i drugi operand biti konvertovan u tip float. Tako ñe se izvesti deljenje 5.0/3.0 i dobiti rezultat 1.666667.
1.9 Nabrojivi tip podataka Sama reå kaæe da se nabrojivi tip podataka dobija nabrajanjem svih vrednosti. Na primer: enum dani {pon, uto, sr, cet, pet, sub, ned};
ili enum meseci {jan, feb, mar, apr, maj, jun, jul, avg, sep, okt, nov, dec};
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
13
Prvim zapisom je definisan nabrojivi tip dani koji ima ukupno sedam vrednosti. Sporedni efekat je da su definisane simboliåke konstante: pon, uto, sr, cet, pet, sub i ned koje imaju vrednosti, redom 0, 1, 2, 3, 4, 5 i 6. Sliåno, drugim zapisom je definisan nabrojivi tip meseci sa ukupno 12 vrednosti i 12 simboliåkih konstanti åije su vrednosti brojevi od 0 do 11. Izmeœu velikih zagrada se navode imena razdvojena zarezima. Ako se drugaåije ne naglasi, prvom imenu se dodeljuje vrednost 0, a svako sledeñe ima za jedan veñu vrednost. Iza nekog imena moæe biti dodat znak = (jednako) za kojim sledi ceo broj. Time je datom imenu dodeljena vrednost (to je broj iza znaka =), da bi sledeña imena opet dobijala za po jedan veñu vrednost (ako im nije dodeljena). Na primer, zapisom: enum boje (crna, plava=2, zelena, zuta, crvena=6, ljubicasta};
definiãemo tip boje i konstante crna, plava, zelena, zuta, crvena i ljubicasta åije su vrednosti, redom, 0, 2, 3, 4, 6 i 7. Promenljivu nabrojivog tipa dani deklariãemo zapisom: enum dani x;
Datoj promenljivoj mogu biti dodeljene vrednosti iz skupa {pon, uto, sr, cet, pet, sub, ned}.
1.10 Sloæeni tipovi podataka Osim navedenih standardnih tipova podataka postoje i sloæeni tipovi, tj. oni koji se dobijaju komponovanjem prostih tipova podataka.
1.10.1 Nizovi u programskom jeziku C Prvi od sloæenih tipova je niz. Promenljiva tipa niz se deklariãe na sledeñi naåin: tip ime[BROJ_ELEMENATA];
Promenljiva ime ima ukupno BROJ_ELEMENATA vrednosti i sve su tipa tip. Svakoj od pojedinih vrednosti moæe se pristupiti pomoñu indeksa, tj. rednog broja te vrednosti u nizu: prvoj se pristupa pomoñu zapisa ime[0], drugoj sa ime[1], poslednjoj sa ime[BROJ_ELEMENATA-1]. Umesto reåi BROJ_ELEMENATA moæe stajati konkretan pozitivan ceo broj ili ime neke simboliåke konstante poznate u fazi prevoœenja programa. Taj broj odreœuje koliko ñe vrednosti (elemenata) imati niz i prema tome, koliko ñe prostora biti rezervisano za njegovo predstavljanje u raåunaru. Programski jezik C dozvoljava da pojedinaåni elementi niza opet budu nizovi tako da se moæe deklarisati: tip ime[BROJ_ELEMENATA1]...[BROJELEMENATAK];
14
ALGORITMI U PROGRAMSKOM JEZIKU C
Ovim je deklarisan niz koji ñe imati k indeksa (kaæemo da je to k-dimenzionalni niz). Svaki od indeksa se navodi u posebnom paru uglastih zagrada. Na primer, zapisom: int x[10][20][30];
je deklarisan niz x koji ima tri indeksa (kaæe se joã da je to trodimenzionalan niz). Pojedinim elementima se pristupa tako ãto se navode tri indeksa, svaki u zasebnom paru zagrada: x[0][0][0] ili x[2][4][6] ili x[9][19][29].
Zapisom x[4][4] je odreœen jednodimenzionalni niz sa ukupno 30 elemenata, a zapisom x[8] dvodimenzionalan niz dimenzija 20×30. Kako se nizovi predstavljaju u raåunaru? U memoriji raåunara niz se predstavlja tako ãto se koristi odreœen broj uzastopnih memorijskih lokacija (bajtova). Broj iskoriãñenih bajtova je odreœen brojem elemenata niza (BROJ_ELEMENATA1 * BROJ_ELEMENATA2 * ... * BROJ_ELEMENATAK) i brojem lokacija (bajtova) potrebnih za predstavljanje jednog elementa niza. U rezervisanom delu memorije, memoriãe se prvo element åiji su svi indeksi jednaki 0. Zatim se memoriãu redom elementi kod kojih se poslednji indeks uveñava postepeno za po jedan. Kada poslednji indeks dostigne najveñu vrednost, onda se pretposlednji indeks uveña za jedan, dok poslednji kreñe ponovo od nule (0) da bi se postepeno uveñavao, itd.
1.10.2 Strukturni tip Osim nizova u programskom jeziku C mogu se definisati strukture. Jedna promenljiva tipa strukture ima viãe vrednosti koje ne moraju obavezno biti istog tipa. Na primer: struct imet{ tip1 ime1; tip2 ime2; ... tipk imek; } imep;
Promenljiva sa imenom imep je tipa struct imet. Ona ima ukupno k vrednosti koje su redom tipova tip1, tip2, ..., tipk. Svakoj od tih vrednosti moæe se pristupiti pomoñu imena te vrednosti navedenog iza imena promenljive i taåke (.). Tako se prvoj pristupa zapisom imep.ime1, drugoj sa imep.ime2 itd., poslednjoj sa imep.imek. Pojedine vrednosti u okviru strukture zovu se polja. Naravno, pojedina polja takoœe mogu biti strukturnog tipa. Ovde sreñemo joã jedan operator, operator . (element strukture).
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
15
1.10.3 Unije Promenljiva tipa unije ima viãe vrednosti koje mogu biti razliåitih tipova, ali je za njih karakteristiåno da su zapamñene u istom memorijskom prostoru. Poãto je za registrovanje iskoriãñen isti memorijski prostor (tj. prostor od iste adrese), pojedine vrednosti nisu u potpunosti nezavisne. Naime, promena jedne od vrednosti moæe dovesti do promene i ostalih. Na primer, zapisom: union pru { char a; int b; float c; } x;
je deklarisana promenljiva x. Promenljiva x ima tri vrednosti. Te vrednosti ñemo zvati polja. Svakom od polja se pristupa navoœenjem njegovog imena iza imena promenljive (x) i taåke: x.a, x.b, x.c. Memorijski prostor potreban za registrovanje promenljive tipa unije odreœen je memorijskim prostorom potrebnim za registrovanje najduæeg polja (tj. polja za åije je registrovanje potreban najveñi prostor). U prethodnom primeru, za polje a potreban je 1 bajt, za polje b 2 bajta i za polje c 4 bajta. Stoga je za registrovanje promenljive x potrebno 4 bajta. Sva polja u okviru unije se registruju od iste memorijske lokacije – to je lokacija sa najniæom adresom u okviru memorijskog prostora rezervisanog za promenljivu tipa unije. Naravno, ne moraju sva polja da iskoriste celokupni memorijski prostor rezervisan za registrovanje unije.
1.10.4 Polja bitova Podatak ovog tipa sadræi viãe vrednosti, ali za predstavljanje pojedinih vrednosti ne mora biti iskoriãñen ceo bajt ili registar (dva bajta), veñ deo bajta ili registra. Na primer, deklaracijom: struct pb { int x:3, y:4; unsigned z:2, w:5; } u;
definisana je promenljiva u. Promenljiva u ima åetiri vrednosti, koje se zovu polja. Pojedinim poljima se pristupa navoœenjem imena iza prefiksa koji se sastoji od imena promenljive i taåke (u.x, u.y, u.z i u.w). Za registrovanje polja x se koriste 3 bita, polja y 4 bita itd. Polja x i y ñe biti registrovana u okviru istog bajta zato ãto je za njihovo registrovanje potrebno ukupno 7 bitova. Za sledeña polja se rezerviãe navedeni broj bitova, poåev od prvog najlakãeg neiskoriãñenog. Polja x i y se interpretiraju kao celi oznaåeni brojevi. Tako ñe vrednost polja x biti u intervalu [–4, 3], a vrednost polja y u intervalu [–8 =–23, 7=23–1]. Polja z i w se interpretiraju kao neoznaåeni brojevi. Tako ñe se vrednost polja z kretati u opsegu [0, 3=22–1], a vrednost polja w u opsegu [0, 31=2 5–1].
16
ALGORITMI U PROGRAMSKOM JEZIKU C
Polja bitova poveñavaju izraæajne moguñnosti programskog jezika C i pribliæavaju ga maãinskom jeziku. Naime, åesto se sadræaj registra deli na manje celine koje za sebe imaju odreœeno znaåenje. To se posebno odnosi na tzv. statusne i kontrolne registre. Na primer, u okviru statusnog registra centralnog procesora se mogu izdvojiti delovi kojima su zapisane sledeñe informacije: znak rezultata poslednje aritmetiåke operacije, da li je rezultat poslednje izvrãene operacije jednak nuli, da li je u toku izvoœenja doãlo do prekoraåenja itd. Takvi registri se na najprirodniji naåin modeliraju poljima bitova. Napomenimo da se prostor za polja rezerviãe od lakãih ka teæim bitovima.
1.11 Pokazivaåi U programskom jeziku C je omoguñen rad sa pokazivaåima. Promenljiva tipa pokazivaå se deklariãe na sledeñi naåin: tip *ime;
gde je tip ime nekog tipa (standardnog ili nekog koji je definisao korisnik). Promenljiva ime je tipa pokazivaå (ili pokazivaåkog tipa). Njena vrednost je obeleæje (obiåno adresa) memorijske lokacije (prostora) na kojoj moæe da se zapiãe vrednost tipa tip. Toj memorijskoj lokaciji pristupamo zapisom *ime. Zapis *ime sa stanoviãta C-a ima gotovo sva svojstva kao i obiåna promenljiva (moæe joj biti dodeljena vrednost ili moæe biti operand u izrazu). Neka imamo sledeñu deklaraciju: tip *ime1, *ime2;
Moæemo zapisati sledeñe izraze: ime1 = ime2; *ime1 = *ime2;
Prvim izrazom izjednaåavamo vrednosti pokazivaåkih promenljivih ime1 i ime2 tako da one pokazuju na istu memorijsku lokaciju (to je lokacija na koju je pre izvrãavanja ovog izraza pokazivala promenljiva ime2). Drugim izrazom samo se izjednaåava sadræaj memorijske lokacije na koju pokazuje pokazivaåka promenljiva ime1 sa sadræajem lokacije na koju pokazuje promenljiva ime2. U bliskoj vezi sa pokazivaåima je i unarni operator & (adresa). Vrednost izraza &ime je obeleæje memorijske lokacije rezervisane za registrovanje promenljive sa imenom ime. Takva vrednost moæe biti dodeljena promenljivoj tipa pokazivaå. Naime, ako imamo deklaraciju: tip *ime1, ime2;
onda zapisom, ime2 = *ime1;
promenljivoj ime2 dodeljujemo vrednost koju sadræi memorijska lokacija na koju pokazuje pokazivaåka promenljiva ime1. Zapisom, ime1=&ime2;
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
17
menjamo vrednost promenljive ime1 tako da je njena vrednost jednaka obeleæju (adresi) lokacije rezervisane za promenljivu ime2. Osim na ovaj naåin (koriãñenjem operatora adresa), vrednost pokazivaåke promenljive moæe da se inicijalizuje i koriãñenjem standardne funkcije alloc. Data funkcija ima samo jedan argument i to je ceo pozitivan broj (n). Funkcija rezerviãe prostor od n bajtova i vraña obeleæje (adresu) poåetka tog prostora. Preporuåljivo je jednom rezervisani prostor (koriãñenjem funkcije alloc) osloboditi pre zavrãetka izvrãavanja programa. To se postiæe pozivanjem funkcije afree. Nedostatak ovih standardnih funkcija je u tome ãto oslobaœanje rezervisanog memorijskog prostora mora biti izvrãeno u obrnutom redosledu od rezervisanja. Zbog toga je naknadno razvijen par funkcija malloc i free, koji to nije zahtevao. Za razliku od nekih programskih jezika, pokazivaåka promenljiva ne mora obavezno pokazivati na memorijsku lokaciju na kojoj je registrovana jedna vrednost, veñ na memorijsku lokaciju od koje je registrovan niz vrednosti istog tipa. Ako imamo deklaraciju: tip *ime;
onda prvoj od vrednosti pristupamo sa *ime, sledeñoj sa *(ime+1), zatim *(ime+2) itd. Meœutim, tim vrednostima moæemo pristupiti i sa ime[0], ime[1], ime[2] itd. Tako pokazivaåi pomalo podseñaju na nizove. Ako imamo deklaraciju: tip *ime1, ime2[NN];
onda moæemo zapisati ime1[0], ime1[1], ime1[2] itd., i sliåno ime2[0], ime2[1], ime2[2] itd. Ove dve promenljive razlikuju se u sledeñem: moæemo napisati, ime1=...
ali ne moæemo napisati, ime2=....
Drugim reåima, ime pokazivaåke promenljive moæe stajati na levoj strani operatora dodeljivanja, dok ime niza ne moæe. Obiåno se kaæe da su nizovi konstantni pokazivaåi.
1.11.1 Operatori definisani nad pokazivaåima Promenljive tipa pokazivaå mogu biti operandi u nekim aritmetiåkim operacijama. Pri tome se pokazivaåi tretiraju kao celi brojevi (jer adrese su isto tako celi brojevi). Tako se mogu koristiti: • unarni operatori uveñavanja (inkrementiranja) i umanjivanja (dekrementiranja); ako napiãemo ime++ onda ñe se vrednost promenljive ime uveñati za jedan (1), a to znaåi da ñe pokazivati na lokaciju na kojoj je registrovan sledeñi podatak tipa na koji pokazuje pokazivaåka promenljiva ime. Na primer, ako imamo deklaraciju, int *x;
onda ñe se izrazom, x++;
18
ALGORITMI U PROGRAMSKOM JEZIKU C
uveñati vrednost promenljive x za 2 jer u veñini implementacija celi brojevi se registruju koriãñenjem 2 bajta. U opãtem sluåaju, ako je x pokazivaåka promenljiva, zapisom, x++;
vrednost promenljive x biñe uveñana za broj memorijskih lokacija koje zauzima podatak na koji pokazuje promenljiva x (odnosno x ñe biti uveñano za sizeof *x). • binarni operatori sabiranja i oduzimanja, pri åemu je drugi operand ceo broj; ako imamo promenljivu deklarisanu sa: tip *x;
onda izraz x+3 ima vrednost istog tipa kao i promenljiva x (pokazivaå tip *) i to je obeleæje lokacije na kojoj se nalazi treña vrednost tipa tip u odnosu na vrednost *x (odnosno obeleæje lokacije na kojoj je x[3]). Drugim reåima, vrednost x se uveñava za proizvod broja 3 i broja bajtova potrebnih za registrovanje jedne vrednosti tipa tip (sizeof *x). Ako je deklarisan pokazivaå na strukturu: struct t *x;
tada se pojedinim poljima strukture struct t ne pristupa zapisom, *x.imep;
kao ãto bi se oåekivalo, veñ zapisom, x–>imep;
gde je imep ime polja u okviru strukture. Tako stiæemo do joã jednog operatora: –> je binarni operator (polje strukture).
1.12 Prioriteti i asocijativnost operatora Navedimo na kraju prioritete pojedinih operatora, kao i vrstu asocijativnosti. Kaæemo da je operator * levo asocijativan ako je zapis, x*y*z
ekvivalentan zapisu, (x*y)*z.
Sliåno, operator * je desno asocijativan ako je zapis, x*y*z
ekvivalentan zapisu x*(y*z).
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
19
Operatorima ñe kao prioritet biti dodeljeni celi brojevi. Manji broj oznaåava da je operator veñeg prioriteta: Prioritet 1 ++ –– –> . Prioritet 2 ++ –– ! ~ – & * sizeof (tip) Prioritet 3 * / Prioritet 4 + – Prioritet 5 << >> Prioritet 6 < <= > >= Prioritet 7 == != Prioritet 8 & Prioritet 9 ^
20
Opis postfiks postfiks polje strukture polje strukture
Asocijativni levo
prefiks prefiks logiåka negacija negacija nad bitovima promena znaka adresa pokazana promenljiva operator veliåine operator konverzije
desno
mnoæenje deljenje
levo
sabiranje oduzimanje
levo levo
pomeranje ulevo pomeranje udesno
levo
manje manje ili jednako veñe veñe ili jednako
levo
jednako razliåito
levo
konjukcija nad bitovima
levo
iskljuåiva disjunkcija nad bitovima
levo
ALGORITMI U PROGRAMSKOM JEZIKU C
Prioritet 10 | Prioritet 11 && Prioritet 12 || Prioritet 13 :? Prioritet 14 = *=, /= %=, += –=, <<= >>=, &= ^=, |= Prioritet 16 ,
Opis disjunkcija nad bitovima
Asocijativni levo
konjukcija
levo
disjunkcija
levo
uslovni operator
desno
dodeljivanje
desno
sloæeni operatori dodeljivanja zarez
levo
1.13 Izrazi U programskom jeziku C izrazi se formiraju od konstanti, imena promenljivih, imena funkcija i navedenih operatora. Pri tome treba voditi raåuna o prioritetu pojedinih operatora, kao i o asocijativnosti operatora (leva ili desna asocijativnost). Primetimo da je za razliku od nekih programskih jezika i dodeljivanje (=) operand. Tako je ono ãto je u nekim jezicima naredba dodeljivanja ili aritmetiåka naredba, u jeziku C izraz.
1.14 Naredbe Napomenuli smo veñ da je skup naredbi u programskom jeziku C vrlo mali. Najjednostavnije naredbe su: • izraz, • prazna naredba i • poziv funkcije. Napomenimo takoœe da se svaka naredba u programskom jeziku C zavrãava znakom taåka i zarez (;). Naime, u programskom jeziku C ovaj znak sluæi kao oznaka za kraj naredbe (engl. terminator), za razliku od nekih programskih jezika u kojima je znak koji razdvaja dve naredbe (engl. separator).
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
21
Naredba moæe (ali ne mora) biti obeleæena. Ako je naredba obeleæena, neposredno ispred nje je postavljeno obeleæje. Obeleæje je neko ime proizvedeno po sintaksnim pravilima programskog jezika C. Obeleæavanje naredbe se postiæe tako ãto se navede obeleæje i iza njega dvotaåka (:). Naglasimo da se u telu jedne funkcije ne moæe viãe puta koristiti isto obeleæje za obeleæavanje. Osim navedenih, postoje i sledeñe grupe naredbi: • • • •
sloæena (komponovana) naredba; naredbe grananja i izbora; naredbe petlji (ciklusa); naredbe za nasilnu promenu toka.
1.14.1 Komponovana naredba Komponovanu naredbu dobijamo od jedne ili viãe narebi koje grupiãemo tako ãto ispred prve stavimo otvorenu veliku (vitiåastu) zagradu ({), a iza poslednje zatvorenu veliku (vitiåastu) zagradu (}). To ovako izgleda: { naredba1 naredba2 ... naredbak }
Komponovana naredba se izvrãava tako ãto se jedna za drugom izvrãavaju naredbe od kojih je ona sastavljena.
1.14.2 Naredbe grananja i izbora Ovu grupu naredbi åine naredbe if i switch. Naredba if ima sledeñu sintaksu: if (izraz) naredba [else naredba]
Naredba poåinje sluæbenom reåju if iza koje se navodi izraz (obavezno u zagradama). Nakon toga navodi se naredba iza koje moæe biti navedena sluæbena reå else, za kojom sledi naredba (reå else i naredba iza nje obrazuju else-deo naredbe i mogu se izostaviti). Dejstvo naredbe je sledeñe: • izraåunava se vrednost izraza; • ako je dobijena vrednost istina (razliåita od nule), izvrãava se naredba koja neposredno sledi iza izraza; • ako je vrednost izraza laæ (vrednost je nula) i ako postoji else-deo, onda se izvrãava naredba iza reåi else. Pored naredbe if postoji i naredba switch. Njena sintaksa je sledeña: switch (izraz) { case v1: naredba11 naredba12 ...
22
ALGORITMI U PROGRAMSKOM JEZIKU C
naredba1k1 case v2: naredba21 naredba22 ... naredba2k2 . . . case vl: naredbal1 naredbal2 ... naredbalkl [ default: naredba1 naredba2 ... naredbak ] }
Kao ãto se vidi, naredba poåinje sluæbenom reåju switch iza koje se navodi izraz (obavezno u zagradama). Izraz je nekog od standardnih tipova razliåitog od float i double. Preostali deo naredbe se navodi u zagradama ({}) i åine ga delovi oblika: case vi: naredbai1 naredbai2 ... naredbaiki
gde je case sluæbena reå, a vi konstanta istog tipa kao i izraz. Osim toga, moæe postojati (ali ne mora) deo: default: naredba1 naredba2 ... naredbak
Efekat izvrãenja naredbe je sledeñi: • izraåunava se vrednost izraza; • pronalazi zapis oblika case v: unutar bloka izmeœu zagrada tako da je konstanta v jednaka izraåunatoj vrednosti izraza. Izvrãavaju se sve naredbe koje se nalaze izmeœu pronaœenog case v i kraja naredbe switch; • ako, pak, vrednost dobijena izraåunavanjem izraza nije pronaœena uz neko case, a postoji deo default, onda se izvrãavaju sve naredbe od tog obeleæja pa do kraja naredbe switch. Izvrãavanje naredbe switch se nasilno prekida ako se pojavi naredba break. Naime, ova naredba proizvodi prekid izvrãavanja naredbe switch i prouzrokuje prelazak na prvu naredbu iza naredbe switch.
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
23
1.14.3 Naredbe petlji Prva od ovih naredbi je naredba while. Ona ima sledeñu sintaksu: while (izraz) naredba
gde je while sluæbena reå. I u ovom sluåaju izraz se obavezno navodi u zagradama. Semantika je sledeña: • izraåunava se vrednost izraza; • ako je dobijena vrednost istina, izvrãava se naredba koja sledi i ponavlja se postupak od poåetka; u suprotnom se prelazi na sledeñu naredbu. Druga naredba je: do naredba while (izraz)
Njena semantika je sledeña: • izvrãava se naredba iza reåi do; • zatim se raåuna vrednost izraza iza reåi while; • ako je vrednost tog izraza istina, onda se sve ponavlja; u suprotnom se prelazi na sledeñu naredbu. Treña naredba je: for (izraz1; izraz2; izraz3) naredba
Izvrãavanje ove naredbe sastoji se od sledeñih radnji: (i) izraåunava se izraz1; (ii) izraåunava se izraz2; (iii) ako je izraz2 taåan, izvrãava se naredba i izraåunava izraz3; zatim se ponovo izraåunava izraz2, tj. ponovo se izvrãava korak (ii); (iv) ako je izraz2 netaåan, prekida se izvrãavanje naredbe for i prelazi na sledeñu naredbu. Moæe se reñi da je naredba for ekvivalentna nizu naredbi: izraz1; while (izraz2) { naredba; izraz3; }
1.14.4 Naredbe promene toka Veñ smo se sreli sa jednom od naredbi promene toka. To je naredba break. Efekat naredbe break zavisi od mesta na kome se ona pojavljuje: • ako se koristi u telu naredbe switch, dovodi do nasilnog prekida izvrãavanja naredbe switch i prelaska na prvu naredbu iza naredbe switch. • ako se koristi u naredbi petlje, dovodi do prekida izvrãavanja petlje i prelaska na prvu naredbu iza naredbe petlje. • u ostalim sluåajevima naredba break je bez efekta.
24
ALGORITMI U PROGRAMSKOM JEZIKU C
Druga naredba promene toka je continue. Koristi se u naredbama petlji tako ãto proizvodi preskakanje preostalih naredbi koje åine telo petlje. Po izvrãavanju naredbe continue odmah se prelazi na ispitivanje uslova za zavrãetak izvrãavanja naredbe petlje (odnosno raåunanje izraza kojim se kontroliãe izvrãavanje naredbe petlje). Treña naredba iz ove grupe je naredba goto. Za razliku od preostalih, upravo navedenih naredbi, ova naredba ima neãto sloæeniju sintaksu: goto obeleæje;
gde je obeleæje neko od obeleæja koje postoji u funkciji u kojoj se nalazi data naredba goto. Efekat ove naredbe je nastavljanje izvrãavanja funkcije od naredbe ispred koje stoji navedeno obeleæje. U grupu ovih naredbi moæe se uvrstiti i naredba return. Njena sintaksa je sledeña: return [izraz];
Efekat ove naredbe ogleda se u prekidu izvrãavanja funkcije koje je trenutno u toku i povratku u funkciju iz koje je pozvana. Nastavlja se izvrãavanjem naredbe neposredno iza poziva funkcije. Kao rezultat svog izvrãavanja, funkcija moæe proizvoditi neku vrednost. Ta vrednost je vrednost izraza koji se navodi iza reåi return. Ukoliko se niãta ne navede, a funkcija treba da vrati neku vrednost, vrañena vrednost je nedefinisana.
1.15 Funkcije Funkcije su sloæene leksiåke konstrukcije. Sastoje se od odreœenog broja deklaracija promenljivih i niza naredbi koji åine logiåku celinu. Izraåunavanje odreœeno nizom naredbi moæe proizvesti jedan rezultat. Tip rezultata je obiåno neki od standardnih tipova ili pokazivaå. Tip rezultata je istovremeno i tip funkcije. U sluåaju kada funkcija nema jedan karakteristiåan rezultat, funkcija je tipa void. Naime, moæe se desiti da je broj rezultata (izraåunatih vrednosti) veñi od jedan. U tom sluåaju rezultati se prenose kroz argument listu. Argument lista je mehanizam ne samo za prenoãenje rezultata, veñ i za prenoãenje argumenata (ulazni podaci) funkcije. Broj argumenata nije ograniåen (0 ili viãe). Takoœe ne postoje ograniåenja u pogledu tipa argumenata (osim da ne mogu biti strukturnog tipa). Na primer: int f1 (int a, int b, int *c) { ... }
predstavlja funkciju f1 koja ima tri argumenta: • dva su tipa int (a i b) i • treñi je tipa pokazivaå na podatak tipa int (c).
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
25
Funkcija kao rezultat daje jednu vrednost tipa int. Funkcija f1 moæe da se pozove iz neke funkcije tako ãto se pojavi u nekom izrazu: x = ... f1(u, v, &w)...
Kao ãto se vidi, u pozivu se navode argumenti funkcije (to su stvarni argumenti funkcije). Primetimo da je na treñi argument primenjen operator adresiranja (jer je odgovarajuñi argument funkcije pokazivaå na ceo broj). U toku izvrãavanja funkcije prvi i drugi argument (a i b) mogu se menjati (biti levi operand nekog operatora dodeljivanja). Meœutim, te promene se neñe odraziti na vrednost odgovarajuñih stvarnih argumenata (u ovom sluåaju to su promenljive u i v). Ovo zato ãto se u toku izvrãavanja funkcije za te argumente rezerviãe prostor na sistemskom steku (kopije odgovarajuñih stvarnih argumenata) i sve radnje nad tim argumentima se registruju na tom mestu. Za treñi argument je karakteristiåno da se moæe pristupiti odgovarajuñem stvarnom argumentu. To se ostvaruje koriãñenjem unarnog operatora * (izrvãavanje izraza *c = ..., dovodi do promene vrednosti stvarnog argumenta åija je adresa stavljena u argument listu). Tako prva dva argumenta odgovaraju vrednosnim, a treñi promenljivim argumentima funkcije. Prethodno navedeni zapis: int f1(int a, int b, int *c) { ... }
predstavlja definiciju funkcije f1. Osim definicije funkcije, u programskom jeziku C postoji deklaracija funkcije. Deklaracija funkcije se razlikuje od definicije po tome ãto se ne navodi telo funkcije (deklaracije promenljivih i niz naredbi od kojih se sastoji funkcija). Za konkretnu funkciju deklaracija ima sledeñi zapis: int f1 (int, int, int *);
Kao ãto se vidi, deklaracija funkcije se sastoji od tipa funkcije za kojim slede ime funkcije i, u zagradama, tipovi pojedinih argumenata. Imena argumenata nisu obavezna. Za svaku deklarisanu funkciju na nekom mestu mora biti navedena definicija. Namena deklaracije je da prevodiocu naglasi da postoji funkcija sa odgovarajuñim imenom, tipom i argumentima. Te informacije prevodilac koristi, ako se u toku prevoœenja pojavi poziv neke funkcije pre njene definicije (radi kontrole da li se tipovi argumenata i rezultata slaæu).
1.16 Program na jeziku C Program na programskom jeziku C åini odreœen broj funkcija. Pri tome obavezno mora postojati funkcija sa imenom main. Funkcija sa imenom main je glavna. Izvrãavanje programa napisanog na jeziku C predstavlja pozivanje (odnosno, izvrãavanje) funkcije main.
26
ALGORITMI U PROGRAMSKOM JEZIKU C
Primer jednog programa na jeziku C: # include # define INC 2.54 main () { int i, n; printf ("n = "): scanf ("%d", &n); for (i = 1; i <= n; i++) printf ("%5d%5x%15.2f\n", i, i, i*INC); }
Prve dve linije predstavljaju takozvane preprocesorske direktive. Sama reå kaæe da su to instrukcije koje ñe biti obraœene pre samog prevoœenja programa. Sve preprocesorske direktive poåinju znakom #. Najåeãñe se koriste direktive include i define. Direktiva include ima zapis: # include ili # include "ime".
Ovom direktivom u program se ukljuåuje sadræaj datoteke åije se ime navodi izmeœu zagrada ili znakova navoda. U toj datoteci najåeãñe su navedene deklaracije funkcija. U ovom sluåaju, u datoteci stdio.h se nalaze funkcije za ulazno-izlazne operacije (uåitavanje, izdavanje podataka, otvaranje i zatvaranje datoteka, åitanje i upis, itd.). Direktiva define ima zapis: # define ime tekst
Ovom direktivom se definiãe simboliåka konstanta ime kojoj odgovara navedeni tekst. Efekat je da ñe svako pojavljivanje te konstante biti zamenjeno tim tekstom. Najåeãñe se koristi za definisanje simboliåkih konstanti i makroa. U naãem primeru funkcija main radi sledeñe: • pozivom funkcije printf na ekranu se ispisuje tekst izmeœu znakova navoda (n = ); • pozivom funkcije scanf uåitava se ceo broj n; • u petlji se za sve brojeve i od 1 do n ispisuje: • broj i u dekadnom brojnom sistemu; • broj i u heksadekadnom brojnom sistemu; • proizvod broja i i konstante INC (2.54).
1.17 Memorijske klase U prethodnom poglavlju smo videli kako se deklariãu promenljive. Meœutim, od mesta na kome je promenljiva deklarisana zavise neke njene karakteristike: gde se sve moæe koristiti, promene vrednosti u toku izvrãavanja itd. Na osnovu tih karakteristika, promenljive su podeljene u memorijske klase.
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
27
Prva klasa je klasa automatskih promenljivih. To su sve promenljive definisane u okviru neke od funkcija ili na poåetku komponovane naredbe. One se vide samo iz funkcije (ili komponovane naredbe) u okviru koje su definisane. Po zavrãetku izvrãavanja funkcije (ili komponovane naredbe) viãe ne postoje. Pri ponovnom izvrãavanju funkcije, vrednost je proizvoljna (neodreœena, zavisi od trenutka). Drugu klasu åine registarske promenljive. Definiãu se tako ãto se ispred imena tipa promenljive doda prefiks register. Za njih je karakteristiåno da se u fazi prevoœenja povezuju sa nekim registrom iz centralnog procesora. Vrednost promenljive se åuva sve vreme izvrãavanja u tom registru. Time se poveñava brzina pristupa takvoj promenljivoj. Promenljiva registarskog tipa se åuva u registru ako je to moguñe, tj. ako ima raspoloæivih registara. Ako nije moguñe, te promenljive se tretiraju kao automatske. Treñu klasu åine statiåne lokalne promenljive. Ove promenljive su definisane u okviru neke funkcije tako ãto je ispred naziva tipa stavljen prefiks static (npr. static int n, m). Ove promenljive su vidljive samo u funkciji u kojoj su definisane (samo se tu mogu koristiti), ali pri ponovnom izvrãavanju funkcije imaju vrednost koju su imale na kraju prethodnog izvrãavanja (åuva se vrednost izmeœu pojedinih izvrãavanja). Åetvrtu klasu åine statiåne globalne promenljive. Ove promenljive se vide iz svih funkcija koje åine jedan modul (nalaze se u jednoj datoteci). Kao i statiåne lokalne promenljive, definiãu se tako ãto se doda prefiks static, ali su definisane pre svih funkcija (izvan funkcija). Petu klasu åine globalne promenljive. Dobijaju se tako ãto se ispred naziva tipa doda prefiks extern. Definiãu se izvan funkcija i vidljive su iz svih funkcija koje åine program (tj. vidljive su iz svih modula). Meœutim, obavezno mora postojati modul u kome je ova promenljiva definisana izvan tela funkcija (i nije statiåna). U suprotnom smatra se da promenljiva nije definisana. Na taj naåin dobijamo klasu promenljivih koje su vidljive iz viãe modula i postoje sve vreme izvrãavanja programa. Åetvrta i peta klasa promenljivih obezbeœuju mehanizam razmene podataka izmeœu funkcija: istina, åetvrta klasa izmeœu funkcija u okviru jednog modula, a peta izmeœu funkcija iz razliåitih modula.
1.18 Sloæene deklaracije u programskom jeziku C Programski jezik C dozvoljava vrlo sloæene deklaracije. Tako mogu postojati deklaracije: char **argv;
argv je pokazivaå na pokazivaå na podatak tipa char; char (*x)[13]
x je pokazivaå na niz od 13 elemenata tipa char; int *x[13]
28
ALGORITMI U PROGRAMSKOM JEZIKU C
x je niz od 13 pokazivaåa na podatke tipa int; int *f()
f je funkcija koja vraña pokazivaå tipa int; int (*x)()
x je pokazivaå na funkciju koja vraña rezultat tipa int; int (*(*x())[])()
x je funkcija koja vraña pokazivaå na niz åiji su elementi pokazivaåi na funkcije koje vrañaju podatke tipa int; int (*(*x[3])())[5]
x je niz duæine 3 åiji su elementi pokazivaåi na funkcije koje vrañaju pokazivaåe na niz duæine 5 åiji su elementi tipa int; Sloæene deklaracije se åitaju pomoñu sintaksnih pravila za deklaracije: decl::=ime dcl; dcl::=[*]*dir-dcl dir-dcl::=ime | (dcl) | dir-dcl() | dir-dcl[[BR_EL]]
Znak | razdvaja pojedine varijante za graœenje konstrukcije (tj. dir-dcl je ime ili dcl u zagradama ili dir-dcl, za kojim sledi par malih zagrada ili dir-dcl, za kojim sledi par uglastih zagrada, pri åemu ne mora biti naveden broj ålanova niza). Prvo pravilo kaæe da se deklaracija nekog objekta sastoji od imena (ime tipa) za kojim sledi konstrukcija dcl. Po drugom pravilu dcl sa sastoji od odreœenog broja zvezdica (nula ili viãe) za kojim sledi konstrukcija dir-dcl (to je zapravo dcl bez zvezdica koje se nalaze na poåetku). Slika 1.1 prikazuje kako se åita deklaracija: (*pfa[])();
U sledeñem odeljku napisañemo program koji åita deklaraciju i proizvodi reåenicu na prirodnom jeziku, koja opisuje tip promenljive deklarisane tom deklaracijom.
POGLAVLJE 1
UKRATKO O PROGRAMSKOM JEZIKU C
29
Slika 1.1
Primer åitanja deklaracije; deklaracija je (*pfa[]) (); (
*
pfa
[]
)
()
ime
dir-dcl dir-dcl
dcl
dir-dcl
dir-dcl
dcl
30
ALGORITMI U PROGRAMSKOM JEZIKU C
PRILOG: JOÃ MALO O VREMENU
Zahtev za prekid broj 8 stiæe sa sistemskog åasovnika na svakih 55 milisekundi. Odgovarajuña sistemska prekidna funkcija aæurira sistemsko vreme. Direktna zamena te prekidne funkcije nekom funkcijom koju bismo sami kreirali, nije preporuåljiva. Meœutim, u samoj prekidnoj funkciji za prekid broj 8 postoji naredba INT 1CH koja obezbeœuje izvrãavanje funkcije pridruæene prekidu broj 1CH. Inicijalno, ta prekidna funkcija se sastoji samo iz jedne naredbe IRET (povratak iz prekidne funkcije). Dotiåna funkcija (1CH) se moæe zameniti nekom korisniåkom funkcijom. Pokaæimo kako se to izvodi. Uobiåajen niz radnji, koje treba obaviti ukoliko se vrãi promena prekidne funkcije pridruæene nekom prekidu, je sledeñi: • zapamtiti staru prekidnu funkciju (prekidni vektor), odnosno mesto (adresu) njenog poåetka, pridruæenu tom prekidu; • pridruæiti novu prekidnu funkciju i • na kraju izvrãavanja programa vratiti staru prekidnu funkciju. Za pamñenje stare prekidne funkcije koristimo DOS-ovu sistemsku funkciju (21H, S(AH)=35H). Ona u paru registara ES i BX postavlja segmentni i ofsetni deo adrese dosadaãnje prekidne funkcije pridruæene prekidu åiji je broj u registru AL. Dobijene vrednosti prenosimo u predviœene promenljive. Postavljanje nove prekidne funkcije postiæemo koriãñenejm DOS-ove funkcije (21H, S(AH)=25H), tako ãto prethodno: • u registrima DS i DX pripremimo segmentni i ofsetni deo adrese nove prekidne funkcije; • u registar AL upiãemo broj prekida kome pridruæujemo novu prekidnu funkciju. Prekidne funkcije obiåno piãemo u asembleru (simboliåki jezik). Razlikuju se od klasiånih funkcija pisanih u asembleru po tome ãto svi registri moraju imati sadræaje u trenutku zavrãetka izvrãavanja, kao i u trenutku poåetka. Zato sadræaje svih registara, koje koristimo i menjamo u toku izvrãavanja prekidne funkcije,
295
zapisujemo na stek. Pre povratka iz prekidne funkcije åitamo sa steka podatke koje smo zapisali na poåetku i pridruæujemo odgovarajuñim registrima. Prekidna funkcija se obavezno zavrãava naredbom IRET (povratak iz prekidne funkcije). Povezivanje funkcija pisanih u programskom jeziku C i funkcija pisanih u asembleru relativno se lako ostvaruje. Treba pomenuti neke elemente: • imenu funkcije pisane u programskom jeziku C u toku prevoœenja biñe dodat prefiks koji se sastoji od jedne crtice (_, engl. underscore). Zato se imenu funkcije pisane u asembleru i pozivanoj iz jezika C dodaje prefiks koji se sastoji od jedne crtice; • argumenti funkcija pisanih na C-u upisuju se na stek u trenutku poziva, tako ãto se prvo upisuje poslednji, zatim pretposlednji argument itd. U naãim primerima ne prenosimo argumente tako da se naåin prenoãenja argumenata neñe videti. Napiãimo prekidnu funkciju koja ñe meriti sistemsko vreme i pripremati odgovarajuñi ispis na monitor u gornjem levom uglu. Koristiñemo åetiri promenljive: prom, sec, min i sat za broj milisekundi, sekundi, minuta i sati. Kako se prekidna funkcija izvrãava na svakih 55 milisekundi, uveñavañemo prom za 55. U trenutku kada vrednost promenljive prom postane veña od 999, umanjujemo je za 1000 i za 1 uveñavamo vrednost promenljive sec. Kada vrednost promenljive sec postane jednaka broju 60, izjednaåavamo je sa 0 i istovremeno uveñavamo vrednost promenljive min za 1. Kada vrednost promenljive min postane jednaka broju 60, izjednaåavamo je sa 0 i uveñavamo vrednost promenljive sat za 1. Konaåno, kada promenljiva sat dobije vrednost 24, izjednaåavamo je sa nulom. U isto vreme u promenljivoj visp pripremamo odgovarajuñi ispis na monitor. To je niz koji se sastoji od: • podataka za ispis i • atributa koji odreœuju naåin ispisa. I podaci i atributi su bajtovi. Za podatke to je ASCII-kod znaka koji treba da se ispiãe. Atribut odreœuje naåin ispisa tog znaka: Ako bajt za atribut ima sadræaj: a7a6a5a4a3a2a1a0 u binarnom brojnom sistemu, onda: a2a1a0 odreœuju boju ispisa znaka (0 – crna, 1 – plava, 2 – zelena itd.), a3
odreœuje da li ñe ispisani znak biti tamnije (0) ili svetlije boje (1),
a6a5a4 odreœuje boju pozadine sliåno kao i za boju samog znaka, a7
odreœuje da li ñe ispisani znak svetlucati (trepereti) (1) ili ne (0).
Mi smo svim znacima stavili atribut 1, ãto znaåi da ñe biti tamnoplave boje na crnoj pozadini.
296
ALGORITMI U PROGRAMSKOM JEZIKU C
Ispis postiæemo direktnim upisom u video memoriju. Adresa poåetka za tekstualni reæim je 0xB000:0x8000. Mi ispisujemo na kraju prvog reda ukupno 11 znakova (cc:mm:ss.pp). Svaka pozicija na monitoru zauzima 2 bajta u video memoriji (1 bajt za znak koji ñe biti ispisan i 1 bajt za atribute). Znaåi, upisujemo u video-memoriju 22 bajta (znaka) od adrese 0xB000:0x808A. U delu koji je napisan na programskom jeziku C poziva se funkcija koja pamti staru prekidnu funkciju za prekid broj 1CH. Nakon toga poziva se funkcija koja postavlja novu prekidnu funkciju za prekid 1CH, ali pre toga oåita trenutno sistemsko vreme. Posle pritiska bilo koje dirke, biñe vrañena stara prekidna funkcija prekidu broj 1CH i okonåañe se izvrãavanje programa: _datasegment public 'data' prom dw 0 ; broj milisekundi sec dw 0; broj sekundi min dw 0; broj minuta sat dw 0; broj sati ; niz za ispis u formatu cc:mm:ss.ss visp db '0',1,'0',1,':',1,'0',1,'0',1,':',1 db '0',1,'0',1,'.',1,'0',1,'0',1 ; registri predviœeni za zapisivanje dosadaãnjeg vektora koji ; odgovara prekidu 1C _1c_sint dw 2 dup(?) _data ends _code segment word public 'code' assume cs:_code, ds:_data _h1c_zap_stari proc far public ; funkcija za pamñenje stare prekidne funkcije push es push bx push ax mov ah, 35h mov al, 1ch int 21h ; oåitana je adresa poåetka stare prekidne funkcije i nalazi se ; u paru registara es:bx push ds mov ax, seg _1c_sint mov ds, ax mov _1c_sint, bx mov _1c_sint+2, es pop ds pop ax pop bx pop es ret _h1c_zap_stari endp _h1c_pos_novi proc far public push ds push dx push cx push ax ; oåitavamo sistemsko vreme mov ah, 2ch int 21h mov ax, seg prom mov ds, ax
PRILOG
PRILOG: JOÃ MALO O VREMENU
297
; broj sati upisujemo u promenljivu sat mov al, ch cbw mov sat, ax ; broj minuta prebacujemo u min mov al, cl cbw mov min, ax
; broj sekundi prebacujemo u sec mov al, dh cbw mov sec, ax ; broj milisekundi upisujemo u promenljivu prom mov al, dl mov ah, 0ah mul ah mov prom, ax ; pripremamo prve dve cifre za ispis ; one odgovaraju broju sati mov ax, sat mov bl, 10 div bl add ah, '0' add al, '0' mov visp[2], ah mov visp[0], al ; pripremamo åetvrti i peti znak za ispis ; oni odgovaraju broju minuta mov ax, min mov bl, 10 div bl add ah, '0' add al, '0' mov visp[8], ah mov visp[6], al mov mov div add add mov mov
ax, sec bl, 10 bl ah, '0' al, '0' visp[0eh], ah visp[0ch], al
mov mov div xor div add add mov mov
ax, prom bl, 10 bl ah, ah bl ah, '0' al, '0' visp[14h], ah visp[12h], al
mov mov mov mov mov
dx, ax, ds, ah, al,
offset _h1c_nint seg _h1c_nint ax 25h 1ch
; u paru registara ds:dx smo pripremili adresu poåetka funkcije ; _1c_nint i to postavljamo za novu prekidnu funkciju koja ; odgovara prekidu 1C
298
ALGORITMI U PROGRAMSKOM JEZIKU C
int 21h pop ax pop dx pop cx pop ds ret _h1c_pos_novi endp
; funkcija za vrañanje starog prekidnog vektora prekida 1C _h1c_vr_stari proc far public push ds push dx push ax ; u paru registara ds:dx pripremamo stari vektor koji se ; trenutno nalazi u promenljivoj _1c_sint mov ax, seg _1c_sint mov ds, ax mov dx, _1c_sint mov ax, _1c_sint+2 mov ds, ax mov ah, 25h mov al, 1ch int 21h pop ax pop dx pop ds ret _h1c_vr_stari endp
_h1c_nint proc far public ; nova prekidna funkcija za prekid broj 1CH push es push ds push di push si push dx push cx push bx push ax ; priprema za ispis na monitor mov ax, 0b000h mov es, ax mov di, 0808ah mov cx, 16h mov ax, seg visp mov ds, ax mov si, offset visp ; aæuriranje vremena ; za poåetak se broj milisekundi uveñava za 55 add prom, 55 cmp prom, 1000 jl promm1000 ; broj milisekundi je veñi od 999 sub prom, 1000 inc sec cmp sec, 60 jl secm60 ; broj sekundi je veñi od 59 sub sec, 60 inc min cmp min, 60 jl minm60
PRILOG
PRILOG: JOÃ MALO O VREMENU
299
; broj minuta je veñi od 59 sub min, 60 inc sat cmp sat, 24 jl satm24 ; broj sati je veñi od 23 mov sat, 0 satm24: mov ax, sat mov bl, 10 div bl add ah, '0' add al, '0' mov visp[2], ah mov visp[0], al minm60: mov ax, min mov bl, 10 div bl add ah, '0' add al, '0' mov visp[8], ah mov visp[6], al secm60: mov ax, sec mov bl, 10 div bl add ah, '0' add al, '0' mov visp[0eh], ah mov visp[0ch], al promm1000: mov ax, prom mov bl, 10 div bl xor ah, ah div bl add ah, '0' add al, '0' mov visp[14h], ah mov visp[12h], al rep movsb pop ax pop bx pop cx pop dx pop si pop di pop ds pop es iret _h1c_nint endp _code ends end
# include # include # include main () { h1c_zap_stari(); h1c_pos_novi(); getch(); h1c_vr_stari(); }
300
ALGORITMI U PROGRAMSKOM JEZIKU C