Személyes eszközök
Keresés

 

A InfoWiki wikiből


Adattípusok áttekintése

Minden magasszintű (legalább III. generációs) programozási nyelv rendelkezik alaptípusokkal, melyek eleve részeit képezik a programozási nyelvnek. Ezek az alaptípusok teszik későbbiekben lehetővé további, felhasználó által definiált típusok létrehozását.

A típusokat a C# nyelvben az alábbiak szerint lehet osztályozni:

  • egész szám típusok
  • valós szám típusok
  • logikai típus
  • karakter típus
  • szöveg típus

Egész szám típusok

Ez egy típuscsalád, konkrétan 8 típus sorolható ide. Ezek a típusok egymástól két dologban különböznek:

  • memóriabeli tárigényük (ez 1 byte, 2 byte, 4 byte és 8 byte lehet)
  • előjelességük (vannak előjeles és előjel nélküli változatok)

Ezen két szempont meghatározza az adott konkrét egész szám típus tárolási lehetőségeit. Ez a tárolási lehetőség egy számintervallumot ír le. Például a byte típus [0,255] intervallumbeli egész számokat képes tárolni.

0..255                         byte <-> sbyte  -128..127
0..65535                     ushort <-> short  -32756..32767
0..4,294,946,295               uint <-> int +/-2,147,483,648
0..18,446,744,073,709,551,615 ulong <-> long  +/-9,223,372,036,854,775,808

Az azonos sorban szereplő típusok egyforma tárolási igényűek. Bal oldali oszlopban vannak azok, amelyek csak pozitív egész számokat képesek kezelni (előjel nélküli típusok), és jobb oldalt vannak azok, amelyek előjelt is tudnak kezelni, így negatív számokat is megengednek.

Figyeljük meg, hogy a negatívat is megengedő típusok ugyanannyi darab szám tárolására képesek, csak épp ezt a képességüket a számegyenesen a nullára szimmetrikus eltolással végzik. Tehát ha a byte típus 0..255 közötti számokat képes kezelni (összesen 256 különböző szám), akkor a párja, az sbyte is 256 különböző számot képes kezelni, csak -128 és +127 közötti számokban gondolkodva.

Az első sorban szereplő két típus (byte és sbyte) mindegyik 1-1 byte tárolásigényű (erre utal a nevük is). A byte típus csak pozitív számok kezelését képes végezni, 0..255 értékek között. Az sbyte (signed byte) előjeles, e miatt negatív számokat is képes kezelni. Csodák azonban nincsenek, ezért azt kell fizetni cserébe, hogy abszolút értékben kisebb számokkal képes dolgozni csak: -128..127 közötti számokkal. Ezek az értékek nem véletlenek, és kerek számok is. A 256 az pontosan 2^8, ami arra utal, hogy 8 biten (1 byte) éppen ennyi különböző szám ábrázolható.

A második sorban lévő két típus (ushort és short) mindegyike 2-2 byte tárolásigényű. Az ushort' 0..65535, a párja a short pedig -32,768 .. +32,767 közötti értékeket képes kezelni. Az u előtag az unsigned (előjel nélküli) angol szóból származik, a short jelentése pedig rövid (eredetileg short integer volt a teljes neve: rövid egész).

A harmadik sorban az uint és int' típusok szerepelnek, mindegyikük 4-4 byte helyigényű. Az uint csak pozitív, a párja negatív számokat is képes kezelni. Nem könnyű megjegyezni a tárolási képességük alsó és felső határát, már az is elég, ha megjegyezzük, hogy az int-é 2-essel kezdődő, 10 számjegy hosszú szám (ebből következik, hogy párja, az uint felső határa 4-essel kezdődő, 10 számjegyből álló szám).

A negyedik sorban állnak a nagyágyúk, az long és ulong. Mindegyikük 8-8 byte tárolási igényű, és csak akkor használjuk őket, ha igazán nagy számokkal kell dolgozni. A long tárolási határai plusz/minusz 9-essel kezdődő, 19 számjegy hosszú számok, az ulong-é pedig ennek kétszerese.

Az INT típus a 32 bites processzorokon kiemelt típusnak minősül! Amennyiben nem tudunk dönteni milyen típust válasszunk adatainknak, és nincs szükség a long extra kapacitására, mindenképpen az int-et válasszuk!


