Szerző Téma: Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban  (Megtekintve 10851 alkalommal)

Nem elérhető tonyo

  • 1335
  • Moderális Generátor
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Dátum: 2012. Július 30. - 13:23:10 »
+26
Ezt a témát már jóval régebben meg akartam írni, mert tudom, hogy sokan hallottak a bitmûveletekrõl, de a nagy többség nem igazán tudja mire is jó, és hogyan kell használni. A téma elsõ felében a bitmûveleteket fogom taglalni, ez egy fõként elméleti rész lesz, míg a második felében gyakorlatban is megmutatom, mire is jó ez az egész, többek között elmagyarázom hogyan is mûködik az a bizonyos 4 függvény, mely egy jármû állapotát állíthatjuk be(encode_lights, encode_tires, encode_doors, encode_panels), és még pár apróbb érdekességet mutatok ezzel kapcsolatban.
Elõször is el kell fogadni egy dolgot: ez nem kezdõknek való. A bitmûveletek nem alapszintû tudást igényelnek, és hál\' Istennek, elég ritkán van rájuk szükség. Azonban, a SA:MP-ban is tudjuk hasznosítani, többféleképpen is. Erre majd késõbb kitérek.


Tartalomjegyzék
 

  • 1. Bitmûveletek


    1. Bitmûveletek
     


    1.1 Bevezetés
    A bitmûveletek arra használhatóak, hogy az egész számok bitjeit külön-külön kezelhessük. Mint írtam, erre egy átlag \"programozónak\" (a pawn scripter nagyon messze áll a programozótól...) elég ritkán van szüksége, de vannak helyzetek, mikor jól jöhet ha ismerjük. A bitmûveletek megértéséhez elõször is tisztában kell lennünk a kettes számrendszerrel.


    1.2 A kettes (bináris) számrendszer
    A kettes vagy bináris számrendszerben az 1-es és a 0-s számjegyekkel ábrázoljuk a számokat. Digitális áramkörökben is a kettes számrendszerrel való számolást a legegyszerûbb megoldani, ezért szinte biztosak lehetünk benne, hogy manapság a számítógépekben, és szinte minden modern elektronikai eszközben ilyen számrendszert használnak  programok.
    Arról nem igazán szeretnék írni, hogy hogyan is lehet számolni kettes számrendszerben, egyrészt mert erre nem lesz feltétlen szükségünk (persze nem árt ha megértjük), másrészt akit érdekel az úgyis utána fog nézni, rengeteg jó forrás, leírás található errõl a neten.


    1.3 Részletesebben a bitekrõl a Pawn-ban
    A bit az információ alapegysége a számítástechnikában. Kétféle értéke lehet: 0(hamis) és 1(igaz). A név az angol binari digit, vagyis bináris számjegy kifejezésbõl származik.
    Manapság rengeteg programozási nyelv létezik, melyek eltérnek abban, hogy hány bites változókat használnak az adott nyelvben. A Pawn egy 32 bites programozási nyelv, vagyis minden változó 4 bájt(byte), vagyis 32 bit(bit). Ebbõl joggal következtethetünk arra, hogy a 232-1 a maximális legnagyobb eltárolható érték. Azért kell a -1, mert 0-tól indítjuk a számolást. Ez azt jelentené, hogy a maximális eltárolható érték 4,294,967,295. Ez azonban nem így van.
    Hogy miért is nem, ahhoz elõször is jöjjön egy kis háttérinfó. A legtöbb(minden) programozási nyelv esetén találkozhatunk integer változókkal. Ezeknek 2 típusát különböztetjük meg. Léteznek az úgynevezett elõjeles egész változók(signed integers), és léteznek elõjel nélküli(más néven pozitív) egész változók(unsigned integers). A különbség a kettõ között, hogy még egy elõjeles egész változóban eltárolhatsz negatív értéket is, addig az pozitív egész változóban nincsenek negatív számok. Utóbbinak tehát 4,294,967,295 a maximális eltárolható értéke. A Pawn azonban nem az utóbbi típust használja.
    A Pawn elõbbi, vagyis elõjeles egész változókat (signed integers) használ. 1 bitet fenntart a 32-bõl az elõjel számára(pozitív, vagy negatív). Ezt a bitet szokás MSB-nek(Most significant bit) hívni. Ha ez a bit be van kapcsolva, akkor a szám negatív, ha ki vna kapcsolva, akkor a szám pozitív. Így már egybõl érthetõ, hogy miért is 231-1 a maximális eltárolható érték, és nem pedig 232-1. Ebbõl következik tehát, hogy a Pawn-ban az integer változók értékkészlete: -231(-2,147,483,648) és 231-1(2,147,483,647) közötti számok.
    Ha egy változó létezik, akkor abban minden esetben, mind a 32 bit \"használtban\" van. A bináris számok elé bármennyi 0-t írhatunk, az értéke nem változik. Ez azt jelenti, hogy figyelembe kell vennünk, hogy ha nekünk az 1-es szám van eltárolva, mely alapból a bináris számrendszerben 1, az a Pawnban 00000000000000000000000000000001-ként lesz eltárolva. Ez késõbb még fontos lesz!
    A késõbbiekben szükségünk lesz még egy háttér információra. A Pawn egy úgynevezett 2-es komplementer rendszert használ, hogy elõállítsa a negatív számokat. Akik jártasak a C-ben, azon belül is a bitmûveletekben, azok tudják mit is jelent ez. Nem olyan bonyolult, tehát: fogjuk az alap számot, és minden bitet kicserélünk az ellentétére. Tehát az egyeseket 0-ra, a nullákat 1-esre. Végül a számhoz hozzáadunk egyet, és ezzel meg is kaptuk a negatív számot.
    Itt egy példa:
     
    00000000000000000000000000000001//
    1
    11111111111111111111111111111110//
    minden bit komplementerét vesszük
    11111111111111111111111111111111//
    hozzáadtunk egyet, így megkaptuk a -1-et.
    [/quote]


1.4 Operátorok
A bitekkel mûveleteket végezhetünk, ehhez különbözõ operátorok állnak rendelkezésünkre. Lentebb minden operátort egyesével ki fogok fejteni, azt is, hogy mit csinál, és leírom az igazságtáblázatukat is. Konkrétan a következõ operátorok állnak rendelkezésünkre:
 
  • Bitenkénti (bitek közötti) ÉS

  • Bitenkénti (bitek közötti) VAGY

  • Bitenkénti (bitek közötti) KIZÁRÓ VAGY

  • Bitenkénti (bitek közötti) NEGÁLÁS

  • Biteltolás (arithmetic shift, shiftelés) operátor(ok)

  • Logikai biteltolás (logical shift, logikai shiftelés) operátor


Az elsõ négy felsorolt operátor tartozik a logikai operátorok közé, még az utolsó elõtti(igazából utolsó elõtti kettõ, mert tolhatjuk jobbra, illetve balra, azaz 2 shiftelõ operátor lesz), és az utolsó biteltolási mûvelet. Az operátorok a számok azonos helyi értékû bitjei között mûködnek, a számok minden egyes bitjére.


1.4.1 Bitenkénti (bitek közötti) ÉS mûvelet
A bitenkénti ÉS mûvelet (AND, &) egy logikai operátor. Fontos, hogy nem összekeverendõ az \"&&\" logikai operátorral! Hasonlít rá, de ez a mûveleteket bitenként végzi. Ez az operátor csak akkor ad eredményül 1-et, ha mindkét bit 1, minden más esetben az eredmény 0. Igazság táblázata:
 
A
B
A&B
0
0
0
1
0
0
0
1
0
1
1
1
[/quote]
Példának nézzük meg, mi történik, ha a bitenkénti ÉS operátort alkalmazzuk, a 11 és a 7 esetén. A 11 bináris számrendszerben 00000000000000000000000000001011-nek felel meg, a 7 pedig 00000000000000000000000000000111-nek. Tehát a 11&7, az ugyaz mint ha ezt írnám: 00000000000000000000000000001011 & 00000000000000000000000000000111. Az átláthatóság kedvéért most egymás alá fogom írni a 2 számot, hogy áttekinthetõbb legyen, és az eredményt pedig a 3. sorba:
 
00000000000000000000000000001011
&00000000000000000000000000000111
00000000000000000000000000000011
[/quote]
Eredményül tehát 00000000000000000000000000000011-et kaptunk, ami a kettes számrendszerben a 3-asnak felel meg. A következõ 2 sor tehát ugyanazt jelenti:
 
Bin:
00000000000000000000000000001011
&
00000000000000000000000000000111
=
00000000000000000000000000000011
Dec:
11
&
7
=
3
[/quote]
Még néhány példa, de ezeket már nem részletezem:
 
00000000000000000000000000000011 & 00000000000000000000000000000101 = 00000000000000000000000000000001
00000000000000000000000001110110 & 00000000000000000000000001011001 = 00000000000000000000000001010000
[/quote]


