基本形式
例如
struct bits{
uint32_t low:9; //低9位
uint32_t middle: 13; //中间13位
uint32_t high: 10; //高10位
};
假设值为0xABCDEF09
, 二进制为10101011110011011110111100001001
1010101111 0011011110111 100001001
high(0x2AF) middle(0X6F7) low(0x109)
本质
本质就是位操作(移位/与/或)
对位域进行操作时, 并不是简单的赋值, 比如
struct bits{
uint32_t low:9; //低9位
uint32_t middle: 13; //中间13位
uint32_t high: 10; //高10位
};
struct bits b = {1, 2, 3};
int x = b.middle;
它所对应的汇编如下
struct bits b = {1, 2, 3};
4004e1: 0f b7 45 f0 movzwl -0x10(%rbp),%eax
4004e5: 66 25 00 fe and $0xfe00,%ax
4004e9: 83 c8 01 or $0x1,%eax
4004ec: 66 89 45 f0 mov %ax,-0x10(%rbp)
4004f0: 8b 45 f0 mov -0x10(%rbp),%eax
4004f3: 25 ff 01 c0 ff and $0xffc001ff,%eax
4004f8: 80 cc 04 or $0x4,%ah
4004fb: 89 45 f0 mov %eax,-0x10(%rbp)
4004fe: 0f b7 45 f2 movzwl -0xe(%rbp),%eax
400502: 83 e0 3f and $0x3f,%eax
400505: 0c c0 or $0xc0,%al
400507: 66 89 45 f2 mov %ax,-0xe(%rbp)
int x = b.middle;
40050b: 8b 45 f0 mov -0x10(%rbp),%eax
40050e: c1 e8 09 shr $0x9,%eax
400511: 66 25 ff 1f and $0x1fff,%ax
400515: 0f b7 c0 movzwl %ax,%eax
400518: 89 45 fc mov %eax,-0x4(%rbp)
上面的汇编可以看出, 编译器自动帮我们生成了各种移位/与/或相关的位操作.
如果不使用位域, 使用普通的结构体
struct bits{
uint32_t low;
uint32_t middle;
uint32_t high;
};
struct bits b = {1, 2, 3};
int x = b.middle;
对应的汇编是这样的
struct bits b = {1, 2, 3};
4004e1: c7 45 f0 01 00 00 00 movl $0x1,-0x10(%rbp)
4004e8: c7 45 f4 02 00 00 00 movl $0x2,-0xc(%rbp)
4004ef: c7 45 f8 03 00 00 00 movl $0x3,-0x8(%rbp)
int x = b.middle;
4004f6: 8b 45 f4 mov -0xc(%rbp),%eax
4004f9: 89 45 ec mov %eax,-0x14(%rbp)
就是简单的赋值操作
应用
压缩结构体大小
这是网上所提到的最多的作用. 这其实是大材小用了(现在的各种设备内存, 也不差这几个字节). 最适合它的用途是协议解析. 比如aac的adts(总共7个字节), 它的定义是这样的
名称 | 比特数 | 说明 |
---|---|---|
syncword | 12 | must be 0xFFF |
ID | 1 | 0 for mpeg-4, 1 for mpeg-2 |
layer | 2 | must be 00 |
protect | 1 | |
profile | 2 | 0 for main profile, 1 for low complexity profile, 2 for scalable sampling rate profile, 3 reserved |
frequency | 4 | |
private | 1 | |
channel | 3 | 0:Defined in AOT Specifc Config, 1-6 for channel count, 7 for 8 channel |
copy | 1 | |
home | 1 | |
copyright | 1 | |
copyright-start | 1 | |
frame-len | 13 | |
adts-fullness | 11 | |
blocks | 2 | 无 |
现在定义一个位域结构体来解析它.
typedef union {
struct {
uint64_t padding:8;
uint64_t block:2;
uint64_t fullness: 11;
uint64_t frame_len:13;
uint64_t coypr_s:2;
uint64_t copy_home:2;
uint64_t channel:3;
uint64_t priv:1;
uint64_t freq:4;
uint64_t profile:2;
uint64_t protect:1;
uint64_t layer:2;
uint64_t id:1;
uint64_t syncword:12;
} bits;
uint64_t value;
} adts;
adts h;
//7个字节的头.
uint8_t bytes[7] = {0xFF, 0xF1, 0x5C, 0x80, 0x05, 0x7F, 0xFC};
memcpy(&h.value, bytes, 7);
//一般的系统都是小端, 上面这句拷贝之后, value的值,0x**FC7F05805CF1FF. (*号表示未知)
//需要将其反转一下. 系统并没有ntoh64, 这是自定义的
h.value = ntoh64(h.value);
//...消息头的处理(略)
printf("syncword: %d\n", h.bits.syncword);
printf("channel: %d\n", h.bits.channel);
上面这段代码, 省略了大量的位操作代码, 全部由编译器代劳了.