Pointers on C——10 Structures and Unions.13

来源:互联网 发布:java 冒泡排序 编辑:程序博客网 时间:2024/05/21 09:02

​10.5 Bit Fields

One last thing to mention about structures is their capability for implementing bit fields. A bit field is declared exactly like a structure except that its members are fields of one or more bits. These variable length fields are actually stored in one or more integer variables.

关于结构,我们最后还必须提到它们实现位段(bit field) 的能力。位段的声明和结构类似,但它的成员是一个或多个位的字段。这些不同长度的字段实际上存储于一个或多个整型变量中。


The declaration of a bit field is the same as the declaration of any ordinary structure member with two exceptions. First, bit field members must be declared as int, signed int, or unsigned int. Second, a colon and an integer appear after the member name, and the integer value specifies the number of bits in that field.

位段的声明和任何普通的结构成员声明相同,但有两个例外。首先,位段成员必须声明为int ,signed int或unsigned int类型。其次,在成员名的后面是一个冒号和一个整数,这个整数指定该位段所占用的位的数目。


It is a good idea to explicitly declare bit fields as either signed or unsigned integers. It is implementation dependent whether bit fields declared as int are interpreted as signed or unsigned values.

用signed 或unsigned 整数显式地声明位段是个好主意。如果把位段声明为int 类型,它究竟被解释为有符号数还是无符号数是由编译器决定的。


Programs that are intended to be portable should avoid bit fields. Because of the following implementation dependencies, bit fields may work differently on various systems.

注重可移植性的程序应该避免使用位段。由于下面这些与实现有关的依赖性,位段在不同的系统中可能有不同的结果。


1. Whether an int bit field is treated as signed or unsigned.

1. int 位段被当作有符号数还是无符号数。


2. The maximum number of bits in a bit field. Many compilers limit bit field members to the size of an integer, so a bit field declaration that works on a machine with 32‐bit integers may not work on one that uses 16‐bit integers.

2. 位段中位的最大数目。许多编译器把位段成员的长度限制在一个整型值的长度之内,所以一个能够运行于32 位整数的机器上的位段声明可能在16 位整数的机器上无法运行。


3. Whether the members in a bit field are allocated from left to right or from right to left in memory.

3. 位段中的成员在内存中是从左向右分自己的还是从右向左分自己的。


4. When a declaration specifies two bit fields and the second is too large to fit in the bits left over from the first, the compiler may either put the second bit field in the next word of memory or immediately after the first field, overlapping the boundary between memory locations.

4. 当一个声明指定了两个位段,第2 个位段比较大,无法容纳于第1 个位段剩余的位时,编译器有可能把第2 个位段放在内存的下一个字,也可能直接放在第1 个位段后面,从而在两个内存位置的边界上形成重叠。


Here is an example of a bit field declaration:

下面是一个位段声明的例子:

struct CHAR {

unsigned ch : 7;

unsigned font : 6;

unsigned size : 19;

};

struct CHAR ch1;


This declaration is from a text formatting program that is capable of manipulating up to 128 different character values (for which seven bits are required), up to 64 different fonts (which takes six bits), in sizes from 0 to 524,287 units. The size field is too large to be held in a short integer, but the other fields are both smaller than a character. The bit field lets the programmer use the bits left over from ch and font to increase the number of bits for size, thus avoiding the need to declare a whole integer to store size.

这个声明取自一个文本格式化程序,它可以处理多达128 个不同的字符值(需要7 个位)、64种不同的字体(需要6 个位)以及0 到524287 个单位的长度。这个size位段过于庞大,无法容纳于一个短整型,但其余的位段都比一个字符还短。位段使程序员能够利用存储ch 和font 所剩余的位来增加size 的位数,这样就避免了声明一个32 位的整数来存储size 位段。


Many compilers for machines with 16‐bit integers will flag this declaration as illegal because the last field is too large. But on a 32‐bit machine, this declaration would create ch1 as one of these two possibilities.

许多16 位整数机器的编译器会把这个声明标志为非法,因为最后一个位段的长度超过了整型的长度。但在32 位的机器上,这个声明将根据下面两种可能的方法创建ch1 。


This example illustrates a good reason to use bit fields: the ability to pack oddsized data together to save storage. This savings becomes particularly important when thousands of these structures are being used.