1.4.2 Bitenkénti (bitek közötti) VAGY mûvelet
A bitenkénti VAGY mûvelet (OR, |) egy logikai operátor. Fontos, hogy nem összekeverendõ a \"||\" logikai operátorral! Hasonlít rá, de ez a mûveleteket bitenként végzi. Ez az operátor akkor ad eredményül 1-et, ha legalább az egyik bit 1. Ha egyik bit sem 1, akkor 0-t ad eredményül, ha mindkét bit 1, akkor is 1 az eredmény. Igazság táblázata:
 
A
B
A|B
0
0
0
1
0
1
0
1
1
1
1
1
[/quote]
Példának nézzük meg, mi történik, ha a bitenkénti VAGY operátort alkalmazzuk, szintén a 11 és a 7 esetén. A 11 bináris számrendszerben mint már írtam 00000000000000000000000000001011-nek felel meg, a 7 pedig 00000000000000000000000000000111-nek. Tehát a 11 | 7, az ugyaz mint ha ezt írnám: 00000000000000000000000000001011 | 00000000000000000000000000000111. Az átláthatóság kedvéért ismét egymás alá fogom írni a 2 számot, hogy áttekinthetõbb legyen, és az eredményt pedig a 3. sorba:
 
00000000000000000000000000001011
|00000000000000000000000000000111
00000000000000000000000000001111
[/quote]
Eredményül tehát 00000000000000000000000000001111-et kaptunk, ami a kettes számrendszerben a 15-nekk felel meg. A következõ 2 sor tehát ugyanazt jelenti:
 
Bin:
00000000000000000000000000001011
|
00000000000000000000000000000111
=
00000000000000000000000000001111
Dec:
11
|
7
=
15
[/quote]
Megint néhány példa:
 
00000000000000000000000000000011 | 00000000000000000000000000000101 = 00000000000000000000000000000111
00000000000000000000000001110110 | 00000000000000000000000001011001 = 00000000000000000000000001111111
[/quote]


1.4.3 Bitenkénti (bitek közötti) KIZÁRÓ VAGY mûvelet
A bitenkénti KIZÁRÓ VAGY mûvelet (XOR, ^) egy logikai operátor. Ez az operátor nagyon hasonlít a VAGY operátorra, az a fõ különbség a kettõ között, hogy amennyiben mindkét bit 1-es, akkor a KIZÁRÓ VAGY operátor 0-t ad eredményül, a többi esetben ugyanúgy viselkedik mint a VAGY operátor. Igazság táblázata:
 
A
B
A^B
0
0
0
1
0
1
0
1
1
1
1
0
[/quote]
Példának nézzük meg, mi történik, ha a bitenkénti KIZÁRÓ VAGY operátort alkalmazzuk, szintén a 11 és a 7 esetén. A 11 bináris számrendszerben mint már írtam 00000000000000000000000000001011-nek felel meg, a 7 pedig 00000000000000000000000000000111-nek. Tehát a 11 ^ 7, az ugyaz mint ha ezt írnám: 00000000000000000000000000001011 ^ 00000000000000000000000000000111. Az átláthatóság kedvéért ismét egymás alá fogom írni a 2 számot, hogy áttekinthetõbb legyen, és az eredményt pedig a 3. sorba:
 
00000000000000000000000000001011
^00000000000000000000000000000111
00000000000000000000000000001100
[/quote]
Eredményül most 00000000000000000000000000001100-t kaptunk, ami a kettes számrendszerben a 12-nek felel meg. Érdekes, hogy teljesen más eredményt kapunk, pedig lényegében ugyanazt az eredményt adja az operátor, - kis különbséggel ugyebár - mint a VAGY operátor.A következõ 2 sor tehát ugyanazt jelenti:
 
Bin:
00000000000000000000000000001011
^
00000000000000000000000000000111
=
00000000000000000000000000001100
Dec:
11
^
7
=
12
[/quote]
Megint néhány példa:
 
00000000000000000000000000000011 ^ 00000000000000000000000000000101 = 00000000000000000000000000000110
00000000000000000000000001110110 ^ 00000000000000000000000001011001 = 00000000000000000000000000101111
[/quote]
Ezzel kapcsolatban egy érdekességet mutatnék meg. Vegyünk 2 számot. A látványosság kedvéért én példának a 118-at és a 89-et veszem.
 
1. szám: 00000000000000000000000001110110 = 118
2. szám: 00000000000000000000000001011001 =  89
Mûvelet elvégzése: 00000000000000000000000001110110 ^ 00000000000000000000000001011001 = 00000000000000000000000000101111 =  47
1. szám az eredmény, 2. szám az eredeti 2. szám: 00000000000000000000000001011001 =  89
Eredmény: 00000000000000000000000000101111 ^ 00000000000000000000000001011001 = 00000000000000000000000001110110 = 118
[/quote]
Vagyis visszakaptuk a kiindulási állapotot ;)


1.4.4 Bitenkénti (bitek közötti) NEGÁLÁS
A bitenkénti NEGÁLÁS (NOT, ~) az utolsó logikai operátor, melyet meg fogok mutatni. Ez az operátor eléggé eltér az eddigiektõl, leginkább abban, hogy ezt a mûveletet nem 2 bitre alkalmazzuk, hanem csak 1-re. Szokás komplementer-operátornak is hívni(konyhanyelven). Lényegében az 1-esekbõl 0-t, a 0-kból 1-est csinál. Igazság táblázata:
 
A
~A
0
1
1
0
[/quote]
Példának nézzük meg, mi történik, ha a bitenkénti NEGÁLÁST alkalmazzuk a 7-es számra. A 7 bináris számrendszerben mint már írtam 00000000000000000000000000000111-nek felel meg. Tehát a ~7, az ugyaz mint ha ezt írnám: ~00000000000000000000000000000111. Nézzük tehát, mi történik:
 
~00000000000000000000000000000111
11111111111111111111111111111000
[/quote]
Eredményül most 11111111111111111111111111111000-t kaptunk, ami a kettes számrendszerben a -8-nak felel meg. A következõ 2 sor tehát ugyanazt jelenti:
 
Bin:
~
00000000000000000000000000000111
=
11111111111111111111111111111000
Dec:
~
7
=
-8
[/quote]
Megint néhány példa:
 
~00000000000000000000000000000101 = 11111111111111111111111111111011
~00000000000000000000000001110110= 11111111111111111111111110001010
[/quote]
Ehhez kapcsolódóan lenne itt egy fontos megjegyzendõ dolog. Aki nem érti, hogy miért -8 a megoldás, az nem olvasta el elég rendesen a \"Részletesebben a bitekrõl a Pawn-ban\" részt.


1.4.5 Biteltolás (arithmetic shift, shiftelés) operátor(ok)
A shift operátorok(<<, >>) a bitek tologatására használhatóak. A << balra tol, a >> jobbra. Bal oldalt található a szám, amit tologatni kívánunk, jobb oldalt pedig az, hogy mennyivel szeretnénk eltolni.
1.4.5.1 Jobbra toló operátor(>>):
Ez az operátor a szám bitjeit jobbra tolja a megadott mértékkel. Egy bittel való eltolás, 2-vel való osztásnak felel meg. Nézzünk egy példát:
 
00000000000000000000000000001000//8
>>2
00000000000000000000000000000010//2
[/quote]
Láthatjuk, hogy semmi más nem történt, mint minden bitet 2-vel hátrébb mozgattunk. Az így \"megüresedett\" bitek helyére pedig 0-k kerültek. Lényegében az történt, hogy az utolsó 2 bit kiesett, az így megmaradt biteket 2-vel hátrébb csúsztattuk, majd az elsõ 2, így \"üres\" bit 0 értéket kapott.
Nézzünk egy példát negatív számmal:
 
11111111111111111111111111110000//-8
>>2
11111111111111111111111111111100//-2
[/quote]
Láthatjuk, hogy itt fontos szerepe van az MSB-nek. Ettõl függ ugyanis, hogy 0-k vagy 1-esek kerülnek a \"megüresedett\" bitek helyére. Fontos tehát megjegyezni, hogy a jobbra shiftelés megtartja a számok elõjelét, nem változtat azon!
1.4.5.2 Balra toló operátor(<<):
Ez az operátor a szám bitjeit balra tolja a megadott mértékkel. Egy bittel való eltolás, 2-vel való szorzásnak felel meg. Nézzünk egy példát:
 
00000000000000000000000000001000//8
<<2
00000000000000000000000000100000//32
[/quote]
Itt megint könnyedén észrevehetõ mi is történt. Minden bitet fentebb \"toltunk\" 2-vel. Azok a bitek, amelyek ezáltal a 32. bit elé kerültek, kiesnek. Az így megüresedett bitek 0-kkal egészültek ki. Eddig nem túl bonyolult. Azonban figyeljünk oda, ha túl sokáig shiftelünk egy pozitív számot, akkor az utolsó érték, melyet fel tud venni a változónk (mielõtt még túlcsordulás miatt elérné a 0-t), az egy negatív szám!
Nézzük mi történik negatív számok esetén:
 
