See Also
Classes, Structures, and Unions
Classes and structures can contain members that occupy less storage than an integral type. These members are specified as bit fields. The syntax for bit-field member-declarator specification follows:
declarator opt : constant-expression
The declarator is the name by which the member is accessed in the program. It must be an integral type (including enumerated types). The constant-expression specifies the number of bits the member occupies in the structure. Anonymous bit fields — that is, bit-field members with no identifier — can be used for padding.
Note An unnamed bit field of width 0 forces alignment of the next bit field to the next type boundary, where type is the type of the member.
The following example declares a structure that contains bit fields:
// bit_fields1.cpp
struct Date
{
unsigned nWeekDay : 3; // 0..7 (3 bits)
unsigned nMonthDay : 6; // 0..31 (6 bits)
unsigned nMonth : 5; // 0..12 (5 bits)
unsigned nYear : 8; // 0..100 (8 bits)
};
int main()
{
}
The conceptual memory layout of an object of type Date is shown in the following figure.
Memory Layout of Date Object
Note that nYear is 8 bits long and would overflow the word boundary of the declared type, unsigned int . Therefore, it is begun at the beginning of a new unsigned int . It is not necessary that all bit fields fit in one object of the underlying type; new units of storage are allocated, according to the number of bits requested in the declaration.
Microsoft Specific
The ordering of data declared as bit fields is from low to high bit, as shown in the figure above.
END Microsoft Specific
If the declaration of a structure includes an unnamed field of length 0, as shown in the following example,
// bit_fields2.cpp
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)
};
int main()
{
}
the memory layout is as shown in the following figure.
Layout of Date Object with Zero-Length Bit Field
The underlying type of a bit field must be an integral type, as described in Fundamental Types .
© Microsoft Corporation. All rights reserved.
以上摘自 MSDN Library for Visual Studio .Net 2003
位域( bit-field ):一种压缩空间的成员
这两天在看公司里关于 MPEG-2码 流结构的代码,发现 “ 师傅 ” 写关于那些结构的代码的时候出现了如下类似的代码(这里的载自 C++ Prime )。
typedef unsigned int Bit;
class File {
public:
Bit mode : 2 ;
Bit modified : 1 ;
Bit prot_owner : 3 ;
Bit prot_group : 3 ;
Bit prot_world : 3 ;
// ...
};
刚开始觉得有些怪怪的,虽然从结构中大概知道是什么意思,但是具体的却不怎么知道。可能是 C/C++ 的知识有些遗漏,实践比较少吧。于是查找资料,得知:
这是一种被称为位域 bit-field 的特 殊的类数据成员。它可以被声明用来存放特定数目的位,位域必须是有序数据类型,它可以有符号也可以无符号例如:
class File {
// ...
unsigned int modified : 1; // 位域 (bit-field)
};
位域标识符后面跟有一个冒号,然后是一个常量表达式指定位数,例如, modified 是一个只有一位构成的位域。
在类体中相邻定义的位域,如果可能的话,它们会被放在同一个整数的连续位中,并以此提供空间压缩。例如,在下列 声明中, 5 个 位域被存储在单个 unsigned int 中,它首先与位域 mode 相关联
typedef unsigned int Bit;
class File {
public:
Bit mode: 2;
Bit modified: 1;
Bit prot_owner: 3;
Bit prot_group: 3;
Bit prot_world: 3;
// ...
};
对于位域的访问方式与其他类数据成员相同。例如,类的私有位域只能在类的成员函数和友元中被访问:
void File::write()
{
modified = 1;
// ...
}
void File::close()
{
if ( modified )
// ... 内容从略
}
下面的例子说明了怎 样使用大于 1 位 的位域。
enum { READ = 01, WRITE = 02 }; // 文件模式
int main() {
File myFile;
myFile.mode |= READ;
if ( myFile.mode & READ )
cout << "myFile.mode is set to READ/n";
}
通常情况下我们会定义一组 inline 成员函数,来测试每个位域成员的值。例如,类 File 可以定义成员 isRead() 和 isWrite() 。
inline int File::isRead() { return mode & READ; }
inline int File::isWrite() { return mode & WRITE; }
if ( myFile.isRead() ) /* ... */
有了这些成员函数,现在位域可以被声明为类 File 的私有成员。
由于取地址操作符 & 不能被应用在位域上,所以也没有能 指向类的位域的指针位域也不能是类的静态成员
C++ 标准库提供了一个 bitset 类模板,它可以辅助操纵位的集合。在可能的情况下,应尽可能 使用它来取代位域。要是你对标准库还没没有什么概念,那你只好用这个了。
之前所说的位域都在类中体现的,那么结构体中呢?
定义位域结构的格式为:
struct < 结构体类型名 >
{
< 类型 1> < 位域名 1>:< 位数 >;
< 类型 2> < 位域名 2>:< 位数 >;
…
< 类型 n> < 位域名 n>:< 位数 >;
};
其中, < 类型 > 必须是 unsigned 、 signed 或 int 类型; < 位域名 > 是用户命名的标识符;
< 位数 > 表示该位域所占的二进制位数,是一个正整数。
例如:
struct bit8253
{
unsigned bcd:1;
unsigned m:3;
unsigned rl:2;
unsigned sc:2;
unsigned black:24;
};
说明:
( 1 )对于各个位域必须依次从低到高进行定义;
( 2 )位数为 1 的位域只能用 unsigned 类型;
( 3 )每个位域的位数应小于计算机的字长,但各个位域的总长度则可以大于一 个字长,超过字长的部分放在下一个存储单元中。
对于这些说明只是针对结构体的呢?还是对于类中的位域一样有适用,还是部分适用呢?有待考证,但是这些都是比较 细节的东西了,编程中如果遇到,要是出了错误往这里想就好了。正如福尔摩斯大致所说: “ 除去那些不可能的,就是这些可能的了。 ”
末 了,我个人觉得位域还是比较有用的。比如在 “ 码流结构 ” 、 “ 某些传输协议结构 ” 中都有一些字段小于计算机的字长,但是他们某几段的总长度 又是比如 4 个 字 节。还有就是有人提出现在的内存都是那么大的了,没有必要 那么 “ 抠 门 ” 了 吧。但是,有人说过: “ 程 序员是为程序服务的,而机器不是。 ” 这句话的意思显然了 吧。好了,关于位域就说到此了,希望能使你也能有所收获。
参考: C++Prime
潭浩强: C++ 程序设计
以上摘自: http://blog.youkuaiyun.com/Adrian_Bu/archive/2006/08/03/1015318.aspx
wave 注:
( 此文内容基本翻译自 C++ Primer)
或者另外参见《 C++ Primer 》