Adattárolás kettes számrendszerben



Hogyan is tárolja a számítógép az olyan egyszerű adatokat mint egy szám vagy egy mondat kettes számrendszerben? Mik vonhatóak le ezekből? Mik azok a bitműveletek?
I, Hány bites a géped?
II, Alaptípusok
    a, Sorszámozott típusok
        2, Az előjel nélküli számok
        3, Az előjeles számok
        5, A logikai típus
        8, A karakter típus
    b, Egyéb típusok
        4, A valós számok
        6, A void típus
        7, A mutató típus
        9, A szöveg típus
        10, A dátum/idő típus


I, Hány bites a géped?

Ez a kérdés alapjában meghatározza a számítás módját, méghozzá a processzor aritmetikai egységének számítási képességét (lásd: A számítógép alkatrészei).

A hány bites kérdés alapvetően azt határozza meg hogy hány bináris számjeggyel tud a gép egy időben dolgozni. Az hogy ez mekkora adatokat eredményez kis kombinatorikával számítható.

Azt tudjuk hogy egy biten két lehetőség van mindig (1/0, igen/nem). Gondoljuk végig hogy mennyi különböző variáció állhat elő pl. négy biten:

Ekkor az első bináris számjegy helyére kerülhet 1/0, a második helyére is, és így tovább. Ebből következik hogy a lehetőségek száma:

N = 2 * 2 * 2 * 2
N = 2^4
N = 16

Tehát összesen 16 különféle állapot állhat elő. Nézzük meg ez egy 32 bites processzor esetén mennyi?

N = 2^32 = 4294967296

Nos, ez már elég sok, lényegében bőségesen elég - vagy mégsem?

2, Az előjel nélküli számok

Egy ilyen szám átváltása kettes számrendszerbe nem egy nagy kaland, csak elosztogatjuk kettővel és felírjuk fordított sorrendben a maradékokat. Íme egy példa:

Az eredmény tehát: 1001 1010 1100, és mivel ez a szám mindössze 12 bitből áll eltárolható egy 32 bites adatként.

Az eredmény 32 biten: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1001 1010 1100

A számítógép az előjel nélküli számokat így tárolja egészen egyszerűen, és mivel hogy a nulla is része ennek a halmaznak a határok [0 .. 2^32 - 1] -ig terjednek.

Mivel a memóriacím (lásd: A számítógép alkatrészei) is egy ilyen szám alapvetően meghatározza hogy mennyi fizikai memória lehet a gépben. Ha megnézzük hogy mire elég 4294967296 byte, osztogassunk egy kicsit - azt kapjuk hogy ez pontosan 4 GB - vagyis e határ fölött nem tudja a gép megcímezni a memóriát 32 biten - ezért vezették be a 64 bites masinákat és rendszereket.

A C/C++ nyelvben ilyen típusú változók felvételére több lehetőségünk is van [8 bit = 1 byte]:

  • 8 bit (1 byte) [0 .. 255]: unsigned short short int, vagy unsigned char
  • 16 bit (2 byte) [0 .. 65535]: unsigned short int
  • processzornak megfelelő: unsigned int
  • 32 bit (4 byte) [0 .. 2^32 - 1]: unsigned long int
  • 64 bit (8 byte) [0 .. 2^64 - 1]: unsigned long long int

Megjegyzés: Egy másik számrendszerbeli szám visszaváltása tizesbe, helyiértékes logikával történhet:
    A tizes számrendszerben, minden számjegy egy 10^x szorzót jelöl, vagyis kettesben ez 2^x, tizenhatosban 16^x, stb.

Pl.: 0x3AC visszaváltása tizesbe [A=10, B=11, C=12, D=13, E=14, F=15]

3 * 16^2 = 768 A * 16^1 = 160 C * 16^0 = 12
3 A C

Így 0x3AC = 768 + 160 + 12 = 940

3, Az előjeles számok

Az előjeles számok képzésének alapja hogy a legelső bit jelöli hogy a szám negatív-e. Ha igen akkor érvénybe lép az ún. komplementer képzés (neten rengeteg leírás van róla), a lényeg hogy a 32 db bináris 1-es a decimális -1, és a 32 db bináris 0 a decimális 0.

Mivel egy bittel kevesebb van a tartomány feleződik így a határok is változnak (felvételkor a signed/unsigned kulcsszó elhagyása esetén signed típusú lesz a változó):

  • 8 bit (1 byte) [-127 .. 126]: signed short short int, vagy signed char
  • 16 bit (2 byte) [-32768 .. 32767]: signed short int
  • processzornak megfelelő: signed int
  • 32 bit (4 byte) [2^31 .. 2^31 - 1]: signed long int
  • 64 bit (8 byte) [2^63 .. 2^63 - 1]: signed long long int

4, A valós számok

A valós számok több byteon vannak tárolva mint az int típus, és bonyolultabb is működésük, a neten szintén van leírás "lebegőpontos számok" címszó alatt.

  • egyszeresen pontos (single): float
  • kétszeresen pontos (double): double
  • többszörösen pontos (long double): long double