11111111111111111111111111111000//-8
<<2
11111111111111111111111111100000//-32
[/quote]
Láthatjuk hogy a balra shifteléskor a szám mindig 0-kkal egészítõdik ki, legyen az akár negatív, akár pozitív. Nem veszi figyelembe az MSB értékét, így ha túl sokat shiftelünk, akkor elõfordulhat, hogy a szám elveszti negatív értékét, és eléri a 0-t.


1.4.6 Logikai biteltolás (logical shift, logikai shiftelés) operátor
Elõször is le kell tisztázni, hogy logikai shiftelésbõl csak egy létezik, jobbra logikai shiftelés(>>>). Sokban hasonlít a jobbra shiftelõ operátorra (>>), azonban van egy fontos különbség.
Nézzük az elsõ példát:
 
00000000000000000000000000001000//8
>>>2
00000000000000000000000000000010//2
[/quote]
Eddig nem igazán tapasztalhattunk semmi változást, minden pont ugyanúgy zajlott, mint a jobbra shiftelésnél(>>).
Nézzük mi történik negatív szám esetén:
 
11111111111111111111111111111000//-8
>>>2
00111111111111111111111111111110 //1073741822
[/quote]
Itt már láthatjuk a különbséget. Logikai shiftelés esetén az üres bitek helyére bekerülõ érték nem függ az MSB-tõl, hanem mindig 0. Emiatt soha nem kaphatunk eredményül negatív számot, logikai shiftelés esetén.


1.5 Elsõ fejezet vége
Nos, ezennel átvettünk mindent, amit érdemes tudni a bitmûveletekrõl. Megmutattam az alapok, hogy hogyan épülnek fel a változók, valamint átvettük a fõ operátorokat. Eddig csak színtiszta elmélet volt. A következõ részben megmutatom, hogyan hasznosíthatjuk mindezt a gyakorlatban, vagyis mire is jó az a sok marhaság, amit eddig leírtam.


2. Gyakorlati példák, érdekességek
 


2.1 Áttekintés, mikrõl is lesz még szó
Az alábbiakban még pár dolgot szeretnék mutatni. Elõször is néhány \"függvénnyel\" fogok foglalkozni, majd mutatok még egy-két érdekességet. Konkrétan megmutatom, hogyan tudjuk egy jármû minden \"paneljének\" adatát kiíratni, részletesen(erre alapból nincs lehetõség, és a függvények is mind \"encode\" függvények, én viszont mutatok egy-egy decode \"verziót\" is.)
Az alábbi 4 függvénnyel fogok foglalkozni:
 
  • encode_tires

  • encode_panels

  • encode_doors

  • encode_lights


De elõbb egy kis háttérinfó:
A Pawnban a forrásban megadhatunk bináris számokat ugyanúgy, ahogyan Hex számokat is megadhatunk. Ehhez a \"0b\" prefixet kell használnunk. Tehát ha bináris számunk az 1011 és ezt akarjuk adni értékül egy változónak, azt így tehetjük meg:
 
new var = 0b1011;

 


2.2 encode_tires
Háttér információ:
A SA:MP-ban egy jármû abroncsainak adataid 4 bit tárolja. Az elsõ bit a jobb hátsó, a második bit a jobb elsõ, a harmadik bit a bal hátsó, a negyedik bit pedig a bal elsõ abroncsról tárol információt. Mivel 4 bitben eltárolhatóak az információk, így decimális formában 0-15 értékekkel is be lehet állítani a jármûvek abroncsait. 16-tól kezdve az értékek már ismétlõdni kezdenek, szóval a 16-nál az eredmény ugyanaz mint 0-nál, 17-nél ugyanaz mind 1-nél, és így tovább. Ez érthetõ, ha tudjuk hogyan épül fel a kettes számrendszer.(Ne felejtsük el, a biteket hátulról számozzuk!)
Nos nézzük hogyan néz ki a kód:
 
encode_tires(tire1, tire2, tire3, tire4)
{
    return tire1 | tire2 << 1 | tire3 << 2 | tire4 << 3;
}

 
Láthatjuk, hogy bekérjük a 4 abroncs adatát(vagyis hogy ép[0], vagy kidurrant[1]), majd egy sima bitenkénti VAGY mûvelet és biteltolás kombinálásával visszatérünk az eredménnyel. A kódban a tire1 felel meg a jobb hátsó abroncsnak, a tire2 a jobb elsõ, a tire3 a bal hátsó, és a tire4 a bal elsõnek.
Nézzünk egy konkrét példát. Tegyük fel, hogy én a hátsó 2 abroncsot ki akarom durrantani, még az elsõ kettõt épre akarom beállítani. Ez esetben így néz ki a dolog:
 
encode_tire(1,0,1,0);

 
Ha kiíratnánk konzolba a visszatérési értéket (%d-vel), 5-öt kapnánk. Miért is? Az 5 bináris alakja 0101. Hoppá, ha megnézzük ez pont fordítottja annak, amit mi megadtunk számjegyekként, csak már konkrét bináris számként. Nézzük hogyan is történhetett mindez, mit is csináltunk. A függvény a következõ mûveletet végzi:
 
tire1 | tire2 << 1 | tire3 << 2 | tire4 << 3

 
Konyhanyelven úgy magyarázhatnánk el a dolgot, hogy ez a kód egymás mellé pakolgatja a megadott számokat, fordított sorrendben. Na de mi most nem a konyhanyelv miatt vagyunk ebben a leírásban, szóval nézzük a kódunkat. Lényegében ez futott le, a konkrét értékeinkkel nézve:
 
1 | 0 << 1 | 1 << 2 | 0 << 3

 
Bármily furcsa is, ez egy mûvelet, melyen szépen végig halad sorban a program. Elõször is írjuk át az egészet bináris alakba(az egyszerûség kedvéért most csak 4 bitet fogok kiírni):
 
0001 | 0000 << 1 | 0001 << 2 | 0000 << 3

 
Na erre se mondtuk volna rá a tutorial elsõ részének elolvasása elõtt, hogy ez egy mûveletsor. A rendszer elsõnek az eltolásokat végzi el (olyan ez mint a 2*3+2*4+2*5 mûveletben elõször a szorzásokat végezzük el, és csak utána adunk össze, ha nincs zárójel), és csak utána a bitenkénti VAGY mûveleteket, ezért mi is így fogunk tenni. Emlékezzünk, eltoláskor az \"üres\" bitek 0 értéket fognak felvenni. Végezzük el külön csak az eltolásokat:
 
0000 << 1 = 0000
0001 << 2 = 0100
0000 << 3 0000

 
Nos, ha ezeket elvégeztük, akkor nézzük, hogy is áll most a kódunk:
 
0001 | 0000 | 0100 | 0000

 
Négy azonos mûvelet van hátra, így hát a program innen más szépen sorban fog haladni. Tegyünk mi is így. Elsõ tehát a 0001 | 0000 mûvelet elvégzése. Emlékeztetõül, a bitenkénti VAGY mûvelet akkor ad 0-t eredményül, ha mind a kettõ érték 0, minden más esetben 1-et. Végezzük el külön az elsõ bitenkénti VAGY mûveletet:
 
0001 | 0000 = 0001

 
Nem volt bonyolult, nézzük tehát hogy áll akkor most a teljes mûveletünk:
 
0001 | 0100 | 0000

 
Haladunk szépen tovább, elvégezzük a 0001 | 0100 mûveletet.
 
0001 | 0100 = 0101

 
Nincs mit magyarázni rajta, egyszerû bitenkénti VAGY mûvelet volt ez is. Már csak ez van hátra:
 
0101 | 0000

 
Nos, ez sem lesz egy bonyolult mûvelet:
 
0101 | 0000 = 0101

 
Ezzel meg is kaptuk a végeredményt, ami nem más, mint a 0101 (tízes számrendszerben 5). Mivel azonban eléggé kusza lett így a számításunk, nézzük egyben hogyan is néz ki az egész, ha nem bontom ennyire részekre:
 
0100 | 0000 << 1 | 0001 << 2 | 0000 << 3
v
0001 | 0000 | 0100 | 0000
v
0001 | 0100 | 0000
v
0101 | 0000
v
0101

 
Nos, ezennel láthattuk a gyakorlatban is, hogyan mûködik a függvény. Kijelenthetjük tehát, hogy a függvény a számokat a sorrendjüknek megfelelõ számú bitbe helyezi. Mivel azonban a biteket hátulról számozzuk, így az 1,0,1,0 bemenetek végeredménye 0101 lesz. Így már nem is olyan bonyolult :)
Na, ha már ilyen sokáig eljutottunk, akkor felhasználhatjuk ezt a kódot arra, hogy az alapjait figyelembe véve elkészítsük a decode változatot. Mivel 4 értékkel térünk vissza, ezért paraméterként kell majd 4 változó, melyeknek módosíthatjuk az értékét, illetve 1 paraméter pedig a dekódolandó szám. A függvényünk \"címsora\" tehát így néz ki:
 
