Professional Documents
Culture Documents
Akademska 2014/2015
Enil Paji
OSNOVE RAUNARSTVA
DODATAK:
Prefiksni, postfiksi i ostali operatori u C-u
(Dio 1)
Sadraj: (Kliknuti za navigaciju po dokumentu)
Tabela svih operatora u C-u ..............................................................................................
Tabela prioriteta i asocijativnosti operatora ......................................................................
Operatori +, , *, /, % ......................................................................................................
Primjer: razbijanja broja na cifre ...................................................................................
Postfiksni operatori ...........................................................................................................
Boni efekti u C-u .............................................................................................................
Prefiksni operatori .............................................................................................................
Ternarni (kondicionalni) operator ......................................................................................
Kombinovani operatori dodjele, zarez operator i operator unarni minus .........................
Konverzioni operator ........................................................................................................
2
4
5
6
7
9
10
12
15
17
*
/
%
+ (unarno)
(unarno)
++ (prefiks)
++ (postfiks)
-- (prefiks)
-- (postfiks)
&&
||
!
>
<
>=
<=
==
!=
~
&
|
^
>>
<<
+=
Znaenje
Sabiranje: a + b
Oduzimanje: a b
Mnoenje: a * b
Dijeljenje: a / b
Ostatak pri cjelobrojnom
dijeljenjeu modulo
dijeljenje: a % b
Unarni plus: +a
Unarni minus: -a
Prefiksno inkrementiranje:
++a
Postfiksno
inkrementiranje: a++
Prefiksno dekrementiranje:
--a
Postfiksno
dekrementiranje: a-Logiko I (engl. and):
a && b
Logiko ILI (enlg. or):
a || b
Logiko NE (engl. not): !a
Vee od: a > b
Manje od: a < b
Vee ili jednako od: a >= b
Manje ili jednako od:
a <= b
Jednako: a == b
Nije jednako: a != b
Binarno NE (not): ~a
Binarno I (and): a & b
Binarno ILI (or): a | b
Binarno iskljuivo ILI
(xor): a ^ b
Binarno desno shiftanje
Binarno lijevo shiftanje
-=
*=
/=
%=
&=
|=
^=
<<=
>>=
=
*
(dereferenciranje)
&
(adresni operator)
[]
->
.
,
?:
sizeof
(tip) - konverzije
Sjedinjeno sabiranje: a +=
b isto kao a = a + b
a -= b isto kao a = a b
a *= b isto kao a = a * b
a /= b isto kao a = a / b
a %= b isto kao a = a % b
Sjedinjeno binarno I: a &=
b isto kao a = a & b
Binarno ILI: a |= b isto kao
a=a|b
Binarno iskljuivo ILI
(xor): a ^= b isto kao a = a
^b
Binarno lijevo shiftanje: a
<<= b isto kao a = a << b
Binarno desno shiftanje: a
>>= b isto kao a = a >> b
Operator dodjele: a = b (a
postaje b)
Dereferenciranje
pokazivake varijable: *a
Uzimanje adrese varijable:
&a
Indeksiranje nizova:
a[broj]
Strukturno
derefenerciranje: a->b
Operator pristupa (kod
struktura): a.b
Zarez operator: a, b
Ternarni operator:
uslov ? tacno : netacno
Operator koji vraa
veliinu promjenljive
Pretvaranje tipova: (a) b
npr. (int) 4.54
1
(najvei)
4
5
6
7
8
9
10
11
12
13
14
15
(najmanji)
Operator
++
-[]
.
->
()
++
-+
!
~
(tip)
*
&
sizeof
*
/
%
+
>>
<<
<
<=
>
>=
==
!=
&
^
|
&&
||
?:
=
+=
-=
*=
/=
%=
<<=
>>=
&=
^=
|=
,
Asocijativnost
Lijevo
Desno
Lijevo
Lijevo
Lijevo
Lijevo
Lijevo
Lijevo
Lijevo
Lijevo
Lijevo
Lijevo
Desno
Desno
Lijevo
Binarni?
Ne
Ne
Da
Da
Ne
Da/Ne
Ne
Ne
Ne
Ne
Ne
Ne
Ne
Ne
Ne
Ne
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Ternarni
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Da
Stoga, treba posebno obratiti panju pri cjelobrojnom dijeljenju. I modulo dijeljenje sa nulom
isto tako stavlja program u nedefinisano stanje (najvjerovatnije e se desiti krahiranje
programa). Npr. int br = 1 % 0; e dovesti program u nedefinisano stanje.
Pokaimo i zanimljiv nain kako saznati cifre nekog broja koritenjem modulo operatora.
Modulo operator daje ostatak pri cjelobrojnom dijeljenju (npr. 10 % 3 = 1, gdje je 1 ostatak),
Primijetimo da smo posebno testirali da li je broj jednak nuli. To smo uradili zbog toga to
uslov u while petlji ispod ne bi bio taan i za broj nula ne bi bile ispisane cifre. Takoer smo
broj testirali na pozitivnost i ako nije pozitivan, pomnoili smo ga sa -1. Moe se sada rei a
ta da je uslov u while petlji bio broj != 0 umjesto broj > 0?. Tano, da je takav uslov
bio, program bi radio i ispisao negativne cifre (npr. za broj 137 bilo bi ispisano -7, -3 i -1,
svaki u novom redu, to je malo runo). Jo je vano da smo u while petlji broj dijelili sa 10,
kako bismo svaki put dobili novu cifru (npr 123 / 10 je 12, pa onda kada uradimo 12 % 10
dobiemo 2, tj. drugu cifru). A to dijeljenje je pomoglo da naa while petlja nije beskonana
(onda broj nikad ne bi bio manji od 0 da ga nismo smanjivali dijeljenjem). Takoer se moe
primijetiti da su cifre ispisane u obrnutom redoslijedu (to je, ako se analizira kd i logino).
Ovo se moe iskoristiti da cifre nekog broja obrnemo, tj. zamjenimo njihov redoslijed.
Kd iznad sadi naredbu koja sa sastoji samo od broj++; tj. jedne komande, pa se isti efekat
moe postii i konstrukcijama napisanim u komentaru. Napomenimo da ovo vrijedi jedino
ukoliko se postfiksni operator ne koristi u nekom izrazu.
int a = 10;
int b = a++;
printf ("a = %d, b = %d", a, b);
U isjeku iznad je koriten postfiksni operator u izrazu. Bie ispisano a = 11, b = 10. Znai,
u varijablu b smo prvo smjestili vrijednost varijable a, pa smo tek onda poveali vrijednost
varijable a za 1. Stoga se postfiksi operatori mogu itati kao daj mi vrijednost varijable, pa
onda izmijeni njenu vrijednost. Isjeak iznad funkcionalno je ekvivalentan sljedeem kdu:
int a = 10;
int b = a;
a++;
printf ("a = %d, b = %d", a, b);
Gdje se jasno vidi da smo u varijblu b smjestili vrijednost varijable a, pa smo tek onda
poveali varijablu a (u novoj naredbi). Pogledajmo i sljedei isjeak:
int a = 10;
printf ("a = %d\n", a++);
printf ("a = %d\n", a);
U drugoj liniji imamo bone efekte jer varijablu a mijenjamo vie od jednom (i ne zna se ta
e prvo biti izvreno, to nije definirano standardom i dato je kompajleru da izvri kako mu je
lake). Moda e kod vas ispisati a = 7, b = 5, ali to ne mora biti tako prilikom drugih
pokretanja i kompajliranja. Takoer, ne moraju izrazi ovako komplikovani biti da bi
izazvali bone efekte. Navedimo jedan prost primjer (koji se inae najee u literaturi koristi
kao primjer bonih efekata).
int a = 5;
a = a++;
Ovdje u jednoj liniji (izrazu) varijablu mijenjamo a dva puta i to prouzrokuje bone efekte.
Ni za operatore poput +, *, / i slino mi ne znamo koji od izraza e biti izvren prvi,
pogledajmo npr.
if (naredba1 == naredba2)
Mi ovdje ne znamo da li e biti prvo izvrena naredba1 ili naredba2, ali ono to sigurno
znamo jeste da e obje biti izvrene prije nego se meusobno porede. Jedini operatori za koje
sigurno znamo koja strana e prva biti izvrena, tj. imamo garanciju da e biti izvrena prvo
lijeva pa onda desna strana su operator logiko I (&&), logiko ILI (||) i zarez operator (,)
kojem je jedna od glavnih primjena i bila navedeno. Stoga ako imamo
if (naredba1 && naredba2);
if (naredba1 || naredba2);
int x = (naredba1, naredba2);
Ovo e ispisati Zdravo, OR-ovci 10 puta, svaki u novom redu. Iako na prvi pogled
programerima izgleda udno, ljudima koji se tek susreu sa programiranjem izgleda
prirodno i tumae uslov kao radi, dokle x tei ka nuli. Ovaj isjeak iznad je potpuno
ispravan i legalan, a izgleda udo zbog stavljanja razmaka na pogreno mjesto. Naime,
itljivije se moglo napisati ovako:
int x = 10;
while (x-- > 0)
printf ("Zdravo, OR-ovci\n");
Jer, izraz x++ je r-value, pa onda na njega ne moemo (ponovo) primijeniti operator ++. U
jeziku C++, za obine (ugraene ) tipove, vrijedi isto pravilo. Ali u C++-u, za razliku od C-a,
mi moemo definisati znaenje pojedinih operatora za nae tipove, pa moemo (mada ne
smisleno) napraviti da varijabla x, tipa kojeg smo mi definisali, ipak moe biti postfiskno vie
puta inkrementirana (npr. x++++++). Ovo nije preporuljivo raditi (jer e programske
konstrukcije biti neintuitivne), i ovdje je spomenuto samo radi kompletnosti.
Isjeak iznad e ispisati x = 11, y = 11. To je zbog zoga to je varijabla x prvo poveana za
1, pa onda dodijeljena varijabli y. Isjeak iznad je ekvivalentan sljedeem:
int x = 10;
x++; /* Isto kao '++x', kao i 'x += 1' tj. 'x = x + 1' */
int y = x;
printf ("x = %i, y = %i", x, y);
Sada nam je jasno zato je vrijednost obje varijable 11, U komentaru u isjeku iznad pie da
su ovi izrazi ekvivalentni, to je tano, ali jedino ukoliko se nalazi taj izraz sm (tj. kao jedna
naredba je).
Kao i postfiksni operatori inkrementiranja i dekrementiranja (++ i --), i prefiksni operatori
vraaju r-value objekat. Zbog toga, u jeziku C, sljedea konstrukcija nije ispravna i kompajler
e javiti greku.
int x = 10;
++++x;
10
11
Svi do sada opisani operatori su bili ili unarni ili binarni, tj. imali su jedan ili dva operanda
nad kojima operiraju. U ovom odjeljku e biti opisan operator koji radi sa tri operanda, tj.
ternarni operator.
Prije svega da napomenemo da ternarni operator (u literalnom znaenju) znai operator koji
operira nad tri operanda. Stoga je operator kojeg emo opisati malo neispravno zvati ternarni
(jer njih moe biti vie); ispravan njegov naziv je kondicionalni operator. Poto u jeziku C
postoji samo jedan ternarni operator, to se vrlo esto za kondicionalni operator koristi naziv
ternarni operator. U izlaganju koje slijedi, mi emo koristiti oba naziva i odnosie se na
kondicionalni operator. Ovaj operator se jo naziva i jednolinijski if (inline if iif) zbog toga
to simulira if else konstrukciju, ali veinom u jednom redu.
ta radi ternarni operator i kako se koristi? On se sastoji iz tri dijela. Prvi dio je uslov, drugi
dio je kd koji se izvrava ako je uslov taan, a trei dio je kd koji se izvrava ako uslov nije
taan. Pogledajmo generalnu sintaksu operatora:
uslov ? uslov_tacan : uslov_netacan
Kao to vidimo, specifian je po tome to koristi znakove ? i :. Prvo ide uslov koji se
evaluira, nakon toga znak ? pa onda dio kda koji e biti izvren ako je uslov taan. Nakon
toga slijedi znak : i nakon njega dio kda koji e biti izvren ako uslov nije taan. Opisani
redoslijed mora biti ispotovan i mi ne moemo na mjesto znaka ? staviti znak : i
obrnuto (bie javljena greka). Ono to je jo zanimljivo, jeste da e ternarni operator
zapravio vratiti vrijednost. Pogledajmo prost primjer. Neka je potrebno u varijablu b staviti
vrijednost 10 ako je vrijednost varijable a manja od 5, u suprotnom emo u varijablu b staviti
vrijednost 11:
int a = 2;
int b = a < 5 ? 10 : 11;
printf ("B = %i", b);
Isjeak iznad e ispisati B = 10. Analizirajmo sada isjeak. U varijablu b emo staviti ili
vrijednost 10 ili vrijednost 11, u zavisnosti od toga da li je vrijednost varijable a manja od 5 ili
ne. Uslov je bio da testiramo da li je vrijednost varijable manja od 5. Ukoliko jeste, onda mi,
pomou kondicionalnog operatora (?:) vraamo vrijednost 10. To je drugi dio, tj. dio ako je
uslov taan. Ukoliko uslov nije taan, onda mi vraamo vrijednost 11 (trei dio). Eh, tu
vrijednost koju je operator ?: vratio mi smjetamo u varijablu b. Ovaj kd iznad se mogao
napisati preko if else konstrukcija:
int a = 2;
int b;
if (a < 5)
12
Pogledajmo sada zanimljiv primjer koji ilustrira upotrebu kondicionalnog operatora pri
ispitivanju koji je broj vei, a koji manji.
#include <stdio.h>
int main ()
{
int a, b;
printf ("Unesite brojeve a i b, razdvojite ih razmakom: ");
scanf ("%i %i", &a, &b);
int max = a > b ? a : b;
int min = a < b ? a : b;
printf ("Veci od ova dva broja je broj: %i\n", max);
printf ("Manji od ova dva broja je broj: %i\n", min);
return 0;
}
Pogledajmo jo jedan primjer: neka je potrebno unijeti tri realna broja, a, b i c. I neka je
potrebno izraunati max {a, b} c b / min {a, c}. Da ovo radimo preko if
konstrukcija, na kd bi bio vei... ali preko ternarnog operatora (koji e automatski vratiti ili
dio nakon ? ili dio nakon :) mi to moemo krae napisati:
#include <stdio.h>
int main ()
{
double a, b, c;
printf ("Unesite brojeve a, b
scanf ("%lf %lf %lf", &a, &b,
printf ("Rezultat onog izraza
(a > b ? a : b) * c
return 0;
}
i c: ");
&c);
je: %f",
- b / (a < c ? a : c));
Ono to nam standard C jezika garantira jeste da e se prvi dio operatora ?: tj. dio koji
predstavlja uslov, sigurno izvriti prije drugog i treeg dijela. Stoga, u sljedeem izrazu nema
bonih efekata (o kojima smo govorili u pretdhodnim poglavljima).
13
Isjeak iznad je ispravan (nema bonih efekata) zbog toga to e sigurno biti prvo izvren dio
kda a++ < 5, pa e onda biti izvreno ili 10 * a++, ili 10 * ++a (ali ne oboje).
Analizirajmo malo detaljnije isjeak. Varijablu a smo inicijalizirali brojem 3. Onda smo u
izrazu a++ < 5 poredili da li je a manje od 5, te onda inkrementirali varijablu (znai,
poreenje je uraeno sa prijanjom vrijednosti varijable, tj. poreenje je izgledalo kao 3 < 5
ali je nakon toga varijabla a poprimila vrijednost 4). Uslov 3 < 5 je taan, te je u varijablu b
smjetena vrijednost izraza 10 * a++. A ta vrijednost je zapravo 40, jer je varijabla a (sa
vrijednosti 4) pomnoena sa brojem 10 i smjetena u varijablu b. Nakon toga je i varijabla a
inkrementirana na 5 (podizraz a++). Konano, isjeak e ispisati b = 40, a = 5.
Da smo uslov promijenili, tj. da je u drugoj liniji isjeka pisalo ovako: int b = a++ > 5 ?
10 * a++ : 10 * ++a; onda bi varijabla b imala vrijednost 50, jer je izvren trei dio ?:
operatora, a on je prvo poveao varijablu a na 5 (++a), pa je onda pomnoio sa 10 i rezultat
toga cijelog izraza smjestio u b varijablu.
Jo napomenemo da je ternarni operator desno asocijativan, tj. izrazi poput
x = a ? b : c ? d : e
a ne kao
x = (a ? b : c) ? d : e
14
Kao to smo ve par puta spominjali, izraze poput a = a b (gdje je neki binarni operator)
moemo napisati krae, tj. kao a = b. Pokaimo na nekoliko primjera, za razliite operatore,
kako krae moemo pisati neke konstrukcije.
a
b
c
3
+=
*=
%=
/=
Ovaj isjeak e u varijablu a smjestiti vrijednost 6. Moemo, isto tako, u jednoj naredbi i
unijeti i ispisati uneseni broj, zahvaljujui zarez operatoru koji, ponavljamo, kae evaluiraj
lijevo i daj mi desno. Pogledajmo:
15
Sa zelenom bojom je oznaen zarez operator koji e evaluirati lijevu stranu (unos broja
pomou scanf funkcije) i vratiti (proslijediti printf funkciji) desnu stranu (tj. uneseni broj n).
Vano je ovdje da e sigurno biti prvo izvrena lijeva strana (tj. unos broja) pa onda desna.
Tu garanciju imamo samo kod zarez operatora i logikih operatora I (&&) i ILI (||). Sa utom
bojom nije oznaen zarez operator, tj. znak zarez oznaen utom bojom se nije koristio kao
operator nego kao separator. Vano je ne mijeati ove dvije funkcionalnosti!
Spomenimo jo i to da zarez operator ima najmanji prioritet od svih operatora u jeziku C, pa
ak manji prioritet i od operatora dodjele (=). Pogledajmo primjer:
int a, b;
a = (5, 6);
/* a je sada
b = 5, 6; /*
/* b je sada
printf ("a =
6 */
Isto kao: "(b = 5), 6" */
5 */
%d, b = %d", a, b);
Operator zarez moemo iskoristiti i za odvajanje izraza C jezika. Ovo moe biti korisno kada
imamo dvije ili vie naredbe u tijelu if, for, while i slinih konstrukcija, pa ne moramo pisati
vitiaste zagrade. Npr.
if (c < 10)
a = 1, b = 0; /* Umjesto {a = 1; b = 0;} */
else
a = 0, b = 1; /* Umjesto {a = 0; b = a;} */
Operator zarez se naziva ejtan operatorom, upravo zbog toga to prouzroukuje vie problema
nego koristi prilikom njegovog (neopreznog) koritenja. Stoga se ne preporuuje koritenje
ovog operatora osim kada se sigurno zna ta se radi.
16
Operator konverzije
Gdje e biti ispisano x = 1.000000. Ovo je zbog toga to su i broj 3 i broj 2 cijeli brojevi,
pa imamo cjelobrojno dijeljenje 3 / 2 koje daje rezultat 1. Onda se taj broj konvertuje i
smjeta u varijablu x. Ovo moemo izbjei eksplicitnim stavljanjem realnih brojeva u izraz,
npr. na jedan od sljedeih naina:
double x1 = 3.0 / 2;
double x2 = 3 / 2.0;
double x3 = 3. / 2;
Dovoljno je samo da jedan od operanada bude realnog tipa kako bismo imali realno dijeljenje
tj. dijeljenje sa realnim brojevima. Sve je do sada ilo normalno kada smo koristi konstante,
pa smo mogli ekspilicitno rei kojeg e tipa biti. Ali ta ako imamo samo varijable na
raspolaganju? Kako onda da dobijemo eljeni efekat? Odgovor je: konverzijom. Pogledajmo
primjer koji e raunati sumu koliinik broja i njegovog kvadrata.
int i;
double suma = 0.0;
for (i = 1; i <= n; i++)
suma += i / (i * i);
Rezultat ovog raunanja sume e uvijek biti 1, za ma kakvo n. Razlog tome jeste to smo
imali cjelobrojno dijeljenje u sljedeem izrazu (varijabla i je tipa int): i / (i * i). Sada ne
moemo dodati .0 kako bismo dijeljenje uinili realnim. Moemo se snai pa pomnoiti
varijablu i sa 1.0 kako bismo dobili realno dijeljenje, tj.
suma += (i * 1.0) / (i * i);
Ovo radi, ali ipak nije preporuljivo ovako raditi iz vie razloga. Meu glavnim razlozima
zbog ega ne treba ovako raditi jeste taj da mi neemo uvijek raditi sa brojevima. Moemo
17
Gdje tip predstavlja tip u koji elimo izvriti konverziju, a izraz predstavlja ono nad ime
vrimo konverziju, tj. ono to pretvaramo u tip. Pogledajmo sada kako emo rijeiti
prthodni problem cjelobrojnog dijeljenja.
int i;
double suma = 0.0;
for (i = 1; i <= n; i++)
suma += (double)i / (i * i);
Podizraz kojeg smo markirali je poziv konverzinogo operatora, tj. konverzija varijable i (tipa
int) u tip double. Nakon ovoga, na isjeak e pravilno raunati sumu jer imamo realno
dijeljenje (ve smo napomenuli da je potrebno da samo jedan od operanada bude realnog tipa
da bi dijeljenje bilo realno). Ovdje treba primijetiti da je ova konverzija napravila novu
privremenu varijablu koja je tipa double i u nju smjestila vrijednost varijable i, tj. mi ovom
konverzijom nismo (zauvijek) promijenili niti tip niti vrijednost varijable i. Tip varijable
naknadno ne moemo ni mijenjati. Isto tako treba primijetiti da je prvo izvrena konverzija,
pa mnonje (jer je u zagradama) pa onda dijeljenje, tj. konverzioni operator je imao prioritet u
odnosu na dijeljenje i mnoenje.
Konverzoni operator se moe iskoristiti i za uklanjanje decimala kod realnih brojeva. Npr.
neka je potrebno uzeti cijeli dio nekog realnog broja:
double x = 12.34;
int cijeli = x;
Ovdje varijabla cijeli ima vrijednost 12. Primijetimo da je ovdje izvrena automatska
konverzija tipa double u tip int. Isjeak iznad je ekvivalentan sljedeem:
double x = 12.34;
int cijeli = (int)x;
18