2012. március 4., vasárnap

C ++ 003 Operátorok


Ha az előző bejegyzés szerint deklarált változóazonosítókat kifejezésben használjuk, operátorandusokként is nevezzük őket, míg a rajtuk végzett műveletek szimbólumai az operátorok lesznek. 
Alapvetően két féle operátort különböztetünk meg: aritmetikai ("számolós") és relációs ("összehasonlító") operátorokat. Külön szokták említeni még a mutató operátorokat (pointer operator). A pointereknek, mutatóknak fontos szerep jut a C++ nyelvben, később részletezzük.
Először tisztázzunk két fontos fogalmat:
Kiértékelési sorrend: A fordítóprogram a kifejezésekben szereplő műveleteket meghatározott sorrendben hajtja végre. A kiértékelési sorrend határozza meg ezt, a sorban előbb álló művelet előbb hajtódik végre mint a sorban mögötte álló. Ezt a matematikában zárójelezve asszociativitásnak is nevezzük.
Precedencia: elsőbbséget jelent. A precedenciasorban előbb álló operátorok előbb fognak kiértékelődni, mint az utánuk következőek. Vannak azonos precedenciájú operátorok is, köztük a kiértékelési sorrend határozza meg a műveletek végrehajtási sorrendjét.

Aritmetikai operátorok: =, +, -, *, /, %
Az aritmetikai operátorok kiértékelése jobbról balra, visszafelé történik.
Precedenciájuk (az azonosak egy sorban, felül a magasabb):
*, /, %
+, -
=
Az = jel az értékadást jelöli. A baloldalon álló változó a jobboldalon álló kifejezés értékét fogja kapni. A kifejezés lehet természetesen egy másik változó valamilyen műveletek, vagy akár egy konstans érték is.
A + az összeadás, - kivonás, * szorzás, / osztás, % maradékképzés.
Világos, hogy amint azt említettem, a típusok és rajtuk végzett műveletek összefüggésben vannak, így pl. 2 gész szám összege, vagy különbsége nyílván egész szám, osztásnál ez már nem biztos, hogy teljesül, ezt úgy nevezzük, kivezet a művelet az értelmezési tartományunkból. Az operátorokat tehát függvényként is felfoghatjuk, értelmezési tartomány lesz a két egészünk, de a művelet, az osztás függvényének eredménye, értékkészlete már tört szám lenne. A fordítóprogram azonban kicsit másképp dolgozik: ha két int-et kap operandusként, és az eredmény tört lenne, akkor egyszerűen levágja a törtrészt, tehát a számításunk pontatlan lesz. Ekkor még kerekítésre sem számíthatunk! Tehát az operandusok típusa fogja meghatározni a kifejezés típusát. Ez néha bosszantó hibákhoz vezethet.
Érdekességképp: Matematikában két egész szám osztásának eredményét racionális számnak neveznénk, és tudjuk, hogy például a gyökvonás művelete már ebből is kivezethet, az irracionális számokba, azaz a gyökvonás értékkészlete a valós számok halmaza. Ám itt, programozáskor nem választjuk őket szét, a tört szám törtszám, a float és double típus magát a teljes valós számok körét jelentené, bár ez a memóriahatárok miatt lehetetlen. 
A % a maradékképzés operátorának első operandusa az osztandó, a második az osztó, míg az eredmény a maradék lesz. Operandusai egész típusúak kell legyenek.
Az itt leírtak megértéséhez és gyakorlásához írjunk egy rövid programot:
#include <iostream>

using namespace std;

int main()
{
    int a=22;
    int b=3;
    cout << "Egesz a=22, b=3" << endl;
    cout << "a+b=" << a+b << endl;
    cout << "a-b=" << a-b << endl;
    cout << "Ket egesz osztasa: a/b=" << a/b << endl;
    float c=22;
    float d=3;
    cout << "Valos c=22, d=3" << endl;
    cout << "Ket valos osztasa: c/d=" << c/d << endl;
    cout << "Maradekkepzes: a%b=" << a%b << endl;
    return 0;
}

Relációs operátorok: ==, !=, <, >, <=, >=
A relációs operátorok kiértékelése balról jobbra történik.
Precedenciájuk (az azonosak egy sorban, felül a magasabb):
<, >, <=, >=
==, !=
A címben szereplők sorban: egyenlőségvizsgálat, nemegyenlőség vizsgálat, baloldali érték kisebb-e a jobboldalinál, baloldali érték nagyobb-e a jobboldalinál, a baloldali érték kisebb, vagy egyenlő a jobboldalinál, a baloldali érték nagyobb vagy egyenlő a jobboldalinál. 
Ezeket az egyszerű kifejezéseket a logikai operátorokkal bővíthetjük:

Logikai operátorok: !, ||, &&
! : logikai tagadás
|| : "vagy" operátor
&& : "és" operátor
A logikai operátorok kiértékelése a tagadáson kívül balról jobbra történik, míg a logikai tagadás jobbról balra értékelődik ki.
Precedenciájuk (felül a magasabb):
!
&&
||
Működésüket táblázatban mutatom be:
Erre is írhatunk egy kis egyszerű programot, amiben gyakorolhatjuk a bool logikai típus működését is:
#include <iostream>

using namespace std;

int main()
{
    bool valasz;
    cout << "A kerdesekre adott valaszok a bool tipusnak megfeleloen: ";
    cout << "0, ha hamis, 1, ha igaz" << endl;
    cout << "15 egyenlo-e 15-tel? ";
    valasz = 15 == 15;
    cout << valasz << endl;
    cout << "15 nemegyenlo-e 15-tel? ";
    valasz = 15 != 15;
    cout << valasz << endl;
    cout << "15 kisebb-egyenlo-e 15-nel? ";
    valasz = 15 <= 15;
    cout << valasz << endl;
    cout << "15 nagyobb-egyenlo-e 14-nel? ";
    valasz = 15 >= 14;
    cout << valasz << endl;
    cout << "15 nagyobb-egyenlo-e 17-nel? ";
    valasz = 15 >= 17;
    cout << valasz << endl;
    cout << "15 egyenlo-e 15-tel ES 15 nagyobb-egyenlo-e 14-nel? ";
    valasz = 15 == 15 && 15 >= 14;
    cout << valasz << endl;
    cout << "15 nemegyenlo-e 15-tel ES 15 nagyobb-egyenlo-e 14-nel? ";
    valasz = 15 != 15 && 15 >= 14;
    cout << valasz << endl;
    cout << "15 egyenlo-e 15-tel VAGY 15 nagyobb-egyenlo-e 14-nel? ";
    valasz = 15 == 15 || 15 >= 14;
    cout << valasz << endl;
    // Változók használata esetén mindenképp
    // ajánlatos zárójeleznünk összetettebb
    // kifejezéseknél, átláthatóbb is, és
    // kevesebb hibát véthetünk:
    int a=15;
    cout << "15 nemegyenlo-e 15-tel VAGY 15 kisebb-egyenlo-e 14-nel? ";
    valasz = a != a || a <= 14;
    cout << valasz << endl;
    // Most lássunk néhány igazán összetett
    // logikai kifejezést:
    cout << "(15 == 15 && 15 >= 14) VAGY (15 != 15 && 15 >= 14)? ";
    valasz = (15 == 15 && 15 >= 14) || (15 != 15 && 15 >= 14);
    cout << valasz << endl;
    cout << "(15 == 15 && 15 <= 14) VAGY (15 != 15 && 15 >= 14)? ";
    valasz = (15 == 15 && 15 <= 14) || (15 != 15 && 15 >= 14);
    cout << valasz << endl;
    cout << "(15 != 15 || 15 >= 14) ES (15 == 15 || 15 <= 14)? ";
    valasz = (15 != 15 || 15 >= 14) && (15 == 15 || 15 <= 14);
    cout << valasz << endl;
    cout << "(15 == 15 || 15 >= 14) ES (15 != 15 || 15 <= 14)? ";
    valasz = (15 == 15 || 15 >= 14) && (15 != 15 || 15 <= 14);
    cout << valasz << endl;
    return 0;
}

Léptető operátorok:
A C++-ban gyakran használatos 
++ : növelő operátor (increment) 
-- : csökkentő operátor (decrement)
Precedenciájuk megegyezik a fenti sorrenddel.
Ezeknek az operátoroknak van úgynevezett előrevetett (prefixes) és hátravetett (postfixes) formájuk. A postfixes forma kiértékelése előbb történik, mint a prefixesé. Alapvetően megegyeznek az eggyel való növeléssel, vagy csökkentéssel. Az x++ és ++x megegyezik azzal, hogy x=x+1; az x-- és --x pedig egyezik az x=x-1 kifejezéssel. Azon kívül, hogy egyszerűbb leírni, az x++ gyorsabban hajtódik végre, sőt, a ++x még gyorsabban. Mégis miért van két alakjuk? Előre és hátravetett formájuk? 
Ha kifejezésben használjuk őket, a hátravetett forma a kifejezés kiértékelése után növeli a változó értékét, míg az előrevetett forma esetén már a kifejezés végrehajtásakor a növelt értéket fogja használni.
Pl.:
Ha az a változónak a 15 értéket adtuk, akkor az alábbi kifejezésekben az első esetben 35-öt kapunk eredményül, miközben az a értéke 16-tá változik, míg a második esetben 36-ot kapunk eredményül, és az a értéke ugyancsak 16 lesz. Sőt, ha a két kifejezést egymás után írjuk egy programon belül, akkor először 35-öt kapunk, a=16 mellett, majd 37-et, a=17 mellett:
20 + a++;
20 + ++a;

Aritmetikai operátoroknak van összevont formája:
Az aritmetikai operátoroknak van összevont formájuk is, amit akkor használunk, ha az eredményt a kifejezésben szereplő egyik operandusban szeretnénk tárolni. Ezt is gyorsabb leírni, valamint gyorsabb kódot is eredményez. Kiértékelésük jobbról balra történik, és precedenciájuk megegyezik. Lássuk mit is tudnak, ha van egy a és egy b operandusunk:
a*=b; megegyezik azzal, hogy a=a*b;
a/=b; // a=a/b;
a%=b; // a=a%b;
a+=b; // a=a+b;
a-=b; // a=a-b;

Összefoglalva az összes csoportba tartozó operátort, lássuk a teljes precedencia sorrendet:
!, ++ prefix, -- prefix
*, /, %
+, - 
<, >, <=, >=
==, !=
&&, ||
*=, /=, %=, +=, -=

Nincsenek megjegyzések:

Megjegyzés küldése