Az operátorok milyen operandusokon vannak értelmezve
Az operátorok mindössze karakterek a kifejezésekben. Az operátorok értelmét a környezete, az operandusok típusa határozza meg.
Pl.: az a+b kifejezés mit jelenthet? Ha az a és b változók szöveg típusok, akkor itt szövegösszefűzést jelöltünk. Ha azonban egész szám típusúak, akkor itt egész számok összeadását kell érteni. Ha azonban az a karakter, a b pedig logikai típusú, akkor a fenti esetben a + operátornak egyszerűen nincs értelme (hibás kifejezés).
A+, -, *, / operátorok értelmezve vannak számok körében, ekkor a megszokott alapműveleteket jelölik.
A / két egész szám között egész osztást jelent.
A % operátor két egész szám között az osztási maradékot jelenti.
A && a logikai ÉS műveletet jelöli, a || pedig a VAGY műveletet. A ! a logikai tagadás jele.
A + operátor karakterek és string típusú operátorok környezetében az összefűzés jele.
Az összehasonlító operátorok, a <, <=, >, >=. Az egyenlőségvizsgálat jele a dupla ==, a nem egyenlő pedig a != operátor.
A kifejezés operátorokból, és megfelelő számú operandusból épül fel. Jellemzője, hogy kiértékelhető, és a kiértékelés eredménye egyetlen egy érték. Ezen érték típusa a kifejezést felépítő operátorokból, és az operandusok típusából meghatározható.
Az operandusok lehetnek:
- literálok
- konstansok
- változók
- függvényhívások
Az operátorok esetén figyelni kell, hogy
- egyes operátor jelentése változó, attól függően, hogy milyen típusúak az operandusai
- az operátorok nem mindegyike értelmezett bármilyen típus esetén
Ha valamely numerikus operátornak két operandusra van szüksége, és ezek típusa nem egyezik meg, akkor a végrehajtás előtt a típusokat közös típusra kell hozni (háttérben zajló implicit típuskonverzió).
Egy kifejezés egyszerű, ha nem tartalmaz operátort (csak egy operandusból áll).
Egy kifejezés összetett, ha tartalmaz operátort (és megfelelő számú operandust).
Operandusok közös típusra hozásának problémái
Ha két szám típusú értékre akarjuk végrehajtani valamely numerikus operátort, akkor a két típust egyeztetni kell végrehajtás előtt. Ugyanis pl. nem lehet közvetlenül összeadni egy int-et egy double-el.
Az egyeztetés szabályai:
Amennyiben mindkét operandus típusa egész (de különböző egész):
- ha a két típus eltérő tárolási igényű, akkor a nagyobb tárigényű típus lesz a közös típus
- a két tárigény egyforma, akkor az előjeles lesz a közös típus
Amennyiben mindkét operandus típusa valós (az egyik float, a másik double):
- akkor a double lesz a közös típus
Amennyiben mindkét operandus közül az egyik valamilyen egész, a másik valós:
akkor a valós típus lesz a közös típus
Amennyiben egy numerikus kifejezés eredményét szeretnénk kiszámolni, úgy ügyelnünk kell az operátorok precedencia-szintjére, és figyelembe kell venni a zárójelezést is. Numerikus kifejezésekben gyakran alkalmazunk numerikus operátorokat:+, -, *, /, %, ^, &,|. Ezen operátorok értelmezve vannak egész számok körében, de a legtöbb a valós számok körében is.
A kifejezést felépítő operandusok lehetnek:
- literálok
- konstansok
- változók
- függvényhívások
A literálok típusát implicit módon határozza meg a fordító (következtet az alakjából, ’megsejti’).
A konstansok, változók, függvények típusát a program szövegében az adott deklarációba bele kell írni, így ezen elemek típusa explicit módon ismert.
Ha a kifejezés tartalmaz változót vagy függvényhívást, úgy csak futási időben kiértékelhető. Az ilyen kifejezések nem alkalmasak konstansok kezdőértékének meghatározására.
A kifejezés kiértékelése lépésről-lépésre zajlik, ’belülről kifelé’. A legutolsó operátor kiértékelésének eredménye határozza meg a kifejezés értékét és típusát.
Az operátorok határozzák meg, hogy az operandusok értékeivel milyen műveleteket kell elvégezni.
Operátorok eltérő jelentése különböző környezetben
A leggyakoribb hiba a numerikus kifejezések esetén a / jel helytelen használata.
A / jel, amennyiben mindkét operandusa egész típus, úgy egész osztást végez.
Pl: int a=3; double d=a/2; esetén a d változó értéke 1 lesz, és nem 1.5! Mivel mind az a változó értéke egész, mint a ’2’ literál típusa egész, ebben a környezetben ez egész osztást jelent.
Tipikus hiba az egész számok átlagának meghatározása során végzett osztás. Tegyük fel, hogy az int osszeg; változónkban van a 13 darab egész számunk összege (az összeg nyilván egész szám). A double atlag=osszeg/13; kifejezés nem az átlagot határozza meg.
Megoldása lehet a problémának a literál típusának megváltoztatása: double atlag=osszeg/13.0; kifejezésben a 13.0 literál típusa double lesz, mert tartalmaz tizedespontot. Ebben a környezetben a / a valódi, hagyományos osztási műveletet jelenti.
Megváltoztatható a másik operandus típusa is explicit típuskonverzió révén: double atlag=(double)osszeg/13;.
Az operátorok operandusaik száma szerint lehetnek:
- egyoperandusú (unáris), pl. a negatív előjel, vagy a logikai tagadás
- kétoprandusú (bináris), pl. az összeadás
- háromoperandusú, egyetlen ilyen van, a ?: (feltételes) operátor
Az egyoperandusú operátor az operandusa előtt szerepel, pl. bool b = !a;
A kétoperandusú operátorok minden esetben infix operátorok, vagyis a két operandus között állnak. Pl. int a = b+c;.
Operátorokat csoportosíthatjuk aszerint is, hogy jellemzően milyen típusokon értelmezettek:
Logikai operátorok: tagadás, és, vagy.
Numerikus operátorok: összeadás, kivonás, stb.
Összehasonlító operátorok: kisebb, kisebb egyenlő, nem egyenlő, stb.
Szöveg operátorok: összefűzés.
Amennyiben egy kifejezés több operátorból épül fel, fontos tudni, hogy azok milyen sorrendben hajtódnak végre.
Az operátorokhoz precedencia-szintet (prioritás) rendelünk. Ha egy kifejezésben több operátor is van, akkor először a magasabb prioritású operátorokat kell kiértékelni.
Ha egy kifejezésben több azonos prioritási szintű operátor is található, akkor az adott szint kötési iránya dönt. A kötési irány általában balról-jobbra irányt jelent, vagyis először a legbaloldalibb ilyen szintű operátort választjuk ki, majd a következőt, stb.
Pl:
5. szint: szorzás és osztás bal->jobb
4. szint: összeadás, kivonás bal->jobb
bool x = a-b*c/2 - 4*a;
első lépésben a * / * operátorok kerülnek kiválasztásra, mivel ezek állnak közösen a legmagasabb prioritáson. Ezen szint bal->jobb kötésű, ezért a legbaloldalibb operátor, az első * kerül kiválasztásra. Így először kiszámolódik a b*c. Aztán a /, majd a második *. Csak ezek után kerülnek sorra a 4. szint operátorai, szintén balról jobbra sorrendben.
Amennyiben egy kifejezés több operátorból épül fel, a precedenciaszint dönti el a végrehajtási sorrendet. Az egyforma precedenciaszintű operátorok egymás közötti sorrendjét a szint kötési iránya határozza meg, mely általában balról jobbra irány.
Ezen elv alól kivételt képez, ha zárójeleket alkalmazunk. Legelső lépés minden esetben a zárójelek felbontása, kiértékelése. Ez minden precedenciaszint felett áll.
Megfogalmazható lenne úgy is, hogy a zárójelek állnak a legmagasabb precedenciaszinten. Ugyanakkor a zárójelek nem tekinthetők operátoroknak, így ezen megfogalmazás kissé erőltetet.
Pl: int x = 3-2*4; értéke 3-8 = -5 lenne a zárójelek nélkül.
Az int x = (3-2)*4; értéke 1*4, vagyis 4 lesz a zárójel alkalmazása miatt.