decode_tires(tires, &tire1, &tire2, &tire3, &tire4)

 
Hogy példánk ne csak elmélet legyen, a tires változó legyen az elõbbi 0101 értékkel egyenlõ.
Na akkor menjünk tovább. Bemeneti értékünk ugye a \"tires\" változóban lesz eltárolva, így ezzel fogjuk a mûveleteket végezni. Kezdjünk el gondolkozni. Hogyan is tudnánk ellenõrizni egy adatot. Legegyszerûbb ha a bitenkénti ÉS mûveletet alkalmazzuk. Miért is? Nos, a bitenkénti ÉS mûvelet ugye akkor ad eredményül 1-et, ha mind a 2 érték egy. Tehát ha min egy számot összehasonlítunk a 1-el(binárisan ugye 00000000000000000000000000000001, akkor az eredményünk lényegében arról fog tájékoztatni, hogy az elsõ bit 0, vagy 1. Nem is olyan bonyolult ez. Nézzük akkor az elsõ bitet. Arra emlékezhetünk még, hogy az elsõ bit a tire1, vagyis jobb hátsó abroncs adatait tárolja. Tehát:
 
tire1 = tires & 1;

 
Vagy konkrétan a példánk esetében:
 
tire1 = 0101 & 0001

 
Mivel a bitenkénti ÉS mûvelet akkor ad 1-et eredményül, ha mind a kettõ bit 1, akkor már láthatjuk is, hogy miért elegendõ az elsõ bitet szemügyre venni. Mivel mind a 2 helyen az elsõ bit 1, ezért a tire1 változónk értéke is 1(0001) lesz. Egyszerû és nagyszerû, máris megkaptuk a jobb hátsó abroncs állapotát.
Gondolkozzunk hát tovább. Nekünk most arra van szükségünk, hogy a 2. bit értékét megkapjuk. Hogyan kaphatjuk ezt meg? Legegyszerûbb megoldás, ha a tires változót jobbra shifteljük 1-el, majd ismételten bitenkénti ÉS mûveletet végzünk. Nézzük tehát, hogyan is nézne ez ki:
 
tire2= tires >> 1 & 1;

 
Vagy konkrétan a mi példánkban:
 
tire2 = 0101 >> 1 & 0001
v
tire2 = 0010 & 0001

 
Ismételten bitenkénti ÉS mûvelet, és az eredményünk 0, mivel csak az egyik bit 1, a másik 0, így a bitenkénti ÉS mûvelet eredménye is 0. Ezen az elven megcsináljuk a maradék 2 abronccsal is:
 
tire3= tires >> 2 & 1;
tire4= tires >> 3 & 1;

 
Vagy a mi esetünkben konkrét példánkkal:
 
tire3 = 0101 >> 2 & 0001 // azaz: --> 0001 & 0001 = 1
tire4 = 0101 >> 3 & 0001 // azaz: --> 0000 & 0001 = 0

 
A teljes kódunk tehát:
 
decode_tires(tires, &tire1, &tire2, &tire3, &tire4)
{
    tire1 = tires & 1;
    tire2 = tires >> 1 & 1;
    tire3 = tires >> 2 & 1;
    tire4 = tires >> 3 & 1;
}

 
A konkrét példánkban a 4 változónk értéke a függvény lefutása után:
 
tire1: 1
tire2: 0
tire3: 1
tire4: 0

 
Ha összehasonlítjuk, ezek pont azok az értékek, mint amiket az encode függvényben megadtunk, tehát úgy tûnik jól dolgoztunk ;)


2.3 encode_lights
Ez a rész lesz a legrövidebb. Az egész lámpás rész pontosan ugyanarra a sémára épül mint az abroncsok. Az adott bitek ugyanazon a \"helyen\" lévõ lámpa adatait szolgáltatják, ugyanúgy 0 az ép, és 1 a sérült lámpák állapot azonosítója, és ugyanúgy 1-1 bit tárolja az értékeket.
Maga a kód:
 
encode_lights(light1, light2, light3, light4)
{
    return light1 | light2 << 1 | light3 << 2 | light4 << 3;
}

 
Láthatjuk hogy halál pontosan ugyanaz mint az encode_tires, csak más változónevekkel. Így hát a decode függvény is ugyan az lesz, csak a változóneveket kell módosítanunk:
 
decode_lights(lights, &light1, &light2, &light3, &light4)
{
    light1 = lights & 1;
    light2 = lights >> 1 & 1;
    light3 = lights >> 2 & 1;
    light4 = lights >> 3 & 1;
}

 
Ha megnézzük látjuk, hogy teljesen ugyan az a séma, ugyan az a felépítés, minden megegyezik, ezért nem is írtam le újból ugyanazt, amit egyszer már kifejtettem.


2.4 encode_panels
Háttér információ:
A SA:MP 7 panelrõl tárol adatokat. Egy panel adatait 4 bit írja le. Nos, ennél több infóra nekünk most nincs szükségünk, az elõzõ kód alapján ezen kód is megérthetõ, így az encode verzióhoz nem is nagyon szeretnék magyarázatot fûzni. A különbség az encode_tires-hez képest, hogy itt 4-essével shiftelünk, mivel 1 panel adatait 4 bit tárolja.
Maga a kód:
 
encode_panels(flp, frp, rlp, rrp, windshield, front_bumper, rear_bumper)
{
    return flp | frp << 4 | rlp << 8 | rrp << 12 | windshield << 16 | front_bumper << 20 | rear_bumper << 24;
}

 
Nézzünk egy decode verziót, ehhez a kódhoz. Alapjaiban ugyanazon a vonalon kell elindulnunk, mint a decode_tires esetében. Itt most 7 visszatérési értékünk lesz, és 1 bemeneti érték. Tehát így néz ki a függvény header:
 
decode_panels(panels, &flp, &frp, &rlp, &rrp, &windshield, &front_bumper, &rear_bumper)

 
A folytatásban megint csak az elõbbi verziót kell alapul vennünk, azonban itt lesz egy kis különbség. Egyértelmû, hogy mivel 1 panel adatait 4 bit tárolja, így a 4 bitet nekünk egyben kell lekérni, és egyben kell vele visszatérnünk. Megint csak a bitenkénti ÉS mûveletet fogjuk alkalmazni, azonban mivel 4 bitrõl van szó, így egy olyan számmal kell összehasonlítanunk, mely bináris alakban 1111. Ez a szám nem más, mint a 15. Hogy miért ez kell?
Ha egy bitsorozat tartalmát ki szeretnénk másolni egy másik változóba, akkor a bitenkénti ÉS mûveletre vna szükségünk. A bitenkénti ÉS mûvelet akkor ad 1-et eredményül, ha mindkét bit 1, ha valamelyik 0, akkor a visszatérés már 0 lesz. Ergó tehát, ha egy csupa 1-esekbõl álló bitsorozattal vetjük össze az eredeti bitsorozatunkat, akkor lényegében lemásoljuk a sorozatot, egy másik változóba, hisz eredményül mindig az eredeti sozatot fogjuk visszakapni. Íme néhány példa:
 
0101 & 1111 = 0101
1111 & 1111 = 1111
0000 & 1111 = 0000
1011 & 1111 = 1011

 
Láthatjuk tehát, hogy ha csupa 1-est tartalmazó bitsorozattal hasonlítjuk össze az eredeti számunkat, akkor lényegében egy másolatot készítünk. Mivel nekünk erre a 4 bitre van szükségünk a 32-bõl, így pontosan ezt kell tennünk, ezen 4 bitet lemásolni egy másik változóba, hogy késõbb ezt majd egy másik függvénnyel még jobban darabokra szedhessük.
Íme tehát akkor a kódunk elsõ sora:
 
flp = panels & 15;

 
Mivel 4-esével vizsgáljuk a biteket, így lényegében annyit kell még változtatnunk az elõzõ kódon(decode_tires), hogy az 1-esével növekvõ shiftelések helyett 4-esével shifteljünk. Azaz a 2. panel adatainak lekérdezése:
 
frp = panels >> 4 & 15;

 
Innen pedig már csak mindig további 4 shiftelést el kell végeznünk, és szépen végig haladhatunk a panels változó bitjein, és megkaphatjuk minden panel adatait külön-külön.
 
rlp = panels >> 8 & 15;
rrp = panels >> 12 & 15;
windshield = panels >> 16 & 15;
front_bumper= panels >> 20 & 15;
rear_bumper = panels >> 24 & 15;

 
Ezennel minden panel adatait megkaptuk. Ezután már akár a decode_tires függvényt felhasználva szét lehet szedni az adatokat darabokra, de akár egy saját névvel, és más változó nevekkel ellátott másik függvényt is létre lehet hozni a decode_tires mintájára.
Tehát az egész kód egyben:
 
