基本形式
例如
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);
上面这段代码, 省略了大量的位操作代码, 全部由编译器代劳了.