A InfoWiki wikiből
(Új oldal, tartalma: „<cim cim3="Értékadó utasítás" cim2="Magasszintű Programozási Nyelvek I." cim1="Imperatív, procedurális nyelvek alapjai" prev="mp1/page140" next="mp1/page1...”) |
Aktuális változat (2009. október 15., 09:43) (lapforrás) |
||
11. sor: | 11. sor: | ||
<path p1="Kezdolap|Kezdőlap" p2="EBooks|Jegyzetek" p3="mp1/Nyitolap|Tartalomjegyzék" | <path p1="Kezdolap|Kezdőlap" p2="EBooks|Jegyzetek" p3="mp1/Nyitolap|Tartalomjegyzék" | ||
xx="-" | xx="-" | ||
- | w1="mp2 | + | w1="mp2/Nyitolap|Gyakorlatok" w2="mp1/Tematika|Vizsgatematika" w3="cs/Blog|Programok C# nyelven" |
/> | /> | ||
Aktuális változat
Értékadó utasítás
Az változóink deklarálása nem elég. Egy deklarált változó önnmagában nem képvisel értéket, ebből a szemponból definiálatlannak tekinthetjük. Egy söröskorsó nem hordozza a sört, csak a lehetőséget.
A változóinkba értéket tudunk elhelyezni az értékadó utasítással. Ennek jele a = jel (más programozási nyelvekben néha := jellel jelölik).
int a; a=12; ...
A fenti példában deklarálunk egy a nevű, int típusú változót, mely deklaráláskor még értékkel nem rendelkezik. A következő utasítás segítségével ezen változó aktuális értékét beállítjuk, egy egész szám literál segítségével 12-re.
Az értékadás után az a változónk értéke 12 lesz. Ezen értéket felhasználhatjuk további értékadásokban:
int b;
b=a;
...
Most deklaráltunk egy b változót (szintén int), és értékét beállítjuk ugyanarra az értékre, mint az a értéke ebben a pillanatban, vagyis 12-re.
int c; c=b*2; ...
Ezen példában a c változó már 24 értékre áll be.
b=8;
...
A b változó, amely korábban a 12 értéket tárolta, most felveszi a 8 értéket. A továbbiakban már nem emlékszik a régi értékére, csak az újra. A c változót ez már nem érinti, ő megkapta a 24 értéket korábban, és mivel nem adtunk ki rá újabb értékadó utasítást, ez nem változik meg.
Kezdőérték beállítás
Nagyon gyakori az, hogy a változó deklarációját követően azonnal be kívánjuk állítani annak értékét. A változóinkat a korábban elmondott okoknál fogva ugyanis a lehető legkésőbbi pillanatban deklaráljuk, leginkább akkor, amikor már ténylegesen szükség is van rá.
int x; x=7; ...
A fenti két utasítás összevonható egyetlen lépésbe:
int x = 7; ...
Ennek során nem csak deklarálunk egy új x nevű változót, de azonnal be is állitjuk annak kezdőértékét. A továbbiakban az x változónk beállított értékkel rendelkező (definiált) változó.
Típusegyezőség
Amikor értékadó utasítást fogalmazunk, ügyelnünk kell a baloldal és a jobboldal típusára.
A bal oldalon kötelezően egy változó áll, melyet deklaráltunk, és eközben 'explicit módon megadtuk a típusát. A bal oldal típusa tehát egyértelműen ismert.
A jobb oldalon minden esetben egy kifejezés áll, mégha az a kifejezés egyetlen literálból vagy konstansból áll is. Az egyszerű kifejezések típusa könnyedén meghatározható, az összetett kifejezéseké (operátorokat, függvényhívásokat is tartalmaz) kicsit több gondolkodással, de szintén meghatározható (lásd a numerikus kifejezések fejezetet).
Ameddig a két oldali típus egyezik, addig nincs semmi gond. Ez az értékadó utasítás biztosan helyes, és végrehajtható.
Amennyiben a két oldal típusa nem egyezik meg tökéletesen, úgy három lehetőség van:
- létezik implicit típuskonverzió
- létezik explicit típuskonverzió
- egyik sem létezik
Implicit típuskonverzió
Vegyük az alábbi példát:
double a = 12;
A bal oldal típusa double, a jobb oldal típusa int. A két típus nem egyezik meg, ezért a fenti három eset valamelyike fog bekövetkezni.
Az implicit típuskonverzió olyan típuskonverzió, amelyet a fordítóprogram automatikusan alkalmazhat, amikor szükségét érzi (maga a típuskonverzió ténye nincs belegépelve látható módon a forráskódba, de a típusok elemzése során kimutatható).
A fenti értékadó utasítás úgy működik, hogy a jobb oldali értéket (12) az értékadás végrehajtása során double típussá alakítja a futtató rendszer (12.0), majd mivel mostmár a jobb oldalon is double áll, végrehajtó az értékadó utasítás.
Milyen implicit típuskonverziók léteznek? Ez a téma elsősorban a numerikus típusok (egész, tört) között merül fel. Az implicit típuskonverziók témakörét a második féléves OOP tananyagban újratárgyaljuk, bővítve a lehetőségeket.
Amikor a fordítóprogram látja, hogy van egy A típusa (pl: int), és kell(ene) belőle csinálni egy B típust (pl: double), akkor a következőképpen jár el:
- ha A és B is egész számtípus
- ha A tárolási igénye kisebb mint B tárolási igénye, akkor elvégzi a típuskonverziót (pl: byte -> ushort működik)
- ha A tárolási igénye nagyobb mint B tárolási igénye, akkor nincs típuskonverzió (pl: int -> sbyte nem működik)
- ha A és B tárolási igénye egyenlő, egyikük előjeles, a másik előjel nélküli, akkor akkor nincs típuskonverzió (pl: int -> uint, sem uint->int)
- ha A egész, B lebegőpontos, akkor van típuskonverzió (pl: int -> float)
- ha A lebegőpontos, B egész, akkor nincs típuskonverzió (pl: double -> int)
A fenti szabályok első olvasásra bonyolultaknak, és megjegyezhetetleneknek tűnnek. Pedig teljesen logikusan működnek. Akkor van típuskonverzió, ha a konvertálandó értéket egy olyan típussá alakítjuk, ami garantáltan képes azt tárolni. Egy kisebb egészből egy nagyobb egészbe konvertálás garantáltan működik, mivel a nagyobb egész típus nagyobb számokat képes kezelni, tehát a kis értéket gond nélkül át tudja venni (byte -> short).
Nagyobb típusból kisebb típusba nem biztos a siker. Ha egy int típusú értékünk van, pl: a 12, az mondjuk épp beleférhetne egy byte típusú változóba, de az int nagyon nagy értéket is felvehet, és általában nem fér el egy byte-on. Ezért ezen irányú típuskonverzió csak bizonyos esetekben működne jól, ezért összességében végrehajtása tilos, nem létezik.
Bármilyen egész típusú érték mindíg konvertálható lebegőpontos értékké, mivel azok elég nagy egész értéket is kezelnek. De fordítva nem, mivel egy tört érték sosem fér be egy egész típusba.
Ha a fordító implicit módon nem képes a jobb oldali típus átalakítani a bal oldal típussá, akkor az értékadó utasítást típus-helytelennek minősíti, és szintaktikai (fordítási) hibával leáll. A programunk ebben a formában nem indítható el.
Ekkor még nem feltétlenül van nagy baj, mert elképzelhető, hogy explicit típuskonverzióval az ellentmondás feloldható.
Explicit típuskonverzió
Amennyiben egy A típusunkat egy B típussá kívánjuk átalakítani, alkalmazhatunk explicit típuskonverziót is.
Az explicit típuskonverziót könnyű felismerni - ugyanis a program szövegébe bele kell írni (a programozó belegépeli a szövegbe):
double a = 12; int x = a; // hibás, nincs double -> int
Az a változónk típusa double, e miatt az értékadó utasítás jobb oldala is double, amely nem alakítható át implicit (automatikus) módon int-é. A fordító cannot implicitly convert type 'double' to 'int'. Az explicit conversion exists (are you missing a cast?) üzenettel jelzi a hibát.
Az eplicit konverzió során nekünk kell megadni a cél típus nevét, melyet gömbölyű zárójelpárban írunk a konvertálandó érték elé:
double a = 12; int x = (int)a; // rendben van
Az (int)a a fordítóprogramnak szól: alakítsd át bármi áron az a változóban lévő értéket int-é. Az átalakítás után a jobb oldalon egy int fog állni, s innentől kezdve a két oldal típusa egyezik - az értékadó utasítás típushelyes, végrehajtható.
De hogyan történik a típusátalakítás? Amennyiben pl az a váltózónkban a 12.3 érték lenne, úgy az int-é alakítás után már csak 12 maradna ezen törtértékből. Tehát eközben információt veszthetünk.
Ezen veszteséget a programozó tudomásul kell vegye, és ennek ellenére kérte az átalakítást. Innentől a felelősség nem a fordítóprogramot terheli, amennyiben ez valamifél rossz számolási végeredményhez vezetne.
Ritkán, de előfordul, hogy az explicit típuskonverzió során nem közvetlenül a céltípusba konvertálunk:
double a = 300.234; int x = (byte)a; // rendben van, de mennyi az eredmény?
A fenti esetben a 300.234 értéket explicit típuskonverzióval byte típussá alakítjuk. Eközben nem 300 lesz belőle, mivel a byte típus értéktartománya [0..255]. Megmagyarázható (most nem megyünk bele), de 44 lesz az eredmény. Ezek után a jobb oldal típusa byte lesz, melyből vezet implicit típuskonverzió int-re, ezt a fordító automatikusan alkalmazza is, így alakul ki a végeredmény - a fenti értékadó utasítás típushelyes. Az x változó értéke 44 lesz.
Amennyiben egyből int-é alakítottuk volna a double-t, ekkora veszteség nem állt volna fenn:
double a = 300.234; int x = (int)a; // rendben van
E fenti esetben az x változóba a 300 érték került volna be.
Egyik sem létezik
Amennyiben a jobb oldal típusára nem alkalmazható implicit típuskonverzió, sem explicit típuskonverzió, akkor az értékadó utasítás nem végrehajtható:
string s = "300"; int x = s; // nem vegrehajthato int z = (int)s; // nem vegrehajthato
Nem létezik string->int irányban sem implicit, sem explicit típuskonverzió, ezért sem az x, sem a z változóra vonatkozó értékadás nem típushelyes.