A C programozási nyelvben a műveletek bitszinten hajthatók végre bitenkénti operátorok segítségével.
A bitenkénti műveletekkel szemben a bájtszintű műveletek jellemzik a bitenkénti operátorok logikai megfelelőit, az AND, OR, NOT (ÉS, VAGY, NEM) operátorokat. Az egyes biteken végzett műveletek helyett a bájtszintű operátorok egyszerre nyolc bites (bájtként ismert) karakterláncokon hajtanak végre műveleteket. Ennek az az oka, hogy a bájt általában a címezhető memória legkisebb egysége (azaz egyedi memóriacímmel rendelkező adat).
Ez a bitenkénti operátorokra is vonatkozik, ami azt jelenti, hogy bár egyszerre csak egy biten dolgoznak, nem fogadhatnak el egy bájtnál kisebb bemenetet.
Mindezek az operátorok elérhetők C++-ban és számos C-családbeli nyelvben is.
Bitenkénti operátorok A C hat operátort biztosít a bitmanipulációhoz. Regarded by many to be the authoritative reference on C.
Szimbólum művelet & bitenkénti ÉS (AND) | bitenkénti VAGY (inclusive OR) ^ bitenkénti kizáró VAGY (XOR, exclusive OR) << léptetés balra (left shift) >> léptetés jobbra (right shift) ~ bitenkénti NOT (egyes komplemens) (unáris)
Bitwise AND & bit a bit b a & b (a AND b) 0 0 0 0 1 0 1 0 0 1 1 1
A bitenkénti AND (ÉS) operátor egyetlen és jel: &. Ez csupán az AND reprezentációja, amely az operandusok bitjein végzi a munkáját, nem pedig az operandusok igazságértékén. A bitenkénti bináris AND logikai konjunkciót végez (a fenti táblázatban látható) a szám minden pozíciójában lévő bitek között bináris formában.
Például egy bájttal (char típusú) való munka: 11001000 & 10111000
= 10001000
Az első szám legnagyobb helyiértékű bitje 1,A számítástechnikában a legkisebb helyiértékű bit least significant bit (LSb) a bináris egész szám azon bitpozíciója, amely az egész szám legalacsonyabb helyiértékű helyiértékét jelöli. Hasonlóképpen, a legnagyobb helyiértékű bit most significant bit (MSb) a bináris egész szám legmagasabb helyiértékét jelöli. és a második számé is 1, tehát az eredmény legnagyobb helyiértékű bitje 1; a második legnagyobb helyiértékű bitben a második szám bitje nulla, tehát az eredmény 0.
Bitwise OR | bit a bit b a b (a OR b) 0 0 0 0 1 1 1 0 1 1 1 1
A bitenkénti ÉS-hez hasonlóan a bitenkénti VAGY logikai diszjunkciót hajt végre bit szinten. Az eredménye 1, ha bármelyik bit 1, és nulla csak akkor, ha mindkét bit 0. A szimbóluma |, amelyet pipe-nak nevezhetünk.
11001000 | 10111000
= 11111000
Bitwise XOR ^ bit a bit b a ^ b (a XOR b) 0 0 0 0 1 1 1 0 1 1 1 0
A bitenkénti XOR (kizáró vagy) egy kizáró diszjunkciót hajt végre, ami egyenértékű két bit hozzáadásával és az átvitel elvetésével. Az eredmény csak akkor nulla, ha két nulla vagy két egyes van. Az XOR segítségével a bitek 1 és 0 között válthatók. Így az i = i ^ 1 egy ciklusban 1 és 0 között váltja az értékeit.
11001000 ^ 10111000
= 01110000
Léptető operátorok (shift) Két bitenkénti léptetés vagy eltolás operátor létezik (shift). Ezek a következők: Léptetés/eltolás jobbra, jele: Léptetés/eltolás balra, jele:
Léptetés jobbra >> A jobbra eltolás operátor szimbóluma >>. Működéséhez két operandus szükséges. A bal operandus minden bitjét jobbra tolja. Az operátort követő szám határozza meg, hogy a bitek hány hellyel legyenek eltolva (azaz a jobb operandus). Így a művelettel az összes bit három hellyel jobbra tolódik, és így tovább.
Azonban vegye figyelembe, hogy egy negatív szám, vagy az értékben lévő bitek teljes számánál nagyobb vagy egyenlő eltolási operandusérték nem definiált viselkedést eredményez. Például egy 32 bites előjel nélküli egész szám eltolásakor a 32-es vagy nagyobb eltolási érték nem definiált lenne.
Példa: Ha a ch változó tartalmazza az 11100101 bitmintát, akkor a a 01110010 eredményt, a pedig a 00111001 eredményt adja.
Itt a bitek jobbra eltolásakor egyidejűleg üres hely keletkezik a bal oldalon. Előjel nélküli típuson vagy előjeles típus nemnegatív értékén végrehajtva a művelet logikai eltolás, aminek következtében az üres helyeket 0-k (nullák) töltik ki. Ha egy előjeles típus negatív értékén hajtják végre, az eredmény technikailag implementációfüggő (fordítófüggő), azonban a legtöbb fordító aritmetikai eltolást hajt végre, aminek következtében az üres hely a bal operandus beállított előjelbitjével lesz kitöltve.
A jobbra eltolás egy bitmintát kettővel oszthat az ábrán látható módon: i = 14; // bitminta: 00001110 j = i >> 1; // a mintát 1 hellyel jobbra mozgattuk, így ezt kapjuk: 00000111 = 7 ami 14/2
Jobbra eltolás operátor használata A C nyelven használt jobbra shift operátor tipikus használatát a következő kód mutatja.
Példa: #include <stdio.h>
void showbits( unsigned int x ) { int i=0; for (i = (sizeof(int) * 8) - 1; i >= 0; i--)
putchar(x & (1u << i) ? '1' : '0');
printf("\n"); }
int main( void ) { int j = 5225; printf("%d in binary \t\t ", j); showbits(j);
/* the loop for right shift operation */ for (int m = 0; m <= 5; m++)
int n = j >> m; printf("%d right shift %d gives ", j, m); showbits(n);
return 0; }
A fenti program kimenete a következő lesz: 5225 in binary 00000000000000000001010001101001 5225 right shift 0 gives 00000000000000000001010001101001 5225 right shift 1 gives 00000000000000000000101000110100 5225 right shift 2 gives 00000000000000000000010100011010 5225 right shift 3 gives 00000000000000000000001010001101 5225 right shift 4 gives 00000000000000000000000101000110 5225 right shift 5 gives 00000000000000000000000010100011
Léptetés balra << A balra eltolás/léptetés operátor szimbóluma . A bal oldali operandus minden bitjét balra tolja el, a jobb oldali operandus által jelzett pozíciószámmal. Ez ellentétesen működik, mint a jobbra eltolás operátor. Így a fenti példában (11100101) a ch << 1 művelettel 11001010-et kapunk. A keletkezett üres helyeket a fentiek szerint nullákkal töltjük ki.
Azonban vegye figyelembe, hogy az olyan eltoló operandusérték, amely vagy negatív szám, vagy nagyobb vagy egyenlő az érték bitjeinek teljes számával, definiálatlan viselkedést eredményez. Ezt az ISO 9899:2011 6.5.7 szabvány definiálja. Például egy 32 bites előjel nélküli egész szám eltolásakor a 32-es vagy nagyobb eltolási érték definiálatlan lenne.
A balra eltolás segítségével egy egész számot 2 hatványaival szorozhatunk, ahogy az a példában látható. int i = 7; // Decimal 7 is Binary (2^2) + (2^1) + (2^0) = 0000 0111 int j = 3; // Decimal 3 is Binary (2^1) + (2^0) = 0000 0011 k = (i << j); // Left shift operation multiplies the value by 2 to the power of j in decimal // Equivalent to adding j zeros to the binary representation of i // 56 = 7 * 2^3 // 0011 1000 = 0000 0111 << 0000 0011
Példa: egy egyszerű összeadó program A következő program két operandust ad össze AND, XOR és balra eltolás (<<) segítségével. #include <stdio.h>
int main( void ) { unsigned int x = 3, y = 1, sum, carry; sum = x ^ y; // x XOR y carry = x & y; // x AND y while (carry != 0)
carry = carry << 1; // left shift the carry x = sum; // initialize x as sum y = carry; // initialize y as carry sum = x ^ y; // sum is calculated carry = x & y; /* carry is calculated, the loop condition is evaluated and the process is repeated until carry is equal to 0. */
printf("%u\n", sum); // the program will print 4 return 0; }
Bitenkénti értékadó operátorok A C nyelv minden bináris aritmetikai és bitenkénti művelethez egy összetett értékadó operátort biztosít.A kiterjesztett értékadás (vagy összetett értékadás) bizonyos értékadó operátorok elnevezése bizonyos programozási nyelvekben (különösen a C-ből származókban). A kiterjesztett értékadást általában egy olyan utasítás helyettesítésére használják, ahol egy operátor egy változót vesz argumentumként, majd az eredményt ugyanahhoz a változóhoz rendeli. Egy egyszerű példa erre az x += 1, amelyet x = x + 1-re bontunk. Hasonló konstrukciók gyakran elérhetők különböző bináris operátorokhoz. Minden operátor elfogad egy bal oldali és egy jobb oldali operandust, mindkettőn végrehajtja a megfelelő bináris műveletet, és az eredményt a bal oldali operandusban tárolja.
A bitenkénti értékadó operátorok a következők: Symbol Operator &= bitwise AND assignment |= bitwise inclusive OR assignment ^= bitwise exclusive OR assignment <<= left shift assignment >>= right shift assignment
Logikai ekvivalensek Négy bitenkénti operátornak van ekvivalens logikai operátora. Ezek abban egyenértékűek, hogy ugyanazokkal az igazságtáblázatokkal rendelkeznek. A logikai operátorok azonban minden operandust egyetlen értékként kezelnek, amely lehet igaz vagy hamis, ahelyett, hogy az operandus minden bitjét független értékként kezelnék. A logikai operátorok a nullát hamisnak, a nullától eltérő értékeket pedig igaznak tekintik. Egy másik különbség, hogy a logikai operátorok rövidzárlat-kiértékelést végeznek.A rövidzárlatos kiértékelés, a minimális kiértékelés vagy a McCarthy-kiértékelés (John McCarthy után) bizonyos programozási nyelvekben található logikai műveletek szemantikája, amelyekben a második argumentum csak akkor hajtódik végre vagy értékelődik ki, ha az első argumentum nem elegendő a kifejezés értékének meghatározásához: amikor az AND függvény első argumentuma false-ra értékelődik, az összesített értéknek false-nak kell lennie; és amikor az OR függvény első argumentuma true-ra értékelődik, az összesített értéknek true-nak kell lennie.
Az alábbi táblázat összepárosítja az ekvivalens operátorokat, és az a-t és b-t mutatja az operátorok operandusaiként.
Bitwise Logical a & b a && b a | b a || b a ^ b a != b ~a !a
A != ugyanazzal az igazságtáblázattal rendelkezik, mint a ^, de a valódi logikai operátorokkal ellentétben a != önmagában szigorúan véve nem logikai operátor. Ez azért van, mert egy logikai operátornak minden nullától eltérő értéket ugyanúgy kell kezelnie. Ahhoz, hogy a != logikai operátorként használható legyen, az operandusokat először normalizálni kell. Ha a logikai műveletet nem alkalmazzuk mindkét operandusra, az nem változtatja meg az eredményül kapott igazságtáblázatot, de biztosítja, hogy az összes nullától eltérő érték azonos értékre konvertálódjon az összehasonlítás előtt. Ez azért működik, mert a nullán lévő ! mindig egyet eredményez, és bármely nullától eltérő érték esetén a ! mindig nullát eredményez.
Példa: /* Equivalent bitwise and logical operator tests */ #include <stdio.h>
void testOperator(char* name, unsigned char was, unsigned char expected);
int main( void ) { // -- Bitwise operators -- //
//Truth tables packed in bits const unsigned char operand1 = 0x0A; //0000 1010 const unsigned char operand2 = 0x0C; //0000 1100 const unsigned char expectedAnd = 0x08; //0000 1000 const unsigned char expectedOr = 0x0E; //0000 1110 const unsigned char expectedXor = 0x06; //0000 0110
const unsigned char operand3 = 0x01; //0000 0001 const unsigned char expectedNot = 0xFE; //1111 1110
testOperator("Bitwise AND", operand1 & operand2, expectedAnd); testOperator("Bitwise OR", operand1 | operand2, expectedOr); testOperator("Bitwise XOR", operand1 ^ operand2, expectedXor); testOperator("Bitwise NOT", ~operand3, expectedNot); printf("\n");
// -- Logical operators -- //
const unsigned char F = 0x00; //Zero const unsigned char T = 0x01; //Any nonzero value
// Truth tables packed in arrays
const unsigned char operandArray1 = {T, F, T, F}; const unsigned char operandArray2 = {T, T, F, F}; const unsigned char expectedArrayAnd = {T, F, F, F}; const unsigned char expectedArrayOr = {T, T, T, F}; const unsigned char expectedArrayXor = {F, T, T, F};
const unsigned char operandArray3 = {F, T}; const unsigned char expectedArrayNot = {T, F};
int i; for (i = 0; i < 4; i++)
testOperator("Logical AND", operandArray1[i] && operandArray2[i], expectedArrayAnd[i]);
printf("\n");
for (i = 0; i < 4; i++)
testOperator("Logical OR", operandArray1[i] || operandArray2[i], expectedArrayOr[i]);
printf("\n");
for (i = 0; i < 4; i++)
//Needs ! on operand's in case nonzero values are different