A valós számok sem képesek végtelen pontos értékeket tárolni, a linken további információk érhetőek el a határokról. Mivel ez az állítás fennáll, sok esetben a számítások durva pontosságvesztéssel járhatnak (pl. float esetén), ha az adott szám végtelen tizedestört.

5, A logikai típus (igaz/hamis)

Bármely olyan sorszámozott típus, ami ha nulla akkor az állítás hamis, egyébként igaz.

6, A void típus

A void típussal direkt módon nem vehetünk fel változót, de helyettesíthet más típust, jelölve hogy "nincs típusa" a változónak, vagy ha van is az érvénytelen. A függvényeknél/mutatóknál van nagy haszna.

7, A mutató típus

Említetve már a memóriacímeket többször is, érdemes bevezetni magát a mutató típust - ami egy előjel nélküli szám (lásd: A számítógép alkatrészei), de ettől függetlenül még lehet típusa.

pl.:

  • int* - egy int típusú változó memóriacímét tartalmazza
  • char* - egy char típusú változó memóriacímét tartalmazza
  • float* - egy float típusú változó memóriacímét tartalmazza
  • void* - egy változó memóriacímét tartalmazza, de nem tudni milyen típusúét (talán nem is fontos?)

Megjegyzés: Létezik egy típus nélküli érvénytelen mutató, a NULL. Ezt lehet minden olyan helyre használni, ahol mutató helyett nem akarunk átadni semmit.

Figyelmeztetés: Minden mutatóművelet veszélyekkel jár - ha az adott mutató rossz helyre mutat, vagy NULL akkor jó eséllyel össze fog omlani (védett mód szépségei).

8, A karakter típus

Néha szükséges lehet az is hogy betűket is tároljunk a gépen, hiszen a szöveges közlésnek ez az alapja. Ezt úgy érték el hogy minden egyes billentyűnek adtak egy számot (igen már megint), és ezeket kódtáblába tették. Több ilyen kódtábla van, mivel hogy a sok nyelv karakterei nem fértek bele 1-1 bájtba.

Mivel hogy az első kettő egy bájton van tárolva, míg a Unicode két bájtos erre a szövegeknél oda kell figyelni.

Természetesen ezektől eltérő kódlapok is léteznek, de ezek a leghagyományosabbak.

Megjegyzés: A programban lehetőség van az ékezetes karakterek helyes kiíratására is, ha a programot 852-es kódlappal mentjük el (a Notepad++ képes erre). Ennek hatására az IDE-ben nem lesznek jól olvashatóak az ékezetes karakterek, de a lefordított programban igen.

9, A szöveg típus

A C/C++ nyelvben a nyers szöveges típus jele a char*, illetve Unicode esetben a wchar_t*.

Ha már egy karaktert tudunk számmal leírni az korrekt, de egy egész mondat leírása azért már összetettebb. Ezt a gép úgy éri el hogy felveszi egymás utáni memóriaterületekre a karaktereket (tömb/vektor), és az első memóriacímét közli velünk.

Két kérdés merülhet fel:

  1. Honnan tudjuk hol a vége?
  2. Honnan tudjuk mennyit kell léptetni?

Teljesen mindegy hogy ASCII/ANSI kódolással van dolgunk kiolvasunk egy betűt, és léptetünk a címen 1 byteot. Unicode esetben erre nagyon oda kell figyelni hogy kettőt.

Szerencsére amint ezt látni fogjuk a puts(), printf(), stb... függvények ezt a léptetgetést elvégzik helyettünk és kiírják a képernyőre a szöveget.

A szöveg vége egy érdekesebb történet, itt többféle megoldás van - egyes programnyelvekben az első karakter nem is karakter hanem egy szám hogy milyen hosszú a szöveg; viszont a C/C++ megoldás más:

A szöveg mindig +1 karakterrel hosszabb mint kéne, és az utolsó karakter egy nullás indexű karakter. Pl. (ANSI):

A z   a l m a   z ö l d .  
65 122 32 97 108 109 97 32 122 246 108 100 46 0

Ezen a módon a program addig írja ki a szöveget ameddig '\0' karaktert nem talál. Ez egy praktikus megoldás, de megint érvényes (lásd: A számítógép alkatrészei), hogyha rossz címet íratunk ki, vagy összeomlik a program, vagy '\0' híján hülyeségeket fogunk kapni, és nem áll le a kiírás.

Megadása (először kezdőértékkel, majd fix hosszal):

char* szoveg = "Alma";

char szoveg2[10];

A C/C++ alapvetően mindig ANSI módban dolgozik ha a char, char*, stb. változókkal dolgozunk és nem használunk speciális beállításokat.

10, A dátum/idő típusa

A dátum/idő tárolására is lehetőség van a C/C++ nyelvben, amihez a time_t típust lehet használni.

A tároláshoz a rendszer egy speciális valós számot használ, amiben az eltelt másodperceket jelöli 1970 január 1.-je, 0:00 óta. Nekünk ez első ránézésre tökéletesen haszontalan ám a gép remekül tud számolni vele.

A dátum típus használatához szükséges a time.h fájl (később lesz szó az ilyen fájlokról).

A dátum/idő tárgyalása nem célja ennek a leírásnak, az ezekkel kapcsolatos információk igény szerint itt megtalálhatóak (angol nyelven).