C++
Операторы бит
Поиск…
замечания
Операции сдвига бит не переносимы во всех архитектурах процессоров, разные процессоры могут иметь разную ширину бит. Другими словами, если вы написали
int a = ~0;
int b = a << 1;
Это значение будет отличаться на 64-битной машине против 32-битной машины или от процессора на базе x86 до процессора на базе PIC.
Endian-ness не нужно принимать во внимание сами бит-операции, т. Е. Правый сдвиг ( >>
) сдвигает биты в сторону младшего значащего бита, а XOR будет выполнять исключение или по битам. Endian-ness нужно учитывать только сами данные, то есть, если конечная точка является проблемой для вашего приложения, это вызывает беспокойство, независимо от бит-мудрейных операций.
& - побитовое И
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;
Выход
a = 6, b = 10, c = 2
Зачем
Немного мудрый AND
работает на уровне бит и использует следующую логическую таблицу истинности:
TRUE AND TRUE = TRUE
TRUE AND FALSE = FALSE
FALSE AND FALSE = FALSE
Когда двоичное значение для ( a
0110
) и бинарное значение b
( 1010
) являются AND
«эд вместе мы получим двоичное значение 0010
:
int a = 0 1 1 0
int b = 1 0 1 0 &
---------
int c = 0 0 1 0
Бит мудрый И не изменяет значения исходных значений, если специально не назначено использование битового умножения оператора-оператора &=
:
int a = 5; // 0101b (0x05)
a &= 10; // a = 0101b & 1010b
| - побитовое ИЛИ
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;
Выход
a = 5, b = 12, c = 13
Зачем
Бит мудрый OR
работает на уровне бит и использует следующую таблицу логических истин:
true OR true = true
true OR false = true
false OR false = false
Когда двоичное значение для ( a
0101
) и бинарное значение b
( 1100
) являются OR
«эд вместе мы получим двоичное значение 1101
:
int a = 0 1 0 1
int b = 1 1 0 0 |
---------
int c = 1 1 0 1
Бит мудрый ИЛИ не изменяет значения исходных значений, если только специально не назначено использование битового умножения оператора соединения |=
:
int a = 5; // 0101b (0x05)
a |= 12; // a = 0101b | 1101b
^ - побитовое XOR (исключающее ИЛИ)
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;
Выход
a = 5, b = 9, c = 12
Зачем
Немного мудрый XOR
(эксклюзивный или) работает на уровне бит и использует следующую логическую таблицу истинности:
true OR true = false
true OR false = true
false OR false = false
Обратите внимание, что при операции XOR true OR true = false
где, как и при действии true AND/OR true = true
, следовательно, исключительный характер операции XOR.
Используя это, когда двоичное значение для ( a
0101
) и бинарное значение b
( 1001
) являются XOR
- е изд «вместе мы получим двоичное значение 1100
:
int a = 0 1 0 1
int b = 1 0 0 1 ^
---------
int c = 1 1 0 0
Бит мудрый XOR не изменяет значения исходных значений, если специально не назначено использование битового оператора присваивания соединения ^=
:
int a = 5; // 0101b (0x05)
a ^= 9; // a = 0101b ^ 1001b
Бит мудрый XOR может использоваться многими способами и часто используется в операциях бит-маски для шифрования и сжатия.
Примечание . Следующий пример часто показан в качестве примера приятного трюка. Но не следует использовать в производственном коде (для достижения такого же результата лучше использовать std::swap()
).
Вы также можете использовать операцию XOR для замены двух переменных без временного:
int a = 42;
int b = 64;
// XOR swap
a ^= b;
b ^= a;
a ^= b;
std::cout << "a = " << a << ", b = " << b << "\n";
Чтобы произвести это, вам нужно добавить чек, чтобы убедиться, что он может быть использован.
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;
}
}
Таким образом, хотя это выглядит как хороший трюк в изоляции, он не полезен в реальном коде. xor не является базовой логической операцией, а комбинацией других: a ^ c = ~ (a & c) & (a | c)
также в 2015 году переменные компиляторов могут быть назначены как двоичные:
int cn=0b0111;
~ - побитовое NOT (унарное дополнение)
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;
Выход
a = 234, b = 21
Зачем
Немного мудрый NOT
(унарное дополнение) работает на уровне бит и просто переворачивает каждый бит. Если оно равно 1
, оно изменилось на 0
, если оно равно 0
, оно изменилось на 1
. Бит wise NOT имеет тот же эффект, что и XOR'ing значение против максимального значения для определенного типа:
unsigned char a = 234; // 1110 1010b (0xEA)
unsigned char b = ~a; // 0001 0101b (0x15)
unsigned char c = a ^ ~0;
Бит мудрый NOT также может быть удобным способом проверки максимального значения для определенного интегрального типа:
unsigned int i = ~0;
unsigned char c = ~0;
std::cout << "max uint = " << i << std::endl <<
"max uchar = " << static_cast<short>(c) << std::endl;
Бит мудрый NOT не изменяет значение исходного значения и не имеет составного оператора присваивания, поэтому вы не можете сделать a ~= 10
например.
Бит мудрый NOT ( ~
) не следует путать с логическим NOT ( !
); где бит мудрый НЕ будет переворачивать каждый бит, логическое НЕ будет использовать все значение для его работы, другими словами (!1) != (~1)
<< - сдвиг влево
int a = 1; // 0001b
int b = a << 1; // 0010b
std::cout << "a = " << a << ", b = " << b << std::endl;
Выход
a = 1, b = 2
Зачем
Левый бит мудрый сдвиг сдвинет биты значения левой руки ( a
) числа, указанного справа ( 1
), по существу, заполняя младшие значащие биты 0, поэтому сдвиг значения 5
(двоичный 0000 0101
) влево 4 раза (например, 5 << 4
) даст значение 80
(двоичный 0101 0000
). Вы можете заметить, что смещение значения влево 1 раз также совпадает с умножением значения на 2, например:
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;
}
Но следует отметить, что операция сдвига влево сдвигает все биты влево, включая бит знака, например:
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;
Возможный выход: a = 2147483647, b = -2
В то время как некоторые компиляторы дают ожидаемые результаты, следует отметить, что если вы оставили сдвиг значного числа, чтобы повлиять на бит знака, результат не определен . Также не определено, если количество бит, которое вы хотите сдвинуть, является отрицательным числом или больше, чем количество бит, которое может удерживать тип слева, например:
int a = 1;
int b = a << -1; // undefined behavior
char c = a << 20; // undefined behavior
Бит мудрый сдвиг влево не изменяет значения исходных значений, если специально не назначено использование битового оператора соединения соединения <<=
:
int a = 5; // 0101b
a <<= 1; // a = a << 1;
>> - правая смена
int a = 2; // 0010b
int b = a >> 1; // 0001b
std::cout << "a = " << a << ", b = " << b << std::endl;
Выход
a = 2, b = 1
Зачем
Правильный битовый сдвиг сдвигает биты значения левой руки ( a
) числа, указанного справа ( 1
); следует отметить, что, хотя операция правого сдвига является стандартной, то, что происходит с битами правого сдвига по значению отрицательного числа, является определенной реализацией и, следовательно, не может быть гарантировано быть переносимым, например:
int a = -2;
int b = a >> 1; // the value of b will be depend on the compiler
Также не определено, если количество бит, которое вы хотите сдвинуть, является отрицательным числом, например:
int a = 1;
int b = a >> -1; // undefined behavior
Правильный сдвиг по битам не изменяет значения исходных значений, если специально не назначено использование битового оператора присваивания соединения >>=
:
int a = 2; // 0010b
a >>= 1; // a = a >> 1;