Битовый флаг

Битовый флаг

Замечали ли вы, что в функциях, например FileOpen, WinGetState, стили в GUICreate используется число определяющее некоторую опцию или включающую некоторый стиль? И эти числа имеют закономерность они как бы образованные многократным умножением на 2. Чтобы понять, почему используется именно такой способ, а не 1, 2, 3, 4, 5 и т.д. попробуем разобраться.

При использовании в качестве параметра функции 1, 2, 3, 4, 5 и т.д. можно задать выбор только одной опции из всех доступных и использовать переключатель Switch для определения флага. Битовый флаг, состоящий из такой последовательности доступных чисел 1, 2, 4, 8, 16, 32 и т.д. позволяет хранить в одном параметре несколько опций, которые имеют состояние включено или выключено. Например максимальное беззнаковое целое число 0xFFFFFFFF может содержать 32 битовых флага. Число в двоичной системе состоит из 0 и 1, соответственно каждый бит можно рассматривать как флаг включено или выключено, а позицию бита рассматривать как одну из опций. В данном случае смущает только десятичный вид представления флагов, которые кажутся не очевидно понятными, но в двоичной системе это в чистом виде набор флагов.

Битовый флаг

DecBinСтепень
100000001  2 ^ 0
200000010  2 ^ 1
400000100  2 ^ 2
800001000  2 ^ 3
1600010000  2 ^ 4
3200100000  2 ^ 5
6401000000  2 ^ 6
12810000000  2 ^ 7

Как видно из выше указанной таблицы, степень числа определяет позицию флага и очевидно, что 2 ^ 31 = 2 147 483 648 является последним доступным флагом.

Комбинация нескольких битовых флагов

DecФлагиBin
128 + 400001100
4432 + 8 + 400101100

Из таблицы видно, что добавление битового флага просто переключает 0 на 1 в соответствующей позиции. Для работы с флагами используются функции BitAND и BitOR.

С помощью функции BitAND проверяется наличие флага, его включенное состояние.

$iNum = 44
If BitAND($iNum, 8) Then
    MsgBox(0, 'Сообщение', 'Да, этот флаг включён')
Else
    MsgBox(0, 'Сообщение', 'Нет, этот флаг выключен')
EndIf


С помощью BitOR можно безопасно объединять флаги

$iNum = 12 ; 8 + 4
$iNum = BitOR($iNum, 4) ; Добавляет флаг 4
MsgBox(0, 'Сообщение', $iNum) ; Число 12 не изменилось, так как флаг был включён прежде

То есть допустимо суммировать битовые флаги без использования BitOR, например 8 + 4 + 2, но иногда при использовании констант, таких как стиль для GUICreate можно совершить ошибку. Некоторые константы уже содержат комбинацию битовых флагов и при добавлении флага, который уже присутствует в константе можно получить совершенно другую комбинацию битовых флагов. Например если попытаться к 14 (8 + 4 + 2) добавить флаг 4, то получится число 18 (16 + 2), а это уже совсем другая комбинация флагов. Совершенно очевидно, что любое десятичное число при разложении на битовые флаги однозначно даст только одну комбинацию флагов, так же как и любая комбинация флагов даёт только одно уникальное десятичное число, потому что это по сути это одно и тоже число только в десятичном или в двоичном представлении.


Использование битового флага

Пример функции с битовым флагом.

MsgBox(0, 'Сообщение', _Check(32 + 8 + 4))

Func _Check($iNum)
    Local $sText
    If BitAND($iNum, 1) Then $sText &= '1' & @LF
    If BitAND($iNum, 2) Then $sText &= '2' & @LF
    If BitAND($iNum, 4) Then $sText &= '4' & @LF
    If BitAND($iNum, 8) Then $sText &= '8' & @LF
    If BitAND($iNum, 16) Then $sText &= '16' & @LF
    If BitAND($iNum, 32) Then $sText &= '32' & @LF
    If BitAND($iNum, 64) Then $sText &= '64' & @LF
    If BitAND($iNum, 128) Then $sText &= '128' & @LF
    Return $sText
EndFunc   ;==>_Check


Способ исключения флага

$iNum = 12
If BitAND($iNum, 4) Then $iNum -= 4
MsgBox(0, 'Сообщение', $iNum)

$iNum = 12
$iNum = BitNOT(BitOR(BitNOT($iNum), 4))
MsgBox(0, 'Сообщение', $iNum)