在 C++ 中使用位掩碼
本文將演示如何在 C++ 中使用位掩碼的多種方法。
使用 struct
在 C++ 中定義位掩碼並實現日期物件
struct
是 C++ 中流行的一種資料結構。而且,相對於 C 語言中的 class
,它只是與 class
略有不同。在這種情況下,我們只需要定義有多個資料成員的 struct
,但在每個變數名和指定的整數後面都使用了一個新的符號:
。如示例程式碼所示,我們宣告瞭一個帶有 uint32_t
型別資料成員的 struct
,右邊的數字之和是 32。這種結構意味著 struct
在記憶體中佔據了單個 uint32_t
資料型別的大小,使用者可以分別訪問它的位域範圍(23:5:4)。
這種方法主要是為了節省可能被擠進類似結構體的資料結構的記憶體利用率。這也是 C++ 程式語言中訪問比一個位元組更小的資料的常用方法之一。請注意,第一個成員 year
只能容納 223-1 以內的整數值,其他成員也是如此。
#include <iostream>
using std::cout;
using std::endl;
struct {
uint32_t year:23;
uint32_t day:5;
uint32_t month:4;
} typedef Bitfield;
int main() {
Bitfield date = {2020, 13, 12};
cout << "sizeof Bitfield: " << sizeof(date) << " bytes\n";
cout << "date: " << date.day << "/"
<< date.month << "/" << date.year << endl;
return EXIT_SUCCESS;
}
輸出:
sizeof Bitfield: 4 bytes
date: 13/12/2020
使用結構體和 union
在 C++ 中定義位掩碼和實現 Date 物件
前面的方法是實現位掩碼的一個充分和正確的方法。不過,它還是有一個缺點-訪問和給成員賦值比對內建型別的操作相對要花費更多的時間。這個問題可以通過實現一個由 struct
和一個內建型別變數組成的 union
型別物件來解決。需要注意的是,struct
的佈局和我們在前面的方法中構造的是一樣的。在這種情況下,不同的是變數 uint32_t ydm
與 struct
佔據相同的大小。
這個想法是為了更快地初始化/分配資料成員的值。也就是說,我們用位運算構造一個整數,當它們儲存在結構體中時,完全匹配位的佈局,然後將其分配給一個 uint32_t
成員。這樣可以節省一次訪問資料的時間,而不是對三個成員分別重複同樣的操作。
位元運算比較簡單,即我們從結構體中第一個成員的值開始,用下一個成員的結果按前一個成員所佔的位數向左移位進行 OR 運算,接下來的運算也是如此。
#include <iostream>
using std::cout;
using std::endl;
union BitfieldFast{
struct {
uint32_t year:23;
uint32_t day:5;
uint32_t month:4;
};
uint32_t ydm;
};
int main() {
BitfieldFast date_f{};
date_f.ydm = 2020 | (13 << 23) | (12 << 28);
cout << "sizeof BitfieldFast: " << sizeof(date_f) << " bytes\n";
cout << "date: " << date_f.day << "/"
<< date_f.month << "/" << date_f.year << endl;
return EXIT_SUCCESS;
}
sizeof BitfieldFast: 4 bytes
date: 13/12/2020
使用 std::bitset
在 C++ 中定義位元掩碼
std::bitset
是一個標準的庫功能,它包含了儲存二進位制掩碼資料的類。bitset
內建了多個有用的操作函式,宣告起來相當輕鬆。它可以用一個二進位制字串值或多個數字值進行初始化。位元操作是內建方法,可以用傳統的操作符過載呼叫。注意,reset
方法會永久修改它所呼叫的 bitset
。
#include <iostream>
#include <bitset>
using std::cout;
using std::endl;
int main() {
std::bitset<16> bs1("1100100011110010");
std::bitset<16> bs2(0xfff0);
cout << "bs1 : " << bs1 << endl;
cout << "bs2 : " << bs2 << endl;
cout << "NOT bs1 : " << ~bs1 << endl;
cout << "bs1 AND bs2: " << (bs1 & bs2) << endl;
cout << "bs1 reset : " << bs1.reset() << endl;
return EXIT_SUCCESS;
}
輸出:
bs1 : 1100100011110010
bs2 : 1111111111110000
NOT bs1 : 0011011100001101
bs1 AND bs2: 1100100011110000
bs1 reset : 0000000000000000
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn