C语言中对位的操作有两种方法,一种是采用位操作符直接对位进行操作,另外一种是使用位字段对位进行操作。对位字段的接触源于这样一段代码:
typedef struct{
unsigned int
raw1:16;
unsigned int raw2:16;
} dataraw0;
最初的代码其实是这样的:
typedef struct{
char
raw1:16;
char
raw2:16;
} dataraw0;
很不幸,上面这样的代码是无法通过编译的,错误的原因并不在于位段的类型是char类型,而是在于位段位数16超出了char的位数范围。代码报错为:
type of bit field too small for number of
bits。实际上char是int类型,因为char存储的是整数而不是字符。
同时,对于位字段 float raw3:32 也会出现错误,代码报错为:bit
field must have type 'int', 'signed int', or 'unsigned int'。
同时,对于位字段 long raw4:32 或 long long
raw5:64 却不会出现错误。
也就是说声明的位字段应是unsigned int 或 signed
int。显然对位字段类型否是short或long、long long 并没有做出明确的规定。
若将上述代码修改为:
typedef struct{
unsigned int
raw1:16;
unsigned int raw2:16;
char
flag:1;
} dataraw1;
我们声明变量
dataraw0 data1;
dataraw1 data2;
通过sizeof()函数查看结构dataraw0与dataraw2的大小:
the size of dataraw0 is 4 byte.
the size of
dataraw1 is 8 byte.
可见,位字段是一个signed int或unsigned
int中一组相邻的位。变量data1完整的使用了unsigned
int类型中的一个存储单元,即4个byte。变量data2使用了两个unsigned
int类型的存储单元,即8个byte;其中一个存储单元的位被完全使用,而第二个存储单元仅仅使用了2位,剩余的30位没有被使用。
需要注意的两点是:
一、不允许一个字段跨越两个unsigned int或singed
int之间的边界。编译器自动地移位一个这样的字段定义,使字段按unsigned int或singed
int边界对齐。发生这样的情况,会在第一个unsigned
int中留下一个未命名的“洞”。可以使用未命名的字段进行填充未命名的洞。使用一个宽度为0的未命名字段迫使下一个字段与下一个整数对齐。如:
typedef struct{
unsigned
int raw1:1;
unsigned
int raw2:2;
unsigned
int :0;
unsigned int
raw3:1;
} dataraw2;
通过sizeof()函数查看结构dataraw2大小为8个字节。可见,宽度为0的未命名字段使得下一个字段raw3与一个整数对齐。
二、一个重要的依赖性是字段放置在int中的顺序,有的机器是从左到右,有的是从右到左。另外,不同机器在两个字段间边界的位置也有区别。因此,位字段往往难以移植。