结构体可以用来实现 位段, 在C语言中多数情况下按照一个字节的整数倍为单位处理数据, 也就是必须是以 8位 为单位处理数据, 比如一个char为8位, 一个int可能为32位. 位段使我们可以按 “任意” 位处理数据, 比如7位, 6位等.
位段的声明如下:
// 位段声明
// [类型] [标识符] : [位宽表达式]
struct CHAR {
int32_t ch : 6; // 位段成员ch使用一个signed int中的7个位
int16_t font : 5; // 位段成员font使用一个unsigned int中6个位
int8_t size : 8; // 位段成员size包含19个位
};
struct CHAR character;
关于位段要注意:
- 位段成员必须声明为 int, signed int, unsigned int (C11中只要是整型就可以, 包括int32_t uint16_t等) 整个位段可使用的位数即该类型在当前硬件中所占位数. 由于int类型具体当作有符号类型还是无符号类型由编译器决定, 所以最好将位段成员声明为signed int或unsigned int类型.
- 标识符后是一个冒号和一个整数, 该整数指定位段成员所占位数.
- 编译器可能将位段成员的长度限制在一个int值长度之内, 所以16位机器上的位段不能用于32位机器.
- 位段成员在内存中可能从左向右分配也可能从右向左分配.
- 几个邻近的位段成员 可能 被拼接到同一可寻址存储单元. 确定该可寻址存储单元的大小的策略如下:
- C语言实现首先尝试将第一个和第二个位段成员拼接在一起, 如果成功, 那么计算它们一共占有的Bit个数, 并向上取满2的n次方. 比如ch与font拼接共占11个Bit, 取满2的n次方得16个Bit, 则 可寻址存储单元 大小为16Bit(2Byte).
- 然后C实现尝试将第三个位段与前两个拼接, 如果三个位段拼接在一起没有超过可寻址存储单元(2Byte)的大小, 则拼接成功, 若超过, 则可能将第三个位段完全放在下一个存储单元, 也能将其放在前两个位段之后, 不足的位从下一个存储单元补齐, 具体的操作由实现决定. 比如size与ch和font拼接后共19个Bit, 超过可寻址单元的大小16Bit, 所以ch font size在内存中的情况可能是下面两种:
由上面的分析可以看出位段的 可移植性差 但是提高了位操作的便捷性 (任何位段实现的任务都可以通过移位和屏蔽来实现), 编程中需要做一些权衡.