decode_panels(panels, &flp, &frp, &rlp, &rrp, &windshield, &front_bumper, &rear_bumper)
{
    flp = panels & 15;
    frp = panels >> 4 & 15;
    rlp = panels >> 8 & 15;
    rrp = panels >> 12 & 15;
    windshield = panels >> 16 & 15;
    front_bumper= panels >> 20 & 15;
    rear_bumper = panels >> 24 & 15;
}

 
Igazából a függvény ugyanarra a sémára épül mint a decode_tires, csak annyi a különbség, hogy 4 bitenként kapjuk az adatokat, nem pedig egyesével, mivel itt minden panelnek 4 adatát tudjuk majd még lekérdezni. Nem tudom lehet-e még ehhez a részhez hozzáfûzni valamit, szóval szerintem ugorjunk.


2.5 encode_doors
Háttér információ:
A SA:MP képes minden jármû 4 \"ajtajának\" állapotát tárolni. Ezek az \"ajtók\"(Doors-t hogy fordíthatnám jobban?) konkrétan a motorháztetõ, csomagtartó, vezetõoldali ajtó, és anyósülés oldali ajtó. A 2 hátsó ajtó állapotát jelenleg még nincs lehetõségünk lekérdezni.
Amikor lekérdezzük egy jármû \"ajtajának\" adatait, azt a SA:MP 4 bájtként(32 bit) adja vissza. Ebbõl a 4 bájtból 1 bájt(8 bit) jut minden egyes objektumra. Vagyis minden egyes objektum adatait 8 bit tárolja. Vagyis tárolhatná...
A SA:MP csak az elsõ 3 bitet használja, a további bitek üresek lesznek, nincs funkciójuk, talán majd késõbb lesz. Az elsõ bit azt tárolja, hogy az objektum zárt[0], vagy nyitott[1](nem az jelenti, hogy bezárt, hanem becsukott/kinyitott) állapotban van-e. A második bit azt adja meg, hogy az objektum ép[0] vagy sérült[1]. A 3. bit arról tájékoztat, hogy az adott objektum rajta van-e még a jármûvön[0] vagy már leesett[1]. A többi bit mint mondtam üres. Ez majd a decode függvénynél lesz fontos.
Nézzük tehát a konkrét kódot:
 
encode_doors(bonnet, boot, driver_door, passenger_door, behind_driver_door, behind_passenger_door)
{
    #pragma unused behind_driver_door
    #pragma unused behind_passenger_door
    return bonnet | boot << 8 | driver_door << 16 | passenger_door << 24;
}

 
A 2 hátsó ajtóval nem foglalkozunk, mivel azok jelenleg még nem támogatottak ugyebár, de a kód készítõje gondolt a jövõre is, vagy fogalmam sincs miért adta meg ezt a két paramétert is, hisz ígyis-úgyis módosítani kell a kódon... Ha behoznak még 2 ajtót, akkor az 1 bájtos tárolással már 6 bájt lenne, de 1 változó maximum 4 bájt lehet, tehát a kódot úgyis módosítani kell, ha meg arra gondoltak, hogy legalább a paramétereket nem kell majd szegény scriptereknek minden sorban átírni, az megint hülyeség, hisz ki adna értéket olyan paraméternek, amely nincs használva?...
Nos mindegy, nem a kritizálásért vagyunk most itt. Ha megnézzük, a kód 8-as shiftelést használ, mivel egy objektum adatait 8 bit tárolja. A mûködési elv ugyanaz, mint az elõzõ encode függvényeknél, így erre nem kívánok megint kitérni.
Nézzük azonban hogyan írnánk ehhez egy decode függvényt. Mikor megkérdeztem errõl ismerõsömet, õ azt mondta, ugyanúgy mint az elõzõ, decode_panels függvénynél, csak 8-as shiftelést használunk, és 15 helyett 255-el végezzük a bitenkénti ÉS mûveletet. 255-el? minek?
No erre is ki fogok térni késõbb, de haladjunk a szokásos módon. 1 bemeneti paraméterünk van, és 4 kimeneti, vagyis a header rész a következõ:
 
decode_doors(doors, &bonnet, &boot, &driver_door, &passenger_door)

 
Ezzel meg is vagyunk, itt nem látok különösebb magyarázni valót. Én most nem fogok törõdni a 2 nem létezõ ajtóval, ha egy jövõbeni verzióban netán azokat is behoznák mint lekérdezhetõ objektum, akkor ígyis-úgyis módosítani kell majd a kódot, addig meg úgysincs rá szükségünk.
Nos, mint az eddigi decode függvényeknél, elsõ feladatunk egy shiftelés mentes bitenkénti ÉS mûvelet elvégzése. Az egyik megoldás a következõ:
 
bonnet = doors & 255;

 
Lehet így is. Azonban véleményem szerint felesleges itt egy ekkora számmal \"szenvednünk\". Miért használjunk ekkora számot, és ezáltal bonyolítsuk feleslegesen a kódunkat, ha nekünk a 8 bitbõl csak az elsõ 3 lesz ami adatot tárol? Bõven elegendõ egy olyan szám, amely bináris alakja 111. Ez nem más, mint a 7. Tehát a következõ megoldás ugyanúgy helyes:
 
bonnet = doors & 7;

 
A két kód pontosan ugyanazt az eredményt fogja visszaadni, semmi különbség a kettõ között. Csupán elsõ esetében nagy számmal dolgozunk, még második verzió esetén csak akkorával, amekkora szükséges.
A kód többi részében csak 8-as shifteléssel végigcammogunk a doors változó többi bitjén is:
 
boot = doors >> 8 & 7;
driver_door = doors >> 16 & 7;
passenger_door = doors >> 24 & 7;

 
A teljes kód egyben:
 
decode_doors(doors, &bonnet, &boot, &driver_door, &passenger_door)
{
    bonnet = doors & 7;
    boot = doors >> 8 & 7;
    driver_door = doors >> 16 & 7;
    passenger_door = doors >> 24 & 7;
}

 
A kód pont ugyanazon az elven épül fel, mint az összes többi decode függvényünk, csak épp a benne használatos értékek az \"ajtó\" objectumok adatstruktúrájához vannak igazítva.


2.6 Egyéb ötletek gyakorlati alkalmazásra, hasznosításra
Joggal felmerülhet a kérdés, hogy ennyi? Emiatt rágtuk végig magunkat ezen néhány tízezer karakteren? Nem. Nem ennyi. A SA:MP-ban rengeteg felhasználási módja lehet még a bitmûveleteknek. Mutatok is néhány dolgot, mire jók még.
2.6.1 RGBA->HEX; HEX->RGBA konvertálások
Nos, bármily \"hihetetlen\", erre is használható. Miért is? Nos, 16-os számrendszerben 0-9, valamint A-F \"szánjegyek\" léteznek. A hexadecimális számjegy decimális értékben 0-15 lehet. Ha megnézzük bináris értékeket, 0-15 az 4 bittel leírható, hiszen a 15 az bináris alakban 1111. De ha józan paraszti ésszel belegondolunk is. 16 az 24. Így tehát 1 hexadecimális érték, az 4 bináris értékkel írható le. Ebbõl már sejthetõ, hogy itt is shiftelésre lesz szükségünk. Naná :) Mivel 4 bit ír le egy hexadecimális számjegyet, így 4-esével kell majd shiftelnünk.
Mi is az az RGBA?
RGBA: A színek olyan meghatározási módja, melyben a 3 alapszín([r]ed, [g]reen, lue) mellett egy 4. érték, az úgynevezett [a]lpha is szerepel, amely az áttetszõséget adja meg. A legtöbb program(pl. paint, PS), és pl. a css(külön függvénnyel!) is elfogadja ezen értékeket decimális számokként(0-255) is, a SA:MP-ban azonban hexadecimális alakban kell megadnunk. Bármily furcsa a következõ 4 sor pont ugyan azt jelenti:


\"Normál\" RGB alak(pl. paint, photoshop): Piros: 0, Zöld: 255, Kék: 255
CSS alak(függvénnyel): rgba(0, 255, 255, 1);
Pawn alak(1): 0x00FFFFFF
Pawn alak(2): 16777215

 

Ó igen, a Pawn-ban decimális számokként is megadhatjuk a színeket, hisz a program mindent bináris számrendszerbe fog átszámolni. De akár bináris alakban is megadhattuk volna:



Pawn alak(3): 0b111111111111111111111111

 

Az, hogy mi a Pawn-nak hexadecimális alakban szoktuk megadni, annak az egyetlen oka, hogy így a leginkább \"átlátható\", és ez egy amúgy is elfogadott módja (mármint a hexadecimális számmal való megadás) a színek leírásának a számítástechnikában.


