Professional Documents
Culture Documents
Nakon upisa kod sauvamo na nekom mjestu na hard disku, npr. pod imenom hello.cpp
2 Osnove programiranja
2.1 Struktura programa
C++ program se sastoji od jedne ili vie cjelina za prevoenje, pri emu ove cjeline predstavljaju
dio programa koji treba kompajlirati odvojeno od drugih cjelina. Tanije, cjelina za prevoenje je
rezultat primjene preliminarne faze kompilacije, koja se naziva predprocesiranje, na izvornu
datoteku (source).
Izvorna datoteka obino poinje sa jednom ili vie (predprocesorskih) direktiva #include, pri emu
svaka od njih navodi predprocesor da kopira deklaracije entiteta (funkcija, globalnih varijabli,
tipova, itd), koji su definisani u ostalim cjelinama za prevoenje.
Posmatrajmo primjer iz prethodnog poglavlja:
1
2
3
4
5
6
7
8
#include <iostream>
using namespace std;
int main()
{
cout << Hello, world! << endl;
system("PAUSE");
return 0;
}
U liniji 1 pozvana je datoteka iostream. Prvi karakter (#) predstavlja symbol, koji daje signal
predprocesoru. Svaki put kada pokrenemo kompajler, predprocesor je ve pokrenut. U principu,
predprocesor ita kroz izvornu datoteku i trai linije koje poinju sa ovim karakterom, tako da ih
predprocesor proe prije nego kompajler starta sa radom. Ukratko ova linija znai: Ono to slijedi je
2
C++
prevodilac
C++
program
C++
prirodni
kompajler
C
kod
C
kompajler
Objektni
kod
linker
Izvrna
datoteka
2.4 Memorija
Za pohranjivanje izvrnog koda kao i podataka sa kojima progam manipulie, kompjuter ima na
raspolaganju RAM memoriju (Read Access Memory). Memorija se moe zamisliti kao neprekidan
4
...
1211
1212
1213
1214
1215
1216
1217
bajt
bajt
bajt
bajt
bajt
bajt
bajt
...
Memorija
bit
navodi kompajler da alocira nekoliko bajta kako bi predstavio varijablu zarada. Taan broj bajta
koji je alociran i metod koji se koristi za binarnu reprezentaciju cijelog broja zavisi od specifinosti
C++ implementacije, ali uzmimo da se radi o 2 bajta. Kompajler koristi adresu prvog bajta na koju
se alocira zarada kako bi oznaio varijablu. Prethodna jednakost uzrokuje da se vrijednost 500
pohrani u ova dva bajta koja su alocirana (Slika II.2.3)
...
1211
1212
1213
bajt
bajt
bajt
1214
1215
10110011 10110011
1216
1217
bajt
bajt
...
Memorija
zarada
#include <iostream>
using namespace std;
3
4
5
6
7
8
9
10
11
12
13
14
plata =
cout <<
cout <<
cout <<
Linija 9 ita ulaznu vrijednost, koju unosi korisnik i kopira je u satnica. Ulazni operator >> uzima
ulazni stream kao lijevi operand (cin je standardni C++ ulazni stream koji odgovara podacima
unesenim pomou tastature), a varijablu (na koju se kopira ulazni podatak) kao desni operand.
2.6 Komentari
Komentar je dio opisnog teksta koji objanjava neke aspekte programa. Kompajler u potpunosti
ignorie komentare u programu, tako da je jedina svrha koju komentar ima, da pomognu onome koji
e itati program. C++ daje dvije mogunosti pisanja komentara:
x Bilo ta napisano nakon //, pa do kraja date linije smatra se komentarom
x Bilo ta napisano izmeu /* i */ smatra se komentarom.
1
*1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;
/* Ovaj program racina ukupnu platu radnika, koja se zasniva na ukupnom broju
radnih sati i satnici. */
int main (void)
{
int
radniDani = 22;
float
radniSati = 7.5;
float
satnica = 33.50;
float
plata;
//
//
//
//
Jasno da je da se prvi primjer moe koristiti za komentar jedne i samo jedne linije (ili dijela jedne
linije), dok se posljednjim moe komentarisati tekts upisan u vie linija.
2.7 Imena
Programski jezici koriste imena kako bi se oznaile razliite cjeline koje ine program. Osim imena
varijabli, ovdje spadaju i imena funkcija, tipova, te makroa. C++ postavlja sljedea pravila za
6
continue
default
delete
do
double
else
enum
extern
float
for
friend
goto
if
inline
int
long
new
operator
private
protected
public
register
return
short
signed
sizeof
static
struct
switch
template
this
throw
try
typedef
union
unsigned
virtual
void
volatile
while
2.11 Stringovi
String je uzastopni niz karaktera koji se zavravaju nultim karakterom. Tako je, barem, bilo u jeziku
C. Ipak, u C++ uveden je novi tip podataka koji se naziva C++ string klasa, pa je mogue na mnogo
prirodniji nain manipulisati varijablama sa vie karaktera. Sljedei primjer pokazuje njenu
upotrebu.
#include <iostream>
using namespace std;
main()
{
char ch;
do {
cout << "Pritisnite K ili k za kraj, a bilo koju tipku za
nastavak \n";
cin >> ch;
if (ch != 'K' && ch != 'k')
cout << "Zelite nastaviti?\n";
else
cout << "Kraj programa";
} while (ch != 'K' && ch != 'k');
}
Ovaj primjer dobro funkcionie jer zavrava program ako se unese "k" ili "K", odnosno program se
nastavlja ako se unese bilo koji drugi karakter. Problem nastaje ako korisnik programa pritisne
samo tipku ENTER. U tom sluaju objekat "cin" oekuje da se unese neka vrijednost, pa tek onda
pritisne ENTER.
3. Operatori
Ovo poglavlje obrauje ugraene C++ operatore koji se koriste za stvaranje izraza, pri emu izraz
predstavlja bilo kakav proraun koji daje neku vrijednost. C++ nudi operatore za izvravanje
aritmetikih, ralacijskih, logikih, bitwise i uslovnih izraza. Takoer nudi veoma korisne popratne
efekte (side-effect) kao to su pridruivanje, inkrement i dekrement.
3.1 Aritmetiki operatori
C++ nudi pet osnovnih operatora, koji su sumirani u Tabeli II.3.1.
8
+
*
/
%
Ime
Sabiranje
Oduzimanje
Mnoenje
Dijeljenje
Ostatak pri dijeljenju
Primjer
12 + 4.9
3.98 - 4
2 * 3.4
9 / 2.0
13 % 3
//
//
//
//
//
daje
daje
daje
daje
daje
16.9
-0.02
6.8
4.5
1
Osim ostatka pri djeljenju (%) svi aritmetiki operatori prihvataju mijeanje cijelih i realnih brojeva.
Openito, ako su oba operanda cijeli brojevi, i rezultat je cijeli broj. Meutim, ako je jedan od
operanada realan, onda je i rezulat realan (tipa double).
Kada su oba operanda pri dijeljenju cijeli brojevi, rezultat je takoer cijeli broj (tzv. cjelobrojno
dijeljenje). U tom sluaju rezultat se zaokruuje na donju vrijednost, tj.
9 / 2
-9 / 2
// daje 4, a ne 4.5!
// daje -5,a ne -4.5!
cijena = 100;
volumen = 80;
jedinicnaCijena = cijena / (double) volumen;
// daje 1.25
Operator % daje ostatak pri dijeljenju dva cijela broja (oba operanda moraju biti cijeli brojevi), npr.
13%3 daje 1
==
!=
<
<=
>
>=
Ime
Jednakost
Nejednakost
Manje od
Manje ili jednako
Vee od
Vee ili jednako
Primjer
5 == 5
5 != 5
5 < 5.5
5 <= 5
5 > 5.5
6.3 >= 5
//
//
//
//
//
//
daje
daje
daje
daje
daje
daje
1
0
1
1
0
1
Treba zapamtiti da se operatori <= i >= mogu korisiti samo u tom obliku, a da =< i => ne znae nita
u ovom kontekstu.
Operandi nekog relacijskog operatora moraju biti brojevi. No, i karakteri su ispravni operandi poto
predstavljaju brojnu vrijednost (sjetimo se ASCII tabele).
Relacijski operaotri se ne smiju korisiti ze poreenje stringova, poto se u tom sluaju porede
njihove adrese, a ne sadraj. U tom sluaju, rezlutat je neodreen. Ipak, postoje funkcije koje mogu
porediti i leksikografsku razliku dva stringa.
9
!
&&
||
Ime
Logika negacija
Logiko i
Logiko ili
Primjer
!(5 == 5)
5 < 6 && 6 < 6
5 < 6 || 6 < 5
// daje 0
// daje 1
// daje 1
Logika negacija je unarni opearator, tj. ima samo jedan operand kojem daje negativnu vrijednost.
3.4 Inkrementalni i dekrementalni operatori
Takozvani auto inkremetalni (++) i auto dekrementalni (--) operatori obezbijeuju prigodan nain
za poveavanje, odnosno smanjivanje brojne varijable za 1. Upotreba ovih operatora je sumirana u
Tabeli II.3.4., pri emu se predpostavlja da je
int k = 5;
++
++
---
Ime
Inkrement (prefiks)
Inkrement (postfiks)
Dekrement (prefiks)
Dekrement (postfiks)
Primjer
++k
k++
--k
k--
+
+
+
+
10
10
10
10
//
//
//
//
daje
daje
daje
daje
16
15
14
15
Kao to se vidi, oba operatora se mogu korisiti u prefiksnom ili postfiksnom obliku. Razlika je
velika, jer kada se operator koristi u prefiksnom obliku prvo se primijenjuje operator, a onda se u
izrazu koristi rezultat. Kada se koristi u postfiksnom obliku, prvo se rauna izraz, a onda se
primijenjuje operator.
Oba operatora se mogu primijeniti kako na cjelobrojne, tako i na realne brojeve, iako se ova
karakteristika veoma rijetko koristi na realnim brojevima.
3.5 Operatori pridruivanja
Operator pridruivanja se koristi za pohranjivanje vrijednosti na neku memorijsku lokaciju (koja je
obino pridruena nekoj varijabli). Lijevi operand operatora treba biti neka lijeva_vrijednost,
dok desni operand moe biti proizvoljni izraz. Desni operand se izrauna i pridrui lijevoj strani. Pri
tome lijeva_vrijednost predstavlja bilo ta to zauzima neku memorijsku lokaciju na koju se
moe pohraniti neka veliina, moe biti varijabla, te zasnovana na pointerima i referencama.
Operator pridruivanja moe imati mnogo varijanti, koje se dobijaju njegovim kombinovanjem sa
aritmetikim i bitwise operatorima. Ove varijante su date u sljedeoj tabeli.
10
=
+=
-=
*=
/=
%=
Primjer
Ekivalentno sa
n
n
n
n
n
n
n
n
n
n
n
= 25
+= 25
-= 25
*= 25
/= 25
%= 25
=
=
=
=
=
n
n
n
n
n
+
*
/
%
25
25
25
25
25
Kako operator pridruivanja sam po sebi predstavlja izraz ija se vrijednost pohranjuje u lijevi
operand, on se moe korisititi kao desni operand za narednu operaciju pridruivanja, odnosno moe
se napisati:
int m, n, p;
m = n = p = 100;
m = (n = p = 100) + 2;
// znai: n = (m = (p = 100));
// znai: m = (n = (p = 100)) + 2;
m = 100;
m += n = p = 10;
// znai: m = m + (n = p = 10);
ili
Postoji jo nekoliko vrsta operatora (npr. zarez operator, sizeof operator), ali o njema nee biti rijei
u ovom kursu.
4 Naredbe
Ovo poglavlje opisuje razne oblike C++ naredbi koje slue za pisanje programa. Kao i veina
ostalih programskih jezika, C++ nudi razliite vrste naredbi koje se koriste u razliite svrhe. Tako se
deklaracione naredbe koriste za definisanje varijabli, naredbe pridruivanja za jednostavne
proraune, itd. U narednom teksu bie objanjene neke od njih.
11
//
//
//
//
deklaraciona naredba
naredba sa popratnim efektom
deklaraciona naredba
beskorisna naredba
Posljednji primjer pokazuje beskorisnu naredbu, jer nema nikakvih popratnih efekata.
Najjednostavniji oblik naredbe je linija koja sadri samo taka-zarez, tzv. null-naredba. No, i
ovakva naredba ponekad ima smisla, to e se pokazati u kasnijem tekstu.
Mnogostruke naredbe se mogu kombinovati u sloene naredbe kada se grupiu izmeu velikih
zagrada ({}), kao na primjer
{ int min, i = 10, j = 20;
min = (i < j ? i : j);
cout << min << '\n';
}
Ovakve naredbe su korisne iz dva razloga: a) omoguuju da se mnogostruka naredba postavi tamo
gdje bi inae mogla da se postavi samo jedna, i b) omoguuju da se u program uvede scope (
prostor). Scope predstavlja dio programa unutar kojeg varijabla ostaje definisana. Izvan scope-a ona
to vie to nije. Ovo je veoma vana osobina o kojoj e vie biti rijei kada se budu objanjavale
funkcije.
4.2 Naredba if
Ponekad je poeljno da se izvri odreena naredba koja zavisi od ispunjenja nekog uslova. Upravo
tu mogunost prua if naredba, iji je opti oblik:
if (izraz)
naredba;
Prvo se izvrava izraz, i ako je rezultat razliit od nule izvrava se naredba. U suprotnom, nita se
ne deava.
Na primjer, ako bismo eljeli provjeriti da li je pri djeljenju djelilac razliit od nule, imali bismo:
if (djelilac != 0)
Kolicnik=djelitelj/djelilac;
Da bismo izvrili vie naredbi koje ovisi o nekom istom uslovu, koristimo sloenu naredbu, tj. sve
naredbe stavljamo izmeu zagrada.
Varijanta if naredbe koja omoguuje da se specificiraju dvije alternativne naredbe, jedna koja se
izvrava kada je uslov ispunjen i druga kada nije, se naziva if-else naredba i ima oblik:
if (izraz)
naredba1;
else
naredba2;
12
Pored prethodno navedenih varijanti, postoji i ugnijedena if naredba, u kojoj se javljaju vie od
dvije alternative. Primjer takve varijente je:
if (callHour > 6) {
if (duzinaPoziva <= 5)
cijena = duzinaPoziva * tarifa1;
else
cijena = 5 * tarifa + (duzinaPoziva - 5) * tarifa2;
} else
cijena = osnovnaCijena;
Prvo se rauna izraz (koji se naziva switch tag), a zatim se rezultat poredi sa svakom od
numerikih konstanti (koje se nazivaju labele), po redu kako se javljaju, dok se ne poklopi sa
jednom od komponenti. Nakon toga se izvravaju naredbe koje slijede. Izvravanje se izvodi sve
dok se ne naie na naredbu break ili dok se ne izvre sve naknadne naredbe. Posljednji sluaj
(default) moe, a i ne mora da se koristi, i pokree se ako nijedna od prethodnih konstanti nije
zadovoljena.
Klasini primjer ocijenjivanja nekog rada na osnovu osvojenih bodova dat je u daljem teksu:
13
Prvo se izraunava izraz (naziva se i uslov petlje). Ako je rezultat razliit on nule tada se izvrava
naredba (naziva se i tijelo petlje) i cijeli proces se ponavlja. U suprotnom, proces se zaustavlja.
Na primjer, ako bismo eljeli izraunati zbir svih brojeva od 1 do n, upotreba while naredbe bi
izgledala kao:
i = 1;
sum = 0;
while (i <= n)
sum += i++;
Interesantno je da nije neuobiajeno za while naredbu da ima prazno tijelo petlje, tj. null-naredbu.
Takav primjer je problem nalaenja najveeg neparnog faktora nekog broja
while (n % 2 == 0 && n /= 2)
;
Ovdje uslov petlje izvrava sve neophodne kalkulacije, tako da nema potrebe za tijelom.
4.5 Naredba do
Naredba do (naziva se i do petlja) je slina naredbi while, osim to se prvo izvrava tijelo petlje, a
zatim se provjerava uslov. Opti oblik naredbe je:
14
Prvo se izvrava naredba, a zatim provjerava izraz. Ako je izraz razliit od nule cijeli proces se
ponavlja. U suprotnom, petlja se zaustavlja.
do petlja se manje koristi nego while petlja. Obino se koristi kada se tijelo petlje mora izvriti
najmanje jedanput bez obzira na ispunjenje uslova. Takav primjer je ponovljeno unoenje nekog
broja i izraunavanje njegovog kvadrata dok se ne unese 0:
do {
cin >> n;
cout << n * n << '\n';
} while (n != 0);
Za razliku od while petlje, do petlja se nikada ne koristi sa praznim tijelom prvenstveno zbog
jasnoe.
4.6 Naredba for
Naredba for (for petlja) je slina naredbi while, ali ima dvije dodatne komponente: izraz koji se
izraunava samo jednom prije svega, i izraz koji se izraunava jednom na kraju svake iteracije.
Opti oblik naredbe for je:
for (izraz1; izraz2; izraz3)
naredba;
Prvo se izraunava izraz1. Svakim prolazom proz petlju se izraunava izraz2. Ako je rezultat
razliit od nule izraunava se izraz3. U suprotnom petlja se zaustavlja. Oblik while petlje koja je
ekvivalenta do petlji je:
izraz1;
while (izraz2) {
naredba;
izraz3;
}
for petlja se najee koristi u situacijama kada se neka promjenljiva poveava ili smanjuje za
neku veliinu u svakoj iteraciji, odnosno kada je broj iteracija unaprijed poznat. Sljedei primjer
rauna zbir svih brojeva od 1 do n:
sum = 0;
for (i = 1; i <= n; ++i)
sum += i;
Bilo koja od komponenti u petlji moe biti prazna. Na primjer, ako se uklone prvi i trei izraz, onda
do petlja lii na while petlju:
15
// beskonana petlja
bilo-sta;
Poto petlje predstavljaju naredbe, mogu se pojaviti unutar drugih petlji (tzv. ugnijedene petlje).
Na primjer:
for (int i = 1; i <= 3; ++i)
for (int j = 1; j <= 3; ++j)
cout << '(' << i << ',' << j << ")\n";
5 Funkcije
Ovo poglavlje opisuje funkcije, koje definie korisnik, kao jedan od glavnih graevinskih blokova u
C++ programiranju. Funkcije obezbijeuju prikladan nain upakivanja nekog numerikog recepta,
koji se moe koristiti koliko god je to puta potrebno.
5.1 Definicija funkcije
Definicija funkcije se sastoji od dva glavna dijela: zaglavlja ili interfejsa, i tijela funkcije. Interfejs
(neki ga nazivaju i prototip) definie kako se funkcija moe koristiti. On se sastoji od tri dijela:
x Imena. Ovo je, u stvari, jedinstveni identifikator.
x Parametara (ili potpisa funkcije). Ovo je niz od nula ili vie identifikatora nekog tipa koji
se koriste za proslijeivanje vrijednosti u i iz funkcije.
x Tipa funkcije. Ovo specificira tip vrijednosti koji funkcija vraa. Funkcija koja ne vraa
nijednu vrijednost bi trebala da ima tip void.
Tijelo funkcije sadri raunske korake (naredbe) koji ine neku funkciju.
Koritenje funkcije se izvodi njenim pozivanjem. Poziv funkcije se sastoji od imena funkcije,
praenim zagradama za pozivanje (). Unutar ovih zagrada se pojavljuje nula ili vie argumenata
koji se odvajaju zarezom. Broj argumenata bi trebao odgovarati broju parametara funkcije. Svaki
argument je izraz iji tip bi trebao odgovarati tipu odgovarajueg parametra u interfejsu funkcije.
Kada se izvrava poziv funkcije, prvo se raunaju argumenti i njihove rezultujue vrijednosti se
pridruuju odgovarajuim parametrima. Nakon toga se izvrava tijelo funkcije. Na kraju, funkcija
vraa vrijednost (ako ista postoji) pozivu.
Sljedei primjer ilustrativno pokazuje definiciju jednostavne funkcije koja izraunava vrijednost
stepen cijelog broja na neki cijeli broj.
16
Linija 1 definie interfejs funkcije. Ona poinje tipom funkcije koji se vraa (u ovom sluaju int).
Nakon toga je dato ime funkcije (Stepen), a zatim njena lista parametara. Funkcija Stepen ima dva
parametra (baza i eksponent) koji su tipa int. Sintaksa parametara je slina sintaksi definisanja
varijabli, tj. nakon tipa daje se ime parametra. Meutim, nije mogue nakon tipa dati niz parametara
odvojenih zarezom, kao u
int Stepen (int baza, eksponent)
// Ovo je pogreno!
Openito, funkcija se treba definisati prije nego se koristi. To se moe uraditi na vie naina, kao to
je pokazano u narednim primjerima.
Primjer 1.
#include <iostream>
using namespace std;
double Stepen (int baza, int eksponent)
{
double
rezultat = 1;
for (int i = 0; i < eksponent; ++i)
rezultat *= baza;
17
Primjer 2.
Treba napomenuti da se deklaracija funkcije sastoji od njenog prototipa, tako da je za deklarisanje
dovoljno ispisati samo njen prototip. Na taj nain, kompletna definicija funkcije se moe dati
kasnije, kao to je pokazano u narednom primjeru. Takoer je mogue izostaviti nazive parametara
u deklaraciji, ali to nije preporuljivo.
#include <iostream>
using namespace std;
double Stepen (int baza, int eksponent);
// deklarisanje funkcije
Primjer 3.
Radi preglednosti veoma je korisno sakupiti sve funkcije u posebne datoteke, i umjesto njihovog
definisanja u sklopu izvrne datoteke, treba samo proitati tu datoteku. Na primjer, ako je definicija
funkcije Stepen data u datoteci StepenInt.h, onda bi prehodni program bio:
#include <iostream>
#include StepenInt.h
using namespace std;
main ()
{
int a,b;
18
Ovdje treba paziti gdje se datoteka StepenInt.h nalazi. U primjeru koji je dat, ona se nalazi u
istom direktoriju kao i izvrna datoteka. U suprotnom, treba dati taan (relativni ili apsolutni)
poloaj (path) iste.
5.2 Parametri i argumenti
C++ podrava dva oblika parametara: vrijednost i referencu. Parametar po vrijednosti prima kopiju
vrijednosti argumenata koja im se prenosi. Kao posljedica toga, ako funkcija napravi bilo kakvu
promjenu na parametrima, ovo nee promijeniti vrijednosti argumenta. Na primjer,
#include <iostream>
using namespace std;
void Foo (int broj)
{
broj = 0;
cout << "broj = " << broj << '\n';
}
int main ()
{
int x = 10;
Foo(x);
cout << "x = " << x << '\n';
system("PAUSE");
return 0;
}
parametar broj u funkciji Foo je parametar po vrijednosti. On se ponaa kao lokalna varijabla u
funkciji. Kada se funkcija pozove i vrijednost x se prenese, varijabla broj primi kopiju vrijednosti
varijable x. Kao rezultat toga, iako varijabla num u funkciji mijenja vrijednost na 0, to nee utjecati
na varijablu x. Program e dati sljedei izlaz:
broj = 0;
x = 10;
Za razliku od parametra po vrijednosti, parametar po referenci prima argument koji se prenosi i sve
obavlja direktno na njemu. Bilo koja promjena parametra po referenci u samoj funkciji, direktno se
odnosi i na argument, tj. i on se mijenja. Da bismo definisali parametar po referenci, potrebno je
dodati simbol & iza tipa parametra u interfejsu funkcije, tj. u predhodnom primjeru interfejs funkcije
Foo bio bi
void Foo (int& num)
// globalna varijabla
// globalna funkcija
// globalna funkcija
Imamo tri razliita scope-a, od kojih svaki ima razliitu varijablu xyz.
Openito, ivotni vijek varijable je ogranien na njen scope. Tako, na primjer, globalne varijable
traju svo vrijeme izvrenja programa, dok se lokalne varijable kreiraju kada se ue u njihov scope, a
unitavaju kada se izlazi iz njihovog scope-a. Memorijski prostor za lokalne varijable je rezervisan
prije izrvrenja programa, dok je memorijski prostor za lokalne varijable alociran u hodu u toku
izvrenja programa.
Poto lokalni scope ponitava globalni, to lokalne varijable sa istim imenom kao globalne varijable
onemoguavaju pristup globalnim varijablama unutar lokalnog scope-a. Na primjer, u
int greska;
void Greska (int greska)
{
//...
}
globalna varijabla greska je nepristupana unutar funkcije Greska, poto je ponitena lokalnim
parametrom greska.
Ovaj problem se moe prevazii koritenjem unarnog operatora :: (unary scope operator), koji
globalni entitet uzima kao argument, kao u primjeru
20
U principu, sve rekurzivne funkcije se mogu napisati koristei iteracije. Naime, treba imati u vidu
da u sluaju velikog broja poziva funkcija (u primjeru faktorijela je to veliki broj n), dolazi do
zauzimanja velikog memorijskog prostora (tzv. runtime stack), pa je upotreba iteracija bolje
rjeenje. No, u nekim sluajevima elegantno i jednostavno rekurzivno rjeenje moe da bude bolja
opcija.
U sluaju faktorijela, iterativna opcija je bolja, pa bi funkcija imala oblik:
int Factorijel (unsigned int n)
{
int rezultat = 1;
while (n > 0) rezultat *= n--;
return rezultat;
}
21
rezultat = 1;
Stepen
sa
jednim
main (void)
{
double a,b;
cout << "Unesi bazu:";
cin >> a;
cout << "\nUnesi eksponent:";
cin >> b;
cout << a<<"^" <<b<<"= " << Stepen(a,b) << '\n';
cout << "2^" <<b<<"= " << Stepen(b) << '\n';
system("PAUSE");
}
Iz primjera se vidi da sve funkcije imaju isto ime, ali razliite tipove, a u drugom sluaju i razliit
broj parametara. Treba paziti da pri pozivu pojedinih funkcija svi tipovi argumenata odgovaraju
tipovima parametara (broj 2 je cijeli broj, dok je 2.0 realan!!!)
6. Polja
Ovo poglavlje objanjava polja i ilustruje njihovu upotrebu pri definisanju varijabli. Polje se sastoji
od niza objekata (nazivaju se i elementi niza), koji su istog tipa i zauzimaju neprekidan memorijski
prostor. Openito, samo polje ima simboliko ime, a ne njegovi elementi. Svaki elemenat je
identificiran njegovim indeksom, koji pokazuje poloaj nekog elementa u nizu. Broj elemenata u
nizu naziva se dimenzija polja. Dimenzija polja je fiksirana i prethodno odreena, i ne moe se
promijeniti u toku izvrenja programa.
Polja su pogodna za predstavljanje podataka koji se sastoje od mnogo slinih, individualnih
objekata. Primjeri za to su lista imena, tabela gradova i njihovih sezonskih temperatura.
6.1 Definisanje i inicializacija polja
Varijabla polje je definisana specificiranjem njegove dimenzije i tipa njegovih elemenata. Na
primjer, polje koje obuhvata 10 mjerenja visine (od kojih je svaka cijeli broj) moe se definisati na
sljedei nain:
int visina[10];
Pristup individualnim elementima nekog niza vri se indeksiranjem niza. Prvi elemenat niza uvijek
ima indeks 0. Na taj nain, visina[0] i visina[9] oznaavaju prvi i posljednji elemenat niza
22
Pokuaj pristupa nepostojeem elementu nekog niza (na primjer visina[-1] ili visina[10]) moe
uzrokovati ozbiljnu greku (tzv. runtime greka, ili greka 'indeks izvan granica).
Procesiranje bilo kojeg niza obino ukljuuje koritenje petlje, koja ide kroz niz od elementa do
elementa. Sljedei primjer pokazuje funkciju koja rauna srednju vrijednost nekog niza:
const int velicina = 3;
double Srednja (int broj[velicina])
{
double srednja = 0;
for (int i = 0; i < velicina; ++i)
srednja += broj[i];
return srednja/velicina;
}
Kao i kod ostalih varijabli, vrijednosti elemenata niza se mogu inicijalizirati. U tu svrhu koriste se
zagrade{}, izmeu kojih se specificira lista poetnih vrijednosti elemenata niza koje su odvojene
zarezom. Na primjer,
int broj[3] = {5, 10, 15};
definie niz broj, i inicijalizira tri elementa ovog niza na vrijednosti 5, 10, 15, respektivno. Ovo je
tzv. eksplicitno inicijaliziranje. Kada je broj vrijednosti u inicijalizatoru manji od dimenzije niza,
ostali elementi su inicijalizirani na nulu, kao u sluaju:
int broj[3] = {5, 10};
// broj[2] je inicijaliziran na 0
Kada se koristi potpuni inicijalizator (broj poetnih vrijednosti odgovara dimenziji niza), dimenzija
niza postaje suvina i moe se izostaviti, tj. broj elemenata je implicitan u inicijalizatoru (implicitna
inicijalizacija). U ovom sluaju inicijalizacija niza broj se moe izvriti i na sljedei nain:
int broj[] = {5, 10, 15}; // dimenzija nije potrebna
Jo jedan primjer u kojem se dimenzija niza moe izostaviti je kada je niz parametar u nekoj
funkciji. Na primjer, funkcija Average iz ranijeg primjera se moe napisati na bolji nain, ako se
postavi da dimenzija niza nije fiksirana na neku konstantnu vrijednost, nego se dodaje jo jedan
parametar, tj.
double Srednja (int broj[], int velicina)
{
double srednja = 0;
for (int i = 0; i < velicina; ++i)
srednja += broj[i];
return srednja/velicina;
}
23
Sljedei program predstavlja primjer pomou kojeg se unosi, sortira i ispisuje neki niz. Pri tome,
sortiranje je izvedeno od najveeg prema najmanjem elementu niza.
#include <iostream>
using namespace std;
int main ()
{
// DEKLARACIJA
int x[10];
int y[10];
int i, j, n;
// UNOSENJE
cout << "Unesite broj clanova polja: ";
cin >> n;
for (i = 0; i < n; i++)
{
cout << "Unesite clan br. " << i << ": ";
cin >> x[i];
y[i] = x[i];
}
// SORTIRANJE
for (i = 0; i < n-1; i++)
{
for (j = i+1; j < n; j++)
{
if (y[i] < y[j]) swap(y[i],y[j]);
}
}
// STAMPANJE
cout << "x:" << '\t' << "y:" << endl;
for (i = 0; i < n; i++)
{
cout << x[i] << '\t' << y[i] << endl;
}
}
24
str[] = "HELLO";
definie varijablu str kao niz est (6) karaktera: pet slova i prazan karakter (null-character).
Zavrni prazan karakter postavlja kompajler. Za razliku od toga,
char
Proljee
Ljeto
Jesen
Zima
26
24
28
34
32
38
22
19
25
17
13
20
godDobTemp[3][4];
Ovo je u memoriji predstavljeno kao neprekidan niz od 12 elemenata tipa cijeli broj. Programer,
meutim, moe to zamisliti kao tri reda od po 4 elementa u svakom, kao na Sl. II.6.1.
...
26
34
22
17
First row
24
32
Second row
Prvi red
19
Drugi red
13
28
38
25
20
...
Third row
Trei red
= {
17},
13},
20}
Poto se ovaj dvodimenzionalni niz mapira kao jednodimenzionalni niz od 12 elemenata, mogue je
koristiti i:
int godDobTemp[3][4] = {
25
Ipak, bolja opcija je koritenje ugnijedenog inicijalizatora, poto ne samo da je pregledniji, nego
daje i dodatne mogunosti. Na primjer, ako nam je samo prvi elemenat svakog reda razliit od nule,
a ostali su jednaki nuli, mogli bismo koristiti:
int godDobTemp[3][4] = {{26}, {24}, {28}};
= 3;
= 4;
int godDobTemp[redovi][kolone] = {
{26, 34, 22, 17},
{24, 32, 19, 13},
{28, 38, 25, 20}
};
int najvecaTemp (int temp[redovi][kolone])
{
int
najveca = 0;
for (int i = 0; i < redovi; ++i)
for (int j = 0; j < kolone; ++j)
if (temp[i][j] > najveca)
najveca = temp[i][j];
return najveca;
}
main ()
{
cout << najvecaTemp(godDobTemp) << "\n";
system("PAUSE");
}
Napomena: Treba paziti kako se inicijalizira vrijednost kontrolne varijable najveca u prethodnom
primjeru. Ovdje se koristila vrijednost nula. Meutim, najbolje bi bilo kada bi se inicijalizirala na
vrijednost prvog elementa niza, jer u sluaju svih negativnih elemenata niza, nula bi rezultat
funkcije bez obzira da li je lan niza ili ne.
7. Datoteke
Ovo poglavlje pokazuje kako se podaci dobiveni pokretanjem nekog programa mogu sauvati
njihovim pohranjivanjem na neku datoteku. S obzirom da uvanje podataka nema svrhu ako tim
podacima ne moemo da pristupamo, bie objanjeno i kako proitati podatke sa neke datoteke.
26
Za razliku od relativnog puta, apsolutni put predstavlja lokaciju koja zapoinje slovom drajva,
sadrei sve direktorije i poddirektorije dok se ne doe do datoteke. Na primjer, ako je datoteka
studenti.txt u direktoriju Pedagoski, a ovaj je podirektorij direktorija UNZE, a sve se nalazi na
tvrdom disku sa slovom C, onda bi se datoteka otvorila na sljedei nain:
ofstream izlaz;
izlaz.open("C:\\MFZE\\Pedagoski\\studenti.txt");
Vidimo da se u tom sluaju koriste po dva znaka \\, jer samo jedan izmeu navodnika predstavlja
escape-sekvencu (poglavlje II.2.10).
Bez obzira da li koristimo relativni ili apsolutni put, argument za funkciju open ne mora da bude
neko ime (rije), nego i (string) varijabla, kao to je to u sljedeem primjeru:
ofstream izlaz;
char imeDatoteke[80];
cout << "Unesite ime datoteke: ";
cin >> imeDatoteke;
izlaz.open(imeDatoteke);
Vano je zapamtiti da je koritenje relativnog puta bolja opcija, jer se moe desiti da neki direktorij
u apsolutnom putu ne postoji, naroito ako se program koristi na nekom drugom raunaru (sa
drugaijim direktorijima).
Koritenje drugog argumenta u funkciji open definie modul u kojem se datoteka treba otvoriti.
Neke od opcija (tzv. file mode flag), koje se mogu koristiti date su u Tabeli II.7.1.
Tabela II.7.1
Opcija (file mode flag)
ios::app
ios::binary
ios::in
ios::out
Opis
Postojei sadraj datoteke je ouvan i sav izlaz se ispisuje na
kraj datoteke.
Informacija se na datoteku pie u binarnom obliku.
Infomacija e se itati sa datoteke. Datoteka se ne kreira ako ne
postoji.
Informacija e se zapisati u datoteku. Postojei sadraj datoteke
je poniten.
28
Meutim, ako se za otvaranje datoteke za pisanje koristi fstream objekat, treba se dodati jo jedan
argument, tj. opcija za pisanje (ios::in). U ovom sluaju bi bilo:
fstream izaz;
izlaz.open("studenti.txt", ios::out);
U oba primjera koriten je tzv. bitwise operator (|), koji ima isto znaenje kao logiki operator ||
(ili).
Provjera da li je datoteka otvorena
Prije nego ponemo itati podatke sa neke datoteke, korisno je znati da li ona uopte postoji.
Provjera se moe izvriti na dva naina. Ako se datoteka ne moe otvoriti za itanje, onda je: (i)
vrijednost ifstream objekta jednaka NULL (nula), (ii) vrijednost funkcije fail objekta ifstream je
true(1). Sljedei primjer ilustruje koritenje oba naina.
#include <fstream>
#include <iostream>
using namespace std;
int main (){
ifstream ulaz;
ulaz.open("studenti.txt");
cout << "(ulaz) = " << ulaz << endl;
cout << "(ulaz.fail()) = " << ulaz.fail() << endl;
return 0;
}
Pri emu 0x22fed4 predstavlja memorijsku lokaciju (adresu) ifstream varijable ulaz.
Za razliku od ifstream objekta, ofstream objekat koji pokuava otvoriti datoteku koja ne jo
postoji nije NULL, a njegova fail funkcija ima vrijednost false(0). To je zbog toga to operativni
sistem kreira datoteku, ako ona ne postoji. Ipak, i u ovom sluaju je korisno provjeriti da li datoteka
postoji. Naime, ako datoteka postoji, ali ima osobinu read-only, dobiemo negativan odgovor o
njenom postojanju (vrijednost iostream objekta je NULL, a funkcije fail je true, tj. 1).
7.2.2 Zatvaranje datoteka
Svaka otvorena datoteka se treba zatvoriti prije nego se napusti program. To je zbog toga to svaka
otvorena datoteka zahtijeva sistemske resurse. Osim toga, neki operativni sistemi imaju ogranienje
na broj otvorenih datoteka kojima se ne 'manipulie'.
Zatvranje datoteka se vri pomou funkcije close. Sljedei primjeri pokazuju njenu upotrebu pri
zatvaranju datoteka za pisanje i itanje:
30
Upotrebom drugog argumenta pri otvaranju datoteke za pisanje (ios::app) moemo dodavati
sadraj na postojeu datoteku kao to to pokazuje sljedei primjer. U ovom primjeru zapisivanje se
prekida nakon unosa znakova ***.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main ()
{
string x;
ofstream izlaz;
izlaz.open("podaci.txt", ios::app);
while (x != "***")
{
cout << "Unesite neki tekst (za kraj unesite ***):" << endl;
cin >> x;
izlaz << x << endl;
}
izlaz.close();
}
31
Meutim, vidimo da je u ovom sluaju koritena C++ string klasa umjesto varijable podaci tipa
char. Na taj nain se pomou funkcije getline moe ispisati ime razreda koje ima vie od jedne
rijei.
Prethodni primjer se moe napisati i na programerski adekvatniji nain upotrebom funkcija za
itanje i pisanje na datoteku.
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
bool upisiDatoteku (ofstream&, char*);
bool citajDatoteku (ifstream&, char*);
int main (){
string podaci;
bool status;
ofstream izlaz;
status = upisiDatoteku(izlaz, "studenti.txt");
if (!status)
{
cout
<<
"Datoteka
za
ispisivanje
otvoriti\n";
cout << "Program se zavrsava\n";
system("PAUSE");
se
ne
moze
32
return 0;
}
else {
cout << "Pisanje u datoteku" << endl;
cout << "==================" << endl;
cout << "Upisite razred: ";
getline(cin, podaci);
izlaz << podaci<< endl;
cout << "Unesite broj studenata: ";
cin >> podaci;
cin.ignore();
izlaz << podaci<< endl;
izlaz.close();
}
ifstream ulaz;
status = citajDatoteku(ulaz, "studenti.txt");
if (!status) {
cout << "Datoteka za citanje se ne moze otvoriti\n";
cout << "Program se zavrsava\n";
system("PAUSE");
return 0;
}
else {
cout << "Citanje se datoteke" << endl;
cout << "===================" << endl;
getline(ulaz, podaci);
while(!ulaz.fail()) {
cout << podaci << endl;
getline(ulaz, podaci);
}
ulaz.close();
}
system("PAUSE");
return 0;
}
bool upisiDatoteku (ofstream& datoteka, char* strDatoteka)
{
datoteka.open(strDatoteka);
if (datoteka.fail())
return false;
else return true;
}
bool citajDatoteku (ifstream& datoteka, char* strDatoteka)
{
datoteka.open(strDatoteka);
if (datoteka.fail())
return false;
else return true;
}
33