这个例子说明了一个使用位段的好理由:它能够把长度为奇数的数据包装在一起,节省存储空间。当程序需要使用成千上万的这类结构时,这种节省方法就会变得相当重要。


The other reason to use bit fields is because they make it convenient to access parts of an integer. Letʹs examine an example that might be found in an operating system. The code to operate the floppy disk must communicate with the controller for the disk. Often these device controllers contain several registers, each of which contains many different values all packed together into one integer. A bit field is a convenient way to access the individual values.

另一个使用位段的理由是由于它们可以很方便地访问一个整型值的部分内容。让我们研究一个例子,它可能出现于操作系统中。用于操作软盘的代码必须与磁盘控制器通信。这些设备控制器常常包含了几个寄存器,每个寄存器又包含了许多包装在一个整型值内的不同的值。位段就是一种方便的访问这些单一值的方法。


Suppose one of the registers for the controller was defined as:

假定磁盘控制器其中一个寄存器是如下定义的:


The first five fields are one bit each, and the remaining fields are larger. On a machine that allocated bit fields from right to left, the following declaration would allow easy access to the various fields in this register.

前5 个位段每个都占1 位,其余几个位段则更长一些。在一个从右向左分配位段的机器上,下面这个声明允许程序方便地对这个寄存器的不同位段进行访问。


struct DISK_REGISTER_FORMAT {

unsigned command : 5;

unsigned sector : 5;

unsigned track : 9;

unsigned error_code : 8;

unsigned head_loaded : 1;

unsigned write_protect : 1;

unsigned disk_spinning : 1;

unsigned error_occurred : 1;

unsigned ready : 1;

};


If the disk register is accessed at memory address 0xc0200142, we would declare the following pointer constant:

假如磁盘寄存器是在内存地址0xc0200142 进行访问的,我们可以声明下面的指针常量:


#define DISK_REGISTER \

((struct DISK_REGISTER_FORMAT *) 0xc0200142)


With this preparation, the code needed to actually access the disk register is simple, as shown in this code fragment.

做了这个准备工作后,实际需要访问磁盘寄存器的代码就变得简单多了,如下面的代码段所示。


/*

** Tell the controller which sector and track,

** and start the read operation.

*/

DISK_REGISTER->sector = new_sector;

DISK_REGISTER->track = new_track;

DISK_REGISTER->command = READ;

/*

** Wait until the operation is done,

** indicated by ready becoming true.

*/

while( ! DISK_REGISTER->ready )

;

/*

** Check for errors.

* /

if( DISK_REGISTER->error_occurred ){

switch( DISK_REGISTER->error_code ){

...

Bit fields are a convenience. Any task that can be completed with bit fields can also be accomplished through shifting and masking. For example, the following code accomplishes exactly the same thing as the first assignment in the previous example.

使用位段只是基于方便的目的。任何可以用位段实现的任务都可以使用移位和屏蔽来实现。例如,下面代码段的功能和前一个例子中第1 个赋值的功能完全一样。


#define DISK_REGISTER (unsigned int *) 0xc0200142

*DISK_REGISTER &= 0xfffffc1f;

*DISK_REGISTER |= ( new_sector & 0x1f ) << 5;


The first assignment uses a bitwise AND to clear all of the bits in the sector field to zero without affecting the other bits. The second takes the value of new_sector,ANDʹs it to make sure that the value does not exceed the width of the field, shifts it left to the proper position, and then uses a bitwise OR to set the field to the desired value.

第1 条赋值语句使用位AND 操作把sector 字段清零,但不影响其他的位段。第2 条赋值语句用于接受new sector 的值, AND 操作可以确保这个值不会超过这个位段的宽度。接着,把它左移到合适的位置,然后使用位OR 操作把这个字段设置为需要的值。


The bit field expresses this process more simply in the source code, but there isnʹt any difference in the object code. The same shifting and masking operations are required whether or not the bit field is used. The only advantage provided by the bit field is simplifying the source code. This advantage must be weighed against the bit fieldʹs lack of portability.

在源代码中,用位段表示这个处理过程更为简单一些,但在目标代码中,这两种方法并不存在任何区别。无论是否使用位段,相同的移位和屏蔽操作都是必需的。位段提供的唯一优点是简化了源代码。这个优点必须与位段的移植性较弱这个缺点进行权衡。

上一章 Pointers on C——10 Structures and Unions.12