Operandusok és operátorok
A kifejezést felépítő operandusok lehetnek:
- literálok
- 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, ’meg sejti’).
A 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.
A kifejezés kiértékelése lépésről-lépésre zajlik, ’belülről-kifele’. 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.
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 egyetlenegy é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).
Operátorok csoportosítása
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 ?: operátor
Az egyoperandusú operátor az operandusa előtt szerepel, pl.
char 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;
C nyelven minden operátor numerikus eredményt állít elő, még az összehasonlító operátorok is, hiszen nincs elkülönített logikai típus. Az összehasonlító operátor amennyiben hamis értékű, úgy a 0 numerikus értéket állítja elő, ellenkező esetben az 1 numerikus értéket (igaz érték).
Operátorok precedenciái
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 legbaloldaliabb 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 legbaloldaliabb 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.
Zárójelezés jelentősége
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őltetett.
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.
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.
A C nyelvben gyakorlatilag csak numerikus típusok vannak. A karakter típus is numerikusnak minősül, a logikai típus helyett a karakter van használatban, szöveg típus pedig egyszerűen hiányzik.
Ezért a C operátorai elsősorban numerikus értékeken értelmezett, numerikus értéket előállító operátoroknak tekintendők. Ez illeszkedik a mikroprocesszor filozófiájához, amely egyébként sem ismer más jellegű értéket.
Az egyetlen kiemelendő operátor az „/” (osztás) operátora, amely más-más jelentésű, amennyiben mindkét operandusa egész szám típusú. Ez esetben egész osztást végez, és az eredmény mindenképpen egész szám lesz. Ellenkező esetben (ha legalább az egyik operandusa valós), úgy a matematikai értelemben vett osztást végez, az eredménye az osztási művelet értéke, valós szám lesz.
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öt 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
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 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;