浅谈位域,以C和C++为例

一、概念

类和结构可以包含比整数类型占用更小空间的成员。 这些成员被指定为位域。

位域是一种特殊的类数据成员(结构体数据成员),用于保存特定的位数。

 


二、位域的数据类型

位域必须是整型数据类型,可以是signed或者是unsigned。

《C++ Primer》一书中给出:“通常最好将位域设为unsigned类型。存储在signed类型中的位域的行为由实现定义”的建议。




三、位域的定义

通过在成员名后面接一个冒号以及指定位数的常量表达式,指出成员是一个位域。



四、位域的内存布局

1.一般情况

struct Date 
{
   unsigned short nWeekDay  : 3;    // 0..7   (3 bits)
   unsigned short nMonthDay : 6;    // 0..31  (6 bits)
   unsigned short nMonth    : 5;    // 0..12  (5 bits)
   unsigned short nYear     : 8;    // 0..100 (8 bits)
};

类型 Date 对象的概念内存布局如下图所示。


图1 数据对象的内容布局


请注意 nYear 长度为 8 位和溢出该声明的类型的字边界,无符号的 short。因此,它开始于新 unsigned short的开头。所有位域适合该基础类型的一个对象不是必须的:根据在声明里要求的位数,分配存储的新单元。

在Microsoft编译器环境下,声明为位域的数据的排序是从低位到高位,如上面图1所示。

2. 特殊情况——声明包括长度为 0 的一个未命名的字段

如下面的示例所示,如果结构的声明包括长度为 0 的一个未命名的字段,

struct Date 
{
   unsigned nWeekDay  : 3;    // 0..7   (3 bits)
   unsigned nMonthDay : 6;    // 0..31  (6 bits)
   unsigned           : 0;    // Force alignment to next boundary.
   unsigned nMonth    : 5;    // 0..12  (5 bits)
   unsigned nYear     : 8;    // 0..100 (8 bits)
};

内存布局如下图中所示。


图2 带有零长度位域的数据对象的布局




五、位域的使用

使用方法与类和结构的其他数据成员的相同方法访问位域。

 

下面就借用《C++ Primer》里面的例子,来说说位域的使用吧。

typedef unsigned int Bit;
class File {
	Bit mode: 2; // mode has 2 bits
	Bit modified: 1; // modified has 1 bit
	Bit prot_owner: 3; // prot_owner has 3 bits
	Bit prot_group: 3; // prot_group has 3 bits
	Bit prot_world: 3; // prot_world has 3 bits
// operations and data members of File
public:
	enum modes { READ = 01, WRITE = 02, EXECUTE = 03 };
	File &open(modes);
	void close();
	void write();
	bool isRead() const;
	void setWrite();
};

//从下面的代码可以看出与我们一般看到的数据成员的操作方式是一样的
void File::write()
{
	modified = 1;
	// . . .
}
void File::close()
{
	if (modified)
	// . . . save contents
}


//通常使用按位操作符来操作超过一位的位域
File &File::open(File::modes m)
{
	mode |= READ; // set the READ bit by default
	// other processing
	if (m & WRITE) // if opening READ and WRITE
	// processing to open the file in read/write mode
	return *this;
}

//通过定义一组内联成员函数来测试和设置位域的值
inline bool File::isRead() const { return mode & READ; }
inline void File::setWrite() { mode |= WRITE; }

值得注意的是,地址操作符(&)不能应用于位域,所以不可能有引用类位域的指针,位域也不能是类的静态成员。



六、位域的特点

1.        位域总是从字的第一个位开始;

2.        位域不能与整数的边界重叠,也就是说一个结构中所有域的长度之和不能大于字段大小。如果比字段大的话,重叠域将字段作为下一个字的开始。

3.        可以给未命名的域声明大小。例如:unsigned:bit-length,这种域在字段中提供填充值。

4.        字段中可以有未使用的位。

5.        不能使用位域变量的地址(同上面下划线的注意事项),这也意味着不能使用scanf函数将数值读入位域(可以使用中间变量赋值的方法来解决)。

6.        也不能使用位域变量的地址来访问位域。

7.        位域不能数组化。

8.        位域必须进行赋值,而且所赋的值必须在位域的大小范围之内,过大会发生不可预知的错误。

9.        位域定义中的数据类型如果是signed,那么其位数就不能少于两位(因为其中一个是符号位)。

 

参考文献

[1] MSDN. C++ 位域. http://msdn.microsoft.com/zh-cn/library/ewwyfdbe.aspx,2014-1-8.

[2]Stanley B.Lippman, Josée Lajoie, BarbaraE. Moo. C++ Primer, Fifth Edition[M]. 2012.

[3]陈正冲. C语言深度解剖[M]. 第二版. 北京: 北京航空航天大学出版社,2012: 41-44.


### C++的使用方法特性 #### 定义与基本语法 在 C++ 中,是一种特殊的结构体成员型,允许定义占用特定宽度比特的字段。这使得可以精确控制每个成员所占的数而非字节数,从而实现更加紧凑的数据表示[^1]。 ```cpp struct BitField { unsigned int flag : 1; // 占用1 unsigned int value : 7; // 占用7 }; ``` 上述代码展示了如何声明一个包含两个成员 `flag` `value` 的结构体 `BitField` 。其中 `unsigned int flag : 1;` 表明该成员仅需一即可存储其值;而 `unsigned int value : 7;` 则指定了此成员应占据七空间[^3]。 #### 数据型的适用范围 对于而言,并不是所有的整型都可以作为合法的基础型。通常情况下,可以选择如下几种无符号整型: - `bool` - `char`, `signed char`, `unsigned char` - `short`, `unsigned short` - `int`, `unsigned int` - `long`, `unsigned long` - `long long`, `unsigned long long` 需要注意的是,在某些编译器环境下可能还支持其他自定义长度的整形数据型作为基础型[^4]。 #### 对齐方式及其影响因素 当涉及到多个不同大小的组合成单个结构体时,则会面临到内存布局以及访问效率方面的问题。为了优化性能并确保兼容性,默认情况下大多数平台都会按照自然边界对齐原则处理这些复合对象中的各个组成部分。然而有时开发者希望打破这种默认行为以便更好地利用有限资源或者遵循特定协议标准的要求——这时就可以借助于编译指示符(pragma pack 或者 attribute((packed))) 来调整整体或局部区内的最小单尺寸限制条件[^2]。 如下面的子演示了通过 GCC 提供的属性机制强制关闭填充操作进而达到压缩效果的方法: ```cpp struct __attribute__((packed)) PackedBitFields { uint8_t fieldA : 4; uint8_t fieldB : 4; }; ``` 这里的关键在于添加了 `__attribute__((packed))` 属性标签之后,即使相邻两部分之间存在潜在冲突也不会额外增加任何不必要的间隔留白区段,因此整个实化后的实体将尽可能紧密排列在一起以减少总体开销成本。 #### 并发修改风险提示 值得注意的一点是在多线程环境中直接操纵共享状态下的可能会引发竞态条件(Race Condition),因为原子性可见性的保障往往依赖于更高层次上的同步原语而不是单纯依靠底层硬件指令集的支持程度。所以在设计此应用逻辑之前务必充分考虑到这一点并采取适当措施加以防范。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值