C/C++ 位域
In C and C++, native implementation-defined bit fields can be created using unsigned int, signed int, or (in C99) _Bool. In this case, the programmer can declare a structure for a bit field which labels and determines the width of several subfields.[6] Adjacently declared bit fields of the same type can then be packed by the compiler into a reduced number of words, compared with the memory used if each ‘field’ were to be declared separately.
由维基百科英文的定义可知:
- 位域的类型用于对齐
- 同类型且相邻的位域会被packed
#include <iostream>
using namespace std;
// opaque and show
#define YES 1
#define NO 0
// line styles
#define SOLID 1
#define DOTTED 2
#define DASHED 3
// primary colors
#define BLUE 0b100
#define GREEN 0b010
#define RED 0b001
// mixed colors
#define BLACK 0
#define YELLOW (RED | GREEN)
#define MAGENTA (RED | BLUE)
#define CYAN (GREEN | BLUE)
#define WHITE (RED | GREEN | BLUE) /* 111 */
const char* colors[8] = {"Black", "Red", "Green", "Yellow", "Blue","Magenta", "Cyan", "White"};
// bit field box properties
struct BoxProps
{
unsigned int opaque : 1;
unsigned int fill_color : 3;
unsigned int : 4; // fill to 8 bits
unsigned int show_border : 1;
unsigned int border_color : 3;
unsigned int border_style : 2;
unsigned char : 0; // fill to nearest byte (16 bits)
unsigned char width : 4, //Split a byte into 2 fields of 4 bits
height : 4;
};
int main() {
cout << sizeof(BoxProps) << endl;
struct BoxProps *ptr = new BoxProps;
cout << (void *)ptr << endl;
}
//4
//0x560e9d6dd280
- 位域的宽度指定为0的未命名位域确保在struct-declaration-list中跟在其后的成员的存 储在边界开始(余下同类型位域再另成一组,见“对其相关问题”);就上面这个例 子,’unsigned char 0’不是必须的
- 取地址运算符(&)不能作用于位域,任何指针都无法指向类的位域
- 位域在内存中的布局是与机器有关的
- 上图中unsigned int虽然是4个字节,但是实际上从opaque到border_style只用了不到 两个字节,所以width可以直接从第三个字节开始,不影响对齐规则
对齐相关问题
超出类型的大小的余下同类型位域再另成一组!
struct stuff
{
unsigned int field1: 30;
unsigned int field2: 4; //超出了类型大小,余下同类型位域再另成一组
unsigned int field3: 3;
};
field1 + field2 = 34 Bits,超出 2 Bits, 编译器会将field2移位至下一个 unsigned int 单元存放, stuff.field1 和 stuff.field2 之间会留下一个 2 Bits 的空隙, stuff.field3 紧跟在 stuff.field2 之后,该结构现在大小为 2 * 32 = 64 Bits。
struct stuff
{
unsigned int field1: 30;
unsigned int : 2; // 如上一个例子,这里不是必须的
unsigned int field2: 4;
unsigned int : 0;
unsigned int field3: 3;
};
这里 stuff.field1 与 stuff.field2 之间有一个 2 Bits 的空隙;stuff.field2 与 stuff.field3 之间 有一个 28 Bits 的空隙;stuff.field3 则存储在下一个 unsigned int 中,该结构现在大小为 3 * 32 = 96 Bits。
位域的初始化
两种方式
struct stuff s1= {20,8,6};
struct stuff s1;
s1.field1 = 20;
s1.field2 = 8;
s1.field3 = 4;
位域的重映射
利用指针重映射
struct box {
unsigned int ready: 2;
unsigned int error: 2;
unsigned int command: 4;
unsigned int sector_no: 24;
} b1;
int* p = (int *) &b1; // 将 "位域结构体的地址" 映射至 "整形(int*) 的地址"
*p = 0; // 清除 s1,将各成员归零
利用UNION重映射
union u_box {
struct box st_box;
unsigned int ui_box;
};
union u_box u;
u.ui_box = 0;