Что такое битовая маскировка?

191

Я довольно новичок в программировании на C и столкнулся с битовой маскировкой. Может ли кто-нибудь объяснить мне общую концепцию и функцию битовой маскировки? Примеры очень ценятся.

Mr.Z
источник
1
Вы понимаете побитовые операторы, такие как & | ^ и т.д. и булева логика в целом? Любое объяснение операций с маской потребует этого.
Пол Р
1
Да, я понимаю побитовые операторы и логическую логику
Mr.Z
2
Я знаю, что ссылки не должны публиковаться, но объяснение в Википедии великолепно: en.wikipedia.org/wiki/Mask_(computing)
pevik
2
@pevik нормально размещать ссылку, но с некоторым описанием, так что, если ссылка когда-нибудь умрет, пост все равно сможет ответить. Также ссылка не должна быть в рекламных целях.
Декстер

Ответы:

246

Маска определяет, какие биты вы хотите сохранить и какие биты вы хотите очистить.

Маскировка - это применение маски к значению. Это достигается путем:

  • Побитовое AND для извлечения подмножества битов в значении
  • Побитовое ИЛИ для установки подмножества битов в значении
  • Побитовое XOR для переключения подмножества битов в значении

Ниже приведен пример извлечения подмножества битов в значении:

Mask:   00001111b
Value:  01010101b

Применение маски к значению означает, что мы хотим очистить первые (старшие) 4 бита и оставить последние (младшие) 4 бита. Таким образом мы извлекли младшие 4 бита. Результат:

Mask:   00001111b
Value:  01010101b
Result: 00000101b

Маскирование реализовано с использованием AND, поэтому в C мы получаем:

uint8_t stuff(...) {
  uint8_t mask = 0x0f;   // 00001111b
  uint8_t value = 0x55;  // 01010101b
  return mask & value;
}

Вот довольно распространенный вариант использования: извлечение отдельных байтов из более крупного слова. Мы определяем старшие биты в слове как первый байт. Мы используем два оператора для этого &, и >>(сдвиг вправо). Вот как мы можем извлечь четыре байта из 32-разрядного целого числа:

void more_stuff(uint32_t value) {             // Example value: 0x01020304
    uint32_t byte1 = (value >> 24);           // 0x01020304 >> 24 is 0x01 so
                                              // no masking is necessary
    uint32_t byte2 = (value >> 16) & 0xff;    // 0x01020304 >> 16 is 0x0102 so
                                              // we must mask to get 0x02
    uint32_t byte3 = (value >> 8)  & 0xff;    // 0x01020304 >> 8 is 0x010203 so
                                              // we must mask to get 0x03
    uint32_t byte4 = value & 0xff;            // here we only mask, no shifting
                                              // is necessary
    ...
}

Обратите внимание, что вы можете переключать порядок операторов выше, вы можете сначала сделать маску, а затем сдвиг. Результаты одинаковы, но теперь вам придется использовать другую маску:

uint32_t byte3 = (value & 0xff00) >> 8;
user239558
источник
5
Хороший ответ, но маскирование также может применяться для установки или переключения определенных битов с помощью операций ИЛИ или XOR и подходящей маски.
Пол Р
@ user239558 спасибо за пример и правильный синтаксис. @ Paul R. Могу ли я просто сказать маску И значение в примере, предоставленном user239558
Mr.Z
@ Mr.Z: в C, C ++ и смежных языках вы бы использовали побитовый оператор AND , который записывается как &.
Пол Р
@ Mr.Z Например: ясно один байт uint32_t путем маскировки содержимого прочь: #define MASK 0x000000FF .... my_uint32_t &= ~MASK.
Лундин
bуказать двоичный литерал не поддерживается всеми компиляторами, правильно?
Ungeheuer
76

Маскировка означает сохранение / изменение / удаление нужной части информации. Давайте посмотрим на операцию маскировки изображения; как эта маскирующая операция удаляет любую вещь, которая не является

введите описание изображения здесь

Мы делаем операцию И в этом примере. Есть и другие маскирующие операторы - ИЛИ , XOR .


Битовая маскировка означает наложение маски на биты. Вот немного маскировки с AND -

     1 1 1 0 1 1 0 1   [input]
(&)  0 0 1 1 1 1 0 0    [mask]
------------------------------
     0 0 1 0 1 1 0 0  [output]

Таким образом, 1остаются только средние 4 бита (поскольку эти биты находятся в этой маске).

Давайте посмотрим это с XOR -

     1 1 1 0 1 1 0 1   [input]
(^)  0 0 1 1 1 1 0 0    [mask]
------------------------------
     1 1 0 1 0 0 0 1  [output]

Теперь, средние 4 бита перевернуты ( 1стали 0, 0стали 1).


Таким образом, используя битовую маску, мы можем получить доступ к отдельным битам [ примеры ]. Иногда этот метод также может быть использован для повышения производительности. Возьмите это, например,

bool isOdd(int i) {
    return i%2;
}

Эта функция сообщает, является ли целое число нечетным / четным. Мы можем достичь того же результата с большей эффективностью, используя

bool isOdd(int i) {
    return i&1;
}

Краткое объяснение : Если младший бит двоичного числа 1является нечетным; потому что 0это будет даже. Таким образом, делая и с 1мы удаляем все остальные биты для наименьшего значащего бита т.е. за исключением:

     55  ->  0 0 1 1 0 1 1 1   [input]
(&)   1  ->  0 0 0 0 0 0 0 1    [mask]
---------------------------------------
      1  <-  0 0 0 0 0 0 0 1  [output]
Минхас Камаль
источник
1
Также для преобразования целого числа в нечетное нет. если это четное число: i = i | 1. Это очень удобно, когда мы пытаемся сгенерировать последовательность вроде 1, 3, 5, ..., 2, 4, 6, ...
Harshit Sharma
Вы также можете использовать следующую операцию, чтобы найти число с целым младшим битом из целого числа: lsb = i & -i
Harshit Sharma