Amikor programozni tanulunk, meg kell tanulnunk ezt a 8 típusnak a nevét, és rövid jellemrajzát (tárolási igény, tárolási képesség alsó-felső értéke). Ugyanakkor ritkán alkalmazzuk ezen típusok mindegyikét egy programban. Egyszerűbb programokban az int típus bőven elég mindenre. Miért pont az int?

Nos, ennek magyarazáta kissé hosszadalmas, de igen tanulságos.

Egy 32 bites mikroprocesszor a memóriár 32 bites ún. buszokon keresztül éri el. Egy ilyen 32 bites buszon a memóriába írhat (feltölthet) egyszerre 32 bitnyi adatot a processzor. Hasonlóan, memóriából letölthet 32 bitnyi adatot egyetlen olvasási művelettel. A 32 bit az pontosan 4 byte. Ennél kisebb tárolási igényű adatokat sem tud a processzor gyorsabban olvasni és írni a memóriába. A nagyobb, például 8 byte-os adatok (pl. a long) kiolvasása 2 lépésben történik, mivel egy lépésben csak 4 byte-t tud olvasni.

Nem igaz az tehát, hogy a byte vagy sbyte, short, ushort típusokkal a 32 bites processzorok gyorsabban dolgoznak, mivel azok kisebb méretű adatok.

Az sem feltétlenül igaz, hogy az a program, amely három darab short adattal dolgozik, kevesebb memóriát köt le, mint amely három darab int-el dolgozik. Ugyanis van még egy érdekes momentuma a processzor memóriakezelésének: az adatok, amelyek a processzor le akar tölteni a memóriából, 4-el osztható memóricímen kell elhelyezkedjenek. Ez a memóriachip-ek címzésének egy ilyen furcsa mellékhatása, és a hardware órán részletesebben lehet utájaráni az okoknak. Nekünk elég a következményekkel foglalkozni.

Mivel a fordítóprogramok semmit sem bíznak a véletlenre, némelyiktől simán kitelik, hogy a három darab short adatot nem egymást követő helyre teszik, így lekötve összesen 6 darab szomszédos byte-t, hanem mindegyiket egy 4-el osztható memóricímen helyezik el. Ez csak úgy lehetséges, ha az első short-t mondjuk berakják az 1200-as memóriacímre, a következőt az 1204-es, a harmadikat az 1208-as memóriacímre.

Mi lesz a maradék helyekkel? Az 1200 és 1201-es byte-okon értelmes adat van, az oda elhelyezett short két byte-ja. Az 1202-as és 1203-as byte-ok meg egyszerűen nem tárolnak semmit. Kihasználatlan terület. Tehát még helykihasználásban sem járunk feltétlenül jobban, ha nem int-el, hanem más, kisebb helyigényű adattípussal dolgozunk.

Más a helyzet akkor, ha nem különálló short (vagy más) típusú adataink vannak, hanem tömbben vagy listában helyezkedik el sok száz, sok ezer ilyen adat. Azok tárolását már tömörebben végzik el a memóriában a futtató rendszerek, de a fenti memóriapazarló elv sok helyen visszaköszönhet.

A kisebb tárolási helyigényű típusok a sebességre optimalizált memóriakiosztásokban nem feltétlenül foglalnak el kevesebb tárterületet a 4 byte-os határra igazítási elv miatt.


Más megközelítés is létezik, amiért ellenjavalt más adattípusok használata. Amennyiben van egy short és egy int típusú adatunk, amelyet össze szeretnénk mondjuk szorozni, a szorzás előtt a két adattípust egységesíti a futtató rendszer, magyarul a short-ból rövid időre int-et csinál, és két int között hatja végre a szorzási műveletet. Ezt automatikus típusegyeztetésnek hívjuk, és természetesen nem hobbiból csinálja a futtató rendszer. Nem is az oka az érdekes, hanem az időigénye. Ennek a plusz lépésnek mindenképpen van időszükséglete, legyen az bármily csekély is. Ez a plusz időköltség nem jelentkezik, amennyiben mindkét adattípus eleve int lett volna.

Kerülendő tehát a mindenféle adattípus átgondolatlan használata, amennyiben ezeket egy kifejezésben akarjuk felhasználni. Nem feltétlenül nyerünk sokat, de veszthetünk sokat.

