C语言中的结构体位域本质(bits field)与应用

基本形式

例如

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个字节), 它的定义是这样的

名称比特数说明
syncword12must be 0xFFF
ID10 for mpeg-4, 1 for mpeg-2
layer2must be 00
protect1
profile20 for main profile, 1 for low complexity profile, 2 for scalable sampling rate profile, 3 reserved
frequency4
private1
channel30:Defined in AOT Specifc Config, 1-6 for channel count, 7 for 8 channel
copy1
home1
copyright1
copyright-start1
frame-len13
adts-fullness11
blocks2

现在定义一个位域结构体来解析它.

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);

上面这段代码, 省略了大量的位操作代码, 全部由编译器代劳了.

转载于:https://my.oschina.net/u/2343729/blog/1838719

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值