PHP中的二進位制位運算和許可權儲存

2022-05-14 20:03:10 字數 4052 閱讀 4437

在很多系統的許可權/選項設定中 很多都用到了位運算的方法來儲存多種標誌位。這樣可以節省字段。乙個欄位只需要乙個數字 就可以標識很多種設定和資訊。

舉例 dicuz的帖子表的status欄位,官方預留了16個標誌位(0x0000 - 0xffff) 即216

目前規劃使用了只有8個標誌位,如下

這8種狀態可以使用乙個數字來同時表示,節省了字段

那麼這種東西的原理是什麼呢

這個我們可以複習一下的位運算單演算法

例子名稱

結果$a & $b

and(按位與)

將把 $a 和 $b 中都為 1 的位設為 1。

$a | $b

or(按位或)

將把 $a 或者 $b 中為 1 的位設為 1。

$a ^ $b

xor(按位異或)

將把 $a 和 $b 中不同的位設為 1。

~ $a

not(按位非)

將 $a 中為 0 的位設為 1,反之亦然。

$a << $b

shift left(左移)

將 $a 中的位向左移動 $b 次(每一次移動都表示「乘以 2」)。

$a >> $b

shift right(右移)

將 $a 中的位向右移動 $b 次(每一次移動都表示「除以 2」)。

比如14 = 0b1110

11 = 0b1011

那麼 14 & 11  = 0b1110 & 0b1011 = 0b1010 = 10

還是上面那個例子

14 | 11 = 0b1110 | 0b1011 = 0b1111 = 15

14 ^ 11 = 0b1110 ^ 0b1011 = 0b0101 = 5

非運算比較特殊 涉及到符號 這裡要說一下補碼 反碼 原碼的概念

1.二進位制最高是符號位  0是正數  1表示負數

2.正數的 原碼 反碼  補碼 都一樣(我上面沒有單獨算補碼的原因 ,正數補碼和反碼一樣)

3.用二進位制表示乙個數  這個碼 就是原碼 比如 14的原碼就是 1110

4.負數的反碼 等於  他符號位不變 其他取反,而負數的補碼等於他的反碼+1

5.計算機運算的時候 全都是以補碼的形式來運算的 不管正負數

那麼

1 是正數,那麼他的原碼 0001 = 反碼 = 補碼 = 0001 =>取反 後補碼1110 <=反碼 1101<=原碼1010

那麼這個符號位是1就是負數 也就是010代表的負數就是-2 也就是 ~1 = -2

1<< 2

1的補碼    00000001

移動2位    00000100

正數的反碼 補碼  原碼 都一樣 所以 是個4

負數的計算過程相同 不再贅述 左移也類似 4>>2  就是1

其實可以理解為右移在十進位制的表現上就是乘以2 左移 在十進位制的表現上就是除以2

那麼回到本文正題 如何用乙個數字來標識這些許可權位呢?

以剛才discuz的帖子表達status欄位為例,檢查帖子回覆是否通知作者 就看二進位製上第六位是否是置位為1 那麼怎麼檢查呢?就是用上面我們提到的與運算。

與運算是將把 $a 和 $b 中都為 1 的位設為 1。那麼假設

$a=36=0b 0010 0100

$b=0b 0010 0000

$a&$b = 0b 0010 0100 & 0b 0010 0000 = 0b 0010 0000 = 32 = 26-1 = 25

因此 檢查,某個數代表的第n個許可權標誌位有沒有置位(是1) 只要選擇該數與僅該標誌位置位的運算元2n-1進行與運算即可,相反要計算某個標誌位被置位的數字 只要選擇合適的運算元進行或運算即可。我們可以看discuz對此的實現:

在很多系統的許可權/選項設定中 很多都用到了位運算的方法來儲存多種標誌位。這樣可以節省字段。乙個欄位只需要乙個數字 就可以標識很多種設定和資訊。

舉例 dicuz的帖子表的status欄位,官方預留了16個標誌位(0x0000 - 0xffff) 即216

目前規劃使用了只有8個標誌位,如下

0000 0000 0000 0001 是否快取帖子位置資訊

0000 0000 0000 0010 是否回帖只對管理人員和發帖者可見

0000 0000 0000 0100 是否搶樓貼

0000 0000 0000 1000 是否倒序檢視回帖

0000 0000 0001 0000 是否存在主題圖章標誌位

0000 0000 0010 0000 回覆是否通知作者

0000 0000 0100 0000 是否推送到qq空間

這8種狀態可以使用乙個數字來同時表示,節省了字段

那麼這種東西的原理是什麼呢

