fel
le

Operátorok

A kifejezésekben adatok szerepelnek, melyeket műveleti jelekkel (operátorok) kapcsolunk össze. Egy operátor lehet:

  • egyoperandusú
  • kétoperandusú
  • háromoperandusú

Egyoperandusú operátorok

Az egyoperandusú, ún. unáris operátor a C#-ban (és sok más programozási nyelvben is) a minusz jel, mint előjel. A -12-ben szereplő minusz jel jelentése: a rákövetkező numerikus értéknek váltsunk előjelet.

Ebben az alakjában sokan nem tekintik tényleges operátorjelnek, azt gondolván hogy ez nem operátor, hanem szerves része a számnak, a minusz-tizenkettőnek. Igen, de az alábbi kódban máris más színben tűnik fel a dolog:

int a = 12;
int b = -a;
 

A -a kifejezésben már nehéz bemagyarázni, hogy ez szerves része az a változónak. Ezért (és inkább ebben a szituációban) tekintjük a '-' jelet műveleti jelnek, mely egyetlen operandusú, egyetlen értékre vonatkozik, és pozícióját tekintve az érték előtt áll.

Szintén az egyoperandusú operátorok közé tartozik a '++' illetve a '--' operátor. Előbbi értéknövelő, második értékcsökkentő operátor:

int a = 12;
a++;
int b=2*a; // b értéke 26 lesz
b--;
int c=b; // c értéke 25 lesz
 

Az operandus mögé írt '++' vagy '--' operátorok mellékhatással (side-effect) is rendelkeznek. Ez azt jelenti, hogy kifejezésben is alkalmazhatóak:

int a = 12;
int b=a--;
 

A fenti példában a jobb oldali kifejezés a-- jelentése: először eredményezze ez a részkifejezés az a változó értékét, de csökkentsük az a értékét 1-el. Vagyis a fenti esetben a b=a-- eredményeképp b értéke is 12 lesz, de a értéke eközben csökken 11-re.

Amennyiben a '++' vagy '--' az operandusa elé van írva, akkor először hajtódik végre a növelés (vagy csökkentés), és az utána kapott érték lesz a kifejezés eredménye:

int a = 12;
int b=--a;
 

Ebben az esetben először csökkentjük a értékét le 11-re, és ezen értéket kapja meg a b, vagyis ő is 11 lesz.

Próbáljuk meghatározni az alábbi kifejezés eredményeképp mennyi lesz a b értéke:

int a = 12;
int b=a-- - --a;
 

Ez nem sorminta. A kifejezés szerint van benne a-- meg --a, és egy kivonás operátor is. A kifejezés eredménye szerint egyébkét a értéke a végén 10 lesz, b értéke pedig 2.

Hogy lehet ez? Először hajtódik végre az a--, mely kifejezésbeli értéke szerint 12, de csökkenti az a értékét 1-el. Egyelőre tehát a kifejezésben 12 - --a van, de addigra a már csak 11. Most következik a --a végrehajtása, mely először csökkenti az a értékét 10-re, majd a kifejezésbe publikálja ezen új értéket: '12-10', így lesz a b értéke 2.

Amennyiben egy a++; kifejezés önmagában áll, úgy egyenértékű a ++a; kifejezéssel, hisz ez esetben a növelés mindenképp végrehajtódik az a változón. Azonban ha ez egy kifejezésben szerepel, akkor már nem mindegy, hogy pl. 3* a++ vagy 3* ++a-ként van írva. Hasonlóan, ha egy értékeadó utasítás jobb oldalán áll, akkor sem egyenértékű a b=a++" illetve a b=++a írásmód.

Kétoperandusú operátorok

A kétoperandusú, ún bináris operátorok két értékre vonatkoznak, pozíciójukat tekintve a két érték között helyezkednek el. Az ilyen operátorokat infix alakú operátoroknak nevezzük. Ilyen pl. a + jel, mint az összeadás jele. Ennek tehát teljes jellemzője: bináris infix operátor.

int a = 12;
int b = 3+a;
 

Az alábbi bináris infix operátorokat ismerjük numerikus környezetben:

  • +, összeadás
  • -, kivonás
  • *, szorzás
  • /, osztás
  • %, maradékképzés

Az első három operátorról nem érdemes sokat beszélni, szokásos matematikai műveleti jelek.

A / mint osztás operátor kicsit érdekesebb. Volt korábban arról szó, hogy amennyiben egy operátor két operandusa egyforma típusú, úgy a művelet eredménye is ezen típusú lesz. Amennyiben két egész szám között szerepel az osztás jel, akkor a fenti szabály értelmében az eredmény is egész szám kell legyen!

  • 6/5 eredménye 1
  • 8/3 eredménye 2
  • -9/4 eredménye -2

A fentiek alapján a C# nyelvben nincs külön osztási művelet egész osztásra, és matematikai osztásra (mint pl. a Pascal alapú nyelvek szokásos div és mod operátorai). Ha egész osztást szeretnénk végrehajtani, akkor legyen mindkét operandusunk egész típusú.