Nos, mivel 1 szín 0 és 255 közötti értéket tartalmaz a piros, zöld és kék színekbõl, ez hexadecimálisan 2 számjeggyel leírható: 00-FF. Az FF értéke 255, aki nem hiszi, számoljon utána :D (16
2-1)

2 hexadecimális számjegy az pedig 8 bináris számjeggyel leírható(00000000-11111111). Így már láthatjuk, hogy 8-as shiftelésre lesz szükségünk. Másra nincs is szükségünk, az RGBA->HEX konverter elkészítéséhez.




stock RGBAToHex( r, g, b, a )
{
return a | b << 8 | g << 16 | r << 24;
}

 

Az, hogy miért fordított sorrendben kellett megadnunk az értékeket, az megint csak azzal magyarázható, hogy a biteket hétköznapi értelemben véve visszafelé számozzuk.


Egy ellentét, azaz HexToRGBA függvény megírása sem túl bonyolult, ugyan ezt kell csinálnunk, csak visszafelé.



stock HexToRGBA( colour, &r, &g, &b, &a )
{
a = colour & 0xFF;
b = (colour >> 8 ) & 0xFF;
g = (colour >> 16 ) & 0xFF;
r = (colour >> 24 ) & 0xFF;
}

 

A 0xFF-et természetesen decimális értékkel is megadhatjuk(255).


No akkor lehet ugye RGBA-t ARGB-be is konvertálni, az így néz ki:



RGBAToARGBHEX(r,g,b,a)
{
return (r & 0xff) << 24 | (g & 0xff) << 16 | (b & 0xff) << 8 | a & 0xff;
}

 

Ez máris egy újabb felhasználási módja a bitmûveleteknek. De, mutatok még :)


2.6.2 Boolen változók helyett Integer

Az al-al-alcímet elolvasva joggal felmerülhet a kérdés, hogy ez meg hogy jön ide. Nos ez úgy jön ide, hogy most egy kis memóriatakarékosságról fogok itt regélni. Adhattam volna azt is címnek, hogy miért ne használjunk boolean változókat, ha legalább 5 igaz/hamis értéket el akarunk tárolni.


Nos akkor egy kis memórialecke. Azt már tisztáztuk, hogy az integer változók 32 bitesek, vagyis 4 bájtot foglalnak le a memóriában maguknak. A boolean típusú változókról azonban még nem volt szó. Mivel 1 értéket tárolnak csak, az is csak igaz vagy hamis lehet, így joggal gondolhatnánk, hogy 1 bit a mérete. Azonban nem...


1 boolean változó 8 bitet, azaz 1 bájtot foglal le magának a memóriában. Hogy ez miért így van, az a számítógépek memóriahasználatával magyarázható. Manapság nincs mód arra, hogy a memóriában egyetlen bitet módosítsunk, csak 8-as \"csomagokban\", vagyis bájtonként tudjuk ezt megtenni. Hogy ez miért van így, azt most nem fogom itt fejtegetni :)


Nos, ha megnézzük tehát, a boolean változó egy nagyon-nagyon pazarló megoldás. Hiszen 1 darab boolean változó 1 bájtot foglal, 4 darab boolean változó esetén ez már 4 bájt, ami megegyezik 1 integer változó méretével. Azonban, még a 4 boolean változóban 4 igaz/hamis érték kerülhet tárolásra, addig egy integerben 32 igaz/hamis értéket is eltárolhatunk.


Bizony, 32 igaz/hamis érték. Hiszen 1 integer változó 32 bitbõl áll, ha minden bitet hasznosítunk, akkor a bitmûveletek felhasználásával 32 igaz/hamis értéket is eltárolhatunk. Összehasonlítva:



32 darab boolean változó mérete 32 bájt.
1 integer változó mérete: 4 bájt.

 

Vagyis 32 boolean változó esetén nyolcad annyi memóriánkba kerül, ha egy darab integerben tároljuk el az értékeket. Persze 32 booleanra ritkán van szükség, de egy RP modban simán elõfordulhat. Gondoljunk csak bele...


Van 32 boolean-tömbös változónk, ez a maximum slotszámot(1000) figyelembe véve: 32*1000 byte azaz 32000 byte ami ~31 kilobyte.


Ha ezt egyetlen integer-tömbbel oldanánk meg: 4*1000 byte, azaz 4000 byte, ami  ~3,9 kilobyte. Azért így nézve már nem annyira \"elhanyagolandó\" az a különbség szerintem =)


Akár írhatnánk erre külön egy függvényt, mely az adott bit értékével tér vissza. Nem is bonyolult. Így már azok számára is használhatóak a bitmûveletek, akik nem értenek hozzá, és rengeteg memóriát takaríthatnak meg.


Íme egy függvény az adatok kiolvasására:



stock GetData(integer, bit)
{
    return integer >> bit & 1; //vagy az 1 helyett akár 0x01 alakban is megadhatjuk, nincs jelentõsége
}

 

És a párja, adat írásra:



stock SetData(&integer, bit, value)
{
    integer |= value << bit;
    return 1;
}

 

Persze ezt még lehetne csinosítani, hogy pl. ne 0-tól hanem 1-tõl számoljuk a biteket, vagy hogy 31(vagy 32 ha 1-tõl számolunk) felett 0 legyen a return értéke, esetleg a value-re is rakhatnánk ellenõrzést, hogy 0-1-e az értéke, vagy más, de ilyenekkel most nem törõdtem.


2.6.3 Több érték, egy integerbe

Na ez az a rész, ami véleményem szerint már csak a nagyon megrögzött memóriafelszabadítók szerint nem lesz felesleges :D Amennyiben nincs szükségünk túl nagy számokra(pl. csak 0-15, vagy akár csak 0-7, de akár még a 0-255 is megfelel), akkor akár egy integerbe is tehetjük õket. Hogyan? Egyszerû. Csoportosítjuk a biteket. Tegyük fel, hogy maradván egy RP szervernél, el akarjuk tárolni ûbergigantikusan memóriakímélõ megoldással a játékos házának számát(ez legyen mondjuk 0-100 közötti szám), a játékos frakcióját(ez legyen 0-15 között), a játékos rangját(0-7 között), hogy a telefonja be van-e kapcsolva(0-1), a játékos szolgálati jármûvének id-jét(legyen 0-255 között), és az alapmunkáját(0-15 között). Ez is megoldható, egyetlen integerben, és ismételten 1000 slottal számolva máris megspóroltunk 4*4*1000+1*1000 byte-t(5 integer, és 1 boolean helyett csupán 1 integert használunk), ami ~16,6 kilobyte. Azért nem mindegy az, hogy 20,5 kb-ot vagy 4 kb-ot használunk szerintem... :)


Ezt a következõ módszerrel valósíthatnánk meg:



#define DATA_HOUSE 0
#define DATA_FACTION 1
#define DATA_RANK 2
#define DATA_PHONESTATE 3
#define DATA_DUTYCAR 4
#define DATA_JOB 5
stock GetData(playerinfo, data)
{
    switch(data)
    {
        case DATA_HOUSE: return playerinfo & 127;//7 bit
        case DATA_FACTION: return playerinfo >> 7 & 15;//4 bit
        case DATA_RANK: return playerinfo >> 11 & 7;//3 bit
        case DATA_PHONESTATE: return playerinfo >> 14 & 1;//1 bit
        case DATA_DUTYCAR: return playerinfo >> 15 & 255;//8 bit
        case DATA_JOB: return playerinfo >> 23 & 15;//4 bit
}
return -1;
}
stock SetData(&playerinfo, data, value)
{
    switch(data)
    {
        case DATA_HOUSE: { playerinfo = playerinfo & ~127 | value; }//7 bit (0-127 közötti maximálisan)
        case DATA_FACTION: { playerinfo = playerinfo & ~(15 << 7) | value << 7; }//4 bit (0-15 között)
        case DATA_RANK: { playerinfo = playerinfo & ~(7 << 11) | value << 11; }//3 bit (0-7 között)
        case DATA_PHONESTATE: { playerinfo = playerinfo & ~(1 << 14) | value << 14; }//1 bit (0-1)
        case DATA_DUTYCAR: { playerinfo = playerinfo & ~(255 << 15) | value << 15; }//8 bit (0-255 között)
        case DATA_JOB: { playerinfo = playerinfo & ~(15 << 23) | value << 23; }//4 bit (0-15 között)
    }
    return -1;
}
stock SetDatas(&playerinfo, house, faction, rank, phonestate, dutycar, job)
{
playerinfo |= house | (faction << 7) | (rank << 11) | (phonestate << 14) | (dutycar << 15) | (job << 23);
}

 

Ez már azért elég morbid, ezt inkább csak érdekességképp mutattam, de éppenséggel ez is használható lenne, csak ehhez már kell egy kis õrültség :D




2.7 Második fejezet vége