這個我們可以複習一下的位運算單演算法

例子 名稱 結果

$a & $b and(按位與) 將把 $a 和 $b 中都為 1 的位設為 1。

$a | $b or(按位或) 將把 $a 或者 $b 中為 1 的位設為 1。

$a ^ $b xor(按位異或) 將把 $a 和 $b 中不同的位設為 1。

~ $a not(按位非) 將 $a 中為 0 的位設為 1,反之亦然。

$a << $b shift left(左移) 將 $a 中的位向左移動 $b 次(每一次移動都表示「乘以 2」)。

$a >> $b shift right(右移) 將 $a 中的位向右移動 $b 次(每一次移動都表示「除以 2」)。

比如與運算

14 = 0b1110

11 = 0b1011

那麼 14 & 11 = 0b1110 & 0b1011 = 0b1010 = 10

或運算還是上面那個例子

14 | 11 = 0b1110 | 0b1011 = 0b1111 = 15

異或運算

14 ^ 11 = 0b1110 ^ 0b1011 = 0b0101 = 5

非運算非運算比較特殊 涉及到符號 這裡要說一下補碼 反碼 原碼的概念

1.二進位制最高是符號位 0是正數 1表示負數

2.正數的 原碼 反碼 補碼 都一樣(我上面沒有單獨算補碼的原因 ,正數補碼和反碼一樣)

3.用二進位制表示乙個數 這個碼 就是原碼 比如 14的原碼就是 1110

4.負數的反碼 等於 他符號位不變 其他取反,而負數的補碼等於他的反碼+1

5.計算機運算的時候 全都是以補碼的形式來運算的 不管正負數

那麼1 是正數,那麼他的原碼 0001 = 反碼 = 補碼 = 0001 =>取反 後補碼1110 <=反碼 1101<=原碼1010

那麼這個符號位是1就是負數 也就是010代表的負數就是-2 也就是 ~1 = -2

左右位移運算

1<< 2

1的補碼 00000001

移動2位 00000100

正數的反碼 補碼 原碼 都一樣 所以 是個4

負數的計算過程相同 不再贅述 左移也類似 4>>2 就是1

其實可以理解為右移在十進位制的表現上就是乘以2 左移 在十進位制的表現上就是除以2

那麼回到本文正題 如何用乙個數字來標識這些許可權位呢?

以剛才discuz的帖子表達status欄位為例,檢查帖子回覆是否通知作者 就看二進位製上第六位是否是置位為1 那麼怎麼檢查呢?就是用上面我們提到的與運算。

與運算是將把 $a 和 $b 中都為 1 的位設為 1。那麼假設

$a=36=0b 0010 0100

$b=0b 0010 0000

$a&$b = 0b 0010 0100 & 0b 0010 0000 = 0b 0010 0000 = 32 = 26-1 = 25

因此 檢查,某個數代表的第n個許可權標誌位有沒有置位(是1) 只要選擇該數與僅該標誌位置位的運算元2n-1進行與運算即可,相反要計算某個標誌位被置位的數字 只要選擇合適的運算元進行或運算即可。我們可以看discuz對此的實現:

注意 寫這段**的人顯然受到了c的影響 其實 $a & ~$b 和 $a ^ $b 是等效的 只不過 ^是php的寫法 另外 pow(2, $position - 1)換成 1 << ($position -1) 其實更好理解。

author info :

二進位制和位運算

逢2進1的計數規則 電子裝置設計成處理2進製,其成本最優。所有程式語言都是一種人和計算機之間的翻譯工具,人的陣列語言翻譯成二進位製給計算機讀取,然後把計算機的二進位制資料翻譯 類的語言呈現出來。16進製制用於縮寫 簡寫 2進製,將2進製從後向前每4位數轉換為1位16進製制。補碼 將固定位數的2進製數...

二進位制位運算

與運算,6 3 2 或運算 6 3 7 異或運算 6 3 5 反碼 6 7 左移 3 2 12 3 2 2 12 右移 3 1 1 3 2 1 無符號右移 3 1 1 3 2 1 按位與,當兩位同時為1時才為1如 1 1 1 1 0 0 0 1 0 6 3相當於 0110 0011 0010 2 按...

二進位制位運算

一 按位與 位全為1,結果才為1 0 0 0 0 1 0 1 0 0 1 1 1 特殊用法 清零。如果想將乙個單元清零,就是使其全部二進位制位為0,只要與乙個各位都為零的數值相與,結果為零 取乙個數中的指定位。如x 10101110要取x的低4位,用x 00001111 00001110即可 二 按...