A különböző adattípusokkal történő műveletvégzés háttérbeli típusegyeztetést igényel, melynek tényét a fordítóprogramok elfedik, de a program futási sebességén akár érezhető mértékben is jelentkezhetnek. Ha nem indokolt, ne alkalmazzunk vegyes típusokat az adataink leírásánál, használjunk eleve egyező adattípusokat.


A gyakorlati életben, adott problémakörben szükség lehet a többi adattípus használatára is, elsősorban ha nagyobb mennyiségű adatot kell kezelni, ahol a kisebb adattípusok használata jelentős mennyiségű memóriaterületet tud spórolni. Kis mennyiségű adat kezelése esetén általában szükségtelen más típusokat alkalmazni mint az int, sőt, gyakran több hátrányt okoz mint előnyt.

Jegyezzük meg, hogy az adott konkrét egész típushoz tartozó legkisebb és legnagyobb számérték könnyedén lekérdezhető (kezdőértékenk és vizsgálatokban is jól felhasználható). Az int esetén ez az int.MinValue és int.MaxValue formájú, hasonlóan, byte esetén a byte.MinValue és byte.MaxValue formájú.

int max = int.MinValue;
foreach(int x in lista)
  if (x>max) max = x;
Console.WriteLine("A legnagyobb szam={0}",max);
Az int.MinValue és az int.MaxValue konstansok segítségével a legkisebb és legnagyobb int-ben tárolható érték a program kódjában felhasználható. De ez nem csak az int, hanem mind a nyolc egész szám típus esetén létező konstans, long.MinValue stb. alakban.


Valós szám típusok

A valós számtípusok esetén tört (racionális véges) számértékek jelenlétére kell készülnünk. Amennyiben a tárolandó, kezelendő adataink mondjuk hőmérséklet adatok, úgy általában egész számok is elegendők szoktak lenni. Ha súlyadatokkal dolgozunk mondjuk egy gyógyszertári alkalmazásban, akkor a dekagramm tört részeit is tudnunk kell leírni.

A valós számtípusoknak két konkrét típusa létezik:

float    4 byte    7 értékes számjegy  3.4*10^38
double   8 byte   15 értékes számjegy  1.7*10^308

A float és a double típusok helyigényükben, és pontosságukban térnek el. A double helyigénye és tárolási pontossága pontosan duplája a float-nak (innen is kapta a nevét).

A tárolási pontosság azt jelöli, hogy hány értékes számjegy tárolására képes az adott típus. Tehát a 123.4567823455 számérték float-ban tárolva nem képes a fenti érték tárolására, mindössze a szám 7 számjegyének pontosságával, vagyis körülbelül 123.4567-ig képes azt tárolni és kezelni. Ez úgy is előadódhat, hogy a 45.68/78.403 osztást végezzük el, melynek pontos értéke valószínűleg nem 0.5826308, de egy float csak 7 számjegyik képes pontosan számolni.

Ha a fenti hányados értéket nem float-ban, hanem double-ban tárolnánk el, máris pontosabban kapnánk meg az eredményt: 0,582630766679846. Ez sem a pontos végeredmény, de 15 számjegy pontossággal megközelíti azt.

A 7 számjegy pontosságot úgy kell érteni, hogy mindegy az, hogy 123.4578-ről, vagy 0,000001234578-ról beszélünk. A tört értékeket a számítógép ún. normál alakban tárolja, vagyis az elsőből 1.234578*10^2, a másodikból 1.234578*10^-6 lesz. A normál alakban ábrázolt érték első 7 számjegyét tárolja el a float, és 15 számjegyét a double.

A 7 és 15 számjegyet nem kell teljesen szó szerint érteni, ezek csak becslések. A pontos tárolási mechnanizmus a kettes számrendszeren alapul, amelyet tizes számrendszerben visszaszámolva kapjuk meg a 7 és 15 számjegy darabszámot. Vagyis néha a 7 számjegy 6 vagy 7 vagy 8 számjegynyit is jelenthet, függően a konkrét értéktől.

A lebegőpontos értkekkel nem maga a processzor számol direkt módon, hanem a matematikai ko-processzor nevű részegysége. Ennek saját utasításkészlete, és felfogása van a számokról. Ezen ko-processzor igazából a double-val tud a leggyorsabban dolgozni, tehát megint nem szabad azt feltételezni, hogy float-ban érdemesebb tárolni értékeket, és számolni velük, mint double-val. Hasonlóan hátrányos hatással van a futási sebességre a float és double értékek keveredése egyazon kifejezésben. Ezért amennyiben nincs különösebb indokunk, használjunk double típust tört étékeink tárolására és kezelésére.

