C++
Bitoperatörer
Sök…
Anmärkningar
Bitskiftningsoperationer är inte bärbara över alla processorarkitekturer, olika processorer kan ha olika bitbredd. Med andra ord, om du skrev
int a = ~0;
int b = a << 1;
Detta värde skulle vara annorlunda på en 64-bitars maskin kontra på en 32-bitars maskin, eller från en x86-baserad processor till en PIC-baserad processor.
Endian-ness behöver inte beaktas för själva lite kloka operationer, det vill säga rätt skift ( >>
) kommer att flytta bitarna mot den minst betydande biten och en XOR kommer att utföra en exklusiv eller på bitarna. Endian-ness behöver bara beaktas med själva uppgifterna, det vill säga om endian-ness är ett problem för din ansökan är det ett problem oavsett lite kloka funktioner.
& - bitvis OCH
int a = 6; // 0110b (0x06)
int b = 10; // 1010b (0x0A)
int c = a & b; // 0010b (0x02)
std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
Produktion
a = 6, b = 10, c = 2
Varför
Lite klok AND
arbetar på bitnivån och använder följande booleska sanningstabell:
TRUE AND TRUE = TRUE
TRUE AND FALSE = FALSE
FALSE AND FALSE = FALSE
När det binära värdet för a
( 0110
) och det binära värdet för b
( 1010
) AND
'redigeras tillsammans får vi det binära värdet 0010
:
int a = 0 1 1 0
int b = 1 0 1 0 &
---------
int c = 0 0 1 0
Den bitmässiga OCH ändrar inte värdet på de ursprungliga värdena såvida inte specifikt tilldelats att använda den bitmässiga tilldelningssammansättningsoperatören &=
:
int a = 5; // 0101b (0x05)
a &= 10; // a = 0101b & 1010b
| - bitvis ELLER
int a = 5; // 0101b (0x05)
int b = 12; // 1100b (0x0C)
int c = a | b; // 1101b (0x0D)
std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
Produktion
a = 5, b = 12, c = 13
Varför
Lite klok OR
fungerar på bitnivån och använder följande booleska sanningstabell:
true OR true = true
true OR false = true
false OR false = false
När det binära värdet för a
( 0101
) och det binära värdet för b
( 1100
) OR
redigeras tillsammans får vi det binära värdet 1101
:
int a = 0 1 0 1
int b = 1 1 0 0 |
---------
int c = 1 1 0 1
Den bitmässiga ELLER ändrar inte värdet på de ursprungliga värdena såvida inte specifikt tilldelats att använda den bitvisa tilldelningens sammansatta operatör |=
:
int a = 5; // 0101b (0x05)
a |= 12; // a = 0101b | 1101b
^ - bitvis XOR (exklusiv ELLER)
int a = 5; // 0101b (0x05)
int b = 9; // 1001b (0x09)
int c = a ^ b; // 1100b (0x0C)
std::cout << "a = " << a << ", b = " << b << ", c = " << c << std::endl;
Produktion
a = 5, b = 9, c = 12
Varför
En lite klok XOR
(exklusiv eller) fungerar på bitnivån och använder följande booleska sanningsbord:
true OR true = false
true OR false = true
false OR false = false
Lägg märke till att med en XOR-operation true OR true = false
där som med operationer true AND/OR true = true
, följaktligen XOR-operationens exklusiva karaktär.
Om detta används, när det binära värdet för a
( 0101
) och det binära värdet för b
( 1001
) är XOR
'tillsammans får vi det binära värdet 1100
:
int a = 0 1 0 1
int b = 1 0 0 1 ^
---------
int c = 1 1 0 0
Den bitvisa XOR ändrar inte värdet på de ursprungliga värdena såvida inte specifikt tilldelats att använda den bitvisa tilldelningen sammansatta operatören ^=
:
int a = 5; // 0101b (0x05)
a ^= 9; // a = 0101b ^ 1001b
Den bitvisa XOR kan användas på många sätt och används ofta vid bitmaskoperationer för kryptering och komprimering.
Obs: Följande exempel visas ofta som ett exempel på ett fint trick. Men bör inte användas i produktionskod (det finns bättre sätt std::swap()
att uppnå samma resultat).
Du kan också använda en XOR-operation för att byta två variabler utan en tillfällig:
int a = 42;
int b = 64;
// XOR swap
a ^= b;
b ^= a;
a ^= b;
std::cout << "a = " << a << ", b = " << b << "\n";
För att producera detta måste du lägga till en check för att se till att den kan användas.
void doXORSwap(int& a, int& b)
{
// Need to add a check to make sure you are not swapping the same
// variable with itself. Otherwise it will zero the value.
if (&a != &b)
{
// XOR swap
a ^= b;
b ^= a;
a ^= b;
}
}
Så även om det ser ut som ett fint trick isolerat, är det inte användbart i riktig kod. xor är inte en baslogisk operation, utan en kombination av andra: a ^ c = ~ (a & c) & (a | c)
även i 2015+ kompilatorvariabler kan tilldelas som binär:
int cn=0b0111;
~ - bitvis INTE (unikt komplement)
unsigned char a = 234; // 1110 1010b (0xEA)
unsigned char b = ~a; // 0001 0101b (0x15)
std::cout << "a = " << static_cast<int>(a) <<
", b = " << static_cast<int>(b) << std::endl;
Produktion
a = 234, b = 21
Varför
Lite klokt NOT
(unikt komplement) fungerar på bitnivån och vänder helt enkelt varje bit. Om det är ett 1
, ändras det till ett 0
, om det är ett 0
, ändras det till ett 1
. Den lite kloka INTE har samma effekt som XOR att sätta ett värde mot maxvärdet för en specifik typ:
unsigned char a = 234; // 1110 1010b (0xEA)
unsigned char b = ~a; // 0001 0101b (0x15)
unsigned char c = a ^ ~0;
Det lite kloka INTE kan också vara ett bekvämt sätt att kontrollera det maximala värdet för en specifik integraltyp:
unsigned int i = ~0;
unsigned char c = ~0;
std::cout << "max uint = " << i << std::endl <<
"max uchar = " << static_cast<short>(c) << std::endl;
Det lite kloka ändrar INTE inte värdet på det ursprungliga värdet och har inte en sammansatt tilldelningsoperatör, så du kan inte göra a ~= 10
till exempel.
Det lite kloka INTE ( ~
) bör inte förväxlas med det logiska INTE ( !
); där lite klokt INTE kommer att vända varje bit, kommer en logisk INTE att använda hela värdet för att utföra sin funktion på, med andra ord (!1) != (~1)
<< - vänster skift
int a = 1; // 0001b
int b = a << 1; // 0010b
std::cout << "a = " << a << ", b = " << b << std::endl;
Produktion
a = 1, b = 2
Varför
Den vänstra bitvisa växlingen kommer att förskjuta bitarna i det vänstra värdet ( a
) antalet som anges till höger ( 1
), väsentligen vadderar de minst betydande bitarna med 0: er, så att värdet på 5
(binär 0000 0101
) 0000 0101
till vänster 4 gånger (t.ex. 5 << 4
) ger värdet 80
(binär 0101 0000
). Du kanske noterar att att flytta ett värde till vänster 1 gång också är detsamma som att multiplicera värdet med 2, exempel:
int a = 7;
while (a < 200) {
std::cout << "a = " << a << std::endl;
a <<= 1;
}
a = 7;
while (a < 200) {
std::cout << "a = " << a << std::endl;
a *= 2;
}
Men det bör noteras att vänsterväxlingsoperationen förskjuter alla bitar till vänster, inklusive teckenbiten, exempel:
int a = 2147483647; // 0111 1111 1111 1111 1111 1111 1111 1111
int b = a << 1; // 1111 1111 1111 1111 1111 1111 1111 1110
std::cout << "a = " << a << ", b = " << b << std::endl;
Möjlig utgång: a = 2147483647, b = -2
Medan vissa kompilatorer kommer att ge resultat som verkar förväntat, bör det noteras att om du lämnar skiftande ett signerat nummer så att teckenbiten påverkas är resultatet odefinierat . Det är också odefinierat om antalet bitar du vill växla med är ett negativt antal eller är större än antalet bitar som typen till vänster kan innehålla, exempel:
int a = 1;
int b = a << -1; // undefined behavior
char c = a << 20; // undefined behavior
Den bitvisa vänsterförskjutningen ändrar inte värdet på de ursprungliga värdena såvida inte specifikt tilldelats att använda den bitvisa tilldelningen sammansatta operatören <<=
:
int a = 5; // 0101b
a <<= 1; // a = a << 1;
>> - höger skift
int a = 2; // 0010b
int b = a >> 1; // 0001b
std::cout << "a = " << a << ", b = " << b << std::endl;
Produktion
a = 2, b = 1
Varför
Den högra bitvisa växlingen förskjuter bitarna i det vänstra värdet ( a
) numret som anges till höger ( 1
); Det bör noteras att även om driften av en högerväxling är standard, är vad som händer med bitarna av en högerförskjutning på ett undertecknat negativt nummer implementeringsdefinierat och därför kan det inte garanteras att det är portabelt, exempel:
int a = -2;
int b = a >> 1; // the value of b will be depend on the compiler
Det definieras också om antalet bitar du vill växla med är ett negativt antal, exempel:
int a = 1;
int b = a >> -1; // undefined behavior
Den bitmässiga höger växlingen ändrar inte värdet på de ursprungliga värdena såvida inte specifikt tilldelats att använda den bitvisa tilldelningen sammansatta operatören >>=
:
int a = 2; // 0010b
a >>= 1; // a = a >> 1;