Professional Documents
Culture Documents
Demistif PDF
Demistif PDF
Prilikom pisanja ove knjige mnogi su nas, s podsmijehom kao da gledaju posljednji
primjerak snjenog leoparda, pitali: No zato se bavite C++ jezikom? To je
kompliciran jezik, spor, nedovoljno efikasan za primjenu u komercijalnim programima.
Na kraju, ja to sve mogu napraviti u obinom C-u. Prije nego to ponemo objanjavati
pojedine znaajke jezika, nalazimo vanim pokuati dati koliko-toliko suvisle odgovore
na ta i slina pitanja.
Osnovno pitanje je to C++ ini boljim i pogodnijim openamjenskim jezikom za
pisanje programa, od operacijskih sustava do baza podataka. Da bismo to razumjeli,
pogledajmo kojim putem je tekao povijesni razvoj jezika. Na taj nain e moda biti
jasnija motivacija Bjarne Stroustrupa, oca i majke jezika C++. Dakle, krenimo od
stoljea sedmog.
pojave C++ programskog jezika: potrebno je razviti programerski alat koji e osloboditi
programera rutinskih poslova te mu dopustiti da se usredotoi na problem koji rjeava.
Zbog toga se pojavljuje niz viih programska jezika, koji preuzimaju na sebe neke
dosadne programerske poslove. Tako je FORTRAN bio posebno pogodan za
matematike proraune, zatim BASIC koji se vrlo brzo uio, te COBOL koji je bio u
pravilu namijenjen upravljanju bazama podataka.
Oko 1972. se pojavljuje jezik C, koji je direktna pretea dananjeg jezika C++. To
je bio prvi jezik ope namjene te je postigao nevien uspjeh. Vie je razloga tome: jezik
je bio jednostavan za uenje, omoguavao je modularno pisanje programa, sadravao je
samo naredbe koje se mogu jednostavno prevesti u strojni jezik, davao je brzi kd. Jezik
nije bio optereen mnogim sloenim funkcijama, kao na primjer skupljanje smea (engl.
garbage collection): ako je takav podsustav nekome trebao, korisnik ga je sam napisao.
Jezik je omoguavao vrlo dobru kontrolu strojnih resursa te je na taj nain omoguio
programerima da optimiziraju svoj kd. Do unatrag nekoliko godina, 99% komercijalnih
programa bili su pisani u C-u, ponegdje dopunjeni odsjecima u strojnom jeziku kako bi
se kritini dijelovi sustava uinili dovoljno brzima.
No kako je razvoj programske podrke napredovao, stvari su se i na podruju
programskih jezika poele mijenjati. Sloeni projekti od nekoliko stotina tisua, pa i vie
redaka vie nisu rijetkost, pa je zbog toga bilo potrebno uvesti dodatne mehanizme
kojima bi se takvi programi uinili jednostavnijima za izradu i odravanje, te kojima bi
se omoguilo da se jednom napisani kd iskoristi u vie razliitih projekata.
Bjarne Stroustrup (roen u Danskoj) je 1979. godine zapoeo rad na jeziku C s
razredima (engl. C with Classes). Prije toga, on je radio na svom doktoratu u
Computing Laboratory of Cambridge te istraivao distribuirane sustave: granu
raunalne znanosti u kojoj se prouavaju modeli obrade podataka na vie jedinica
istodobno. Pri tome koristio se jezikom Simula, koji posjedovao neka vana svojstva
koja su ga inila prikladnim za taj posao. Na primjer, Simula je posjedovala pojam
razreda: strukture podataka koje objedinjavaju podatke i operacije nad podacima.
Koritenje razreda omoguilo je da se koncepti problema koji se rjeava izraze direktno
pomou jezinih konstrukcija. Dobiveni kd je bio vrlo itljiv i razumljiv, a g.
Stroustrup je bio posebno fasciniran nainom na koji je sam programski jezik upuivao
programera u razmiljanju o problemu. Takoer, jezik je posjedovao sustav tipizacije,
koji je esto pomagao korisniku u pronalaenju pogreaka ve prilikom prevoenja.
Naoko idealan u teoriji, jezik Simula je posrnuo u praksi: prevoenje je bilo
iznimno dugotrajno, a kd se izvodio izuzetno sporo. Dobiveni program je bio
neupotrebljiv i, da bi ipak poteno zaradio svoj doktorat, gospodin Stroustrup se morao
potruditi i ponovo napisati cjelokupni program u jeziku BCPL jeziku niske razine koji
je omoguio vrlo dobre performanse prevedenog programa. No iskustvo pisanja
sloenog programa u takvom jeziku je bilo uasno i g. Stroustrup je, po zavretku svog
posla na Cambridgeu, vrsto sebi obeao da vie nikada nee takav sloen problem
pokuati rijeiti neadekvatnim alatima poput BCPL-a ili Simule.
Kada se 1979. zaposlio u Bell Labs (kasnije AT&T) u Murray Hillu, zapoeo je rad
na onome to e kasnije postati C++. Pri tome je iskoristio svoje iskustvo steeno
Takoer, kada projektanti u Renaultu ele izraditi novi model automobila, imaju
dva izbora: ili mogu poeti od nule i ponovo proraunavati svaki najmanji dio motora,
asije i ostalih dijelova, ili mogu jednostavno novi model bazirati na nekom starom
modelu. Kako je kompaniji vrlo vjerojatno u cilju to bre razviti novi model kako bi
pretekla konkurenciju, gotovo sigurno e jednostavno uzeti uspjean model automobila i
samo izmijeniti neka njegova svojstva: promijenit e mu liniju, pojaati motor, dodati
ABS konice. Slino je i s programskim komponentama: prilikom rjeavanja nekog
problema moemo uzdahnuti i poeti kopati, ili moemo uzeti neku ve gotovu
komponentu koja je blizu rjeenja i samo dodati nove mogunosti. To se zove ponovna
iskoristivost (engl. reusability) i vrlo je vano svojstvo. Za novu programsku
komponentu kae se da je naslijedila (engl. inherit) svojstva komponente iz koje je
izgraena.
Korisnik koji kupuje auto sigurno nee biti presretan ako se njegov novi model
razlikuje od starog po nainu koritenja (primjerice da se umjesto pritiskom na papuicu
gasa auto ubrzava povlaenjem ruice na krovu vozila ili sputanjem suvozaevog
sjedala): on jednostavno eli pritisnuti gas, a stvar je nove verzije automobila primjerice
krae vrijeme ubrzanja od 0 do 100 km/h. Slino je i s programskim komponentama:
korisnik se ne treba optereivati time koju verziju komponente koristi on e
jednostavno traiti od komponente uslugu, a na njoj je da to obavi na adekvatan nain.
To se zove polimorfizam (engl. polimorphism).
Gore navedena svojstva zajedno sainjavaju objektno orijentirani model
programiranja. Evo kako bi se postupak rotiranja trodimenzionalnih likova proveo
koristei objekte:
1. Listaj sve objekte redom.
2. Zatrai od svakog objekta da se zarotira za neki kut.
Sada glavni program vie ne mora voditi rauna o tome koji se objekt rotira on
jednostavno samo zatrai od objekta da se zarotira. Sam objekt zna to uiniti ovisno o
tome koji lik on predstavlja: kocka e se zarotirati na jedan nain, a kubini spline na
drugi. Takoer, ako se bilo kada kasnije program proiri novim tijelima, nije potrebno
mijenjani program koji rotira sve objekte samo je za novi objekt potrebno definirati
operaciju rotacije.
Dakle, ono to C++ jezik ini vrlo pogodnim jezikom ope namjene za izradu
sloenih programa jest mogunost jednostavnog uvoenja novih tipova te naknadnog
dodavanja novih operacija.
proceduralnog kda. Iako se nekome moe uiniti kao prednost, ovo je zamka u koju
vrlo lako mogu upasti poetnici, posebice ako prelaze sa nekog proceduralnog
programskog jezika, kao to je jezik C. Naime, ako usvoji proceduralni nain
razmiljanja, programer e se teko prealtati na objektni pristup i iskoristiti sve
njegove pogodnosti. Ovo je danak to ga jezik C++ plaa zbog insistiranja na
kompatibilnosti s jezikom C. Stvaratelji jezika C++ (ukljuujui i gospodina
Stroustrupa) nisu eljeli izmisliti potpuno novi jezik koji bi iziskivao da se sve aplikacije
prepiu u tom novom jeziku, ve su nastojali da postojei izvorni kdovi mnotva
aplikacija pisanih u jeziku C (ne zaboravimo da je jezik C osamdesetih godina bio
najrasprostranjeniji programski jezik) budu potpuno zdruivi s novim jezikom i da se ti
kdovi bez potekoa mogu prevesti na prevoditeljima za jezik C++. Ovakav pristup
podrazumijevao je mnotvo kompromisa, ali je omoguio tisuama programera i
programskih kua postepeni i bezbolan prijelaz s jezika C na jezik C++. To je znaajno
doprinijelo popularnosti i opem prihvaanju jezika C++.
S druge strane, Java je potpuno novi programski jezik, neoptereen takvim
nasljeem zbog toga su neke stvari rijeene daleko elegantnije nego u jeziku C++.
Uostalom, Javu je stvorila grupa C++ programera koji su bili frustrirani nedostacima
jezika C++.
0.4.2. Java kd se izvodi na virtualnom stroju
Java izvorni kd se ne prevodi u strojni kd matinog procesora (matini kd, engl.
native code) nego u poseban, tzv. Java binarni kd (engl. Java bytecode) kojeg Java
virtualni stroj (engl. Java Virtual Machine, JVM) interpretira i izvodi. Prednost ovakvog
pristupa jest da e prevedeni Java kd biti izvodljiv na bilo kojem raunalu s bilo kojim
procesorom i operacijskim sustavom. Naravno, pod uvjetom da na tom stroju postoji i
vrti se (odgovarajui) virtualni stroj. No, takav kd nee biti optimiziran za dotini
procesor. Na primjer, velika veina dananjih procesora ima ugraene instrukcije za
operacije s realnim brojevima koje omoguavaju da se dijeljenje dva realna broja izvede
u svega nekoliko taktova procesora. Budui da se Java binarni kd mora izvoditi i na
raunalima s procesorima koja nemaju ugraene instrukcije za realne brojeve, izvoenje
takvih operacija bit e openito sporije.
Zbog toga, kao i zbog injenice da se Java binarni kd interpretira, a ne izvodi
izravno, Java programi su i do desetak puta sporiji od ekvivalentnih programa
prevedenih iz nekog drugog jezika. Dodue, Java virtualni strojevi pruaju mogunost
Prevoenja upravo na vrijeme (engl. Just-In-Time compilation), kojim se Java binarni
kd prevodi u matini izvedbeni kd i kao takav izvodi; brzina izvoenja takvog kda je
usporediva s brzinom matinog izvedbenog kda. Meutim, taj kd treba prvo prevesti,
to znai da se dio vremena namijenjenog izvoenju programa troi na prevoenje iz
Java binarnog u matini izvedbeni kd. Za programe u kojima se vei dio kda izvodi
samo jednom to e predstavljati znaajni gubitak vremena, odnosno usporenje
programa.
10
11
0.6. Literatura
Tijekom pisanja knjige koristili smo sljedeu literaturu (navodimo ju abecednim
slijedom autora):
[ANSI95]
Working Paper for Draft Proposed International Standard for
Information Systems Programming Language C++, Technical
Report X3J16/95-0087, American National Standards Institute (ANSI),
April 1995
[Barton94]
John J. Barton, Lee R. Nackman: Scientific and Engineering C++ An
Introduction with Advanced Techniques and Examples, AddisonWesley, Reading, MA, 1994, ISBN 0-201-53393-6
[Borland94]
Borland C++ 4.5 Programmers Guide, Borland International, Scotts
Valley, CA
[Carroll95]
Martin D. Carroll, Margaret A. Ellis: Designing and Coding Reusable
C++, Addison-Wesley, Reading, MA, 1995, ISBN 0-201-51284-X
12
[Eckel99]
0.6. Literatura
13
Iako se radi o nacrtu Standarda, on u potpunosti definira sve karakteristike programskog jezika
kako je definirano u konanoj verziji Standarda. Sve promjene koje su se dogaale od objave
Nacrta u travnju 1995. odnosile su se iskljuivo na tekst standarda sintaksa jezika i definicije
biblioteka ostale su nepromijenjene.
14
- Jeff Prosise: Programming Windows with MFC (2nd Edition), Microsoft Press, 1999,
ISBN 1-57231-695-0
0.7. Zahvale
Zahvaljujemo se svima koji su nam izravno ili posredno pomogli pri izradi ove knjige.
Posebno se zahvaljujemo Branimiru Pejinoviu (Portland State University) koji nam je
omoguio da doemo do Nacrta ANSI C++ standarda, Vladi Glaviniu (Fakultet
elektrotehnike i raunarstva) koji nam je omoguio da doemo do knjiga [Stroustrup94]
i [Carroll95] te nam je tijekom pripreme za tisak dao na raspolaganje laserski pisa, te
Zdenku imiu (Fakultet elektrotehnike i raunarstva) koji nam je, tijekom svog
boravka u SAD, pomogao pri nabavci dijela literature.
Posebnu zahvalu upuujemo Ivi Mesari koja je proitala cijeli rukopis te svojim
korisnim i konstruktivnim primjedbama znatno doprinijela kvaliteti iznesene materije.
Takoer se zahvaljujemo Zoranu Kalafatiu (Fakultet elektrotehnike i raunarstva) i
Damiru Hodaku koji su itali dijelove rukopisa i dali na njih korisne opaske.
Boris se posebno zahvaljuje gospoama ribar na tonama kolaa pojedenih za
vrijeme dugih, zimskih noi itanja i ispravljanja rukopisa, a koje su ga kotale kure
mravljenja.
0.7. Zahvale
15
16
17
17
18
Poetak
Upis i ispravke
izvornog kda
Prevoenje
Da
Pogreke tijekom
prevoenja?
Ne
Povezivanje
Da
Pogreka tijekom
povezivanja?
Ne
Izvoenje programa
19
Prva faza programa je pisanje izvornog kda. U principu se izvorni kd moe pisati
u bilo kojem programu za ureivanje teksta (engl. text editor), meutim velika veina
dananjih prevoditelja i povezivaa se isporuuje kao cjelina zajedno s ugraenim
programom za upis i ispravljanje izvornog kda. Te programske cjeline poznatije su pod
nazivom integrirane razvojne okoline (engl. integrated development environment, IDE).
Nakon to je (prema obino nekritikom miljenju programera) pisanje izvornog kda
zavreno, on se pohrani u datoteku izvornog kda na disku. Toj datoteci se obino daje
nekakvo smisleno ime, pri emu se ono za kdove pisane u programskom jeziku C++
obino proiruje nastavkom .cpp, .cp ili samo .c, na primjer pero.cpp. Nastavak je
potreban samo zato da bismo izvorni kd kasnije mogli lake pronai.
Slijedi prevoenje izvornog kda. U integriranim razvojnim okolinama program za
prevoenje se pokree pritiskom na neku tipku na zaslonu, pritiskom odgovarajue tipke
na tipkovnici ili iz nekog od izbornika (engl. menu) ako prevoditelj nije integriran,
poziv je neto sloeniji. Prevoditelj tijekom prevoenja provjerava sintaksu napisanog
izvornog kda i u sluaju uoenih ili nasluenih pogreaka ispisuje odgovarajue poruke
o pogrekama, odnosno upozorenja. Pogreke koje prijavi prevoditelj nazivaju se
pogrekama pri prevoenju (engl. compile-time errors). Nakon toga programer e
pokuati ispraviti sve navedene pogreke i ponovo prevesti izvorni kd sve dok
prevoenje kda ne bude uspjeno okonano, nee se moi pristupiti povezivanju kda.
Prevoenjem izvornog dobiva se datoteka objektnog kda (engl. object code), koja se
lako moe prepoznata po tome to obino ima nastavak .o ili .obj (u naem primjeru bi
to bio pero.obj).
Nakon to su ispravljene sve pogreke uoene prilikom prevoenja i kd ispravno
preveden, pristupa se povezivanju objektnih kdova u izvedbeni. U veini sluajeva
objektni kd dobiven prevoenjem programerovog izvornog kda treba povezati s
postojeim bibliotekama (engl. libraries). Biblioteke su datoteke u kojima se nalaze ve
prevedene gotove funkcije ili podaci. One se isporuuju zajedno s prevoditeljem, mogu
se zasebno kupiti ili ih programer moe tijekom rada sam razvijati. Bibliotekama se
izbjegava opetovano pisanje vrlo esto koritenih operacija. Tipian primjer za to je
biblioteka matematikih funkcija koja se redovito isporuuje uz prevoditelje, a u kojoj
su definirane sve funkcije poput trigonometrijskih, hiperbolnih, eksponencijalnih i sl.
Prilikom povezivanja provjerava se mogu li se svi pozivi kdova realizirati u
izvedbenom kdu. Uoi li poveziva neku nepravilnost tijekom povezivanja, ispisat e
poruku o pogreki i onemoguiti generiranje izvedbenog kda. Ove pogreke nazivaju se
pogrekama pri povezivanju (engl. link-time errors) sada programer mora prionuti
ispravljanju pogreaka koje su nastale pri povezivanju. Nakon to se isprave sve
pogreke, kd treba ponovno prevesti i povezati.
Uspjenim povezivanjem dobiva se izvedbeni kd. Meutim, takav izvedbeni kd
jo uvijek ne jami da e program raditi ono to ste zamislili. Primjerice, moe se
dogoditi da program radi pravilno za neke podatke, ali da se za druge podatke ponaa
Ovdje neemo opisivati konkretno kako se pokreu postupci prevoenja ili povezivanja, jer to
varira ovisno o prevoditelju, odnosno povezivau. Saeti opis za neke popularnije prevoditelje
dan je u Prilogu D na kraju knjige.
20
nepredvidivo. U tom se sluaju radi o pogrekama pri izvoenju (engl. run-time errors).
Da bi program bio potpuno korektan, programer treba istestirati program da bi uoio i
ispravio te pogreke, to znai ponavljanje cijelog postupka u lancu ispravljanje
izvornog kda-prevoenje-povezivanje-testiranje. Kod jednostavnijih programa broj
ponavljanja e biti manji i smanjivat e se proporcionalno s rastuim iskustvom
programera. Meutim, kako raste sloenost programa, tako se poveava broj moguih
pogreaka i cijeli postupak izrade programa neiskusnom programeru moe postati
mukotrpan.
Za ispravljanje pogreaka pri izvoenju, programeru na raspolaganju stoje programi
za otkrivanje pogreaka (engl. debugger). Radi se o programima koji omoguavaju
prekid izvoenja izvedbenog kda programa koji testiramo na unaprijed zadanim
naredbama, izvoenje programa naredbu po naredbu, ispis i promjene trenutnih
vrijednosti pojedinih podataka u programu. Najjednostavniji programi za otkrivanje
pogreaka ispisuju izvedbeni kd u obliku strojnih naredbi. Meutim, veina dananjih
naprednih programa za otkrivanje pogreaka su simboliki (engl. symbolic debugger)
iako se izvodi prevedeni, strojni kd, izvoenje programa se prati preko izvornog kda
pisanog u viem programskom jeziku. To omoguava vrlo lagano lociranje i ispravljanje
pogreaka u programu.
Osim pogreaka, prevoditelj i poveziva redovito dojavljuju i upozorenja. Ona ne
onemoguavaju nastavak prevoenja, odnosno povezivanja kda, ali predstavljaju
potencijalnu opasnost. Upozorenja se mogu podijeliti u dvije grupe. Prvu grupu ine
upozorenja koja javljaju da kd nije potpuno korektan. Prevoditelj ili poveziva e
zanemariti nau pogreku i prema svom nahoenju generirati kd. Drugu grupu ine
poruke koje upozoravaju da nisu sigurni je li ono to smo napisali upravo ono to smo
eljeli napisati, tj. radi se o dobronamjernim upozorenjima na zamke koje mogu
proizii iz naina na koji smo program napisali. Iako e, unato upozorenjima, program
biti preveden i povezan (moda ak i korektno), pedantan programer nee ta upozorenja
nikada zanemariti ona esto upuuju na uzrok pogreaka pri izvoenju gotovog
programa. Za precizno tumaenje poruka o pogrekama i upozorenja neophodna je
dokumentacija koja se isporuuje uz prevoditelj i poveziva.
Da zakljuimo: to nam dakle treba za pisanje programa u jeziku C++? Osim
raunala, programa za pisanje i ispravljanje teksta, prevoditelja i povezivaa trebaju vam
jo samo tri stvari: interes, puno slobodnih popodneva i dooobra knjiga. Interes
vjerojatno postoji, ako ste s itanjem stigli ak do ovdje. Slobodno vrijeme e vam
trebati da isprobate primjere i da se sami okuate u bespuima C++ zbiljnosti. Jer, kao
to stari junohrvatski izrijek kae: Nima dopisne kole iz plivanja. Stoga ne gubite
vrijeme i odmah otkaite sudar deku ili curi. A za dooobru knjigu(ta-ta-daaam
tu sada mi upadamo!).
21
U nekim programskim jezicima glavna funkcija se zove glavni program, a sve ostale funkcije
potprogrami.
22
neki prevoditelji e javiti upozorenje oblika Funkcija bi trebala vratiti vrijednost. Iako
se radi o pogreki, prevoditelj e umetnuti odgovarajui kd prema svom nahoenju i
prevesti program. Ne mali broj korisnika e zbog toga zanemariti takva upozorenja.
Meutim, valja primijetiti da esto taj umetnuti kd ne odgovara onome to je
programer zamislio. Zanemarivanjem upozorenja programer gubi pregled nad
korektnou kda, pa se lako moe dogoditi da rezultirajui izvedbeni kd daje na prvi
pogled neobjanjive rezultate.
Zadatak. Izbacite iz gornjeg kda desnu vitiastu zagradu } te pokuajte prevesti kd.
Pogledajte koje e pogreke javiti prevoditelj.
23
U ovom primjeru uoavamo tri nova retka. U prvom retku nalazi se naredba
#include <iostream> kojom se od prevoditelja zahtijeva da u na program ukljui
biblioteku iostream. U toj biblioteci nalazi se izlazni tok (engl. output stream) te
funkcije koje omoguavaju ispis podataka na zaslonu. Ta biblioteka nam je neophodna
da bismo u prvom retku glavnoga programa ispisali tekst poruke. Naglasimo da
#include nije naredba C++ jezika, nego se radi o pretprocesorskoj naredbi
(pretprocesorskim naredbama posveeno je poglavlje 17). Naletjevi na nju, prevoditelj
e prekinuti postupak prevoenja kda u tekuoj datoteci, skoiti u datoteku iostream,
prevesti ju, a potom se vratiti u poetnu datoteku na redak iza naredbe #include. Sve
pretprocesorske naredbe poinju znakom #.
iostream je primjer datoteke zaglavlja (engl. header file). U takvim datotekama se
nalaze deklaracije funkcija sadranih u odgovarajuim bibliotekama. Jedna od osnovnih
znaajki (zli jezici e rei mana) jezika C++ jest vrlo oskudan broj funkcija ugraenih u
sam jezik. Ta oskudnost olakava uenje samog jezika, te bitno pojednostavnjuje i
ubrzava postupak prevoenja. Za specifine zahtjeve na raspolaganju je veliki broj
odgovarajuih biblioteka funkcija i razreda.
Datoteke zaglavlja nalaze se obino u potkazalu include unutar kazala u kojem je
instaliran prevoditelj. Znatieljnicima eljnima znanja preporuujemo da zavire u te
datoteke budui da se u njima moe nai dosta korisnih informacija, ali svakako valja
paziti da se sadraj tih datoteka ne mijenja.
U drugom retku kda napisana je naredba using namespace std. using i
namespace su kljune rijei jezika C++ kojima se aktivira odreeno podruje imena
(imenik, engl. namespace), a std je naziv imenika u kojem su obuhvaane sve
standardne funkcije, ukljuujui i funkcije iz gore opisane iostream biblioteke. Imenici
su se prilino kasno pojavili u jeziku C++, a uvedeni su da se izbjegne kolizija istih
imena funkcija ili varijabli iz razliitih biblioteka. Na primjer, ako dvije razliite
funkcije iz razliitih biblioteka imaju isto ime, prevoditelj e javiti pogreku. Kada ne
bismo imali na raspolaganju imenike, jedino rjeenje u takvom sluaju bilo bi
promijeniti ime funkcije u jednoj od biblioteka, to ponekad nije mogue jer proizvoai
redovito te biblioteke isporuuju u ve prevedenom obliku. Detaljnije o imenicima bit e
govora u poglavlju 11, a do tada uzmite naredbu using namespace std zdravo za
gotovo.
24
25
Njima se deklarira znakovna (char) varijabla z, i potom u naredbi cin program oekuje
da utipkamo vrijednost te varijable; kada program prilikom izvoenja doe na naredbu
cin, izvoenje e se prekinuti sve dok ne pritisnemo neki znak (samo jedan znak!) i
potom tipku Enter. To e nam omoguiti da u miru Bojem, duboko u no prouavamo
ispis naih umotvorina. No, unaprijed vas upozoravamo da neke od ispisanih poruka
neete uspjeti vidjeti niti na ovaj nain. Naime, u poglavlju o razredima i njihovim
destruktorima neki od primjera poruke ispisuju pri izlasku iz programa, tj. nakon gore
spomenutih naredbi.
Zadatak. Kako bi izgledao izvorni kd ako bismo eljeli ispis endl znaka staviti u
posebnu naredbu? Dodajte u gornji primjer ispis poruke Imamo C++!!! u sljedeem
retku (popratno euforiko sklapanje ruku iznad glave nije neophodno). Nemojte
zaboraviti dodati i ispis endl na kraju tog retka!
Zadatak. Izbacite iz gornjeg kda pretprocesorsku naredbu #include te pokuajte
prevesti kd. Pogledajte koje e pogreke javiti prevoditelj. Ponovite to za sluaj da
izbacite naredbu using namespace.
c = a + b;
// rauna njihov zbroj
// ispisuje rezultat (vidi sljedeu stranicu):
26
int varijabli . Unos veeg broja nee imati nikakve dramatine posljedice na daljnji rad
vaeg raunala, ali e dati krivi rezultat na kraju rauna.
Svaki puta kada nam u programu treba ispis podataka na zaslonu ili unos
podataka preko tipkovnice pomou ulazno-izlaznih tokova cin ili cout, treba
ukljuiti zaglavlje odgovarajue iostream biblioteke pretprocesorskom
naredbom #include.
Radi saetosti kda, u veini primjera koji slijede, pretprocesorska naredba za
ukljuivanje iostream biblioteke nee biti napisana, ali se ona podrazumijeva. Koriste li
se ulazno-izlazni tokovi, njeno izostavljanje prouzroit e pogreku kod prevoenja.
Opseg dozvoljenih vrijednosti ovisi o prevoditelju i nije strogo definiran Standardom. Za neke
prevoditelje raspon je od 2.147.483.648 do 2.147.483.647. Detaljnije o rasponu dozvoljenih
vrijednosti bit e rijei u odjeljku 2.4.1.
1.4. Komentari
27
No, zavrimo s naim primjerom. Nakon unosa prvog broja, po istom principu se
unosi drugi broj b, a zatim slijedi naredba za raunanje zbroja
c = a + b;
Ona kae raunalu da treba zbrojiti vrijednosti varijabli a i b, te njihov zbroj pohraniti u
varijablu c, tj. u memoriju na mjesto gdje je prevoditelj rezervirao prostor za tu
varijablu.
U poetku e itatelj zasigurno imati problema s razluivanjem operatora << i >>. U
vjeri da e olakati razlikovanje operatora za ispis i uitavanje podataka, dajemo sljedei
naputak.
Operatore << i >> moemo zamisliti kao strelice koje pokazuju smjer
prijenosa podataka [Lippman91].
Primjerice,
>> a
preslikava vrijednost iz c.
Zadatak. Pokrenite Moj trei program nekoliko puta, unosei svaki puta sve vee
brojeve i pratite rezultate. Na primjer, pokuajte sa sljedeim parovima: 326766 i 1;
326766 i 2; 2147483646 i 1; 2147483646 i 2. Ponovite to i za identine negativne
brojeve.
1.4. Komentari
Na nekoliko mjesta u gornjem kdu moemo uoiti tekstove koji zapoinju dvostrukim
kosim crtama (//). Radi se o komentarima. Kada prevoditelj naleti na dvostruku kosu
crtu, on e zanemariti sav tekst koji slijedi do kraja tekueg retka i prevoenje e
nastaviti u sljedeem retku. Komentari dakle ne ulaze u izvedbeni kd programa i slue
programerima za opis znaenja pojedinih naredbi ili dijelova kda. Komentari se mogu
pisati u zasebnom retku ili recima, to se obino rabi za dulje opise, primjerice to
odreeni program ili funkcija radi:
//
// Ovo je moj trei C++ program, koji zbraja
// dva broja unesena preko tipkovnice, a zbroj
// ispisuje na zaslonu.
//
Autor: N. N. Hacker III
//
28
Za krae komentare, na primjer za opise varijabli ili nekih operacija, komentar se pie u
nastavku naredbe, kao to smo vidjeli u primjeru.
Uz gore navedeni oblik komentara, jezik C++ podrava i komentare unutar para
znakova /* */. Takvi komentari zapoinju slijedom /* (kosa crta i zvjezdica), a
zavravaju slijedom */ (zvjezdica i kosa crta). Kraj retka ne znai podrazumijevani
zavretak komentara, pa se ovakvi komentari mogu protezati na nekoliko redaka
izvornog kda, a da se pritom znak za komentiranje ne mora ponavljati u svakom retku:
/*
Ovakav nain komentara
preuzet je iz programskog
jezika C.
*/
29
// zbraja a i b
// uveava i za 1
// poziva funkciju sqrt
mogli pisati
c=a+b;
30
c =
a + b;
Prevoditelj e javiti pogreku da znakovni niz Pojavom ... ostvaren je nije zakljuen
znakom dvostrukog navodnika, jer prevoditelj ne uoava da se niz nastavlja u sljedeem
retku. Da bismo mu to dali do znanja, zavretak prvog dijela niza treba oznaiti lijevom
kosom crtom \ (engl. backslash):
cout << "Pojavom jezika C++ ostvaren je \
tisuljetni san svih programera" << endl;
pri emu nastavak niza ne smijemo uvui, jer bi prevoditelj dotine praznine prihvatio
kao dio niza. Stoga je u takvim sluajevima preglednije niz rastaviti u dva odvojena
niza:
cout << "Pojavom jezika C++ ostvaren je "
"tisuljetni san svih programera" << endl;
Pritom se ne smije zaboraviti staviti prazninu na kraj prvog ili poetak drugog niza.
Gornji niz mogli smo rastaviti na bilo kojem mjestu:
cout << "Pojavom jezika C++ ostvaren je tis"
"uljetni san svih programera" << endl;
31
#include <iostream>
using namespace std;
int main() {
int a, b, c; cout << "Upii prvi broj:"; cin >> a;
cout << "Upii i drugi broj:"; cin >> b; c = a + b;
cout << "Njihov zbroj je: " << c << endl;
return 0;
}
Program e biti preveden bez pogreke i raditi e ispravno. No, oito je da je ovako
pisani izvorni kd daleko neitljiviji pisanje vie naredbi u istom retku treba
prakticirati samo u izuzetnim sluajevima.
32
33
Svaki program sadri u sebi podatke koje obrauje. Njih moemo podijeliti na
nepromjenjive konstante, odnosno promjenjive varijable (promjenjivice).
Najjednostavniji primjer konstanti su brojevi (5, 10, 3.14159). Varijable su podaci koji
openito mogu mijenjati svoj iznos. Stoga se oni u izvornom kdu predstavljaju ne
svojim iznosom ve simbolikom oznakom, imenom varijable.
Svaki podatak ima dodijeljenu oznaku tipa koja govori o tome kako se dotini
podatak pohranjuje u memoriju raunala, koji su njegovi dozvoljeni rasponi vrijednosti,
kakve su operacije mogue s tim podatkom i sl. Tako razlikujemo cjelobrojne, realne,
logike, pokazivake podatke. U poglavlju koje slijedi upoznat emo se ugraenim
tipovima podataka i pripadajuim operatorima.
2.1. Identifikatori
Mnogim dijelovima C++ programa (varijablama, funkcijama, razredima) potrebno je
dati odreeno ime, tzv. identifikator. Imena koja dodjeljujemo identifikatora su
proizvoljna, uz uvjet da se potuju sljedea tri osnovna pravila:
1. Identifikator moe biti sastavljen od kombinacije slova engleskog alfabeta (A - Z, a z), brojeva (0 - 9) i znaka za podcrtavanje '_' (engl. underscore).
2. Prvi znak mora biti slovo ili znak za podcrtavanje.
3. Identifikator ne smije biti jednak nekoj od kljunih rijei (vidi tablicu 2.1) ili nekoj
od alternativnih oznaka operatora (tablica 2.2). To ne znai da kljuna rije ne moe
biti dio identifikatora moj_int je dozvoljeni identifikator iako je int kljuna rije.
Takoer, treba izbjegavati da naziv identifikatora sadri dvostruke znakove
podcrtavanja (_ _) ili da zapoinje znakom podcrtavanja i velikim slovom, jer su
takve oznake rezervirane za C++ implementacije i standardne biblioteke (npr.
_ _LINE_ _, _ _FILE_ _).
Stoga si moemo pustiti mati na volju pa svoje varijable i funkcije nazivati svakojako.
Pritom je vjerojatno svakom jasno da je zbog razumljivosti kda poeljno imena
odabirati tako da odraavaju stvarno znaenje varijabli, na primjer:
Pribrojnik1
Pribrojnik2
rezultat
33
34
else
enum
explicit
export
extern
false
float
for
friend
goto
if
inline
int
long
mutable
namespace
new
operator
private
protected
public
register
reinterpret_cast
return
short
signed
sizeof
static
static_cast
struct
switch
template
this
throw
true
try
typedef
typeid
typename
union
unsigned
using
virtual
void
volatile
wchar_t
while
bitand
bitor
compl
not
not_eq
or
or_eq
xor
xor_eq
35
Snjeguljica_i_7_patuljaka
Snjeguljica_i_sedam_patuljaka
interpretirati kao iste, iako se razlikuju od petnaestog znaka ('7' odnosno 's') na dalje.
Naravno da dugaka imena treba izbjegavati radi vlastite komocije, posebice za
varijable i funkcije koje se esto ponavljaju.
Ponekad je pogodno koristiti sloena imena zbog boljeg razumijevanja kda. Iz
gornjih primjera itateljica ili itatelj mogli su razluiti dva najea pristupa
oznaavanju sloenih imena. U prvom pristupu rijei od kojih je ime sastavljeno
odvajaju se znakom za podcrtavanje, poput
Snjeguljica_te_sedam_patuljaka
Mi emo u primjerima preferirati potonji nain samo zato jer tako oznaene varijable i
funkcije zauzimaju ipak neto manje mjesta u izvornom kdu.
Iako ne postoji nikakvo ogranienje na poetno slovo naziva varijabli, veina
programera preuzela je iz programskog jezika FORTRAN naviku da imena cjelobrojnih
varijabli zapoinje slovima i, j, k, l, m ili n.
36
njen tip. U Naem treem C++ programu varijable su deklarirane u prvom retku
glavne funkcije:
int a, b, c;
Varijable postaju realna zbiljnost tek kada im se pokua pristupiti, na primjer kada im se
pridrui vrijednost:
a = 2;
b = a;
c = a + b;
Prevoditelj tek tada pridruuje memorijski prostor u koji se pohranjuju podaci. Postupak
deklaracije varijable a i pridruivanja vrijednosti simboliki moemo prikazati slikom
2.1. Lijevo od dvotoke je kuica koja simbolizira varijablu s njenim tipom i imenom.
int
int
aa
int
int
22
tada se u memoriju raunala na mjestu gdje je prije bio smjeten broj 2 pohranjuje broj
5, kako je prikazano slikom 2.2.
Deklaracija varijable i pridruivanje vrijednosti mogu se obaviti u istom retku kda.
Umjesto da piemo poseban redak s deklaracijama varijabli a, b i c i zatim im
pridruujemo vrijednosti, inicijalizaciju moemo provesti ovako:
37
int
int
aa
int
int
55
int a = 2;
int b = a;
int c = a + b;
// pogreka!!!
// pogreka!!!
// Bingooo!!! Trea pogreka!
Pokuamo li prevesti ovaj kd, prevoditelj e javiti pogreke. Objekti koji se smiju
nalaziti s lijeve strane znaka pridruivanja nazivaju se lvrijednosti (engl. lvalues, kratica
od left-hand side values). Pritom valja znati da se ne moe svakoj lvrijednosti
pridruivati nova vrijednost neke varijable se mogu deklarirati kao konstantne (vidi
38
// OK!
// pogreka: b + 1 nije lvrijednost
39
2.4.1. Brojevi
U jeziku C++ ugraena su u sutini dva osnovna tipa brojeva: cijeli brojevi (engl.
integers) i realni brojevi (tzv. brojevi s pominom decimalnom tokom, engl. floatingpoint). Najjednostavniji tip brojeva su cijeli brojevi njih smo ve upoznali u Naem
treem C++ programu. Cjelobrojna varijabla deklarira se rijeju int i njena e
vrijednost u memoriji raunala obino zauzeti dva bajta (engl. byte), tj. 16 bitova. Prvi
bit je rezerviran za predznak, tako da preostaje 15 bitova za pohranu vrijednosti. Stoga
se varijablom tipa int mogu obuhvatiti svi brojevi od najmanjeg broja (najveeg
negativnog broja)
215 = 32768
do najveeg broja
215 1 = 32767
Za veinu praktinih primjena taj opseg vrijednosti je dostatan. Meutim, ukae li se
potreba za veim cijelim brojevima varijablu moemo deklarirati kao long int, ili krae
samo long:
long int HrvataUDijasporiZaVrijemeIzbora = 3263456;
long ZrnacaPrasineNaMomRacunalu = 548234581;
// mogao bih uzeti krpu za prainu i svesti to na int!
Takva varijabla e zauzeti u memoriji vie prostora i operacije s njom e dulje trajati.
Cjelobrojne konstante se mogu u izvornom kdu prikazati u razliitima brojevnim
sustavima:
dekadskom,
oktalnom,
heksadekadskom.
Dekadski prikaz je razumljiv veini obinih nehakerskih smrtnika koji se ne sputaju
na razinu raunala. Meutim, kada treba raditi operacije nad pojedinim bitovima, oktalni
i heksadekadski prikazi su daleko primjereniji.
U dekadskom brojevnom sustavu, koji koristimo svakodnevno, postoji 10 razliitih
znamenki (0, 1, 2,..., 9) ijom kombinacijom se dobiva bilo koji eljeni vieznamenkasti
broj. Pritom svaka znamenka ima deset puta veu teinu od znamenke do nje desno. U
oktalnom brojevnom sustavu broj znamenki je ogranien na 8 (0, 1, 2,..., 7), tako da
svaka znamenka ima osam puta veu teinu od svog desnog susjeda. Na primjer, 11 u
oktalnom sustavu odgovara broju 9 u dekadskom sustavu (118 = 910). U
heksadekadskom sustavu postoji 16 znamenki: 0, 1, 2,..., 9, A, B, C, D, E, F. Budui da
za prikaz brojeva postoji samo 10 brojanih simbola, za prikaz heksadekadskih
Kod nas se esto za bajt koristi autohtona hrvatska rije oktet, budui da se byte sastoji od 8
bitova. U engleskom rije byte nema nikakvo izvorno znaenje, ve je samo umjetna
modifikacija rijei bit (engl. komadi, trun, mrvica).
Kao to smo ve spomenuli, ove granine vrijednosti nisu uvijek iste, ve ovise o
implementaciji prevoditelja.
40
znamenki iznad 9 koriste se prva slova engleskog alfabeta, tako da je A16 = 1010,
B16 = 1110, itd. (vidi tablicu 2.3). Oktalni i heksadekadski prikaz predstavljaju
Tablica 2.3. Usporedba brojeva u razliitim sustavima
Dekadski
Oktalno
Heksadekadski
1
2
M
7
8
9
10
11
12
13
14
15
16
17
M
32
1
2
M
7
10
11
12
13
14
15
16
17
20
21
M
40
1
2
M
7
8
9
A
B
C
D
E
F
10
11
M
20
kompromis izmeu dekadskog prikaza kojim se koriste ljudi i binarnog sustava kojim se
podaci pohranjuju u memoriju raunala. Naime, binarni prikaz pomou samo dvije
znamenke (0 i 1) iziskivao bi puno prostora u programskom kdu, te bi bio nepregledan.
Oktalni i heksadekadski prikazi iziskuju manje prostora i iskusnom korisniku omoguuju
brzu pretvorbu brojeva iz dekadskog u binarni oblik i obrnuto.
Oktalne konstante se piu tako da se ispred prve znamenke napie broj 0 iza kojeg
slijedi oktalni prikaz broja:
int SnjeguljicaIPatuljci = 010;
// odgovara dekadsko 8!
// dekadsko 12
41
Jo jednom uoimo da e bez obzira na brojevni sustav u kojem broj zadajemo, njegova
vrijednost u memoriju biti pohranjena prema predloku koji je odreen deklaracijom
pripadne varijable. To najbolje ilustrira sljedei primjer:
int i = 32;
cout << i << endl;
i = 040;
cout << i << endl;
i = 0x20;
cout << i << endl;
Bez obzira to smo varijabli i pridruivali broj 32 u tri razliita prikaza, u sva tri sluaja
na zaslonu e se ispisati isti broj, u dekadskom formatu.
Ako je potrebno raunati s decimalnim brojevima, najee se koriste varijable tipa
float:
float pi = 3.141593;
float brzinaSvjetlosti = 2.997925e8;
float nabojElektrona = -1.6E-19;
// pogreka
interpretirati samo do etvrte znamenke. Prazninu koja slijedi prevoditelj e shvatiti kao
zavretak broja, pa e po nailasku na znak e javiti pogreku.
Osim to je ogranien raspon vrijednosti koje se mogu prikazati float tipom (vidi
tablicu 2.4), treba znati da je i broj decimalnih znamenki u mantisi takoer ogranien na
7 decimalnih mjesta. ak i ako se napie broj s vie znamenki, prevoditelj e zanemariti
sve nie decimalne znamenke. Stoga e ispis varijabli
Broj tonih znamenki ovisi o prevoditelju i o raunalu. U veini sluajeva broj tonih
znamenki i dozvoljene pogreke pri osnovnim aritmetikim operacijama ravnaju se po IEEE
standardu 754 za binarni prikaz brojeva s pominim decimalnim zarezom.
42
float pi_tocniji
= 3.141592654;
float pi_manjeTocan = 3.1415927;
dati potpuno isti broj! Usput spomenimo da su broj i ostale znaajnije matematike
konstante definirani u biblioteci cmath, i to na veu tonost, tako da ukljuivanjem te
datoteke moete prikratiti muke traenja njihovih vrijednosti po raznim prirunicima i
srednjokolskim udbenicima.
Ako tonost na sedam decimalnih znamenki ne zadovoljava ili ako se koriste
brojevi vei od 1038 ili manji od 1038, tada se umjesto float mogu koristiti brojevi s
dvostrukom tonou tipa double koji pokrivaju opseg vrijednosti od 1.710308 do
1.710308, ili long double koji pokrivaju jo iri opseg od 3.4104932 do 1.1104932.
Brojevi vei od 1038 vjerojatno e vam zatrebati samo za neke astronomske proraune,
ili ako ete se baviti burzovnim meetarenjem. Realno gledano, brojevi manji od 1038
nee vam gotovo nikada trebati, osim ako elite izraunati vjerojatnost da dobijete dekpot na lutriji ili da Miloevi zavri u Haagu (ovo je napisano u prosincu 2000.).
Iako double ima veu duljinu i zauzima vie mjesta u memoriji, operacije s double
podacima ne moraju nuno biti sporije nego s podacima tipa float. Naime, velika
veina dananjih procesora ima ugraene instrukcije za aritmetike operacije s
brojevima tipa double pa se te operacije izvode gotovo jednako brzo kao i na kraim
tipovima podataka. Stoga se ne treba previe ustruavati u koritenju podataka tipa
double, naroito gdjegod nam je potrebna iole vea tonost.
U tablici 2.4 dane su tipine duljine ugraenih brojevnih tipova u bajtovima,
rasponi vrijednosti koji se njima mogu obuhvatiti, a za decimalne brojeve i broj tonih
decimalnih znamenki. Duljina memorijskog prostora i opseg vrijednosti koje odreeni
tip varijable zauzima nisu standardizirani i variraju ovisno o prevoditelju i o platformi za
koju se prevoditelj koristi. Stoga itatelju preporuujemo da za svaki sluaj konzultira
dokumentaciju uz prevoditelj koji koristi. Relevantni podaci za cjelobrojne tipove mogu
se nai u datoteci climits, a za decimalne tipove podataka u cfloat. Tako, primjerice
su konstante INT_MIN i INT_MAX (definirane pretprocesorskom naredbom #define u
datoteci climits) jednake najmanjoj (najveoj negativnoj) i najveoj vrijednosti brojeva
tipa int.
Ako elite sami provjeriti veliinu pojedinog tipa, to moete uiniti i pomou
sizeof operatora:
cout
cout
long
cout
Kod nekih prevoditelja navedeni podaci se nalaze u zaglavljima limits.h odnosno float.h
(koja su naslijeena iz jezika C), a ova su ukljuena u datoteke climits, odn. cfloat (glede
imena vidi slino zapaanje vezano uz iostream biblioteku na str. 24)
43
Tablica 2.4. Ugraeni brojevni tipovi, njihove tipine duljine i rasponi vrijednosti
tip konstante
bajtova
raspon vrijednosti
tonost
char
short int
int
2
2
(4)
128 do 127
32768 do 32767
long int
float
3,41038 do 3,41038 i
3,41038 do 3,41038
7 dec. znamenki
double
1,710308 do 1,710308 i
1,710308 do 1,710308
15 dec. znamenki
long double
10
1,1104932 do 3,4104932 i
3,4104932 do 1,1104932
18 dec. znamenki
32768 do 32767
2147483648 do 2147483647
2147483648 do 2147483647
Gornje naredbe e ispisati broj bajtova koliko ih zauzimaju podaci tipa int, float,
odnosno double. Najvee i najmanje vrijednosti moete vidjeti ispisom odgovarajuih
konstanti iz prije navedenih climits i cfloat datoteka:
cout
cout
cout
cout
cout
cout
<<
<<
<<
<<
<<
<<
Kada ete isprobavati gornji kd, nemojte zaboraviti ukljuiti zaglavlja climits i
cfloat!
Svi navedeni brojevni tipovi mogu biti deklarirani i bez predznaka, dodavanjem
rijei unsigned ispred njihove deklaracije:
unsigned int i = 40000;
unsigned long int li;
unsigned long double BrojZvijezdaUSvemiru;
U tom sluaju e prevoditelj bit za predznak upotrijebiti kao dodatnu binarnu znamenku,
pa se najvea mogua vrijednost udvostruuje u odnosu na varijable s predznakom, ali
se naravno ograniava samo na pozitivne vrijednosti. Pridruimo li unsigned varijabli
negativan broj, ona e poprimiti vrijednost nekog pozitivnog broja. Na primjer,
programski slijed
unsigned int i = -1;
cout << i << endl;
44
e za varijablu i ispisati broj 65535 (ili 4294967295, ovisno o broju bajtova koji se
koriste za pohranjivanje int brojeva). Uoimo da je taj broj za 1 manji od najveeg
dozvoljenog unsigned int broja 65536 (odnosno 4294967296). Zanimljivo da
prevoditelj za gornji kd nee javiti pogreku.
Prevoditelj ne prijavljuje pogreku ako se unsigned varijabli pridrui
negativna konstanta korisnik mora sam paziti da se to ne dogodi!
Stoga ni za ivu glavu nemojte u programu za evidenciju stanja na tekuem raunu
koristiti unsigned varijable. U protivnom se lako moe dogoditi da ostanete bez
ekovne iskaznice, a svoj bijes iskalite na raunalu, ni krivom ni dunom.
2.4.2. Aritmetiki operatori
Da bismo objekte u programu mogli mijenjati, na njih treba primijeniti odgovarajue
operacije. Za ugraene tipove podataka definirani su osnovni operatori, poput zbrajanja,
oduzimanja, mnoenja i dijeljenja (vidi tablicu 2.5). Ti operatori se mogu podijeliti na
unarne, koji djeluju samo na jedan objekt, te na binarne za koje su neophodna dva
objekta. Osim unarnog plusa i unarnog minusa koji mijenjaju predznak broja, u jeziku
C++ definirani su jo i unarni operatori za uveavanje (inkrementiranje) i umanjivanje
vrijednosti (dekrementiranje) broja. Operator ++ uveat e vrijednost varijable za 1, dok
e operator --) umanjiti vrijednost varijable za 1:
int i = 0;
++i;
cout << i << endl;
// uvea za 1
// ispisuje 1
unarni operatori
x++
++x
x---x
x + y
x - y
binarni operatori
x * y
x / y
x % y
unarni plus
unarni minus
uveaj nakon
uveaj prije
umanji nakon
umanji prije
zbrajanje
oduzimanje
mnoenje
dijeljenje
modulo
45
--i;
cout << i << endl;
// umanji za 1
// ispisuje 0
Pritom valja uoiti razliku izmeu operatora kada je on napisan ispred varijable i
operatora kada je on napisan iza nje. U prvom sluaju (prefiks operator), vrijednost
varijable e se prvo uveati ili umanjiti, a potom e biti dohvaena njena vrijednost. U
drugom sluaju (postfiks operator) je obrnuto: prvo se dohvati vrijednost varijable, a tek
onda slijedi promjena. To najbolje doarava sljedei kd:
int i =
cout <<
cout <<
cout <<
cout <<
cout <<
1;
i << endl;
(++i) << endl;
i << endl;
(i++) << endl;
i << endl;
//
//
//
//
//
= 2.;
= 3.;
(a + b)
(a - b)
(a * b)
(a / b)
<<
<<
<<
<<
endl;
endl;
endl;
endl;
//
//
//
//
ispisuje
ispisuje
ispisuje
ispisuje
5.
-1.
6.
0.666667
Operator modulo kao rezultat vraa ostatak dijeljenja dva cijela broja:
int i = 6;
int j = 4;
cout << (i % j) << endl;
// ispisuje 2
On se vrlo esto koristi za ispitivanje djeljivosti cijelih brojeva: ako su brojevi djeljivi,
ostatak nakon dijeljenja e biti nula.
Za razliku od veine matematiki orijentiranih jezika, jezik C++ nema ugraeni
operator za potenciranje, ve programer mora sam pisati funkciju ili koristiti funkciju
pow(). iz standardne matematike biblioteke deklarirane u datoteci zaglavlja cmath.
Valja uoiti dva sutinska problema vezana uz aritmetike operatore. Prvi problem
vezan je uz pojavu numerikog preljeva, kada uslijed neke operacije rezultat nadmai
opseg koji dotini tip objekta pokriva. Drugi problem sadran je u pitanju kakvog e
tipa biti rezultat binarne operacije s dva broja razliitih tipova?.
Razmotrimo pojavu brojanog preljeva. Donje naredbe e prvo ispisati najvei broj
tipa int. Izvoenjem naredbe u treem retku, na zaslonu raunala e se umjesto tog
broja uveanog za 1 ispisati najvei negativni int (32768 ili 2147483648)!
int i = INT_MAX;
cout << i << endl;
cout << (++i) << endl;
46
Uzrok tome je preljev podataka do kojeg dolo zbog toga to oekivani rezultat vie ne
stane u bitove predviene za int varijablu. Podaci koji su se prelili uli su u bit za
predznak (zato je rezultat negativan), a raspored preostalih bitova daje broj koji
odgovara upravo onom to nam je raunalo ispisalo. Slino e se dogoditi oduzmete li
od najveeg negativnog broja neki broj. Ovo se moe ilustrirati brojevnom krunicom na
kojoj zbrajanje odgovara kretanju po krunici u smjeru kazaljke na satu, a oduzimanje
odgovara kretanju u suprotnom smjeru (slika 2.3).
Ako postoji mogunost pojave numerikog preljeva, tada deklarirajte
varijablu s veim opsegom u gornjem primjeru umjesto int uporabite
long int.
Gornja situacija moe se poistovjetiti s kupovinom automobila na sajmu rabljenih
automobila. Naravno da auto star 10 godina nije preao samo 5000 kilometara koliko
pie na brojau kilometara (kilometer-cajgeru), ve je nakon 99999 kilometara broja
ponovno krenuo od 00000. Budui da broja ima mjesta za 5 znamenki najvia
znamenka se izgubila! Sutinska razlika je jedino u tome da je kod automobila do
preljeva dolo hardverskom intervencijom prethodnog vlasnika, dok u programu do
preljeva obino dolazi zbog nepanje programera pri izradi programa.
Ponekad se pojava preljeva moe izbjei promjenom redoslijeda operacija.
Pogledajmo to na primjeru raunanja aritmetike sredine dva broja:
int prvi = INT_MAX - 1;
int drugi = i - 2;
int AritSred = (prvi + drugi) / 2;
Zagrade oko zbroja su neophodne, jer dijeljenje ima vii prioritet od zbrajanja (o
hierarhiji operacija govorit emo u odjeljku 2.7) da zagrada nema, s 2 bi se dijelio
-1
oduzimanje
-2
-3
zbrajanje
int
32766
-32767
-32768
32767
47
samo drugi pribrojnik. Oba su pribrojnika manja od najveeg dozvoljenog broja tipa int
pa od toga mora biti manja i njihova aritmetika sredina. Meutim, meurezultat (zbroj
oba pribrojnika prije dijeljenja s 2) e premaiti tu granicu doi e do njegovog
preljeva zbog ega e i krajnji rezultat biti pogrean. No, prepiemo li zadnju naredbu na
drugi nain:
int AritSred = prvi / 2 + drugi / 2;
48
int i = 3;
float a = 0.5;
cout << (a * i) << endl;
uzrokovat e ispis broja 1.5 na zaslonu, jer je od tipova int i float sloeniji tip float.
Cjelobrojnu varijablu i prevoditelj prije mnoenja pretvara u float (prema pravilu u
toki 3), tako da se provodi mnoenje dva broja s pominom tokom, pa je i rezultat tipa
float. Da smo umnoak prije ispisa pridruili nekoj cjelobrojnoj varijabli
int c = a * i;
cout << c << endl;
dobili bismo ispis samo cjelobrojnog dijela rezultata, tj. broj 1. Decimalni dio rezultata
se gubi prilikom pridruivanja umnoka cjelobrojnoj varijabli c.
Problem pretvorbe brojevnih tipova najjae je izraen kod dijeljenja cijelih brojeva,
to poetnicima (ali i nepaljivim C++ guruima) zna prouzroiti dosta glavobolje.
Pogledajmo sljedei jednostavni primjer:
int Brojnik = 1;
int Nazivnik = 4;
float Kvocijent = Brojnik / Nazivnik;
cout << Kvocijent << endl;
Brojeve 3 i 2 prevoditelj e shvatiti kao cijele, jer ne sadre decimalnu toku. Zato e
primijeniti cjelobrojni operator /, pa e rezultat toga dijeljenja biti cijeli broj 1.
Da bi se izbjegle sve nedoumice ovakve vrste, dobra je programerska navada
sve konstante koje trebaju biti s pominom decimalnom tokom (float i
double) pisati s decimalnom tokom, ak i kada nemaju decimalnih mjesta.
Evo pravilnog naina da se provede eljeno dijeljenje:
float TocniKvocijent = 3. / 2.;
49
Dovoljno bi bilo decimalnu toku staviti samo uz jedan od operanada prema pravilima
aritmetike pretvorbe i drugi bi operand bio sveden na float tip. Iako je kd ovako
dulji, izaziva manje nedoumica i stoga svakom poetniku preporuujemo da ne tedi na
decimalnim tokama.
Neupueni poetnik postavit e logino pitanje zato uope postoji razlika izmeu
cjelobrojnog dijeljenja i dijeljenja decimalnih brojeva. Za programera bi bilo
najjednostavnije kada bi se oba operanda bez obzira na tip, prije primjene operatora
svela na float (ili jo bolje, na double), na takve modificirane operande primijenio
eljeni operator, a dobiveni rezultat se pretvorio u zajedniki tip. U tom sluaju
programer uope ne bi trebao paziti kojeg su tipa operandi rezultat bi uvijek bio
korektan (osim ako nemate procesor s pogrekom, to i nije nemogue). Nedostatak
ovakvog pristupa jest njegova neefikasnost. Zamislimo da treba zbrojiti dva broja tipa
int! U gornjem programer-ne-treba-paziti pristupu, izvedbeni kd dobiven nakon
prevoenja trebao bi prvo jedan cjelobrojni operand pretvoriti u float. Budui da se
razliiti tipovi podataka pohranjuju u memoriju raunala na razliite naine, ta pretvorba
nije trivijalna, ve iziskuje odreeni broj strojnih instrukcija. Istu pretvorbu treba
ponoviti za drugi operand. Ve sada uoavamo dvije operacije pretvorbe tipa koje kod
izravnog zbrajanja cijelih brojeva ne postoje! tovie, samo zbrajanje se provodi na dva
float-a, koji u memoriji zauzimaju vei prostor od int-a. Takvo zbrajanje iziskivat e
puno vie strojnih instrukcija i strojnog vremena, ne samo zbog vee duljine float
brojeva u memoriji, ve i zbog sloenijeg naina na koji su oni pohranjeni. Za mali broj
operacija korisnik zasigurno ne bi osjetio razliku u izvoenju programa s izravno
primijenjenim operatorima i operatorima la programer-ne-treba-paziti. Meutim, u
sloenim programima s nekoliko tisua ili milijuna operacija, ta razlika moe biti
zamjetna, a esto i kritina. U krajnjoj liniji, shvatimo da prevoditelj poput vjernog psa
nastoji to bre ispuniti gospodarevu zapovijed, ne pazei da li e pritom protrati kroz
upravo ureen susjedin cvjetnjak ili zagaziti u svjee betoniran plonik.
Zadatak. to e ispisati sljedee naredbe:
int a = 10;
float b = 10.;
cout << a / 3 << endl;
cout << b / 3 << endl;
= a
c *
3.;
c *
a;
c /
/ 3;
b << endl;
b << endl;
3 << endl;
50
gdje se izmeu znakova < i > (manje i vee) navodi tip u koji elimo pretvoriti
izraz unutar zagrada koje slijede. U gornjem kdu se tako vrijednosti varijabli Brojnik
i Nazivnik pretvaraju u tip float prije nego to se one meusobno podijele, tako da je
krajni rezultat korektan. Naravno, bilo bi dovoljno operator dodjele tipa primijeniti
samo na jedan operand prema pravilima aritmetike pretvorbe i drugi bi operand bio
sveden na float tip. Da ne bi bilo zabune, same varijable Brojnik i Nazivnik i nadalje
ostaju tipa int, tako da e njihovo naknadno dijeljenje
float OpetKriviKvocijent = Brojnik / Nazivnik;
Takoer, operator dodjele tipa esto se koristi kada se koristi naslijeeni kd kojeg je
nezgodno mijenjati; ubacivanjem dodjela tipa iskljuuju se upozorenja o neistovjetnosti
tipova podataka koja bi inae prevoditelj izbacivao.
51
Jezik C++ dozvoljavao je i funkcijski oblik dodjele tipa u kojem se tip navodi ispred
zagrade, a ime varijable u zagradi:
float TocniKvocijent2 = float(Brojnik) / float(Nazivnik);
Iako su ova dva oblika dodjele tipa u potpunosti podrana standardom, njihova upotreba
se ne preporuuje.
Operator dodjele tipa moe se koristiti i u obrnutom sluaju, kada elimo iz
decimalnog broja izluiti samo cjelobrojne znamenke:
float a = 3.1415926
cout << "Cijeli dio broja a: " << static_cast<int>(a) << endl;
O operatorima dodjele tipa bit e jo detaljno govora u 12. poglavlju koje se bavi
identifikacijom tipa tijekom izvoenja.
Zadatak. Odredite koje e od navedenih naredbi za ispis:
int a = 100000;
int b = 200000;
long c = a * b;
cout
cout
cout
cout
cout
<<
<<
<<
<<
<<
c << endl;
(a * b) << endl;
(static_cast<float>(a) * b) << endl;
static_cast<long>(a * b) << endl;
(a * static_cast<long>(b)) << endl;
52
// long int
// unsigned int
// unsigned int
sufiks
rezultirajui tip
cijeli
L, l
U, u
int
long int
unsigned int
decimalni
F, f
L, l
double
float
long double
53
parametri poput maksimalnog broja prozora ili maksimalne duljine znakovnog niza, koji
se namjetaju prije prevoenja kda i ulaze u izvedbeni kd kao konstante.
Zamislimo da za neki zadani polumjer elimo izraunati i ispisati opseg krunice,
povrinu kruga, oploje i volumen kugle. Pri raunanju sve etiri veliine treba nam
Ludolfov broj =3,14159...:
float Opseg = 2.
float Povrsina =
double Oplosje =
double Volumen =
* r * 3.14159265359;
r * r * 3.14159265359;
4. * r * r * 3.14159265359;
4. / 3. * r * r * r * 3.14159265359;
kojom ste promijenili vrijednost varijable pi. Prevoditelj vas nee upozoriti (Opet taj
prokleti kompjutor!) i dobit ete da zemaljska kugla ima dva puta vei volumen nego
to ga je imala proli puta kada ste koristili isti program, ali bez inkriminirane naredbe.
Koristite li program za odreivanje putanje svemirskog broda, ovakva pogreka sigurno
e rezultirati odailjanjem broda u bespua svemirske zbiljnosti.
Da bismo izbjegli ovakve peripetije, na raspolaganju nam je kvalifikator const
kojim se prevoditelju daje na znanje da varijabla mora biti nepromjenjiva. Na svaki
pokuaj promjene vrijednosti takve varijable, prevoditelj e javiti pogreku:
const double pi = 3.14159265359;
pi = 2 * pi;
// sada je to pogreka!
54
Ona daje uputu prevoditelju da, prije nego to zapone prevoenje izvornog kda, sve
pojave prvog niza (PI) zamijeni drugim nizom znakova (3.14159265359). Stoga e
prevoditelj naredbe
double Volumen = 4. / 3. * r * r * r * PI;
PI = 2 * PI;
// pogreka
// pogreka
55
2.4.7. Pobrojenja
Naredbom
cout << HvalaBoguDanasJe << endl;
56
<<
<<
<<
<<
<<
//
//
//
//
//
ispisuje
ispisuje
ispisuje
ispisuje
ispisuje
0
3
4
5
7
Ako nam ne treba vie varijabli tog tipa, ime tipa iza rijei enum moe se izostaviti:
enum {NE = 0, DA} YesMyBabyNo, ShouldIStayOrShouldIGo;
enum {TRUE = 1, FALSE = 0};
57
// ispisuje 10
// ispisuje 9.42477
// pogreka
// OK!
Logiki podaci su takvi podaci koji mogu poprimiti samo dvije vrijednosti, na primjer:
da/ne, istina/la, dan/no. Jezik C++ za prikaz podataka logikog tipa ima ugraen tip
bool, koji moe poprimiti vrijednosti true (engl. true tono) ili false (engl. false
pogreno):
bool JeLiDanasNedjelja = true;
bool SloboZeliIzZemlje = false;
bool NjofraMozeIzZemlje = false;
Pri ispisu logikih tipova, te pri njihovom koritenju u aritmetikim izrazima, logiki
tipovi se pravilima cjelobrojne promocije (vidi prethodno poglavlje) pretvaraju u int:
true se pretvara u cjelobrojni 1, a false u 0. Isto tako, logikim varijablama se mogu
pridruivati aritmetiki tipovi: u tom sluaju se vrijednosti razliite od nule pretvaraju u
true, a nula se pretvara u false.
Gornja svojstva tipa bool omoguavaju da se za predstavljanje logikih podataka
koriste i cijeli brojevi. Broj 0 u tom sluaju odgovara logikoj neistini, a bilo koji broj
razliit od nule logikoj istini. Iako je za logiku istinu na raspolaganju vrlo iroki
raspon cijelih brojeva (zlobnici bi rekli da ima vie istina), ona se ipak najee zastupa
brojem 1. Ovakvo predstavljanje logikih podataka je vrlo esto, budui da je tip bool
vrlo kasno uveden u standard jezika C++.
Programski jezik C dozvoljava da se pobrojanom tipu pridrui int vrijednost. Zato, radi
prenosivosti kda pisanog u programskom jeziku C, te kda pisanog u starijim varijantama
jezika C++, ANSI/ISO C++ standard dozvoljava da se u nekim implementacijama prevoditelja
omogui to pridruivanje, uz napomenu da to moe prouzroiti neeljene efekte.
Rije bool dolazi od prezimena engleskog matematiara Georgea Boolea (18151864),
utemeljitelja logike algebre.
58
tono
tono
pogreno
pogreno
(1)
(1)
(0)
(0)
tono
pogreno
tono
pogreno
a .i. b
(1)
(0)
(1)
(0)
tono
pogreno
pogreno
pogreno
(1)
(0)
(0)
(0)
tono
tono
pogreno
pogreno
(1)
(1)
(0)
(0)
tono
pogreno
tono
pogreno
a .ili. b
(1)
(0)
(1)
(0)
tono
tono
tono
pogreno
(1)
(1)
(1)
(0)
59
Osim aritmetikih operacija, jezik C++ omoguava i usporedbe dva broja (vidi tablicu
2.10). Kao rezultat usporedbe dobiva se tip bool: ako je uvjet usporedbe zadovoljen,
rezultat je true, a ako nije rezultat je false. Tako e se izvoenjem kda
cout
cout
cout
cout
cout
cout
<<
<<
<<
<<
<<
<<
(5
(5
(5
(5
(5
(5
//
//
//
//
//
//
je
je
je
je
je
je
li
li
li
li
li
li
5
5
5
5
5
5
vee od 4?
vee ili jednako 4?
manje od 4?
manje ili jednako 4?
jednako 4?
razliito od 4?
< y
<= y
> y
>= y
== y
!= y
manje od
manje ili jednako
vee od
vee ili jednako
jednako
razliito
60
to upozorenje e izostati, jer ovdje poredbeni operator moe imati smisla. Za poetnike
jedno od nezgodnih svojstava jezika C++ jest da trpi i besmislene izraze, poput:
i + 5;
ili
i == 5 == 6;
Znakovne konstante tipa char piu se uglavnom kao samo jedan znak unutar
jednostrukih znakova navodnika:
char SlovoA = 'a';
cout << 'b' << endl;
61
novi redak
horizontalni tabulator
vertikalni tabulator
pomak za mjesto unazad (backspace)
povrat na poetak retka (carriage return)
nova stranica (form feed)
zvuni signal (alert)
kosa crta ulijevo (backslash)
upitnik
jednostruki navodnik
dvostruki navodnik
zavretak znakovnog niza
znak iji je kd zadan oktalno s 1, 2 ili 3 znamenke
znak iji je kd zadan heksadekadski
<<
<<
<<
<<
endl;
endl;
endl;
<< endl;
// usporedba jednostrukog
// i dvostrukog navodnika
62
// ASCII kd 104
// ispisuje 105
// ispisuje 'i'
Kod prvog ispisa znakovna varijabla a (koja ima vrijednost slova h) se, zbog operacije
sa cijelim brojem 1, pretvara se u ASCII kd za slovo h, tj. u broj 104, dodaje joj se 1, te
ispisuje broj 105. Druga naredba za ispis daje slovo i, jer se broj 105 pridruuje
znakovnoj varijabli b, te se 105 ispisuje kao ASCII znak, a ne kao cijeli broj.
Zadatak. Razmislite i provjerite to e se ispisati izvoenjem sljedeeg kda:
char a = 'a';
char b = a;
int asciiKod = a;
cout << a << endl;
cout << b << endl;
cout << 'b' << endl;
cout << asciiKod << endl;
Za pohranjivanje znakova iji kd ne stane u tip char moe se koristiti tip wchar_t.
Razjasnimo ukratko problem pohranjivanja znakova.
char tip podataka je duljine 1 bajta, tj. 8 bitova. Budui da se s 8 bitova moe
prikazati 28=256 razliitih brojeva (tj. brojevi od 0 do 255), to znai da e se tipom char
moi istovremeno prikazati najvie 256 znakova. ASCII je rezervirao kdove od 0 do
127 za znakove engleskog alfabeta i specijalne znakove (interpunkcija, kontrolni
kdovi) za nacionalne znakove neengleskih jezika preostali su kodovi od 127 do 255.
Taj skup je dovoljan da obuhvati jezino specifine znakove samo za nekoliko jezika.
Tako primjerice prema ISO-8859-1 kdiranju taj skup popunjavaju znakovi
zapadnoeuropskih pisama, dok ga prema ISO-8859-2 kdiranju popunjavaju znakovi
srednjeeuropskih jezika (ukljuujui Hrvatski).
Time je rijeen problem razmjene tekstova unutar podruja u kojem se koristi isto
8-bitno kodiranje. Na primjer, poaljete li datoteku s naim slovima kodiranu prema
ISO-8859-2 standardu nekom Maaru, on e sve nae znakove vidjeti korektno, jer se i
u Maarskoj koristi taj standard. Meutim, poaljete li taj dokument nekome Dancu (ili
bilo kome u zapadnoj Europi), njemu e se umjesto naih slova pojaviti potpuno
drugaiji znakovi (npr. umjesto slova , on e na ekranu imati slovo ). Uspije li
Dodue, postoje neki lokalni 7-bitni standardi koji znakove unutar ASCII skupa
nadomjetaju jeziki specifinim znakovima, no oni se danas uglavnom naputaju.
63
promijeniti kodiranje tako da mu se prikae pravi znak, vie nee imati na raspolaganju
svoje znakove.
Da se izbjegnu ovakva ogranienja u razmjeni dokumenata, napravljen je
univerzalni svjetski standard Unicode. U taj su standard ukljueni znakovi svih
svjetskih pisama (ukljuujui irilicu, arapsko, japansko, kinesko pismo) pa e zbog toga
bilo koji dokument pisan u Unicodu biti korektno prikazan u bilo kojem dijelu svijeta.
Da bi se obuhvatili svi znakovi, kd za pojedine znakove trebalo je proirit na 2 bajta
(to daje mjesto za 65536 razliitih znakova). Koritenjem Unicoda u programima
olakava se izrada programa za jezino razliita podruja.
Na alost, da bi se Unicode mogao koristiti, treba ga podravati operacijski sustav
koji se vrti na raunalu i koji je zaduen za pretvorbu brojanih kdova u grafiki prikaz
na zaslonu. U ovom trenutku praktiki ne postoji operacijski sustav koji bi izravno
podravao Unicode, ve se obavljaju translacije iz Unicoda u neki lokalni 8-bitni
standard i onda se takvi znakovi prikazuju.
wchar_t tip omoguava pohranjivanje znakova po Unicode standardu pa tako
moemo unutar istog programa kombinirati slova razliitih pisama (naravno, uz
pretpostavku operacijski sustav podrava Unicode):
wchar_t
wchar_t
wchar_t
wchar t
DomaceJeDomace = L'';
OvakoToPisuPrekoDrine = L'';
NegdjeDalje = L'';
JosDalje = L'';
Prefix L naznaava da je znak unutar apostrofa tipa wchar_t. Duljina tipa wchar_t nije
definirana standardom jezika C++; ona mora biti jednaka duljini nekog cjelobrojnog
tipa. Na prevoditelju koji smo koristili pri pisanju knjige ona je iznosila dva bajta, tako
da smo u jedan znak tipa wchar_t mogli smjestiti dva "obina" znaka (uoite da sada
nema prefiksa L ispred apostrofa):
wchar_t podebljiZnak = 'ab';
komplement
binarni i
binarni ili
iskljuivi ili
pomakni ulijevo
pomakni udesno
64
B6
B5
B4
B3
B2
B1
B0
1
~00100101 = 11011010
B7
B6
B5
B4
B3
B2
B1
B0
// 00100101 u heksadek.prikazu
// pridrui bitovni komplement
65
Operator & (bitovni i) postavlja u 1 samo one bitove koji su kod oba operanda
jednaki 1 (slika 2.5); matematikom terminologijom reeno, trai se presjek bitova dvaju
brojeva.
B7
B6
B5
B4
B3
B2
B1
B0
1
Slika 2.5. Bitovni operator i
66
B7
B6
B5
B4
B3
B2
B1
B0
int a = 0x0025;
int maska = 0x000f;
cout << hex << (a | maska) << endl;
B7
B6
B5
B4
B3
B2
B1
// ispisuje 0x2a
B0
// ispisuje 0x25
67
Ova injenica esto se koristi za jednostavnu zatitu podataka ili kda od nepoeljnog
itanja, odnosno koritenja: primjenom iskljuivog ili na podatke pomou tajne maske
podaci se ifriraju. Ponovnom primjenom iskljuivog ili s istom maskom podaci se
vraaju u izvorni oblik.
Operatori << (pomak ulijevo, engl. shift left) i >> (pomak udesno, engl. shift right)
pomiu sve bitove lijevog operanda za broj mjesta odreen desnim operandom, kako je
prikazano na slici 2.8.
B7
0
B7
B6
B5
B4
B3
B2
B1
B5
B4
B3
B2
B1
B5
B4
B3
B2
B1
B0
1
0
B6
B0
B7
B7
B6
B6
B5
B4
B3
B2
B1
B0
B0
0
B7
B6
B5
B4
B3
B2
B1
B0
Pri pomaku ulijevo najznaajniji (tj. krajnji lijevi) bitovi se gube, dok najmanje
znaajni bitovi poprimaju vrijednost 0. Slino, pri pomaku udesno, gube se najmanje
znaajni bitovi, a najznaajniji bitovi poprimaju vrijednost 0. Uoimo da pomak bitova
u cijelim brojevima za jedno mjesto ulijevo odgovara mnoenju broja s 2 situacija je
potpuno identina dodavanju nula iza dekadskog broja, samo to je kod dekadskog broja
baza 10, a u binarnoj notaciji je baza 2. Slino, pomak bitova za jedno mjesto udesno
odgovara dijeljenju cijelog broja s 2:
int a = 20;
cout << (a << 1) << endl;
cout << (a >> 2) << endl;
// pomak za 1
//
ispisuje
// pomak za 2
//
ispisuje
bit ulijevo 40
bita udesno 5
Meutim, pri koritenju pomaka valja biti krajnje oprezan, jer se kod cjelobrojnih
konstanti s predznakom najvii bit koristi za predznak. Ako je broj negativan, tada se
pomakom ulijevo bit predznaka e se izgubiti, a na njegovo mjesto e doi najvia
binarna znamenka. Kod pomaka udesno bit predznaka ulazi na mjesto najvie znamenke.
Istina, neki prevoditelji vode rauna o bitu predznaka, ali se zbog prenosivosti kda ne
treba previe oslanjati na to.
68
Broj stop-bitova pohranit emo u B4 i B5. Ako se trai jedan stop-bit, tada e B4 biti 1,
a za dva stop-bita B5 je jednak 1:
enum {StopB1 = 0x10, StopB2 = 0x20};
Konano, podatke o paritetu emo pohraniti u dva najvia bita (B6 - B7):
enum {ParitetNjet = 0x00, ParitetNeparni = 0x40,
ParitetParni = 0x80};
B7
0
B7
0
B7
0
B7
B6
B5
0
B6
0
B5
0
B6
0
B5
0
B6
1
B5
bez
pariteta
$"%"&
B7
B6
69
B4
0
B4
0
B4
0
B4
B3
0
B3
1
B3
0
B3
B2
B1
1
B2
0
B1
0
B2
0
B1
0
B2
0
B1
B0
Baud1200 |
B0
Bitova8 |
B0
StopB2 |
B0
ParitetNjet
8 bitova
podataka
$%&
B5
B4
"
"!
B3
B2
B1
""
2 stop-bita
B0
0
""!
1200 bauda
bitovnim i operatorom s brojem koji u binarnom prikazu ima 1 na tim mjestima (tj. s 07
heksadekadsko), izluiti podatak o brzini (vidi sliku 2.10):
int Speed = SerCom & 0x07;
// dobiva se 4
Prenosi li se osam bitova podataka provjerit emo sljedeom bitovnom ili operacijom:
int JeLiOsamBita = SerCom & 0x08;
// dobiva se 8
budui da 816 ima u binarnom prikazu 1 samo na mjestu B3. Ako je bit B3 postavljen, tj.
ako ima vrijednost 1, varijabla JeLiOsamBita e poprimiti vrijednost dekadsko (i
heksadekadsko!) 8; u protivnom e biti 0.
Analogno, za izluivanje broja stop-bitova posluit emo se maskom 3016 koja na
mjestima B4 i B5 ima 1:
B7
0
B7
0
B7
B6
0
B6
0
B6
B5
0
B5
1
B5
B4
0
B4
0
B4
B3
0
B3
1
B3
B2
1
B2
1
B2
B1
1
B1
0
B1
B0
00000111 &
B0
SerCom
B0
00000100
70
1
B7
B6
"""
B5
B4
B3
"""!
B2
B1
B0
00100000 >> 4
$"""%"""&
B7
B6
B5
B4
B3
B2
B1
B0
00000010
// dobiva se 32
U naem primjeru kao rezultat emo dobiti 32. Ako rezultantu bitovnu strukturu
pomaknemo udesno za 4 mjesta tako da B4 i B5 dou na B0 odnosno B1 (slika 2.11):
BrojStopBita = BrojStopBita >> 4;
ekvivalentna je naredbi
a = a + 5;
+=
-=
*=
/=
%=
>>=
<<=
^=
&=
|=
int n = 10;
n += 5;
cout << n <<
n -= 20;
cout << n <<
n *= -2;
cout << n <<
n %= 3;
cout << n <<
71
//
//
//
//
//
//
//
//
endl;
endl;
endl;
endl;
isto kao: n =
ispisuje: 15
isto kao: n =
ispisuje: -5
isto kao: n =
ispisuje: 10
isto kao: n =
ispisuje 1
n + 5
n - 20
n * (-2)
n % 3
// to je zapravo a = a - (b - c)
ve kao
a -= b + c;
// a = a - (b + c)
Na nekim starijim operacijskim sustavima (npr. starije inaice MS DOS-a) koristilo se 7bitno kodiranje, gdje za prikaz svih znakova stoji na raspolaganju samo 128 kdova. Da
bi se s takvim kodiranjem mogli prikazati nacionalni znakovi, neki rjee koriteni
specijalni znakovi poput [, ], {, }, |, \, ~ i ^ nadomjeteni su u pojedinim europskim
jezicima nacionalnim znakovima kojih nema u engleskom alfabetu. Tako su u hrvatskoj
inaici (udomaenoj pod nazivom CROSCII) ti znakovi nadomjeteni slovima , , , ,
, , , odnosno . Oito je da e na raunalu koje podrava samo takav sustav znakova
biti nemogue pregledno napisati ak i najjednostavniji C++ program. Na primjer, na
zaslonu bi kd nekog trivijalnog programa mogao izgledati ovako (onaj tko deifrira
kd, zasluuje bronani Velered Bjarnea Stroustrupa, bez pletera):
int main()
int a = 5;
char b = '0';
int c = a b;
return 0;
72
osnovna
alternativa
osnovna
{
}
[
]
#
##
<%
%>
<:
:>
%:
%:%:
&&
||
!
&
|
^
alternativa
and
or
not
bitand
bitor
xor
osnovna
~
!=
&=
|=
^=
alternativa
compl
not_eq
and_eq
or_eq
xor_eq
namjenu je u jeziku C++ dozvoljena i upotreba nizova od tri znaka (engl. trigraph),
naslijeenih iz programskog jezika C (tablica 2.15). Sreom, dananji operacijski sustavi
za predstavljanje znakova koriste neki 8-bitni standard (npr. kodna stranica 852 pod
DOS-om ili kodna stranica 1250 pod Windows-ima) pa omoguavaju istovremeno
koritenje nacionalnih znakova i specijalnih znakova neophodnih za pisanje programa u
jeziku C++, tako da veini korisnika tablice 2.14 i 2.15 nee nikada zatrebati. Ipak za
ilustraciju pogledajmo kako bi izgledao neki program napisan pomou alternativnih
oznaka (itatelju preputamo da deifrira kd):
%:include <iostream>
using namespace std;
int main() <%
bool prva = true;
bool druga = false;
bool treca = prva and druga;
cout << treca << endl;
cout << not treca << endl;
cout << prva or druga << endl;
return 1;
%>
trigraf
zamjena za
trigraf
zamjena za
trigraf
zamjena za
??=
??/
??'
#
\
^
??(
??)
??!
[
]
|
??<
??>
??-
{
}
~
73
Osim standardno ugraenih tipova podataka koje smo u prethodnim odjeljcima upoznali,
te operatora koji su za njih definirani, u programskom jeziku C++ na raspolaganju su
izvedeni tipovi podataka kao to su polja, pokazivai i reference (njih emo upoznati u
sljedeem poglavlju). Meutim, ono to programski jezik C++ ini naroito monim su
razredi koji omoguavaju uvoenje potpuno novih, korisniki definirani tipova
podataka. Tako programer vie nije sputan osnovnim tipovima koji su ugraeni u jezik,
ve ih moe po volji dopunjavati i definirati operacije na novostvorenim tipovima.
Detaljnije o razredima bit e govora u poglavlju 6.
2.4.15. Deklaracija typedef
Kljuna rije typedef omoguava uvoenje novog imena za ve postojei ugraeni ili
korisniki definirani tip podataka. Na primjer, deklaracijom:
typedef float broj;
Budui da deklaracija typedef ne uvodi novi tip podataka, niti mijenja standardna
pravila pretvorbe podataka, sljedea pridruivanja su dozvoljena i nee prouzroiti
nikakve promjene u tonosti:
float a = pi;
typedef float pliva;
pliva = pi;
promjene:
b;
h;
y;
// nakon promjene:
double a, b;
float k, h;
// ...
double x, y;
Kada je broj objekata mali to i nije teko, ali za veliki broj deklaracija te zamjene mogu
biti naporne i podlone pogrekama. Posao emo si bitno olakati, ako u gornjem
primjeru dodamo deklaraciju typedef za podatke iji tip emo povremeno mijenjati:
74
Sada e svi podaci tipa brojevi biti prevedeni kao double podaci, bez potrebe daljnjih
izmjena u izvornom kdu.
Osim u ovakvim jednostavnim sluajevima, kljuna rije typedef se esto koristi
prilikom deklaracija pokazivaa i referenci na objekte, pokazivaa na funkcije i kod
deklaracija polja. Naime, sintaksa kojom se ti tipovi opisuju moe esto biti vrlo
sloena. Zbog toga, ako esto koristimo pokaziva na neki tip, programski kd moe
postati vrlo neitljiv. U tom sluaju, jednostavnije je pomou kljune rijei typedef
definirati novi tip, koji oznaava esto koriteni tip pokaziva na objekt. Takav tip
postaje ravnopravan svim ostalim ugraenim tipovima, te se moe pojaviti na svim
mjestima na kojima se moe pojaviti ugraeni tip: prilikom deklaracije objekata,
specificiranja parametara funkcije, kao parametar sizeof operatoru i sl. Primjena
typedef kljune rijei na takve tipove bit e objanjena u kasnijim poglavljima.
Valja naglasiti da standard jezika C++ ne definira veliinu bajta, osim u smislu rezultata
to ga daje sizeof operator; tako je sizeof(char) jednak 1. Naime, duljina bajta (broj
bitova koji ine bajt) ovisi o arhitekturi raunala. Mi emo u knjizi uglavnom
podrazumijevati da bajt sadri 8 bitova, to je najei sluaj u praksi.
Operand sizeof operatora moe biti identifikator tipa (npr. int, float, char) ili
konkretni objekt koji je ve deklariran:
float f;
cout << sizeof(f) << endl;
int i;
cout << sizeof(i) << endl;
// duljina float-a
// duljina int-a
75
76
za to napie naredbu
c = a---b;
to e stvarno ta naredba uraditi pouzdano emo saznati ako ispiemo vrijednosti svih
triju varijabli nakon naredbe:
int main() {
int a = 2;
int b = 5;
int c;
c = a---b;
cout << "a = " << a << ", b = " << b << ", c = " << c << endl;
return 1;
}
tj. uzeo je vrijednost varijable a, od nje oduzeo broj b te rezultat pridruio varijabli c, a
varijablu a je umanjio (ali tek nakon to je upotrijebio njenu vrijednost).
77
operator
znaenje
primjer
::
::
::
globalno podruje
podruje razreda
podruje imenika
::ime
razred::ime
imenik::ime
.
->
[]
()
()
++
-typeid
typeid
const_cast
dynamic_cast
static_cast
reinterpret_cast
izbor lana
izbor lana
indeksiranje
poziv funkcije
stvori tip
uveaj nakon
umanji nakon
identifikacija tipa
identifikacija tipa tijekom izvoenja
pretvorba nepromjenjivosti tipa
pretvorba tipa tijekom izvoenja
pretvorba tipa tijekom prevoenja
neprovjerena pretvorba tipa
objekt.clan
pokazivac->clan
varijabla[indeks]
funkcija(argumenti)
tip(argumenti)
lvrijednost++
lvrijednost-typeid(tip)
typeid(izraz)
const_cast<tip>(izraz)
dynamic_cast<tip>(izraz)
static_cast<tip>(izraz)
reinterpret_cast<tip>(izraz)
sizeof
sizeof
++
-+ - ! ~
*
&
new
new
new
new
delete
delete
()
veliina objekta
veliina tipa
uveaj prije
umanji prije
unarni operatori
dereferenciranje
adresa objekta
stvori (alociraj) objekt
stvori (i inicijaliziraj) objekt
stvori (smjesti) objekt
stvori (smjesti i inicijaliziraj) objekt
uniti (dealociraj) objekt
uniti (dealociraj) poljet
dodjela tipa
izbor lana
izbor lana
mnoenja
zbrajanja
bitovni pomaci
poredbeni operatori
operatori jednakosti
sizeof izraz
sizeof(tip)
++lvrijednost
--lvrijednost
~lvrijednost
*izraz
&lvrijednost
new tip
new tip(argumenti)
new(argumenti) tip
new(argumenti) tip(argumenti)
delete pokazivac
delete[] pokazivac
(tip) izraz
.*
->*
* / %
+ << >>
< > <= >=
== !=
objekt.*pokazivac_na_clan
pokazivac->*pokazivac_na_clan
izraz % izraz
izraz izraz
izraz >> izraz
izraz <= izraz
izraz != izraz
(nastavlja se)
78
operator
znaenje
primjer
&
bitovni i
bitovno iskljuivo ili
bitovni ili
logiki i
logiki ili
uvjetni izraz
pridruivanja
baci iznimku
razdvajanje
throw izraz
^
|
&&
||
?:
= *= /= += -= &=
^= |= %= >>= <<=
throw
,
izraz ^ izraz
izraz | izraz
izraz && izraz
izraz || izraz
izraz ? izraz : izraz
lvrijednost += izraz
izraz , izraz
prvo zbrojiti varijable b i c, a tek potom e se njihov zbroj mnoiti s varijablom a. esto
je zgodno zagrade koristiti i radi itljivosti kda kada one nisu neophodne. Na primjer, u
gore razmatranom primjeru mogli smo za svaki sluaj pisati
c = a - (--b);
79
U veini realnih problema tok programa nije pravocrtan i jedinstven pri svakom
izvoenju. Redovito postoji potreba da se pojedini odsjeci programa ponavljaju
programski odsjeci koji se ponavljaju nazivaju se petljama (engl. loops). Ponavljanje
moe biti unaprijed odreeni broj puta, primjerice elimo li izraunati umnoak svih
cijelih brojeva od 1 do 100. Meutim, broj ponavljanja moe ovisiti i o rezultatu neke
operacije. Kao primjer za to uzmimo uitavanje podataka iz neke datoteke datoteka se
ita podatak po podatak, sve dok se ne uita znak koji oznaava kraj datoteke (end-offile). Duljina datoteke pritom moe varirati za pojedina izvoenja programa.
Gotovo uvijek se javlja potreba za grananjem toka, tako da se ovisno o
postavljenom uvjetu u jednom sluaju izvodi jedan dio programa, a u drugom sluaju
drugi dio. Na primjer, elimo izraunati realne korijene kvadratne jednadbe. Prvo emo
izraunati diskriminantu ako je diskriminanta vea od nule, izraunat emo oba
korijena, ako je jednaka nuli izraunat emo jedini korijen, a ako je negativna ispisat
emo poruku da jednadba nema realnih korijena. Grananja toka i ponavljanja dijelova
kda omoguavaju posebne naredbe za kontrolu toka.
80
{
int a = 1;
cout << a << endl;
}
return 0;
proi uredno, ali ako naredbu za ispis lokalne varijable a prebacimo izvan bloka
#include <iostream>
using namespace std;
int main() {
{
int a = 1;
}
cout << a << endl;
return 0;
}
// ispisuje 1
// ispisuje 5
Prva naredba za ispis dohvatit e lokalnu varijablu a = 1, budui da ona ima prednost
pred istoimenom varijablom a = 5 koja je deklarirana ispred bloka. Po izlasku iz bloka,
lokalna varijabla a se gubi te je opet dostupna samo varijabla a = 5. Naravno da bi
ponovna deklaracija istoimene varijable bilo u vanjskom, bilo u unutarnjem bloku
rezultirala pogrekom tijekom prevoenja. Podrujem dosega varijable pozabavit emo
se detaljnije u kasnijim poglavljima.
Ako se blok u naredbama za kontrolu toka sastoji samo od jedne naredbe, tada se
vitiaste zagrade mogu i izostaviti.
81
Ako je vrijednost izraza iza rijei if logika istina (true), izvodi se blok naredbi koje
slijede iza izraza. U protivnom se taj blok preskae i izvoenje nastavlja od prve
naredbe iza bloka. Na primjer:
if (a < 0) {
cout << "Broj a je negativan!" << endl;
}
U sluaju da blok sadri samo jednu naredbu, vitiaste zagrade koje omeuju blok mogu
se i izostaviti, pa smo gornji primjer mogli pisati i kao:
if (a < 0)
cout << "Broj a je negativan!" << endl;
ili
if (a < 0) cout << "Broj a je negativan!" << endl;
Druga naredba ispod if uvjeta izvest e se i za pozitivne brojeve, jer vie nije u bloku,
pa e se za pozitivne brojeve ispisati pogrena apsolutna vrijednost! Inae, u primjerima
emo izbjegavati pisanje vitiastih zagrada gdje god je to mogue radi utede na
prostoru.
elimo li da se ovisno u rezultatu izraza u if uvjetu izvode dva nezavisna
programska odsjeka, primijenit emo sljedei oblik uvjetnog grananja:
82
if ( logiki_izraz )
// prvi_blok_naredbi
else
// drugi_blok_naredbi
Kod ovog oblika, ako izraz u if uvjetu daje kao rezultat logiku istinu (true), izvest e
se prvi blok naredbi. Po zavretku bloka, izvoenje programa nastavlja se od prve
naredbe iza drugog bloka. Ako izraz daje kao rezultat logiku neistinu (false), preskae
se prvi blok, a izvodi samo drugi blok naredbi, nakon ega se nastavlja izvoenje
naredbi koje slijede. Evo jednostavnog primjera u kojem se raunaju presjecita pravca s
koordinatnim osima. Pravac je zadan jednadbom a x + b y + c = 0.
#include <iostream>
using namespace std;
int main() {
cout << "Upii koeficijente pravca:" << endl;
float a, b, c;
// koeficijenti pravca
cout << "a = ";
cin >> a;
// uitaj koeficijent a
cout << "b = ";
cin >> b;
// uitaj koeficijent b
cout << "c = ";
cin >> c;
// uitaj koeficijent c
cout << "Koeficijenti: " << a << ","
<< b << "," << c << endl; // ispii ih
cout << "Presjecite s apscisom: ";
if (a != 0)
cout << -c / a << ", ";
else
cout << "nema, ";
// pravac je horizontalan
cout << "presjecite s ordinatom: ";
if (b != 0)
cout << -c / b << endl;
else
cout << "nema" << endl; // pravac je vertikalan
return 0;
}
Kada prevedete i pokrenete gornji program, ne zaboravite nakon upisa svake vrijednosti
pritisnuti tipku Enter.
Zadatak. Napiite program kojim ete (pomou operatora modulo) ispitati da li je
upisani broj paran ili neparan i shodno tome ispisati poruku.
83
// diskriminanta
84
#include <iostream>
using namespace std;
int main() {
float a, b, c;
cout << "Unesi koeficijente kvadratne jednadzbe:"
<< endl;
cout << "a = ";
cin >> a;
cout << "b = ";
cin >> b;
cout << "c = ";
cin >> c;
if (a) {
float diskr = b * b - 4. * a * c;
cout << "Jednadba ima ";
if (diskr == 0)
cout << "dvostruki realni korijen." << endl;
else if (diskr > 0)
cout << "dva realna korijena." << endl;
else
cout << "kompleksne korijene." << endl;
}
else
cout << "Jednadba je linearna." << endl;
return 0;
}
Za logiki izraz u prvom if uvjetu postavili smo samo vrijednost varijable a ako e
ona biti razliita od nule, uvjet e biti zadovoljen (pravilima pretvorbe vrijednost
varijable svest e se na logiku istinu) i izvest e se naredbe u prvom if bloku. Taj blok
sastoji se od niza if-else blokova identinih onima iz prethodnog primjera. Ako
poetni uvjet nije zadovoljen, tj. ako varijabla a ima vrijednost 0 (to rezultira logikom
neistinom nakon pretvorbe), preskae se cijeli prvi blok i ispisuje poruka da je
jednadba linearna. Uoimo u gornjem primjeru dodatno uvlaenje ugnijeenih
blokova.
Zadatak. Proirite prethodni zadatak za provjeru parnosti tako da za neparne brojeve
dodatno ispitate djeljivost s 3 ako je broj djeljiv s 3, ispiite dodatnu poruku o tome.
Pri definiranju logikog izraza u naredbama za kontrolu toka poetnik treba biti
oprezan. Na primjer, elimo li da se dio programa izvodi samo za odreeni opseg
vrijednosti varijable b, naredba
if (-10 < b < 0) //...
nee raditi onako kako bi se prema ustaljenim matematikim pravilima oekivalo. Ovaj
logiki izraz u biti se sastoji od dvije usporedbe: prvo se ispituje je li 10 manji od b, a
potom se rezultat te usporedbe usporeuje s 0, tj.
85
Kako rezultat prve usporedbe moe biti samo true ili false, odnosno 1 ili 0, druga
usporedba dat e uvijek logiku neistinu. Da bi program poprimio eljeni tok, usporedbe
moramo razdvojiti i logiki izraz formulirati ovako: ako je 10 manji od b i ako je b
manji od 0:
if (-10 < b && b < 0) //...
Druga nezgoda koja se moe dogoditi jest da se umjesto operatora za usporedbu ==, u
logikom izrazu napie operator pridruivanja =. Na primjer:
if (k = 0)
k++;
else
k = 0;
// pridruivanje, a ne usporedba!!!
Ako izraz uvjet daje logiku istinu, izraunava se izraz1, a u protivnom izraz2. U
primjeru
x = (x < 0) ? -x : x;
// x = abs(x)
86
Zadatak. Koristei uvjetni operator, ispitajte parnost unesenog broja i ako je broj
neparan poveajte mu vrijednost za 1.
87
88
break;
default:
cout << "petak." << endl;
}
return 0;
}
/*...*/
Budui da odmah iza naredbe case 0: slijedi case 1:, blok naredbi do naredbe break
e se izvravati i za sluaj kada je rezultat izraza u switch jednak 0. default blok e se
izvoditi za sve ostale sluajeve.
Zadatak. U gornjem primjeru pomaknite default blok naredbi ispred case naredbi i
pokrenite program. Nemojte zaboraviti dodati naredbu break na kraj default bloka!
to bi se dogodilo da taj break izostavite?
Blok default moe biti smjeten bilo gdje unutar switch bloka, no zbog
preglednosti je preporuljivo staviti ga na kraj. On se smije i izostaviti, ali ga je redovito
zgodno imati da bi kroz njega program proao za vrijednosti koje nisu obuhvaene case
blokovima. Ovo je naroito vano tijekom razvijanja programa, kada se u default blok
moe staviti naredba koja e ispisivati upozorenje da je cjelobrojni_izraz u switch
poprimio neku nepredvienu vrijednost.
Zadatak. Zadnji primjer modificirajte tako da dodatno ispitujete da li zadani datum
pada na neki blagdan (za poetak ispitajte samo za 25.12.). Ako pada na blagdan ili
89
ako pada u dane weekenda, ispiite da je taj dan neradni, inae da je radni. Uputa: na
poetku programa definirajte bool varijablu DaLiJePraznik i postavite ju poetno na
false. Vrijednost te varijable promijenite ako je zadovoljen uvjet ispitivanja za
blagdane ili ako se unutar switch-a ispostavi da datum pada u subotu ili nedjelju. Na
kraju programa ispitajte vrijednost varijable i ispiite odgovarajuu poruku.
Iza kljune rijei for, unutar okruglih zagrada () navode se tri grupe naredbi,
meusobno odvojenih znakom toka-zarez ;. Postupak izvoenja for-bloka je sljedei:
1. Izraunava se poetni_izraz. Najee je to pridruivanje poetne vrijednosti
brojau kojim e se kontrolirati ponavljanje petlje.
2. Izraunava se uvjet_izvoenja, izraz iji rezultat mora biti tipa bool. Ako je rezultat
jednak logikoj neistini, preskae se blok_naredbi i program se nastavlja prvom
naredbom iza bloka.
3. Ako je uvjet_izvoenja jednak logikoj istini, izvodi se blok_naredbi.
4. Na kraju se izraunava izraz_prirasta (npr. poveavanje brojaa petlje). Program
se vraa na poetak petlje, te se ona ponavlja od toke 2.
Programski odsjeak se ponavlja sve dok uvjet_izvoenja na poetku petlje daje
logiku istinu; kada rezultat tog izraza postane logika neistina, programska petlja se
prekida.
Kao primjer za for petlju, napiimo program za raunanje faktorijela (faktorijela od
n je umnoak svih brojeva od 1 do n):
n! = 1 2 (n 2) (n 1) n
Pogledajmo kd:
#include <iostream>
using namespace std;
int main() {
int n;
cout << "Upii prirodni broj: ";
cin >> n;
long int fjel = 1;
for (int i = 2; i <= n; i++)
fjel *= i;
// manji od 13!?
90
Prije ulaska u petlju trebamo definirati poetnu vrijednost varijable fjel u koju emo
gomilati umnoke. Na ulasku u petlju deklariramo broja petlje, varijablu i tipa int te
joj pridruujemo poetnu vrijednost 2 (poetni_izraz: int i = 2). Unutar same petlje
mnoimo fjel s brojaem petlje, a na kraju tijela petlje uveavamo broja
(izraz_prirasta: i++). Petlju ponavljamo sve dok je broja manji ili jednak unesenom
broju (uvjet_izvoenja: i <= n). Pri testiranju programa pazite da uneseni broj ne
smije biti vei od 12, jer e inae doi do brojanog preljeva varijable fjel.
Zanimljivo je uoiti to e se dogoditi ako za n unesemo brojeve manje od 2. Ve
pri prvom ulasku u petlju nee biti zadovoljen uvjet ponavljanja petlje te e odmah biti
preskoeno tijelo petlje i ona se nee izvesti niti jednom! Ovo nam odgovara, jer je 1! =
1 i (po definiciji) 0! = 1. Naravno da smo petlju mogli napisati i na sljedei nain:
for (int i = n; i > 1; i--)
fjel *= i;
Rezultat bi bio isti. Iskusni programer bi gornji program mogao napisati jo saetije
(vidi poglavlje 3.11), no u ovom trenutku to nam nije bitno.
Moe se dogoditi da je uvjet izvoenja uvijek zadovoljen, pa e se petlja izvesti
neogranieni broj puta. Program e uletjeti u slijepu ulicu iz koje nema izlaska, osim
pomou tipki Power ili Reset na kuitu vaeg raunala. Na primjer:
// ovaj primjer pokreete na vlastitu odgovornost!
cout << "Beskonana petlja";
for (int i = 5; i > 1; )
cout << "a";
Varijabla i je uvijek vea od 1, tako da ako se i usudite pokrenuti ovaj program, ekran
e vam vrlo brzo biti preplavljen slovom a. Iako naizgled banalne, ovakve pogreke
mogu poetniku zadati velike glavobolje.
Uoimo da smo u gornjem kdu izostavili izraz_prirasta. Openito, moe se
izostaviti bilo koji od tri izraza u for naredbi jedino su oba znaka ; obavezna.
Izostavi li se uvjet_izvoenja, podrazumijevana vrijednost e biti true i
petlja e biti beskonana!
tovie, mogu se izostaviti i sva tri izraza:
for ( ; ; )
91
Naredba unutar petlje potpuno potire izraz prirasta, te varijabla i alternira izmeu 1 i 0.
Poetni izraz i izraz prirasta mogu se sastojati i od vie izraza odvojenih operatorom
nabrajanja , (zarez) . To nam omoguava da program za raunanje faktorijela napiemo
i (neto) krae:
#include <iostream>
using namespace std;
int main() {
int n;
cout << "Upii prirodni broj: ";
cin >> n;
long fjel;
int i;
for (i = 2, fjel = 1; i <= n; fjel *= i, i++) ;
cout << n << "! = " << fjel;
return 0;
}
92
#include <iostream>
using namespace std;
int main() {
int i = -1;
for (int i = 1; i <= 10; i++)
cout << i << endl;
cout << i << endl;
// ispisuje -1
return 0;
}
Zadatak. Napiite program s dvije uzastopne petlje koje e ispisati tablicu sa slovima
engleskog alfabeta. Tablica neka ima dva stupca: u prvom stupcu neka je ASCII kd (od
65 do 90 ukljuivo za velika slova, odnosno 97 do 122 ukljuivo za mala slova). Uputa:
slovo ispiite koristei dodjelu tipa static_cast<char>. Potom preuredite program
tako da imate samo jednu petlju i ispis u etiri stupca.
for petlje mogu biti ugnijeene jedna unutar druge. Ponaanje takvih petlji
razmotrit emo na sljedeem programu:
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
for (int redak = 1; redak <= 10; redak++) {
for (int stupac = 1; stupac <= 10; stupac++)
cout << setw(5) << redak * stupac;
cout << endl;
}
return 0;
}
2
4
6
8
10
12
14
16
18
20
3
6
9
12
15
18
21
24
27
30
4
8
12
16
20
24
28
32
36
40
5
10
15
20
25
30
35
40
45
50
6
12
18
24
30
36
42
48
54
60
7
14
21
28
35
42
49
56
63
70
8
16
24
32
40
48
56
64
72
80
9
18
27
36
45
54
63
72
81
90
10
20
30
40
50
60
70
80
90
100
Kako se gornji program izvodi? Pri ulasku u vanjsku petlju, inicijalizira se broja redaka
na vrijednost 1 te se s njom ulazi u unutarnju petlju. U unutarnjoj petlji se broja stupaca
mijenja od 1 do 10 i za svaku pojedinu vrijednost izraunava se njegov umnoak s
93
brojaem redaka (potonji je cijelo to vrijeme redak = 1). Po zavrenoj unutarnjoj petlji
ispisuje se znak za novi redak, ime zavrava blok naredbi vanjske petlje. Slijedi prirast
brojaa redaka (redak = 2) i povrat na poetak vanjske petlje. Unutarnja petlja se
ponavlja od stupac = 1 do stupac = 10 itd. Kao to vidimo, za svaku vrijednost brojaa
vanjske petlje izvodi se cjelokupna unutarnja petlja.
Da bismo dobili ispis brojeva u pravilnim stupcima, u gornjem primjeru smo rabili
operator za rukovanje (manipulator) setw(). Argument tog manipulatora (tj. cijeli broj
u zagradi) odreuje koliki e se najmanji prostor predvidjeti za ispis podatka koji slijedi
u izlaznom toku. Ako je podatak krai od predvienog prostora, preostala mjesta bit e
popunjena prazninama. Manipulator setw() definiran je u datoteci zaglavlja iomanip.
Zadatak. Preuredite prethodni primjer tako da ispie samo lanove u donjem lijevom
trokutu:
1
2
3
4
...
4
6
8
9
12
16
Uputa: u unutarnjoj petlji kao uvjet izvoenja postavite da je stupac manji ili jednak
retku.
Zadatak. Kd iz prethodnog zadatka modificirajte tako da se ispiu samo lanovi u
gornjem desnom trokutu. Uputa: unutar vanjske petlje smjestite dvije petlje po
stupcima, s time da prva treba ispisivati odgovarajui broj praznina, a druga traene
brojeve.
94
Konkretnu primjenu while bloka dat emo programom kojim se ispisuje sadraj
datoteke s brojevima. U donjem kdu je to datoteka brojevi.dat, ali uz promjene
odgovarajueg imena, to moe biti i neka druga datoteka.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream ulazniTok("brojevi.dat");
cout << "Sadraj datoteke:" << endl << endl;
float broj;
while ((ulazniTok >> broj) != 0)
cout << broj << endl;
return 0;
}
ita podatak. Ako je uitavanje bilo uspjeno, ulTok e biti razliit od nule (neovisno o
vrijednosti uitanog broja), te se izvodi blok naredbi u while-petlji ispisuje se broj na
izlaznom toku cout. Nakon to se izvede naredba iz bloka, izvoenje se vraa na
ispitivanje uvjeta petlje. Ako ulTok poprimi vrijednost 0, to znai da nema vie brojeva
u datoteci, petlja se prekida. Blok petlje se preskae, te se izvoenje nastavlja prvom
naredbom iza bloka.
Primijetimo zgodno svojstvo petlje while: ako je datoteka prazna, ulazni tok e
odmah poprimiti vrijednost 0 te e uvjet odmah prilikom prvog testiranja biti
neispunjen. Blok petlje se tada nee niti jednom izvesti, to u naem sluaju i trebamo.
Zadatak. Dopunite gornji program tako da nakon sadraja datoteke ispie i broj
iitanih brojeva i njihovu srednju vrijednost.
95
Trebalo je samo poetni izraz (int i = 2) izluiti ispred while naredbe, a izraz prirasta
(i++) prebaciti u blok naredbi.
Zadatak. Program za ispis datoteke napiite tako da umjesto while-bloka upotrijebite
for-blok naredbi.
Koji e se pristup koristiti (for-blok ili while-blok) prvenstveno ovisi o
sklonostima programera. Ipak, zbog preglednosti i razumljivosti kda for-blok je
preporuljivo koristiti kada se broj ponavljanja petlje kontrolira cjelobrojnim brojaem.
U protivnom, kada je uvjet ponavljanja odreen nekim logikim uvjetom, praktinije je
koristiti while naredbu.
96
97
(1)i
i =0
x 2i +1
x 3 x5 x 7 x 9
= x +
+
K
(2i + 1)!
3! 5! 7! 9 !
Petlju za raunanje lanova reda treba ponavljati sve dok je apsolutna vrijednost
zadnjeg izraunatog lana reda vea od nekog zadanog broja (npr. 107). Uputa: ispred
do-while petlje definirajte realnu varijablu zbrojReda (poetno 0), te cjelobrojne
varijable red i preznak koje poetno postavite na +1. Unutar do-while petlje
definirajte realnu varijablu NtiClan i inicijalizirajte ju na vrijednost uitanog x, iza
ega slijedi for petlja. Indeks petlje j ide od 1 do red i unutar nje se NtiClan mnoi s
kvocijentom x/j. Na izlasku iz for petlje pomnoite tako izmnoeni NtiClan s predznak,
dodajte to u zbrojReda, promijenite predznak i na kraju ispitajte da li je NtiClan vei
od postavljene granice ako jest, petlju ponovite.
Na IBM kompatibilnim osobnim raunalima referentno vrijeme je pono (0:00 sati) 1. sijenja
1970. godine.
98
Naredba continue takoer uzrokuje skok programa na kraj petlje, ali se potom
njeno ponavljanje nastavlja. Na primjer, elimo li da na program za ispis sadraja
datoteke ispisuje samo one znakove koji se mogu prikazati na zaslonu, jedna mogua
varijanta bila bi:
// ...
while ((znak = fgetc(ulazniTok)) != EOF) {
if (znak < ' ' && znak != '\r')
continue;
// preskae naredbe do kraja petlje
cout << znak;
}
// ...
Nailaskom na znak iji je kd manji od kda za prazninu i nije jednak znaku za novi
redak, izvodi se naredba continue preskau se sve naredbe do kraja petlje (u ovom
sluaju je to naredba za ispis znaka), ali se ponavljanje petlje dalje nastavlja kao da se
nita nije dogodilo.
Ako je vie petlji ugnijeeno jedna unutar druge, naredba break ili naredba
continue prouzroit e prekid ponavljanja, odnosno nastavak okolne petlje u kojoj se
naredba nalazi.
Valja izbjegavati esto koritenje break i continue naredbi u petljama, jer
one naruavaju strukturiranost programa.
Koristite ih samo za izvanredna stanja, kada ne postoji drugi prikladan nain da se
izvoenje naredbi u petlji prekine. Naredba continue redovito se moe izbjei ifblokom.
Kao ilustraciju nesvrhovitog koritenja break naredbe pogledajmo primjer kakav se
nerijetko moe vidjeti kod samozvanih C++ gurua:
while (1) {
int i;
cin >> i;
if (i == 0)
break;
}
// beskonana petlja!
// ako je uitani broj 0,
// tada prekini petlju
Budui da je uvjet izvoenja petlje uvijek 1 (tj. true), petlja je beskonana; prekida se
tek izvoenjem break naredbe. Ljepe napisana petlja bi izgledala ovako:
int i;
do {
cin >> i;
} while(i != 0);
99
Naredba na koju se eli skoiti moe se nalaziti bilo gdje (ispred ili iza naredbe goto)
unutar iste funkcije. ime_oznake mora biti jedinstveno unutar funkcije, ali moe biti
jednako imenu nekog objekta ili funkcije. Ime oznake jest identifikator, pa vrijede
pravila navedena u poglavlju 0.
U pravilno strukturiranom programu naredba goto uope nije potrebna, te ju
velika veina programera uope ne koristi.
Zadnju naredbu za kontrolu toka koju emo ovdje spomenuti upoznali smo na samom
poetku knjige. To je naredba return kojom se prekida izvoenje funkcija te emo se
njome pozabaviti u poglavlju 5 posveenom funkcijama.
100
101
ili
a=b*c-d/(2.31+e)+e/8.21e-12*2.43;
Koji ete pristup preuzeti ovisi iskljuivo o vaoj odluci, ali ga onda svakako
koristite dosljedno.
s potpuno istim efektom. Ovakvo saimanje kda ne samo da zauzima manje prostora u
editoru i izvornom kdu, ve esto olakava prevoditelju generiranje kraeg i breg
izvedbenog kda. tovie, mnoge naredbe jezika C++ (poput naredbe za
inkrementiranje) vrlo su bliske strojnim instrukcijama mikroprocesora, pa se njihovim
prevoenjem dobiva maksimalno efikasan kd. Pri saimanju kda posebno valja paziti
na hijerarhiju operatora (vidi tablicu 2.16). esto se zaboravlja da logiki operatori
imaju nii prioritet od poredbenih operatora, a da operatori pridruivanja imaju najnii
prioritet. Zbog toga nakon naredbe
102
c = 4 * (a = b - 5);
Iako e oba kda raditi potpuno jednako, sutinski gledano je prvi pristup ispravniji (i
itljiviji) izraz u if ispitivanju po definiciji mora davati logiki rezultat (tipa bool). U
drugom (saetom) primjeru se, sukladno ugraenim pravilima pretvorbe, aritmetiki tip
pretvara u tip bool (tako da se brojevi razliiti od nule pretvaraju u true, a nula se
pretvara u false), pa je konani ishod isti.
Slina situacija je prilikom ispitivanja da li je neka varijabla jednaka nuli. U
dosljednom pisanju ispitivanje bismo pisali kao:
if (a == 0) {
// ...
}
I u ovom sluaju e oba ispitivanja poluiti isti izvedbeni kd, ali e neiskusnom
programeru iitavanje drugog kda biti zasigurno tee. tovie, neki prevoditelji e
prilikom prevoenja ovako saetih ispitivanja ispisati upozorenja.
103
tovie, zbrajanje moemo ubaciti u uvjet za poveavanje kontrolne varijable for petlje:
// ver. 3.0
//...
for (int i = 1; i <= 100; zbroj += i, i++) ;
//...
Blok naredbi u petlji je sada ostao prazan, ali ne smijemo izbaciti znak ;. U dosadanjim
realizacijama mogli smo broja petlje i poveavati i prefiks-operatorom:
// ver. 3.1
//...
for (int i = 1; i <= 100; zbroj += i, ++i) ;
//...
Efekt je potpuno isti u izrazu prirasta for-naredbe izrazi odvojeni znakom , (zarezom)
se izraunavaju postupno, slijeva na desno. Naravno, da su izrazi napisani obrnutim
redoslijedom
for (int i = 1; i <= 100; i++, zbroj += i)
konani zbroj bi bio pogrean prvo bi se uveao broja, a zatim tako uvean dodao
zbroju kao rezultat bismo dobili zbroj brojeva od 2 do 101. Meutim, kada stopimo
oba izraza za prirast
104
// ver. 4.0
//...
for (int i = 1; i <= 100; zbroj += i++) ;
//...
Usporedimo li zadnju inaicu (4.0) s prvom, skraenje kda je oevidno. Ali je isto tako
oito da je kd postao nerazumljiviji: prebacivanjem naredbe za pribrajanje brojaa u
for-naredbu, zakamuflirali smo osnovnu naredbu zbog koje je uope petlja napisana.
Budui da veina dananjih prevoditelja ima ugraene postupke optimizacije
izvedbenog kda, pitanje je koliko e i hoe li uope ovakva saimanja rezultirati brim
i efikasnijim programom. Stoga vam preporuujemo:
Ne troite previe energije na ovakva saimanja, jer e najvjerojatnije
rezultat biti neproporcionalan uloenom trudu, a izvorni kd postati
neitljiv(iji).