Nos, láthattuk hogyan is mûködik mindazon sok \"marhaság\", amit az elsõ fejezetben elméletként átvettünk. Láthattuk hogyan mûködnek a jármûvek paneljének adataival babráló encode függvények, valamint megmutattam, hogyan írhatunk hozzájuk decode verziókat. Láthattunk még példát egyéb gyakorlati hasznosításra, kicsit megkoccoltuk a hexadecimális számok világát is, majd szóba került egy kis memóriatakarékosság. Végül mutattam egy összetettebb, elvetemült példát is, a bitmûveletek hasznosítására.





3. Összegzés, végszó
 

Úgy gondolom, hogy elég részletesen körbe jártuk a témát. Megnéztük a bitmûveletek elméleti, és gyakorlati oldalát is, láthattunk gyakorlati példákat. Nem kell elsõre megérteni ezt az egészet, sõt... Elegendõ ha egyszer elolvassuk, emésztjük kicsit, majd késõbb megint elõvesszük, akkor netán már kicsit többet is megértünk belõle. Nem egy kihagyhatatlan része a Pawno-nak, de azért van annak haszna, ha az ember jártas ebben a témában is, bár sokak szerint nem éri meg a sok fáradtságot, és idõt, mely a megértéséhez, begyakorlásához, használatának elsajátításához szükséges.


Próbáltam mindent részletesen kifejteni. A bonyolultabb dolgokhoz sokat magyarázni, az egyszerûbbek esetén csak visszautalni az alapokhoz. Sok dolgot talán már túl is magyaráztam. Ezt a leírást nem 1 nap alatt készítettem el, kb. 1 hónapja írtam meg az elméleti oldalát, most az elmúlt pár napban pedig a gyakorlatot. Esténként 1-2 órát szántam rá csupán, és mivel ilyenkor már nem vagyok épp a toppon, elképzelhetõ, hogy néhány helyen helyesírási, vagy elvi hibát vétettem. Amennyiben valaki ilyet találna, kérem szóljon, és korrigálni fogom. Nos, azt hiszem nincs is más hátra, mint hogy sok szerencsét kívánjak a bitmûveletek elsajátításához, és megosszam a forrásokat. Remélem néhányan hasznosnak találják, eme igen hosszúra sikerült leírást.





4. Források:
 

Internet:





Könyv, e-book:




  • Pohl László - A programozás alapjai(BME - Elektronikus Eszközök Tanszéke, Budapest, 2010)

  • Siroki László - C programozás - Bitmûveletek jegyzete





ˆ Anthony, 2012 - Minden jog fenntartva.

A leírás egészben, vagy részleteiben szabadon terjeszthetõ, ameddig egyértelmûen fel van tüntetve az eredeti készítõ, valamint hogy honnan is származik.
« Utoljára szerkesztve: 2012. Augusztus 01. - 10:53:54 írta Anthony »

Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #1 Dátum: 2012. Július 30. - 13:37:48 »
0
ezt eddig is értettem.. az egy másik dolog, hogy unálom, mint a sz4rt, fõleg, ha dolgozatban kérdezik. >.>


Szép leírás. :)

Nem elérhető kurta999

  • 2759
  • Éllő fédisznó
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #2 Dátum: 2012. Július 30. - 17:40:35 »
+1
Nagyon szép leírás, köszönöm, hogy megosztottad!
Az operátorok pontos mûködését azt mai napig nemtudtam, csak tudtam úgyahogy, hogy mikor mit kell használni, de azt nem, hogy mér.
Hálás vagyok a leírásért  ;D
A végét azt nem teljesen futottam át, de ezen valahogy megakadt a szemem:
A 0xFF-et természetesen decimális értékkel is megadhatjuk(15).
Itt 255-nek kéne lenni, mivel nem 0xF  :D

Nem elérhető tonyo

  • 1335
  • Moderális Generátor
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #3 Dátum: 2012. Július 30. - 17:47:32 »
0
Örülök ha hasznos volt  :)
Az a 0xFF-et tényleg elírtam, köszönöm, javítom is :D

Nem elérhető kurta999

  • 2759
  • Éllő fédisznó
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #4 Dátum: 2012. Július 30. - 20:15:10 »
0
A több érték egy interger részhez még szerintem hozzá lehetne azt tenni, ha te mondjuk csak egy \"részét\" akarod átírni, mondjuk a munka ID-jét, akkor fölösleges az egész változót újraírni.
Ezt nemtudom én teljesen elmagyarázni, de megpróbálom, hátha valakinek segít xD
Belemész a számológépbe, átálítod, hogy csak Duplaszó legyen a mód, (DWORD) azokat a biteket ahol ennek a résznek a dolgai találhatók, kikapcsolod, aztán a kapott értékkel a következõt csinálod:
(Én itt felszoktam írni papírra és szépen bejelölni, hogy mi honnan indul és meddig, azaz mennyi bitet foglal le)
Pl:
 

stock Biznis_SetIcon(biznisid, icon)
{
g_iBiznisFlags[biznisid] &= 0x81FFFFFF; // Értéket ide írod a szám helyére.
g_iBiznisFlags[biznisid] = icon << 25 | g_iBiznisFlags[biznisid]; // Aztán beleírjuk újra az ikont, és ez csak ezt a részét írja felül. (Max Icon ID 63, 6bit)
}
#define Biznis_GetIcon(%1) \\
    (g_iBiznisFlags[(%1)] >>> 25 & 0x3F)
« Utoljára szerkesztve: 2012. Július 30. - 20:18:51 írta kurta999 »

Nem elérhető tonyo

  • 1335
  • Moderális Generátor
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #5 Dátum: 2012. Július 30. - 23:06:51 »
0
Valóban, ez is egyfajta megoldás. Bár én úgy írnám, hogy a 0x81FFFFFF helyett ~(0x3F << 25), és akkor már jobban látható egy kevésbé hozzáértõ számára is, hogy mirõl is van szó :D
Igazából a bitmûveletek \"szépsége\", hogy rengeteg lehetõséget nyitnak meg. Az ötletedet (azaz hogy ne a teljes változót kelljen felülírni néhány bit miatt) felhasználva kicsit változtattam az általam írt 2 függvényen, egyrészt hozzáadtam egy 3.-at, másrészt a SetData-t SetDatas-ra neveztem át, és most már a SetData-val konkrétan célirányosan írhatunk felül. (most hogy így belegondoltam, kompett függvénykönyvtárat lehetne írni bitmûveletes változókezelésre, csak nem tudom lenne-e értelme, és hogy a gyakorlatban mennyi haszna lenne, megérné-e egyeltalán a belefektetett idõt, hisz nem lenne könnyû, és kellene a mûködéséhez egy \"konfig\" rész is, akkor meg már nem hinném, hogy \"kezdõ\" is belekezdene, aki meg érti hogy mûködik, annak egyszerûbb lenne sajátot írni)
Íme a módosított verzió:
 
#define DATA_HOUSE 0
#define DATA_FACTION 1
#define DATA_RANK 2
#define DATA_PHONESTATE 3
#define DATA_DUTYCAR 4
#define DATA_JOB 5
stock GetData(playerinfo, data)
{
    switch(data)
    {
        case DATA_HOUSE: return playerinfo & 127;//7 bit
        case DATA_FACTION: return playerinfo >> 7 & 15;//4 bit
        case DATA_RANK: return playerinfo >> 11 & 7;//3 bit
        case DATA_PHONESTATE: return playerinfo >> 14 & 1;//1 bit
        case DATA_DUTYCAR: return playerinfo >> 15 & 255;//8 bit
        case DATA_JOB: return playerinfo >> 23 & 15;//4 bit
}
return -1;
}
stock SetData(&playerinfo, data, value)
{
    switch(data)
    {
        case DATA_HOUSE: { playerinfo = playerinfo & ~127 | value; }//7 bit (0-127 közötti maximálisan)
        case DATA_FACTION: { playerinfo = playerinfo & ~(15 << 7) | value << 7; }//4 bit (0-15 között)
        case DATA_RANK: { playerinfo = playerinfo & ~(7 << 11) | value << 11; }//3 bit (0-7 között)
        case DATA_PHONESTATE: { playerinfo = playerinfo & ~(1 << 14) | value << 14; }//1 bit (0-1)
        case DATA_DUTYCAR: { playerinfo = playerinfo & ~(255 << 15) | value << 15; }//8 bit (0-255 között)
        case DATA_JOB: { playerinfo = playerinfo & ~(15 << 23) | value << 23; }//4 bit (0-15 között)
    }
    return -1;
}
stock SetDatas(&playerinfo, house, faction, rank, phonestate, dutycar, job)
{
playerinfo |= house | (faction << 7) | (rank << 11) | (phonestate << 14) | (dutycar << 15) | (job << 23);
}

 
Fõpostban is frissítettem :)

Nem elérhető DrAkE

  • 2078
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #6 Dátum: 2012. Július 31. - 08:46:28 »
0
Írtam ennél egyszerûbb változatot is:
 

