Professional Documents
Culture Documents
Scripta C++
Scripta C++
-sve varijable u programu se nalaze na nekoj lokaciji u memoriji. Npr, sljedee dvije linije:
float x;
x=3.5;
zahtijevaju od kompajlera da odvoji etiri bajta memorije za realnu varijablu x, a da zatim tu smjesti
vrijednost 3.5. Lokaciju u memoriji (adresa prvog bajta) na kojoj je varijabla x smejtena je mogue
dobiti stavljajui operator & (adresa od) ispred naziva varijable. Tako &x oznaava adresu varijable x.
-pokaziva (pointer) je varijabla ija je vrijednost memorijska adresa neke druge varijable (odreenog tipa). Sintaksa
definisanja je:
<ime_tipa> * <naziv_pokazivake_varijable>;
-sadraj adrese na koju pointer pokazuje se moe dobiti pomou operatora * (tzv. derefenciranje pointera).
Primjer:
float x, y;
float* px;
x = 6.5;
px = &x;
y=*px;
Ovaj kod definie float varijable x i y, te pokaziva na float varijable px. Zatim u x stavlja vrijednost
6.5, a u px adresu varijable x. Potom u y stavlja sadraj adrese na koju px pokazuje (to je u ovom
sluaju 6.5).
-C omoguava vrenje aritmetikih operacija nad pointerima (+, -, ++, --). Meutim, treba shvatiti kako "jedinica
mjere" pri ovim raunanjima zavisi od tipa pointera. Npr, koristei prethodni primjer, izraz px+1 pokazuje ne na
naredni bajt, ve na prvu lokaciju iza varijable x (tj. 4 bajta iza lokacije na koju pokazuje px). Kada bi px bio tipa
double (ili char), tada bi px+1 pokazivao 8 bajtova (odnosno 1 bajt) iza lokacije na koju pokazuje px.
99
px
100
101
102
103
px+1 104
105
106
107
108
Primjer:
char* pc;
float* px;
float x;
x = 6.5;
px = &x;
pc = (char*) px;
Sada bi pc+1 pokazivao na lokaciju 101, zato to je pc pokaziva tipa char.
-treba shvatiti da, mada je vrijednost pointera obino neki veliki cijeli broj (koji se moe npr. odtampati sa printf),
pointeri i cjelobrojne promjenjive su dva potpuno razliita tipa podataka
-elementi nizova u C-u se smjetaju u susjedne memorijske lokacije. C tretira ime niza kao pointer na prvi element.
Npr, ako definiemo niz:
int a[10];
sada je a[0] isto to i *a, a[1] isto to i *(a+1) itd.
-pointeri omoguavaju dinamiku alokaciju memorije. Ovo je pogodno npr. ako elimo koristiti niz iju veliinu
neemo znati do vremena izvravanja programa. Za dinamiku alokaciju se koriste funkcije malloc i free,
slino kao new i dispose u Pascal-u. malloc alocira onoliko prostora koliko mu se to navede u argumentu.
-parametri funkcija u C-u se uvijek prenose po vrijednost. Prenos preko referenci se vri pomou pointera.
-mogue je definisati i pointer na pointer:
int **dupli;
pointer na pointer varijabla
pointer
PRIMJER 1:
#include <stdio.h>
int main(){
PRIMJER 2:
#include <stdio.h>
int main()
{
scanf("%d", &dniz);
niz=(int *) malloc(dniz*sizeof(int));
for(i=0; i<dniz; i++)
scanf("%d", (niz+i));
Objanjenje: funkcija sizeof(tip) vraa broj bajtova koji u memoriji zauzima jedan podatak odgovarajueg
tipa. Npr, sizeof(int) vraa vrijednost 2. Funkcija malloc vraa pointer tipa void, koji se ne moe
dereferencirati, tj. pristupiti podatku na koji on pokazuje, poto se ne zna njegov tip. Zato se mora izvriti eksplicitna
konverzija u int pokaziva.
PRIMJER 3:
#include <stdio.h>
temp = a;
a = b;
b = temp;
printf("Iz funkcije zamjena1:");
printf("a = %d, b = %d\n", a, b);
}
temp = *a;
*a = *b;
*b = temp;
printf("Iz funkcije zamjena2: ");
printf("a = %d, b = %d\n", *a, *b);
}
int main()
{
int a, b;
a = 5;
b = 7;
printf("Iz funkcije main: a = %d, b = %d\n", a, b);
zamjena1(a, b);
printf("Iz funkcije main: a = %d, b = %d\n", a, b);
zamjena2(&a, &b);
printf("Iz funkcije main: a = %d, b = %d\n", a, b);
}
Objanjenje: C++ predaje argumente funkcijama pomou vrijednosti. Dakle, nema naina da se direktno promijeni
varijabla u funkciji iz koje je druga funkcija pozvana. Tako, potprogram za sortiranje zamjenjuje dva neporedana
elementa funkcijom sa imenom swap. Nije dovoljno napisati
swap(a, b);
Zbog poziva preko vrijednosti, funkcija swap ne moe uticati na argumente a i b u funkciji koja ju je
pozvala. Ona samo zamjenjuje preslike (moda je ipak razumljivije rei kopije) varijabli a i b.
Da bi se dobio eljeni efekt, potrebno je pozivnim programom predati pokazivae do vrijednosti ovih
varijabli:
swap(&a, &b);
VEKTORI I ITERATORI
Vektor je objekat koji se koristi za skladitenje neogranieno mnogo objekata bilo kog tipa. Po tome je slian
dinamikoj listi, s tim to je obezbijeen pristup elementima po indeksu, dakle kao obian niz. Kada se kae
dinamiki, misli se da mu se veliina mijenja dinamiki, tj. da mu se memorija prazni automatski. Zbog ega je to
tako, to je ve neka druga pria, za vas trenutno nebitna.
moj_vektor[0] = 5;
int a = moj_vektor[5];
#include <vector>
using namespace std;
Kada predstavljate (deklariete, uvodite, pravite) objekat klase vektor, potrebno je da kaete kojeg tipa e biti objekti
u tom vektoru. Da ne bude zabune, kada se kae "vektor se koristi za skladitenje neogranieno mnogo objekata bilo
kog tipa", ne misli se da svaki element vektora moe biti proizvoljnog tipa, nego da moete napraviti vektor objekata
tipa "int", kao i vektor objekata tipa "double", ba kao i vektor objekata koji pripadaju proizvoljnoj klasi (vaoj ili
sistemskoj). To ukljuuje i da moete napraviti vektor objekata tipa vektor, kao i sve ostale akrobacije koje vam
padnu na pamet. Dakle, da se vratimo na temu. Kada predstavljate objekat klase vektor, potrebno je da kaete kojeg
tipa e biti objekti u tom vektoru. Deklaracija glasi:
vector<TIP> ime_vektora;
Umjesto rijei TIP unesite tip objekata koji e biti unutar vektora, npr. int, double, string, char *, MojaKlasa, itd.
vector<int> vektor_intova;
vector<char *> vektor_stringova;
vector<MojaKlasa> moj_vektor; //ovaj dio se odnosi na klase i za vas trenutno
nije bitan
Kada predstavite vektor na ovaj nain, on predstavlja prazan vektor, vektor bez ikakvih elemenata. Da biste ubacili
element odgovarajueg tipa u vektor, koristite njegovu metodu push_back(), na sljedei nain:
vektor_intova.push_back( 5 );
vektor_intova.push_back( 10 );
vektor_intova.push_back( 1 );
vektor_intova.push_back( 3 );
Pri ubacivanju elemenata u vektor, koristei metodu push_back, svaki sljedei element ide na kraj vektora, i pri
tome se on KOPIRA u vektor, tj. element unutar vektora NEMA nikakve VEZE sa elementom koji ste zadali kao
argument metoda push_back(). Nijedna izmjena elementa unutar vektora se ne odraava na element koji ste zadali
kao argument metoda push_back(). To je veoma vano za zapamtiti.
Nakon to ste kreirali vektor i ubacili u njega elemente koji vam u njemu trebaju, velika je vjerovatnoa da e vam
kasnije trebati da te elemente proitate ili izmijenite, tj. da im pristupite. Vektor, kao to je to reeno na poetku
ovog poglavlja, omoguava indeksni pristup svojim elementima, dakle moete da pristupite elementu vektora na
osnovu njegovog rednog broja. Prvi element koji ste ubacili u vektor ima redni broj 0, drugi koji ste ubacili ima
redni broj 1, ..., n-ti element koji ste ubacili ima redni broj n-1.
cout << vektor_intova[0] << endl; // 5
cout << vektor_intova[1] << endl; // 10
cout << vektor_intova[2] << endl; // 1
cout << vektor_intova[3] << endl; // 3
// ili:
for ( int i = 0; i < 4; i++ )
cout << vektor_intova[i] << endl; // isti rezultat
Ukoliko niste sigurni koliko vektor ima tano elemenata, moete koristiti njegovu ugraenu metodu size():
Da biste ispraznili vektor, koristite njegovu metodu clear(), a da biste provjerili da li je ve prazan, koristite njegovu
metodu empty():
vektor_intova.clear();
moj_vektor.clear();
if ( ! vektor_intova.empty() || ! moj_vektor.empty() )
abort();
Naravno, postoji nain da se objekat ubaci u vektor na odgovarajue mjesto, kao i nain da izbacite pojedinane
elemente iz vektora, ali ta pria slijedi tek nakon zavrene prie o iteratorima.
ITERATORI
Iteratori predstavljaju posebnu vrstu objekata koji po mnogo emu lie na pokazivae. Prisutni su u skoro svim
klasama Standardne Biblioteke, sa malim meusobnim razlikama. Potreba za iteratorima je viestruka. Medjutim,
ono to je nama najbitnije je to da mnogo metoda klase vektor po svojoj definiciji trai iterator kao argument. Ono
to je nama takoe bitno je da se asistenti obraduju kad vide da koristite iteratore. Prema tome, hajde da vidimo kako
deklarisati iterator:
vector<TIP>::iterator i, j, k;
Nakon ovakve deklaracije, promjenjive i, j i k su spremne da pokazuju na objekte tipa TIP, a koji se nalaze u okviru
vektora takvih objekata. Nakon deklaracije, iteratori jo uvijek ne pokazuju ni na jedan objekat, pa ih je potrebno
inicijalizovati. Metoda begin() klase vektor, vraa iterator na prvi element vektora. Metoda end() klase vektor
vraa iterator na ELEMENT KOJI SE NALAZI IZA POSLJEDNJEG ELEMENTA VEKTORA. Ovo je jako bitno
za upamtiti. Dakle, metoda end() NE vraa iterator na posljednji element vektora, nego na jedan iza njega. Ovaj
element naravno ne postoji u vektoru, ali vidjeemo poslije zato je bitno da to bude ba tako.
vector<int>::iterator i, e;
i = vektor_intova.begin();
e = vektor_intova.end();
Kao to je ve reeno, iteratori po mnogo emu lie na pokazivae, ak moete slobodno da gledate ovako: iteratori
se razlikuju od pokazivaa samo po deklaraciji; u svim ostalim situacijama se koriste kao pokazivai. To ukljuuje
sljedee:
vector<int> vektor_intova;
vector<int>::iterator i, e;
vektor_intova.push_back( 5 );
vektor_intova.push_back( 10 );
vektor_intova.push_back( 1 );
vektor_intova.push_back( 3 );
e = vektor_intova.end();
for ( i = vektor_intova.begin(); i != e; i++ )
cout << (*i) << endl;
i = vektor_intova.begin() + 2; // pokazuje na treci (tj. drugi) element
vektora, 1
i = vektor_intova.begin() + 3; // pokazuje na cetvrti (tj. treci) element
vektora, 3
Nakon to smo ovako predstavili iteratore, i ako ste to dosad dobro shvatili, moemo prei na onu priu koja se
ticala izbacivanja proizvoljnog elementa iz vektora, ili ubacivanje objekta u vektor, tako da ne dodje na kraj nego na
proizvoljnu poziciju.
Da biste izbrisali (zapravo izbacili) element iz vektora, koristite njegovu metodu erase(). Ona po definiciji
prihvata iterator na element koji elite da izbacite. Ukoliko znate da je to npr. element sa rednim brojem 3, onda ga
izbacujete na slijedei nain:
vektor_intova.erase( vektor_intova.begin() + 3 );
// ili:
vector<int>::iterator i = vektor_intova.begin() + 3;
vektor_intova.erase( i );
...a ukoliko elite da izbacite element koji ima vrijednost npr. 10, onda to radite ovako:
Slino, ako elite da ubacite novi objekat u vektor ali tako da dodje na proizvoljno mjesto u vektoru, onda koristite
njegovu metodu insert(). Ona takoe prihvata iterator kao svoj argument, i taj iterator treba da predstavlja
element PRIJE kojega elite da ubacite novi element. Na taj nain, ukoliko elite da ubacite novi objekat tako da on
ide na poetak vektora, radite neto ovako:
...a ako elite da ga dodate na kraj (bez da koristite push_back()), onda ovako:
vektor_intova.insert( vektor_intova.end(), 100 );
Ako elite da novi objekat zauzme takvo mjesto da mu je redni broj 3, onda:
vektor_intova.insert( vektor_intova.begin() + 3 );
NAPOMENE
Ukoliko budete intenzivno koristili iteratore, sigurno ete doi u situaciju da imate konstantan vektor (ili
koji sadri konstantne elemente), a elite proitati njegov sadraj koritenjem iteratora. Tada vam
kompajler nee dozvoliti da to uinite na do sada opisan nain. Sve zapravo ostaje isto, samo to ete
morati koristiti const_iterator umjesto dosadanjeg iterator. Dakle, deklaracija e morati
glasiti ovako:
vector<int> vektor_intova;
for ( unsigned i = 0; i < vektor_intova.size(); i++ )
cout << vektor_intova[i] << endl;
// a NE:
for ( int i = 0; i < vektor_intova.size(); i++ )
cout << vektor_intova[i] << endl;
... jer e se i smanjivati do nule, a onda kada bude trebalo da postane jednako -1, budui da je tipa
unsigned, dobie neku bezveze pozitivnu vrijednost, i zapaete u beskonanu petlju, i mogue je da e
vam pui program.
1. Zadaci 1a i 1b.
Trivijalan primjer gdje vector moe obraditi milion lanova dok obian niz crashuje.
2. Zadatak 2.
Trivijalan primjer raunanja aritmetike sredine koristei vectore.
3. Zadatak 3.
Tu je prikazano korienje permutacija, tj funkcije next_permutation(pocetak, kraj);Na
poetku se uitavavaju brojevi u vektor, a nakon toga se ispisuju pomou iteratora. Funkcija kao
parametar uzima poetnu i krajnju vrijednost vektora koje su predstavljene iteratorima.