Amennyiben az egyik résztvevő érték típusa törtszám típuscsaládba tartozik, úgy a másikat is ebben fogja implicit módon átalakítani a futtató rendszer, és a végeredményünk is tört érték lesz:

  • 6/5.0 eredménye 1.2
  • 8.0/3 eredménye 2.6666666
  • -9/4D eredménye -2.25

A % operátor helyettesíti a Pascal nyelvek mod operátorát. Ekkor a rendszer elvégzi az osztást, de csak a maradék érdekes:

  • 7%2 eredménye 1 (7-ben a 2 megvan háromszor, maradt az 1)
  • 11%4 eredménye 3 (11-ben a 4 megvan kétszer, maradt a 3)

Elég rapszodikusan (de kis gondolkodással megmagyarázhatóan) működik a % jel negatív értékekre. Általánosan c=a%b, amelyet úgy kell értelmezni, hogyha n=a/b, akkor c=a-b*n.

  • -9%4 eredménye -1, mivel n=-9/4 így n=-2, ez esetben c=-9 - (4*-2).
  • -9%-4 eredménye is -1, mivel n=-9/-4, így n=2, ez esetben c=-9 - (-4*2).

Hasonlóan látható be tört operandusok esetén az eredmény:

  • -11.2 % -4.3 eredménye -2.59999999, mivel n=2 lesz, így az eredmény nem más mint -11.2 - (-4.2*2).

Ezzel együtt ilyen körülmények között (tört szám környezetben) ritkán alkalmazzuk a % operátort.

Összehasonlító operátorok

A C# az alábbi összehasonlító operátorokkal rendelkezik:

  • </b> kisebb-e
  • nagyobb-e
  • <= kisebb vagy egyenlő
  • nagyobb vagy egyenlő
  • == egyenlő-e
  • != nem egyenlő-e (különböző-e)

A fentiek közül különösen érdekes az egyenlő-e operátor, mely a dupla egyenlőség jelből áll (ellentétben sok programozási nyelvvel, ahol ez csak egyetlen egyenlőségjel). Ugyanakkor ez utóbbi nyelvekben az értékadó egyenlőség viszont nem az egyszeres egyenlőségjel, hanem például a ':=' (kettőspont-egyenlő, definiáló egyenlőség) alakú.