A matematikai co-processor a double típust preferálja, ezért különösebb indok hiányában használjuk ezt a típust, és ne a float-t.


A double és float esetén is léteznek a .MinValue és .MaxValue konstansok, értelmezésük megegyezik az egész számoknál tapasztaltal. A double.MinValue = -1,79*10^308. A double.MaxValue ugyanez, csak pozitívban.

Érdekesebb viszont, hogy létezik a NaN fogalom mindkét típus esetén. A NaN jelentése: Not-a-Number, vagyis "nem szám". Ez egy speciális érték, melyet akár kezdőértékként használhatunk, és egyes függvényhívások probléma esetén ilyen értéket ad(hat)nak vissza. Hogy egy double változónk ilyen értékű-e, azt egyenlőségvizsgálattal vizsgálhatjuk meg. Ugyanakkor ez sajnos (érdekes módon) néha hibás eredményt ad, ezért inkább az IsNan(...) függvényt kell használnunk:

double d = Double.NaN;
...
if (Double.IsNaN(d)) ...

Hasonló speciális érték az infinity, vagyis a végtelen érték, melyből van negatív és pozitív is:

double d = Double.PositiveInfinity;
...
if (Double.IsInfinity(d)) ...

Logikai típus

A logikai típus használata esetén mindössze két érték tárolását tudjuk végezni: az igaz-hamis, igen-nem értéket. A C# programban ezek angol megfelelőjét kell használni, ezek a true és false értékek.

A logikai típus neve bool, amelyet egy matematikusról, George Boole-ról kapta, aki a matematikai logika alapjait vetette meg.

A logikai típus tárolási igénye 1 byte. Mivel a számítógép memóriája számértékek tárolására képes, a logikai igaz/hamis értékek tárolása során trükköt kell alkalmazni. A logikai hamis (false) értéket 0 számérték, az igaz (true) értéket pedig az 1 számérték jelzi.

Megj.: igazából a true értéket a nem nulla érték jelöli, vagyis bármi más érték, például az 1, de akár a 2, 3, 126, ezek mind true értéket jeleznek.

A logikai értékek ezen számszerűsítése miatt működhet két logikai érték közötti kisebb-e vizsgálat, amelynek jelentése ezek alapján a false az kisebb mint a true.

A logikai értékek valójában 1 biten is eltárolhatóak lennének, hiszen 1 biten is ábrázolható a 0 és az 1. Az 1 byte elpazarlása erre a célra azért indokolt, mert a számítógép processzora nem képes egyetlen bitet megcímezni, beolvasni, módosítani a memóirában. A legkisebb kezelt egység a byte. Hiába van tehát 3 bool értékünk, amelyek együtt is békésen elférnének egyetlen byte-nyi memóriában - ezek külön-külön 1-1 byte-ot foglalnak el. Figyelembe véve az egész típusoknál megadott sebesség-optimalizációkat, az sem kizárt, hogy a logikai értékeink 4 byte-osával kerülnek be a memóriába, így 12 byte-t foglalván el belőle.

Karakter típus

A karakter típus neve char, és ezen típus egyetlen karaktert képes kezelni, tárolni.

A karakter fogalma bővebb, mint a betű, vagy a számjegyek, ebbe beletartoznak az írásjelek, egyéb szimbólumok is, mint pl. az e-mail címekben szereplő @ karakter is.

A karakterek tárolását szintén számokkal kell megoldani. Egy táblázat, név szerint az ASCII kódtáblázat írja le melyik karakternek mi a számkódja. Ez tehát egy egyszerű kétoszlopos táblázatnak fogható fel, melyben egyszerűen fel vannak sorolva melyik karakternek milyen számérték felel meg. A kis a betű kódja pl. a 97, míg a nagy A betűjé a 65. A É szimbólum kódja a 144 (ASCII kódtáblázat).

