資料表示法:位元、位元組與數值

位元遮罩與位元旗標

你常想把許多微小的是/否設定打包進一個數字——這個檔案可讀嗎?可寫嗎?隱藏嗎?——而不是為每一個花一個獨立的變數。一個 32 位元整數有空間容納 32 個獨立的開/關開關,每個位元一個。要使用它們,你需要兩個概念協同運作:位元旗標(一個位元代表一個選項)和遮罩(一個精心選定、用來挑出你想處理哪些位元的模式)。

遮罩不過是個值,它為 1 的位元標出感興趣的位置,把它與一個位元運算子結合,就能乾淨地做四種基本位元操作。要「測試」一個位元是否被設定,與一個只在那裡有 1 的遮罩做 AND,再檢查結果是否非零:(x & 0x04) 恰好在 x 的第 2 位元被設定時非零。要「設定」一個位元(強制為 1),與遮罩做 OR:x = x | 0x04。要「清除」一個位元(強制為 0),與「反轉」的遮罩做 AND:x = x & ~0x04。要「切換」一個位元(翻轉它),與遮罩做 XOR:x = x ^ 0x04。按慣例每個旗標有個名字,常定義成 2 的次方,好讓它恰好佔一個位元,而多個旗標用 OR 結合——例如 open() 接受像 O_RDONLY、O_CREAT、O_APPEND 這類用 OR 結合成一個引數的旗標。

這個模式在系統程式設計裡無所不在:檔案權限、open() 和 mmap() 的選項旗標、硬體暫存器位元、事件遮罩和精簡資料格式全都仰賴它。要記住兩個陷阱。第一,把每個旗標定義成不同的 2 的次方(1、2、4、8……),好讓沒有兩個旗標共用一個位元;不小心重用一個位元會讓旗標互相干擾。第二,記住運算子優先順序:位元運算子的結合力比比較鬆,所以「x & 0x04 == 0」會被解析成「x & (0x04 == 0)」,這幾乎絕不是你要的——要加括號成「(x & 0x04) == 0」。

測試第 2 位元:if (x & 0x04) ... ;設定它:x |= 0x04;清除它:x &= ~0x04;切換它:x ^= 0x04。為 open() 結合旗標:open(path, O_WRONLY | O_CREAT | O_APPEND, 0644)。

AND 來測試/清除,OR 來設定,XOR 來切換。

因為「&」的優先順序低於「==」,要寫「(x & MASK) == 0」,而非「x & MASK == 0」(後者意思是「x & (MASK == 0)」);並把每個旗標定義成不同的 2 的次方,好讓旗標永不在共用的位元上衝突。

又称
bit maskbitfield flagsset/clear/toggle/test a bit位元遮罩位元旗標