10.3. Bit-Fields
Members of structures or unions can also be bit-fields
. A bit-field
is an integer variable that consists of a specified number of bits. If you declare several small bit-fields in succession, the compiler packs them into a single machine word. This permits very compact storage of small units of information. Of course, you can also manipulate individual bits using the bitwise operators, but bit-fields offer the advantage of handling bits by name, like any other structure or union member.
The declaration of a bit-field
has the form:
type [member_name] : width ;
The parts of this syntax are as follows:
type
An integer type that determines how the bit-field's value is interpreted. The type may be _Bool, int, signed int, unsigned int, or another type defined by the given implementation. The type may also include type qualifiers.
Bit-fields with type signed int are interpreted as signed; bit-fields whose type is unsigned int are interpreted as unsigned. Bit-fields of type int may be signed or unsigned, depending on the compiler.
member_name
The name of the bit-field, which is optional. If you declare a bit-field with no name, though, there is no way to access it. Nameless bit-fields can serve only as padding to align subsequent bit-fields to a certain position in a machine word.
width
The number of bits in the bit-field. The width must be a constant integer expression whose value is non-negative, and must be less than or equal to the bit width of the specified type.
Nameless bit-fields can have zero width. In this case, the next bit-field declared is aligned at the beginning of a new addressable storage unit.
When you declare a bit-field in a structure or union, the compiler allocates an addressable unit of memory that is large enough to accommodate it. Usually the storage unit allocated is a machine word whose size is that of the type int. If the following bit-field fits in the rest of the same storage unit, then it is defined as being adjacent to the previous bit-field. If the next bit-field does not fit in the remaining bits of the same unit, then the compiler allocates another storage unit, and may place the next bit-field at the start of new unit, or wrap it across the end of one storage unit and the beginning of the next.
The following example redefines the structure type struct Date so that the members month and day occupy only as many bits as necessary. To demonstrate a bit-field of type _Bool, we have also added a flag for Daylight Saving Time. This code assumes that the target machine uses words of at least 32 bits:
struct Date { unsigned int month : 4; // 1 is January; 12 is December.
unsigned int day : 5; // The day of the month (1 to 31).
signed int year : 22; // (-2097152 to +2097151)
_Bool isDST : 1; // True if Daylight Saving Time is
// in effect.
};
A bit-field of n bits can have 2n distinct values. The structure member month now has a value range from 0 to 15; the member day has the value range from 0 to 31; and the value range of the member year is from -2097152 to +2097151. We can initialize an object of type struct Date in the normal way, using an initialization list:
struct Date birthday = { 5, 17, 1982 };
The object birthday occupies the same amount of storage space as a 32-bit int object. Unlike other structure members, bit-fields generally do not occupy an addressable location in memory. Thus you cannot apply the address operator (&) or the offsetof macro to a bit-field.
In all other respects, however, you can treat bit-fields the same as other structure or union members; use the dot and arrow operators to access them, and perform arithmetic with them as with int or unsigned int variables. As a result, the new definition of the Date structure using bit-fields does not necessitate any changes in the dateAsString( ) function:
const char *dateAsString( struct Date d )
{
static char strDate[12];
sprintf( strDate, "%02d/%02d/%04d", d.month, d.day, d.year );
return strDate;
}
The following statement calls the dateAsString( ) function for the object birthday, and prints the result using the standard function puts( ):
puts( dateAsString( birthday ));
|