Previous Table of Contents Next


3.7.4. Bit-Fields

Within structures and unions (unlike in any other declarations in C), it is possible to specify a member’s size exactly, in bits. The size specification takes the form of a colon and an integer constant following the declarator:

struct bitfieldexamp
       {
       int i3b : 3;
       unsigned int i1b : 1;
       signed int i7b : 7;
       };

This declaration says that the structure bitfieldexamp consists of three members, all bit-fields. Member i3b is a 3-bit integer, member i1b is a 1-bit unsigned integer, and member i7b is a 7-bit signed integer.

Like char variables, and unlike other flavors of int, it is implementation defined whether a “plain” integer bit-field (e.g., i3b) is signed or unsigned. The keyword signed can be used to force a signed value. (Notice that a 1-bit field, if signed, can hold only the values 0 and -1.) Whether signed, unsigned, or questionably signed, bit-fields must always have a base type of int.

Adjacent bit-field members are packed into machine words, although the order is implementation defined. Bit-fields are not therefore individually addressable, and it is not possible to create arrays of bit-fields or pointers to bit-fields.

Bit-fields are useful for saving space when a structure contains multiple small, reduced-range members, such as boolean flags. (When using bit-fields, the compiler worries about the details of shifting and masking to extract ranges of bits from individual words, operations that we saw how to do explicitly in section 3.3.5.) It is also possible to utilize bit-fields when attempting to match the memory layout of a structure to some externally defined data format, although any attempt to do so is inherently machine and compiler dependent because of the imprecisely defined ordering of bit-fields within words (not to mention all the other implementation-defined aspects of numeric formats and structure layout).

When you’re attempting to define the memory layout of bit-fields within a structure exactly, two additional mechanisms are available. It is permissible to omit the name (i.e., the identifier) for a bit-field member, with the result that the compiler leaves a “hole” of the specified number of bits between the surrounding bit-field members. Furthermore, it is permissible for the size of an unnamed bit-field member to be 0: An unnamed, size-0 bit-field requests that assignment of bit-field members to one machine word be completed and that succeeding bit-fields be assigned to a new word. For example, the declaration

struct bitfieldexamp2
       {
       int i3b : 3;
       unsigned int i1b : 1;
       signed int i7b : 7;
       int : 2;
       int i2b: 2;
       int : 0;
       int i4b : 4, i5b : 5;
        };

describes a structure containing 3-, 1-, and 7-bit fields as before, followed by a 2-bit hole, followed by a 2-bit field i2b, and then, in the next word, 4-bit and 5-bit fields i4b and i5b.

3.7.5. Enumerations

C’s third user-defined type is the enumeration, or enum. An enumeration is simply an integral type with associated symbolic constants for certain values. Though convenient, these types are not as fundamentally important as the struct and union types; a programmer could accomplish approximately the same effect using ordinary integral variables and preprocessor macros (see section 3.8.2).

As a simple example, here is an enumeration for recording the days of the week:

enum wday {Sunday, Monday, Tuesday, Wednesday, Thursday,
        Friday, Saturday};

Besides defining the new enumeration type wday, this declaration also defines seven constants that represent seven different integer values. (As it happens, they are assigned in order, starting with 0; Sunday is 0 and Saturday is 6.) Enumeration tags (e.g., wday) share the same namespace as struct and union tags, whereas the enumeration constants (e.g., Sunday to Saturday) must coexist (that is, share a namespace) with the names of ordinary variables.

Once again, a variable or two of the new type can be declared for holding the new values:

enum wday today, tomorrow;
today = Wednesday;
tomorrow = Thursday;

It is also possible to specify the values for some or all of the enumeration constants:

enum cardname {ace = 1, jack = 11, queen, king};

Uninitialized enumeration constants always receive a value one greater than the previous constant, so queen is 12 and king is 13.

Enumerations are not particularly “strong” or “strict” types in C. It is possible to intermix them with integers, perform arithmetic on them as if they were integers, and so on. For example, there is no prohibition against writing code like

int i = Saturday;

or

cardname c = 10;
c++;                /* c is now 11, which is jack */

In fact, because the type of an enumeration constant is int, it is not uncommon to see enumeration constants used freely as manifest constants (i.e., without restricting their use to enum variables of the corresponding type), much in the manner of preprocessor macros (which are discussed in section 3.8.2).


Previous Table of Contents Next