Sziasztok,
Gondoltam ismét készülök nektek valami újdonsággal
A fórumozók többsége szerintem már látott ilyen operátorokat: ^, |, ~, &... Azonban csak kevesen tudják mire is jók ezek, mit is csinálnak, ezért gondoltam, hogy röviden, tömören átrágom a dolgot. Elsősorban tisztázzuk az elméleti részét, majd miután ez meglenne próbálok gyakorlati példákat mutatni, hogy mire is jók ezek. Próbálok eléggé konyhanyelven fogalmazni, de nem ide nem árt, ha képben vagy az alapokkal ( Metál alapozói ).
Mire is jók ezek?Ezek az operátorok lehetőséget adnak, hogy az egész számok bitjeit külön - külön tudjuk kezelni, ki - be "kapcsolni". Ezek megértéséhez jól jön, ha tisztában vagyunk a kettes ( bináris ) számrendszerrel, nem kell mindent tudni, de az azért nem árt, ha tudod, hogy csak a 0 és az 1 létezik, mint számjegy ebben a számrendszerben és hogy bármennyi nullát tolunk egy szám elé az nem változtatja az értékét.
( 0001 = 000001 = 1 ) Ha kicsit visszagondolsz a matekórákra rögtön beugorhat, hogy miért is van így, 0 * 2^10 meg 0 * 2^20 még mindig csak 0 lesz
Bitek a pawnbanMint tudjuk a mi kis pawnnunkban nincs nagyon típusosság, és minden változó 4 bájtot azaz 32 bitet foglal le magának ( Ebből jön, hogy a pawn egy 32 bites programozási nyelv. ). Azonban fontos lehet még megemlíteni, hogy ezek előjeles egész változók ( signed integers ) ami annyit takar, hogy minden változó fent tart 1 bitet a 32-ből ami jelöli, hogy pozitív vagy negatív szám ( Ezt a bitet szokták MSB-nek hívni ). Illetve azt is érdemes tudni, hogy a pawn 2-es komplementer rendszert használ, hogy előállítsa a negatív számokat, ez annyiból áll, hogy vesszük az egész, pozitív számot, majd minden bitjét kicseréljük az ellentétére és utána még hozzáadunk egyet, és meg is lenne a negatív szám. Példa:
Kód:
00000000000000000000000000000001 // 1
11111111111111111111111111111110 // Vesszük minden bit ellentétét
11111111111111111111111111111111 // hozzáadunk egyet, így megkaptuk a -1-t.
Operátorok, műveletek
Most pedig szépen nézzük végig mi áll rendelkezésünkre:
1. Bitenkénti ( bitek közötti ) ÉSA bitenkénti ÉS művelet ( AND, & ) egy logikai operátor, ami csak akkor ad vissza 1-t , ha mindkét bit 1. Fontos, hogy nem egyezik meg a "&&" logikai operátorral.
Igazság táblázata ( 2 ábrázolás, kinek hogy... ):
Kód:
& 0 1
0 0 0
1 0 1
Kód:
A B A&B
0 0 0
1 0 0
0 1 0
1 1 1
Példa ( baloldalon bináris számrendszerben, ahogy a memóriában van, jobbon decimálisban ):
Kód:
00000000000000000000000000001011 // 11
&00000000000000000000000000000111 // 7
=00000000000000000000000000000011 // 3
2. Bitenkénti ( bitek közötti ) VAGYA bitenkénti VAGY művelet ( OR, | ) egy logikai operátor, ami mindig 1 -t ad vissza, kivéve, ha mindkét bit 0. Fontos, hogy nem összekeverendő a "||" logikai operátorral.
Igazság táblázata:
Kód:
| 0 1
0 0 1
1 1 1
Kód:
A B A|B
0 0 0
1 0 1
0 1 1
1 1 1
Példa:
Kód:
00000000000000000000000000001011 // 11
|00000000000000000000000000000111 // 7
=00000000000000000000000000001111 // 15
3. Bitenkénti ( bitek közötti ) KIZÁRÓ VAGYA bitenkénti KIZÁRÓ VAGY művelet ( XOR, ^ ) egy logikai operátor, ami mindig 1 -t ad vissza, ha különbözik a két bit.
Igazság táblázata:
Kód:
^ 0 1
0 0 1
1 1 0
Kód:
A B A^B
0 0 0
1 0 1
0 1 1
1 1 0
Példa:
Kód:
00000000000000000000000000001011 // 11
^00000000000000000000000000000111 // 7
=00000000000000000000000000001100 // 12
4. Bitenkénti ( bitek közötti ) NEGÁLÁSA bitenkénti NEGÁLÁS ( NOT, ~ ) egy logikai operátor, ami eltér az előbbiektől, mivel 2 helyett csak 1 bitre alkalmazzuk. Az 1-kből 0-t a 0-kból 1-t csinál.
Igazság táblázata:
Kód:
A~A
0 1
1 0
Példa:
Kód:
~00000000000000000000000000001111 // 15
=11111111111111111111111111110000 // -16
5. Biteltolás ( shiftelés ) operátorokA shift operátorok ( <<, >> ) a bitek tologatására szolgálnak. A << balra tol, a >> jobbra ( amerre mutatnak a nyilak ). Az operátor előtt ( bal oldalt ) szereplő szám az amit eltolunk, és az operátor után ( jobb oldalt ) lévő pedig, hogy mennyivel.
Jobbra tolás ( >> ):
Pozitív számmal:
Kód:
00000000000000000000000000001100 // 12
>> 2
00000000000000000000000000000011 // 3
Láthatjuk, hogy csak minden bit hátrébb került illetve az "üres" helyekre 0-k kerültek.
Negatív számmal:
Kód:
11111111111111111111111111111000 // -8
>> 2
11111111111111111111111111111110 // -2
Itt már látni, hogy szerepe van az MSB -nek, most 1-k kerültek az "üres" helyekre.
Balra tolás ( << ):
Pozitív számmal:
Kód:
00000000000000000000000000000100 // 4
<< 2
00000000000000000000000000010000 // 16
Ismét 0-kal egészül ki a szám, azonban itt már figyelni kell, ha túl sokáig shiftelünk egy pozitív számot akkor abból a túlcsordulás előtt ( Balról az első bit az MSB, ha az 1 lesz onnantól negatív a számunk. ) egy negatív szám lesz, túlcsordulásnál pedig a 0 -t veszi fel értékként.
Negatív számmal:
Kód:
11111111111111111111111111111000 // -8
<< 2
11111111111111111111111111100000 // -32
Láthatjuk, hogy itt megint balra shiftelésnél mindig 0-kal egészül ki a szám, legyen az pozitív vagy negatív. Azonban, ha túl sokat toljuk, akkor előfordulhat, hogy a szám elveszíti a negatív értéket és 0 lesz.
6. Logikai biteltolás ( logikai shiftelés ) operátorA logikai shiftelésből csak egy létezik, mégpedig a jobbra logikai shiftelés ( >>> ). Sokban hasonlít a jobbra shiftelő operátorra ( >> ), azonban van egy fontos különbség.
Nézzük is példákkal:
Kód:
00000000000000000000000000010000 // 16
>>> 2
00000000000000000000000000000100 // 4
Itt még nem látni semmi különlegeset, ugyanaz, mint a jobbra shiftelés( >> ).
Negatív számmal:
Kód:
11111111111111111111111111111000 // -8
>>> 2
00111111111111111111111111111110 // 1073741822
Itt már látszik a különbség, logikai shiftelésnél nem számít az MSB értéke, mindig 0 kerül az üresedő helyekre, emiatt sose kaphatunk negatív eredményt.
Na, akkor itt lenne az elméleti rész vége.
Őszintén nekem a felhasználásukra amxmdox -en belül nincs más ötletem, csak, hogy logikai változók helyett használjunk bitműveleteket, így memóriát spórolva.
Hogyan lehetséges ez?
A bit műveletek segítségével tudjuk a változó bitjeit külön - külön kezelni, mint már említettem feljebb, és egy bit lehet 0 vagy 1, ergo egy változóban lehetőségünk van 32 igaz - hamis érték tárolására.
Egy példa kód, ha a játékos be van vakítva, és megöl közbe valakit kiírja, hogy "TE CSALOOO, WH-s!":
#include < amxmodx >
new g_BitVakJatekos
const TASK_VAKULAS_ID = 1000
public plugin_init()
{
register_plugin( "TESZT", "1.0", "Akosch:." );
register_event( "DeathMsg", "evHalal", "a" );
register_event( "ScreenFade", "evVakitas", "bef", "4=255", "5=255", "6=255", "7>199" );
}
public evHalal( )
{
new iGyilkos = read_data( 1 );
new iAldozat = read_data( 2 );
if ( iGyilkos != iAldozat )
if ( g_BitVakJatekos & ( 1 << ( iGyilkos & 31 ) ) ) // Lekérés, h vak-e
client_print( iGyilkos, print_chat, "TE CSALOOO, WH-s!" );
}
public evVakitas( iJatekos )
{
g_BitVakJatekos |= ( 1 << ( iJatekos & 31 ) ); // "true"-ra állítás
new Float:fVakulasIdeje = float( read_data( 2 ) / 4096 );
set_task( fVakulasIdeje, "fnVakulasVege", TASK_VAKULAS_ID + iJatekos );
}
fnVakulasVege( iId )
{
iId -= TASK_VAKULAS_ID;
g_BitVakJatekos &= ~( 1 << ( iId & 31 ) ); // "false"-ra állítás
}
Felmerülhet sokakban a kérdés, hogy az 1 -t miért ( JátékosID & 31 )-el shifteljuk. Gondoljunk vissza kicsit, ha 32 biten 32-vel ( maximum játékos id ) tolnánk akkor az adatvesztéssel járna, kicsúszna a változóból amit bele akarunk tenni. Ezért van szükség egy kis trükre, nézzük csak mi lesz az eredménye a 32 & 31 -nek: 0. Miért jó ez nekünk? Mert a játékos azonosítók 1-től 32-ig tartanak, szóval a 0 szabad, és shiftelésnél se okoz gondot ( 1 << 0 = 1 ), szóval befér még a 32. játékos dolga is a változónkba.
Pár makró, apróság ehhez:
//X helyére megy a játékos azonosítója, és iVar lesz a változó amibe tárolunk.
// Bekapcsolni ( igazra állítani )
iVar |= ( 1 << ( X & 31 ) );
// Kikapcsolni ( falsera állítani )
iVar &= ~( 1 << ( X & 31 ) );
// Átkapcsolni ( igazról hamisra, hamisról igazra )
iVar ^= ( 1 << ( X & 31 ) );
// Lekérni, hogy x -nek igaz v hamis
iVar & ( 1 << ( X & 31 ) );
#define Bit_Set(%0,%1) %0 |= ( 1 << ( %1 & 31 ) )
#define Bit_Clear(%0,%1) %0 &= ~( 1 << ( %1 & 31 ) )
#define Bit_Toggle(%0,%1) %0 ^= ( 1 << ( %1 & 31 ) )
#define Bit_Get(%0,%1) %0 & ( 1 << ( %1 & 31 ) )
Végül pedig szeretném megemlíteni, hogy honnan szedtem az infókat a tutoriálhoz:
Anthony - Bitműveletek, valamint gyakorlati alkalmazás a SA:MP-ban( főleg innen, nagyon szép összeszedett tut )
Bugsy - [TUT] Bits, Bit-fields, and Bit-wise OperatorsVéleményeket várom, ha lenne valami jó ötletetek a használatukra akkor azt is szívesen látnám