fel
le

Példa

A harmadik generációs programozási nyelvek egyik nagy előnye, hogy numerikus kifejezéseket írhatunk. A korábbi generációkban (gépi kód, assembly) egy numerikus kifejezés kiértékelését lépésekre kellett bontani. Ezen lépéssorozat elolvasásával deríthető csak ki, hogy mi is az, amit éppen számolunk.

int r = 14;
double a = 2*r*3.14;
 

A fenti példában az a változóta vonatkozó értékadó utasítás jobb oldalán egy numerikus kifejezés áll, három adattal, és két operátorral. Ismert, hogy egy értékadó utasítás akkor végrehajtható, ha a jobb oldal típusa megegyezik, vagy implicit módon konvertálható a bal oldal típusába.

Írjuk fel ezt az értékadó utasítást tisztán típusokkal:

double = int * int * double;
 

Határozzuk meg a jobb oldal típusát. Ehhez a kifejezésbeli operátorok végrehajtási sorrendjét kell ismerni. Ez esetben egyszerű a helyzet, mindkét operátorunk a szorzás operátor, ezek balról jobbra sorrendben hajtódnak végre:

Először végrehajtódik az int*int, amelynek eredménye szintén int. Ha az első szorzás után vizsgáljuk a kifejezést, akkor már csak ennyi van ott:

double = int * double;
 

Itt most gond van, mivel int*double következik.

Korábban volt már utalás arra, hogy nem egyforma típusú adatok közötti műveletvégzés során a háttérben típusegyeztető lépések történnek. Ennek oka, hogy a lebegőpontos ko-processzor csak egyforma adattípusok között tud műveletet végrehajtani, különböző típusok között nem (ennek is oka van persze, bővebben erről a Bevezetés az informatika-ba, illetve a Számítógép-architektúrák tárgy keretein belül lehet olvasni).

A típusegyeztetést az előző fejezetben nevén neveztük: implicit típuskonverzió hajtódik végre. Mivel double->int implicit típuskonverzió nincs, de int->double van, ezért a fordítóprogram itt automatikusan alkalmazza azt, és a fenti kifejezés double*double típusok között fog végrehajtódni, mely szorzásnak ez esetben az eredménye is double.

Ezzel meg is kaptuk a fenti kifejezés végeredményének a típusát. Ezt most a fordító összeveti a bal oldal típusával:

double = double;
 

Mivel a két oldal megegyezik, nem kell további implicit típuskonverziót alkalmazni sem - az értékadó utasítás típushelyes, tehát végrehajtható.

Numerikus kifejezések

A numerikus kifejezések kiértékelésének két szintje van:

  • a konkrét végeredmény-értéktől függetlenül csupán a kifejezésben részt vevő típusok és operátorok ismeretében a kifejezés eredméynének típusa levezethető
  • a kiértékelés során lépésenként egyszerűsítjük a kifejezést, egy lépésben egy operátort végrehajtva mindaddig, amíg a konkrét végerdményt meg nem kapjuk. Ennek során a műveleti prioritásokat és kötési szabályokat kell betartani.

Típuslevezetés numerikus esetben

A kifejezés eredményének típusát a fordítóprogram vezeti le. Erre azért van szükség, hogy ezen típust összevethesse pl. az értékadó utasítás bal oldalának típusával, és eldönthesse, hogy kell-e (tud-e) alkalmazni implicit típuskonverzót, illetve az értékadó utasítás típushelyes-e.

A típuslevezetés során minden esetben csak egy konkrét operátor végrehajtását kell vizsgálni. Majd ezt a szabályt alkalmani újra-és-újra, amíg az egyszerűsített kifejezésünk egy elemű nem lesz. Ekkor megkaptuk a kifejezés eredmény-típusát.

Az A op B alakú kifejezések (ahol A és B egy-egy típus, az op egy numerikus operátor) az alábbi szabályok figyelhetők meg:

  • ha A és B típusa egyforma, akkor ez lesz az eredmény típusa is,
  • az eredmény típusa legalább 4 byte-os (int vagy uint),
  • ha A típusa kevesebb byte helyigényű, mint a B típusa, akkor a B (nagyobb) típus lesz az eredmény is,
  • ha A típusa tört, a B típusa egész szám, akkor a tört szám típus lesz az eredmény típusa is,
  • ha A és B típusának helyigénye egyforma helyigényű, de az egyikük signed, a másikuk unsigned, akkor az eredmény típusa az egy fokozattal nagyobb helyigényű signed típus lesz.

Az alábbiakban bemutatjuk aprólékosan megadjuk az A op B alakú kifejezésekre (ahol A és B egy-egy típus, az op egy numerikus operátor, pl. összeadás, kivonás, stb) eredmény típusainak szabályait:

  • amennyiben mindkettő (A és B típusa is) kisebb mint 4 byte (tehát az 1 vagy 2 byteos típusok valamelyike), akkor az operandusok típusai az int típusra lesz alakítva. Az eredmény típusa is int lesz.

byte a=12;
short b=-4;
int x = a-b; // az eredmény int lesz
 
  • ha a két operandus egyikének típusa int, a másik típus kisebb mint 4 byte, a közös típus marad az int

int a=12;
ushort b=14;
int x = a-b; // az eredmény int lesz
 
  • ha a két operandus egyikének típusa int, a másiké uint, akkor a közös típus a long lesz

int a=12;
uint b=14;
long x = a-b; // az eredmény long lesz
 
  • ha a két típus egyike 8 byte-os (long vagy ulong), a másiké kisebb mint 8 byte, akkor a közös típus a megadott 8 byteos típus lesz

int a=12;
long b=14;
long x = a-b; // az eredmény long lesz
 

Megj.: long és int közötti kivonás művelet értelmezve van, míg ulong és int között nincs. A probléma forrása, hogy az egyik előjeles, a másik nem előjeles típus. A különböző méretű egészek közötti numerikus művelet általában elvégezhető, míg az eltérő méretű és előjelű általában nem.

  • ha a két típus mindegyike ugyanaz a 8 byte-os (long vagy ulong), akkor az eredmény típusa is ezen típus lesz

ulong a=12;
ulong b=14;
ulong x = a-b; // az eredmény ulong marad
 
  • ha a két típus egyike long, a másiké ulong, akkor az operátor nem alkalmazható

long a=12;
ulong b=14;
long x  = a+b; // nem kiszámítható
ulong z = a+b; // nem kiszámítható
 
  • ha az egyik operandus típusa float, a másiké valamely egész szám típus, az eredmény is float lesz
  • ha az egyik operandus típusa double, a másiké bármely egész vagy tört szám típus lehet, az eredmény típusa double lesz

A fenti szabályok figyelembevételével, lépésenkét haladva tetszőleges bonyolultságú kifejezés típusát le lehet vezetni.

Karakter operátorok

Két karakter típusú operandus között sok nyelven csak a + operátor alkalmazható. Ennek eredménye ekkor string (két betűs szöveg, pl 'c'+'d' eredménye "cd" lenne). A C# nyelven ez nem így van. A karakter típusú érték ekkor implicit módon egész szám típussá kerül konvertálásra (a karakter számkódjának értékét kapjuk meg). Az operátort ezen két egész szám között értelmezni a továbbiakban a rendszer:

char a = 'A'; // a karakter kódja: 65 az ASCII szerint
char b = 'B'; // a karakter kódja: 66 az ASCII szerint
int x = a+b;  // x értéke 131 lesz
int y = a-b;  // y értéke -1 lesz
// stb
 
Hernyák Zoltán
2013-01-24 10:10:16