stock RGBAToHex( r, g, b, a )
{
return a | b << 8 | g << 16 | r << 24;
}

 
Ez:
 

stock ShiftRGBAToHex(color)
return (color >>> 8);

 
Használata meg egyszerû:
 

printf(\"%x\", ShiftRGBAToHex(0xFFFFFFFF));
printf(\"%x\", ShiftRGBAToHex(0x8B0000FF)); //sötét piros

 
Az alábbit fogja kiprintelni nekünk:
 

FFFFFF
8B0000

 
Illetve egy integer típúsú változó -2,147,483,648 és 2,147,483,647 közötti értéket vehet fel. Szerintem ide még besorolhatnád a \"char\" típusú tömböket is, amely lecsökkenti ezen értékek számát 0 és 255 közé. Szóval a cellák méretét 1bájtnyira csökkenti, magyarán 8bites-be.
Illetve a gumikat, ajtókat, világítást, illetve a panelt a SA-MP képes decimális számban is elmenti illetve beolvasni.


Igaz, nem olvastam el, mivel most keltem, viszont szép leírás. :D
« Utoljára szerkesztve: 2012. Július 31. - 08:59:38 írta DrAkE »

Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #7 Dátum: 2012. Július 31. - 10:25:42 »
0
Szép leírás, csak az a baj hogy én még nem értek belõle semmit sem.  :(

Nem elérhető tonyo

  • 1335
  • Moderális Generátor
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #8 Dátum: 2012. Július 31. - 11:03:12 »
0
Idézetet írta: DrAkE date=1343717188\" data-ipsquote-contentapp=\"forums\" data-ipsquote-contenttype=\"forums\" data-ipsquote-contentid=\"25200\" data-ipsquote-contentclass=\"forums_Topic
Írtam ennél egyszerûbb változatot is:
 

stock RGBAToHex( r, g, b, a )
{
return a | b << 8 | g << 16 | r << 24;
}

 
Ez:
 

stock ShiftRGBAToHex(color)
return (color >>> 8);

 
Használata meg egyszerû:
 

printf(\"%x\", ShiftRGBAToHex(0xFFFFFFFF));
printf(\"%x\", ShiftRGBAToHex(0x8B0000FF)); //sötét piros

 
Az alábbit fogja kiprintelni nekünk:
 

FFFFFF
8B0000

 
Ez sem rossz, csak nem ugyanaz a kettõ :D Végül is a név az megtévesztõ. Az enyém az decimális RGBA értékekbõl konvertál egy Hex RGBA-t. Ez jól jöhet h pl. Paintban, PS-ben, vagy valami olyan programban, amely csak így írja ki a számokat(decimális alakban) h piros: 92, zöld: 72, kék:69, akkor azt tudod használni SA:MP-ban is. Amit te mutattál, az inkább RGBA > RGB konverter, mivel az alpha \"tartományt\" távolítja el.
 
Illetve egy integer típúsú változó -2,147,483,648 és 2,147,483,647 közötti értéket vehet fel.[/quote]
Igaz, elírtam, javítva.
 
Illetve a gumikat, ajtókat, világítást, illetve a panelt a SA-MP képes decimális számban is elmenti illetve beolvasni.[/quote]
A SA:MP hál istennek korlátok nélkül képes dec-bin-hex-oct konvertálásokra, mivel amit mi decimális integernek látunk, valójában az is bináris szám, csak a mi kedvünkért konvertálja \"fel\" a program, a könnyebb kezelhetõség érdekében. Így változót kiírathatunk 4 féle képpen is:
 
printf(\"%b, %o, %d, %x\", integer, integer, integer, integer);

 
Ergó 4 alakban is \"elmenthetjük\", de integerként a legegyszerûbb beolvastatni. (bár most így belegondolva, fogalmam sincs, h a strval képes-e a decimálison túl más értékeket is kezelni, de kétlem).
 
Szerintem ide még besorolhatnád a \"char\" típusú tömböket is, amely lecsökkenti ezen értékek számát 0 és 255 közé. Szóval a cellák méretét 1bájtnyira csökkenti, magyarán 8bites-be.[/quote]
Hú, erre nem is gondoltam. Annyit foglal mint egy boolean, és 8 igaz/hamis értéket el lehet benne tárolni.
 
Igaz, nem olvastam el, mivel most keltem, viszont szép leírás. [/quote]
 

Szép leírás, csak az a baj hogy én még nem értek belõle semmit sem.  :(
 
[/quote]
Köszönöm :)

Nem elérhető IXT

  • 2502
  • Steph Curry
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #9 Dátum: 2012. Július 31. - 14:29:28 »
0
Ki vagy teee? :D Zseniális,gratulálok!  ;)

Nem elérhető DrAkE

  • 2078
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #10 Dátum: 2012. Július 31. - 14:36:55 »
0
Igazság szerint, nem csak az Alphát képes eltüntetni. Ha alkalmazod egy HEXa színkódon, akkor az utolsó kettõ szám fog eltûnni.

Nem elérhető tonyo

  • 1335
  • Moderális Generátor
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #11 Dátum: 2012. Július 31. - 15:41:47 »
0
Igen, csak én abból indultam ki, hogy RGBA-t adunk meg, de végül is igazad van :D

Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #12 Dátum: 2012. Augusztus 01. - 00:41:33 »
0
Azért utána érdeklõdhetnél Okoskánál, mindent jól írtál-e le, elvégre õ jobban ért hozzá.
Najó, talán nem.. Szép leírás, bevallom, nem olvastam el, egyrészt este van, másrészt majd iskola idõben..:D

Nem elérhető kurta999

  • 2759
  • Éllő fédisznó
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #13 Dátum: 2012. Augusztus 01. - 01:34:18 »
0
Amúgy énmég erre figyeltem fel, hogy ezt még elírtad.
 -2^32(-2,147,483,648) és 2^32-1(2,147,483,647)
Csak a 31.-en, mivel nem elõjel nélküli.


Még azt megkérdezném, hogy hogyan tárolja maga a gép azt, hogy most ez elõjeles, vagy nem.
Azt értem, hogy az utolsó bit jelenti azt, de ha te most lefoglalsz egy elõjel nélkülit (unsiged int pl), akkor azt most a gép honnan fogja tudni, hogy ott az utolsó bit nem az elõjelet jelenti?
Tárol erre még valahol valamit, hogy tudja, hogy nem az elõjel az utolsó bit, hanem azis \"bele tartozik\".?

Nem elérhető tonyo

  • 1335
  • Moderális Generátor
    • Profil megtekintése
Bitmûveletek, valamint gyakorlati alkalmazás a SA:MP-ban
« Válasz #14 Dátum: 2012. Augusztus 01. - 11:39:35 »
+1
Idézetet írta: kurta999 date=1343777658\" data-ipsquote-contentapp=\"forums\" data-ipsquote-contenttype=\"forums\" data-ipsquote-contentid=\"25200\" data-ipsquote-contentclass=\"forums_Topic
Amúgy énmég erre figyeltem fel, hogy ezt még elírtad.
 -2^32(-2,147,483,648) és 2^32-1(2,147,483,647)
Csak a 31.-en, mivel nem elõjel nélküli.
 
Igazad van, köszönöm a jelzést, javítottam.
 

Még azt megkérdezném, hogy hogyan tárolja maga a gép azt, hogy most ez elõjeles, vagy nem.
Azt értem, hogy az utolsó bit jelenti azt, de ha te most lefoglalsz egy elõjel nélkülit (unsiged int pl), akkor azt most a gép honnan fogja tudni, hogy ott az utolsó bit nem az elõjelet jelenti?
Tárol erre még valahol valamit, hogy tudja, hogy nem az elõjel az utolsó bit, hanem azis \"bele tartozik\".?
 
[/quote]
A Pawn mint tudjuk, egy eléggé korlátozott nyelv. Engem is érdekelt ez a kérdés, ezért utána kerestem, és SA:MP wikin (ITT) meg is találtam a választ. A Pawn-ban nincs lehetõségünk unsigned int használatára. (csupán meg van említve, hogy létezik ilyen is). Ellenben a C-ben pl. elég sokféle változó létezik:
 
short int
unsigned short int
unsigned int
int
long int
signed char
unsigned char
float
double
long double

 
Azt most csak példának mutattam, mert az elv ugyanaz. Létezik egy úgynevezett szimbólumtábla, ami a következõ adatokat tárolja:
 
  • szimbólum neve

  • szimbólum attribútumai:
    • definíció adatai

    • típus

    • tárgyprogram-beli cím

    • definíció helye a forrásprogramban

    • szimbólumra hivatkozások a forrásprogramban


     


Tehát innen tudja a program, hogy egy változó milyen típusú, így pl. az X-edik bit(jelen esetünkben pl. egy C int esetén, a 32. bit) MSB, vagy nem.

 

SimplePortal 2.3.7 © 2008-2024, SimplePortal