C/C++ 位域

黎 浩然/ 15 5 月, 2022/ C/C++, 计算机/COMPUTER/ 0 comments

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;
Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*