Каковы некоторые случаи использования в реальном мире следующих побитовых операторов?
- И
- XOR
- НЕ
- ИЛИ
Каковы некоторые случаи использования в реальном мире следующих побитовых операторов?
Бит-поля (флаги)
Они являются наиболее эффективным способом представления того, чье состояние определяется несколькими свойствами "да" или "нет". ACL - хороший пример; если вы допустили 4 дискретных разрешения (чтение, запись, выполнение, изменение политики), лучше хранить это в 1 байте, а не в отходах 4. Они могут быть сопоставлены типам перечислений на многих языках для дополнительного удобства.
Связь через порты/сокеты
Всегда включает в себя контрольные суммы, четность, стоп-биты, алгоритмы управления потоком и т.д., Которые обычно зависят от логических значений отдельных байтов, а не от числовых значений, поскольку среда может быть способна передавать только один бит за раз.
Сжатие, шифрование
Оба они сильно зависят от побитовых алгоритмов. Посмотрите на алгоритм deflate для примера - все в битах, а не в байтах.
Конечные машины
Я говорю прежде всего о том, что встраивается в какое-то оборудование, хотя их можно найти и в программном обеспечении. Они являются комбинаторными по своей природе - они могут буквально "скомпилироваться" до кучи логических ворот, поэтому их нужно выражать как AND
, OR
, NOT
и т.д.
Графика
Здесь почти нет места, чтобы попасть в каждую область, где эти операторы используются в графическом программировании. XOR
(или ^
) особенно интересен здесь, потому что повторное применение второго входа отменяет первый. Старые графические интерфейсы использовали этот подход для выделения выделения и других наложений, чтобы исключить необходимость дорогостоящего перерисовки. Они по-прежнему полезны в медленных графических протоколах (т.е. Удаленный рабочий стол).
Это были только первые несколько примеров, которые я придумал - это вряд ли исчерпывающий список.
Это странно?
(value & 0x1) > 0
Он делится на две (четные)?
(value & 0x1) == 0
Хорошим примером является низкоуровневое программирование. Например, вы можете написать конкретный бит в регистр, привязанный к памяти, чтобы сделать какое-то часть оборудования так, как вы хотите:
volatile uint32_t *register = (volatile uint32_t *)0x87000000;
uint32_t value;
uint32_t set_bit = 0x00010000;
uint32_t clear_bit = 0x00001000;
value = *register; // get current value from the register
value = value & ~clear_bit; // clear a bit
value = value | set_bit; // set a bit
*register = value; // write it back to the register
Кроме того, htonl()
и htons()
реализованы с использованием операторов &
и |
(на машинах, endianness (Порядок байтов) не соответствует порядку сети):
#define htons(a) ((((a) & 0xff00) >> 8) | \
(((a) & 0x00ff) << 8))
#define htonl(a) ((((a) & 0xff000000) >> 24) | \
(((a) & 0x00ff0000) >> 8) | \
(((a) & 0x0000ff00) << 8) | \
(((a) & 0x000000ff) << 24))
Я использую их, чтобы получить значения RGB (A) из упакованных значений цвета, например.
Вот некоторые общие идиомы, касающиеся флагов, хранящихся как отдельные биты.
enum CDRIndicators {
Local = 1 << 0,
External = 1 << 1,
CallerIDMissing = 1 << 2,
Chargeable = 1 << 3
};
unsigned int flags = 0;
Установите флаг Chargeable:
flags |= Chargeable;
Удалить флаг CallerIDMissing:
flags &= ~CallerIDMissing;
Проверьте, установлены ли CallerIDMissing и Chargeable:
if((flags & (CallerIDMissing | Chargeable )) == (CallerIDMissing | Chargeable)) {
}
Я использовал побитовые операции при реализации модели безопасности для CMS. У него были страницы, к которым пользователи могли получить доступ, если они были в соответствующих группах. Пользователь может быть в нескольких группах, поэтому нам нужно проверить, было ли пересечение между группами пользователей и группами страниц. Поэтому мы назначили каждой группе уникальный идентификатор power-of-2, например:
Group A = 1 --> 00000001
Group B = 2 --> 00000010
Group C = 3 --> 00000100
Мы ИЛИ эти значения вместе и сохраняем значение (как один int) со страницей. Например. если к странице можно получить доступ к группам A и B, мы сохраняем значение 3 (которое в двоичном формате равно 00000011) в качестве контроля доступа к страницам. Точно так же мы сохраняем значение идентификаторов группы ORed с пользователем, чтобы представлять, в каких группах они находятся.
Таким образом, чтобы проверить, может ли данный пользователь получить доступ к данной странице, вам просто нужно СО И значения вместе и проверить, не имеет ли значение ненулевое значение. Это очень быстро, так как эта проверка реализована в одной команде, без циклов, без дублирования базы данных.
Когда у меня есть куча логических флагов, мне нравится хранить их все в int.
Я получаю их, используя побитовое-И. Например:
int flags;
if (flags & 0x10) {
// Turn this feature on.
}
if (flags & 0x08) {
// Turn a second feature on.
}
и др.
Шифрование - это все побитовые операции.
Я просто использовал bitwise-XOR (^
) около трех минут назад для вычисления контрольной суммы для последовательной связи с ПЛК...
Вы можете использовать их как быстрый и грязный способ хэш-данных.
int a = 1230123;
int b = 1234555;
int c = 5865683;
int hash = a ^ b ^ c;
& = И:
Выделите определенные биты.
Вы определяете конкретные биты, которые должны отображаться
или не отображается. 0x0 и x очищают все биты в байте, а 0xFF не изменят x.
0x0F отображает бит в нижнем полубайте.
Конверсия:
Чтобы преобразовать более короткие переменные в более длинные с идентификатором бита, необходимо отрегулировать биты, потому что -1 в int равен 0xFFFFFFFF, тогда как -1 в длинном 0xFFFFFFFFFFFFFFFF. Сохранять
личность, которую вы применяете для маски после преобразования.
| = OR
Установите биты. Биты будут установлены независимо, если они уже установлены. Многие datastructures (битовые поля) имеют флаги, такие как IS_HSET = 0, IS_VSET = 1, которые могут быть установлены отдельно.
Чтобы установить флаги, вы применяете IS_HSET | IS_VSET (в C и сборке это очень удобно читать)
^ = XOR
Найдите биты, которые являются одинаковыми или разными.
~ = НЕ
Флип-бит.
Можно показать, что все возможные локальные битовые операции могут быть реализованы этими операциями. Поэтому, если вам нравится, вы можете реализовать инструкцию ADD только с помощью операций с битами.
Некоторые замечательные хаки:
http://www.ugcs.caltech.edu/~wnoise/base2.html
http://www.jjj.de/bitwizardry/bitwizardrypage.html
Побитовое и используется для маскировки/извлечения определенной части байта.
1 Байт-переменная
01110010
&00001111 Bitmask of 0x0F to find out the lower nibble
--------
00000010
В частности, для расчетов часто используется оператор сдвига (< < → ).
Это пример чтения цветов из растрового изображения в байтовом формате
byte imagePixel = 0xCCDDEE; /* Image in RRGGBB format R=Red, G=Green, B=Blue */
//To only have red
byte redColour = imagePixel & 0xFF0000; /*Bitmasking with AND operator */
//Now, we only want red colour
redColour = (redColour >> 24) & 0xFF; /* This now returns a red colour between 0x00 and 0xFF.
Я надеюсь, что это крошечные примеры помогут...
В качестве примера используется кодировка Base64. Кодировка Base64 используется для представления двоичных данных в качестве печатных символов для отправки по электронной почте (и в других целях). Кодировка Base64 преобразует серию 8-битных байтов в 6-разрядные индексы поиска символов. Бит-операции, сдвиг, и, или, не требуется, очень полезны для реализации бит-операций, необходимых для кодирования и декодирования Base64.
Это, конечно, всего лишь один из бесчисленных примеров.
В абстрактном мире современности современный язык, не слишком много. Файл IO - это легкий, который приходит на ум, хотя он выполняет побитовые операции над уже реализованным и не реализует что-то, что использует побитовые операции. Тем не менее, как простой пример, этот код демонстрирует удаление атрибута "только для чтения" в файле (чтобы его можно было использовать с новым FileStream, определяющим FileMode.Create) в С#:
//Hidden files posses some extra attibutes that make the FileStream throw an exception
//even with FileMode.Create (if exists -> overwrite) so delete it and don't worry about it!
if(File.Exists(targetName))
{
FileAttributes attributes = File.GetAttributes(targetName);
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
File.SetAttributes(targetName, attributes & (~FileAttributes.ReadOnly));
File.Delete(targetName);
}
Что касается пользовательских реализаций, здесь приведен пример: Я создал "центр сообщений" для отправки защищенных сообщений от одной установки нашего распределенного приложения к другому. В принципе, он аналогичен электронной почте, в комплекте с Inbox, Outbox, Sent и т.д., Но также имеет гарантированную доставку с чтением квитанций, поэтому есть дополнительные подпапки за пределами "входящих" и "отправленных". Для меня это требовало, чтобы я определял в общем, что такое "в папке" Входящие "или что" в папке отправлено ". Из отправленной папки мне нужно знать, что читать и что непрочитанное. Из того, что непрочитанное, мне нужно знать, что получено и что не получено. Я использую эту информацию для создания предложения dynamic where, которое фильтрует локальный источник данных и отображает соответствующую информацию.
Здесь, как перечисляется объединение:
public enum MemoView :int
{
InboundMemos = 1, // 0000 0001
InboundMemosForMyOrders = 3, // 0000 0011
SentMemosAll = 16, // 0001 0000
SentMemosNotReceived = 48, // 0011
SentMemosReceivedNotRead = 80, // 0101
SentMemosRead = 144, // 1001
Outbox = 272, //0001 0001 0000
OutBoxErrors = 784 //0011 0001 0000
}
Вы видите, что это делает? По указанию (&) с значением перечисления "Входящие" InboundMemos я знаю, что InboundMemosForMyOrders находится в папке "Входящие".
Здесь приведенная версия метода, который создает и возвращает фильтр, который определяет представление для текущей выбранной папки:
private string GetFilterForView(MemoView view, DefaultableBoolean readOnly)
{
string filter = string.Empty;
if((view & MemoView.InboundMemos) == MemoView.InboundMemos)
{
filter = "<inbox filter conditions>";
if((view & MemoView.InboundMemosForMyOrders) == MemoView.InboundMemosForMyOrders)
{
filter += "<my memo filter conditions>";
}
}
else if((view & MemoView.SentMemosAll) == MemoView.SentMemosAll)
{
//all sent items have originating system = to local
filter = "<memos leaving current system>";
if((view & MemoView.Outbox) == MemoView.Outbox)
{
...
}
else
{
//sent sub folders
filter += "<all sent items>";
if((view & MemoView.SentMemosNotReceived) == MemoView.SentMemosNotReceived)
{
if((view & MemoView.SentMemosReceivedNotRead) == MemoView.SentMemosReceivedNotRead)
{
filter += "<not received and not read conditions>";
}
else
filter += "<received and not read conditions>";
}
}
}
return filter;
}
Чрезвычайно простая, но аккуратная реализация на уровне абстракции, которая обычно не требует побитовых операций.
Я удивлен, что никто не выбрал очевидный ответ на интернет-возраст. Вычисление допустимых сетевых адресов для подсети.
Никто, кажется, не упомянул математику с фиксированной точкой.
(Да, я стар, хорошо?)
Если вы хотите изменить некоторые биты выходов микроконтроллера, но регистр для записи в это байт, вы делаете что-то вроде этого (псевдокод):
char newOut = OutRegister & 0b00011111 //clear 3 msb's
newOut = newOut | 0b10100000 //write '101' to the 3 msb's
OutRegister = newOut //Update Outputs
Конечно, многие микроконтроллеры позволяют изменять каждый бит отдельно...
он также может быть полезен в реляционной модели sql, скажем, у вас есть следующие таблицы: BlogEntry, BlogCategory
traditonally вы можете создать связь n-n между ними, используя таблицу BlogEntryCategory или, если записей в BlogCategory не так много, вы можете использовать одно значение в BlogEntry для ссылки на несколько записей в BlogCategory так же, как и с флаговыми перечислениями, в большинстве СУБД есть также очень быстрые операторы для выбора в столбце "помечены"...
Является ли число x
мощностью 2? (Полезно, например, в алгоритмах, где счетчик увеличивается, а действие должно выполняться только логарифмическим числом раз)
(x & (x - 1)) == 0
Каков максимальный бит целого числа x
? (Это, например, можно использовать для поиска минимальной мощности 2, которая больше, чем x
)
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return x - (x >>> 1); // ">>>" is unsigned right shift
Каков самый младший бит 1
целого числа x
? (Помогает найти количество раз, делящееся на 2.)
x & -x
Побитовые операторы полезны для циклических массивов, длина которых равна 2. Как упоминалось многими людьми, побитовые операторы чрезвычайно полезны и используются в Флагах, Графика, Сеть, Шифрование. Не только это, но и очень быстро. Мое личное любимое использование - это цикл массива без условностей. Предположим, у вас есть массив с нулевым индексом (например, индекс первого элемента равен 0), и вам нужно зацикливать его на неопределенный срок. Неоднократно я имею в виду переход от первого элемента к последнему и возвращение к первому. Один из способов реализовать это:
int[] arr = new int[8];
int i = 0;
while (true) {
print(arr[i]);
i = i + 1;
if (i >= arr.length)
i = 0;
}
Это самый простой подход, если вы хотите избежать инструкции if, вы можете использовать подход модуля:
int[] arr = new int[8];
int i = 0;
while (true) {
print(arr[i]);
i = i + 1;
i = i % arr.length;
}
Нижняя сторона этих двух методов заключается в том, что оператор модуля стоит дорого, так как он ищет остаток после целочисленного деления. И первый метод запускает оператор if на каждой итерации. Однако с побитовым оператором, если длина вашего массива равна 2, вы можете легко создать последовательность, подобную 0 .. length - 1
, используя &
(побитовый и) оператор, например i & length
. Поэтому, зная это, код сверху становится
int[] arr = new int[8];
int i = 0;
while (true){
print(arr[i]);
i = i + 1;
i = i & (arr.length - 1);
}
Вот как это работает. В двоичном формате каждое число, которое является степенью 2, вычитаемой на 1, выражается только с единицами. Например, 3 в двоичном формате 11
, 7 - 111
, 15 - 1111
и т.д., Вы получаете идею. Теперь, что произойдет, если вы &
любое число против числа, состоящего только из двоичных? Скажем, мы это делаем:
num & 7;
Если num
меньше или равно 7, результат будет num
, потому что каждый бит &
-ed с 1 сам по себе. Если num
больше 7, на компьютере &
компьютер рассмотрит 7 ведущих нулей, которые, конечно, останутся нулями после операции &
, останется только конечная часть. Как и в случае 9 & 7
в двоичном формате, это будет выглядеть как
1001 & 0111
результат будет равен 0001, который равен 1 в десятичном значении и адресует второй элемент в массиве.
Обычно побитовые операции выполняются быстрее, чем умножение/деление. Поэтому, если вам нужно умножить переменную x на 9, вы сделаете x<<3 + x
, которая будет на несколько циклов быстрее, чем x*9
. Если этот код находится внутри ISR, вы сэкономите время отклика.
Аналогичным образом, если вы хотите использовать массив как круговую очередь, было бы быстрее (и более элегантно) обрабатывать проверки вокруг с помощью бит-мутных операций. (размер вашего массива должен быть 2). Например: вы можете использовать tail = ((tail & MASK) + 1)
вместо tail = ((tail +1) < size) ? tail+1 : 0
, если вы хотите вставить/удалить.
Также, если вы хотите, чтобы флаг ошибки содержал несколько кодов ошибок вместе, каждый бит может содержать отдельное значение. Вы можете И это с каждым отдельным кодом ошибки в качестве проверки. Это используется в кодах ошибок Unix.
Также n-разрядная растровая карта может быть действительно крутой и компактной структурой данных. Если вы хотите выделить пул ресурсов размером n, мы можем использовать n-биты для представления текущего состояния.
Я использую их для нескольких опций выбора, таким образом я сохраняю только одно значение вместо 10 или более
Я видел, как они использовались в системах контроля доступа на основе ролей.
Всякий раз, когда я впервые начинал программирование на С, я понял таблицы истинности и все такое, но не все нажимали на то, как на самом деле использовать его, пока не прочитаю эту статью http://www.gamedev.net/reference/articles/article1563.asp (который дает реальные примеры жизни)
В моем вопросе есть реальное использование мира -
Отвечать только на первое уведомление WM_KEYDOWN?
При использовании сообщения WM_KEYDOWN в Windows C api бит 30 задает предыдущее состояние ключа. Значение равно 1, если клавиша не указана до отправки сообщения или равна нулю, если клавиша поднята
Они в основном используются для побитовых операций (сюрприз). Вот несколько примеров реального мира, найденных в PHP-кодовой базе.
Кодировка символов:
if (s <= 0 && (c & ~MBFL_WCSPLANE_MASK) == MBFL_WCSPLANE_KOI8R) {
Структуры данных:
ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
Драйверы базы данных:
dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY);
Реализация компилятора:
opline->extended_value = (opline->extended_value & ~ZEND_FETCH_CLASS_MASK) | ZEND_FETCH_CLASS_INTERFACE;
Я не думаю, что это считается поразрядным, но Ruby Array определяет заданные операции через нормальные целочисленные побитовые операторы. Итак [1,2,4] & [1,2,3] # => [1,2]
. Аналогично для a ^ b #=> set difference
и a | b #=> union
.
Если вы когда-либо захотите рассчитать свой mod mod (%) определенную мощность 2, вы можете использовать yourNumber & 2^N-1
, который в этом случае будет таким же, как yourNumber % 2^N
.
number % 16 = number & 15;
number % 128 = number & 127;
Это, вероятно, полезно только в качестве альтернативы работе модуля с очень большим дивидендом, равным 2 ^ N... Но даже тогда его ускорение скорости по сравнению с работой модуля незначительно в моем тесте на .NET 2.0. Я подозреваю, что современные компиляторы уже выполняют такие оптимизации. Кто-нибудь знает об этом больше?
Линейное решение Tower Of Hanoi использует побитовые операции для решения проблемы.
public static void linear(char start, char temp, char end, int discs)
{
int from,to;
for (int i = 1; i < (1 << discs); i++) {
from = (i & i-1) % 3;
to = ((i | i-1) + 1) % 3;
System.out.println(from+" => "+to);
}
}
Объяснение этого решения можно найти здесь