A kezdeti időkben úgy találták, hogy 256 féle karakter elég sokmindenre elég, ezért az ASCII kódtábla 0..255 közötti számkódokat tartalmaz. Idővel ennyi karakter, szimbólum kevésnek bizonyult, ezért másik kódtáblát készítettek, melyet UNICODE-nak neveznek. Ez az eredeti ASCII tábla bővítményének fogható fel. A UNICODE táblázatban nem áll meg a sorszámozás 255-nél, hanem újabb szimbólumok felvezetése mellett tart 65535-ig (alap UNICODE tábla). A UNICODE kódolás ennél a valóságban sokkal összetettebb, további információk a linken olvashatók.

Mivel unicode sorszámok már 65535-ig tartanak, ezen sorszámok leírására nem elég 1 byte, hanem 2 byte kell. A C# char típusa a unicode kódtábla alapján működik, ezért egy karakterérték tárolásához 2 byte-ra van szükség a memóriában.

Szöveg típus

Amennyiben nem egy, hanem sok karakterről, karaktersorozatról beszélünk, akkor a char típus már nem jöhet szóba, helyette a string típust kell használnunk.

A string típus rugalmas tárigényű. A szia szöveg tárolása kevesebb memóriába kerül, mint a malacka szövegé. A tárolási igény számításához ismerni kell a tárolandó karaketrek számát. A szia szöveg 4 karakteres, karakterenként 2-2 byte igényű, vagyis 8 byte. Ennyi azonban nem elég, mivel a memóriában egymást követő s, z, i, a karakterek tárolása 8 byte-on megoldható, de jelen esetben jelezni kell azt is, hogy itt vége is van a szövegnek. Ezt egy újabb, speciális karakterrel kell jelezni. Ez a 0-a kódú karakter a sorozat végére illesztésével oldható meg. Vagyis a szia végére még egy plusz karaktert kell tárolni, szintén 2 byte-on.

Egy n hosszú karakterből álló string tárolása n+1 karakter tárolását igényli, (n+1)*2 byte lekötésével jár. A valóságban ezen felül még körülbelül 20 byte ehhez hozzáadódik a tényleges szöveghossztól függetlenül, egyéb járulékos adat kezelése és tárolása miatt.


Kérdéses még, hogy milyen maximális hosszú string tárolására képes a .NET.

A string típusban tárolható maximális hosszúságú szöveg-sorozat a konkrét memóriától függ. De elvi felső korlát a 32 bites processzorok azon viselkedése, hogy 4 Gb-nál nagyobb összefüggő memóriaterületet (lap) nem képesek kezelni. 4 Gb-nyi (~4 milliárd byte) területen 2 milliárd karakterből álló string tárolható, tehát ez elvi felső korlát.

Gyakorlatilag nem könnyű ezt a felső határt megközelíteni. Erre több tesztelési metódus is létezik, melynek során nagyon hosszú string-eket próbál a program létrehozni, és vizsgálni, melyik az a pont amikor már nem sikerül. Sajnos a korábbi próbálkozások is befolyásolják, hogy egy új próbálkozás sikeres-e vagy sem, ezért ezek a tesztek eredményei nehezen értékelhetők, ráadásul más gépeken nem feltétlenül reprodukálhatóak. Mindenesetre megállapítható, hogy néhány százezer karakter hosszú string-eket problémamentesen tud kezelni a rendszer.

A string típusban tárolható szöveg hosszának felső korlátja függ a konkrét operációs rendszertől, a memória méretétől, és annak töredezettségétől. A felső korlátot nem maga a .NET, hanem az egyéb tényezők befolyásolják. Elvi felső korlát a 2 milliárd karakter hosszúságú string.


Speciális szöveg típusú érték az üres string. Ezt a "" formában (is) írhatjuk, ez egy nulla hosszú string. Ezt speciálisan a String.Empty konstanssal is leírhatjuk:

string s = String.Empty;
..
if (s==String.Empty) ...;

Az utóbbi feltételvizsgálatot az alábbi módon (is) szoktuk végezni:

if (s.Length==0) ...;

Hasonlónak tűnő érték a null érték, mely azonban nem egyenlő az üres string értékkel. De nem az! A null érték azt jelenti, hogy nincs is string. Egy ilyen értékű string változónak .Length tulajdonsága sincs:

string s = null;
..
if (s==null) ...; // ez jó
if (s==String.Empty) ...; // ez jó, bár nem egyenlők
if (s.Length==0) ...; // ez hibát okozna
Hernyák Zoltán
A lap eredeti címe: „http://wiki.ektf.hu/wiki/Mp1/page110
Nézetek
nincs sb_34.231.21.123 cikk