Viszont a C alapú nyelvekben (mint a C# is), az értékadó egyenlőség jele a =, megkülönböztetésül pedig az egyenlőségvizsgálat jele a dupla egyenlőség ('==').

Szintén odafigyelést érdemel a nem egyenlő operátor. Ez más nyelveken a '<>' jel lehet, a C alapú nyelvekben viszont a '!=' jel. A ! operátor a tagadás jele, így egy C-hez szokott programozó szeme a != jelsorozatot automatikusan nem egyenlő-nek olvassa.

Az összehasonlító operátorok széles körben vannak értelmezve. Nem csak numerikus értékek között működnek, hanem jellemzően logikai, és karakter és string típusokra is.

Karakterek közötti vizsgálatok esetén a két karakter unicode táblabeli sorszámait hasonlítja össze. Ennek megfelelően értelmes a 'a'<'#' összehasonlítás, holott egyébként hétköznapi értelemben nem szoktuk megkérdezni, hogy az 'a' betű kisebb-e mint a '$' jel.

Mivel a UNICODE tábla szerint a kisbetűk sorszámai nagyobbak, mint a nagybetűk sorszámai, így ne lepődjünk meg, ha az alábbi kód esetén a "masodik eset" üzenet jelenik meg:

if ('a'<'A') Console.WriteLine("elso eset");
else Console.WriteLine("masodik eset");
 

Logikai értékek összehasonlításának alapja általában az, hogy a false kódja 0, a true kódja 1. A C# nyelvben a logikai típus azonban olyan szigorúan van logikai-nak értelmezve hogy nem megengedett közöttük a kisebb-e, nagyobb-e, stb jellegű vizsgálat, csak az egyenlő-e és a nem egyenlő vizsgálat:

bool b = false;
if (b==true) Console.WriteLine("igaz");
else Console.WriteLine("hamis");
 

Jegyezzük meg azért, hogy az egyenlőségvizsgálat logikai esetben nagyon ritkán indokolt. A fenti kód is írható az alábbi módon:

if (b) Console.WriteLine("igaz");
else Console.WriteLine("hamis");
 

Az if(b)... felírás hatékonyabb, és ugyanazon eredményre vezet. A b egyszerű kifejezés pontosan akkor ad logikai true eredményt, mint a b==true vizsgálat.

Az összehasonlító operátorok sajnos string típusra nem teljes értékűen vannak kidolgozva a C#-ban. Két string között működik az '==' és '!=' operátorok, de a többi nem. Két string akkor egyenlő, ha betűről-betűre megegyeznek (ebbe az esetleg bennük szereplő szóközök is beleszámítódnak, valamint a kisbetű-nagybetű eltérések is számítanak).

Console.WriteLine("Hogy hívnak:");
string s=Console.ReadLine();
if (s=="lajos") Console.WriteLine("Szia Lajcsika!");
else Console.WriteLine("Téged nem ismerlek.");
 

A kisebb-e, nagyobb-e, stb. vizsgálatok a string-ek között nem operátorokkal, hanem a CompareTo(...) függvénnyel van megoldva. Ha A és B egy-egy string érték, az A < B vizsgálatnak az A.CompareTo(B)<0 felel meg. A CompareTo függvény gyakorlatilag két string-et hasonlít össze. Az első string az a példány, akire alkalmazzuk az összehasonlítást, a második string értéket át kell adni paraméterként. A függvény egész számmal tér vissza, amely negatív, 0, vagy pozitív lehet. Az A.CompareTo(B) lehetséges értékei:

  • -1, ha A < B
  • 0, ha A == B
  • +1, ha A > B

Ennek megfelelően az A<=B helyes megfogalmazása: A.CompareTo(B)<=0.

Az összehasonlítás során az A karakterei kerülnek összehasonlításra a B megfelelő karaktereivel. Ha a soron következő két karakter egyforma, úgy a következő két karakter dönt. Ha azok is egyformák, akkor a következő két karakter dönt. Ha az egyik string hamarabb fogy el, akkor ő a kisebb. Ha minden karakterük egyforma, és egyforma hosszúak is, akkor a két string egyforma (visszatérési érték 0). Egyébként ha valamelyik karakterük eltérő, akkor a karakterösszehasonlítás szabályai szerint dől el melyik string a kisebb, és további karakterek vizsgálatára már nem kerül sor.

Háromoperandusú eset

Ilyen operátorok numerikus környezetben nincsenek. A C# egyetlen háromoperandusú operátort ismer, a kérdőjel-kettőspont operátort.

A kérdőjel-kettőspont operátor három részből áll:

  • logikai kifejezés
  • eredmény-1
  • eredmény-2

int b = (a==3 ? 8 : 4);
 

A fenti példa szerint: ha a==3 (a egyenlő 3), akkor legyen 8, különben 4. Vagyis b értéke vagy 8 lesz, vagy 4, attól függően, mennyi az a változó értéke. Ennek megfelelően az alábbi működéssel ekvivalens:

int b;
if (a==3) b=8;
else b=4;
 

Vegyük észre, hogy a kérdőjel-kettőspont operátorral írt alak 3 sor helyett csak 1 soros, és rutinosabb szeműeknek sokkal áttekinthetőbb is. Kevésbé gyakorlottak azonban szívesebben alkalmazzák az if-else szerkezetet, számukra az biztonságérzetet ad. Ugyanakkor gyakran sokat egyszerűsít a működésen, és sűrűn lehet olyan programrészeket találni, ahol az if egyszerűen nem használható, mivel nem kifejezés:

int c= (b<5 ? 8 : 4)*2;
 

A c értéke vagy 16, vagy 8 lesz, attól függően hogy b értéke kisebb-e mint 5 vagy sem.

Jegyezzük meg, hogy a kérdőjel-kettőspont operátor nem csak numerikus értéket adhat meg:

string c= b<2 ? "egy" : "sok";
 

Ha b kisebb mint 2, akkor a c string változó értéke "egy" lesz, különben "sok".

Nézzük azt az esetet, amikor egy logikai változó értékét kell "magyarul" kiírni a képernyőre:

bool b = true;
Console.WriteLine("A b változó értéke:{0}",  (b==true?"igen":"nem")  );
 

Két megjegyzést érdemes még megtenni:

  • a kérdőjel-kettőspont operátort nem kötelező külön zárójelezni, de ez gyakran jelentősen növeli az olvashatóságot
  • a b==true forma szintén csak olvashatósági alak, a b értéke már önnmagában is logikai értékű, így önállóan is használható

bool b = true;
Console.WriteLine("A b változó értéke:{0}", b?"igen":"nem");
int eletkor = int.Parse(Console.ReadLine());
bool fiatalkoru= x<18 ? true : false;
 

Összevont operátor precedencia táblázat

1. szint:. fvhíváss tömbindexelés x++ x-- new
2. szint:+ - ! ~ ++x --x (typecast)x
3. szint:* / %
4. szint:+ -
5. szint:< >
6. szint:< > <= >= is as
7. szint:== !=
8. szint:&
9. szint:|
10. szint:&&
11. szint:||
12. szint:?:
13. szint:= *= /= += -= <= >= &= ^= |=

A 2. szinten az egyoperandusú előjel készítése (-2, +4) operátorai, a tagadás művelete látható. A 3. szinten a szorzás, osztás, maradékképzés. A 4. szinten az összesadás, kivonás. A 6. szinten állnak a numerikus összehasonlító operátorok. Az egyenlőség, nem egyenlőség vizsgálata már a 7. szinten van. Az ÉS művelete a 10. szinten erősebb mint a VAGY művelet a 11. szinten.

Hernyák Zoltán
2013-01-24 17:49:56