Bit Fields
C++ provides the ability to specify the number of bits in which an integral type or enum type member of a class or a structure is stored. Such a member is referred to as a bit field. Bit fields enable better memory utilization by storing data in the minimum number of bits required. Bit field members must be declared as an integral or enum type.
Performance Tip 22.2
Bit fields help conserve storage. |
Consider the following structure definition:
struct BitCard { unsigned face : 4; unsigned suit : 2; unsigned color : 1; }; // end struct BitCard
The definition contains three unsigned bit fieldsface, suit and colorused to represent a card from a deck of 52 cards. A bit field is declared by following an integral type or enum type member with a colon (:) and an integer constant representing the width of the bit field (i.e., the number of bits in which the member is stored). The width must be an integer constant.
The preceding structure definition indicates that member face is stored in 4 bits, member suit in 2 bits and member color in 1 bit. The number of bits is based on the desired range of values for each structure member. Member face stores values between 0 (Ace) and 12 (King)4 bits can store a value between 0 and 15. Member suit stores values between 0 and 3 (0 = Diamonds, 1 = Hearts, 2 = Clubs, 3 = Spades)2 bits can store a value between 0 and 3. Finally, member color stores either 0 (Red) or 1 (Black)1 bit can store either 0 or 1.
The program in Figs. 22.1422.16 creates array deck containing 52 BitCard structures (line 21 of Fig. 22.14). The constructor inserts the 52 cards in the deck array, and function deal prints the 52 cards. Notice that bit fields are accessed exactly as any other structure member is (lines 1820 and 2833 of Fig. 22.15). The member color is included as a means of indicating the card color on a system that allows color displays.
Figure 22.14. Header file for class DeckOfCards.
1 // Fig. 22.14: DeckOfCards.h 2 // Definition of class DeckOfCards that 3 // represents a deck of playing cards. 4 5 // BitCard structure definition with bit fields 6 struct BitCard 7 { 8 unsigned face : 4; // 4 bits; 0-15 9 unsigned suit : 2; // 2 bits; 0-3 10 unsigned color : 1; // 1 bit; 0-1 11 }; // end struct BitCard 12 13 // DeckOfCards class definition 14 class DeckOfCards 15 { 16 public: 17 DeckOfCards(); // constructor initializes deck 18 void deal(); // deals cards in deck 19 20 private: 21 BitCard deck[ 52 ]; // represents deck of cards 22 }; // end class DeckOfCards |
Figure 22.15. Class file for DeckOfCards.
(This item is displayed on pages 1075 - 1076 in the print version)
1 // Fig. 22.15: DeckOfCards.cpp 2 // Member-function definitions for class DeckOfCards that simulates 3 // the shuffling and dealing of a deck of playing cards. 4 #include 5 using std::cout; 6 using std::endl; 7 8 #include 9 using std::setw; 10 11 #include "DeckOfCards.h" // DeckOfCards class definition 12 13 // no-argument DeckOfCards constructor intializes deck 14 DeckOfCards::DeckOfCards() 15 { 16 for ( int i = 0; i <= 51; i++ ) 17 { 18 deck[ i ].face = i % 13; // faces in order 19 deck[ i ].suit = i / 13; // suits in order 20 deck[ i ].color = i / 26; // colors in order 21 } // end for 22 } // end no-argument DeckOfCards constructor 23 24 // deal cards in deck 25 void DeckOfCards::deal() 26 { 27 for ( int k1 = 0, k2 = k1 + 26; k1 <= 25; k1++, k2++ ) 28 cout << "Card:" << setw( 3 ) << deck[ k1 ].face 29 << " Suit:" << setw( 2 ) << deck[ k1 ].suit 30 << " Color:" << setw( 2 ) << deck[ k1 ].color 31 << " " << "Card:" << setw( 3 ) << deck[ k2 ].face 32 << " Suit:" << setw( 2 ) << deck[ k2 ].suit 33 << " Color:" << setw( 2 ) << deck[ k2 ].color << endl; 34 } // end function deal |
Figure 22.16. Bit fields used to store a deck of cards.
(This item is displayed on page 1077 in the print version)
1 // Fig. 22.16: fig22_16.cpp 2 // Card shuffling and dealing program. 3 #include "DeckOfCards.h" // DeckOfCards class definition 4 5 int main() 6 { 7 DeckOfCards deckOfCards; // create DeckOfCards object 8 deckOfCards.deal(); // deal the cards in the deck 9 return 0; // indicates successful termination 10 } // end main
|
It is possible to specify an unnamed bit field, in which case the field is used as padding in the structure. For example, the structure definition uses an unnamed 3-bit field as paddingnothing can be stored in those 3 bits. Member b is stored in another storage unit.
struct Example { unsigned a : 13; unsigned : 3; // align to next storage-unit boundary unsigned b : 4; }; // end struct Example
An unnamed bit field with a zero width is used to align the next bit field on a new storage-unit boundary. For example, the structure definition
struct Example { unsigned a : 13; unsigned : 0; // align to next storage-unit boundary unsigned b : 4; }; // end struct Example
uses an unnamed 0-bit field to skip the remaining bits (as many as there are) of the storage unit in which a is stored and align b on the next storage-unit boundary.
Portability Tip 22.5
Bit-field manipulations are machine dependent. For example, some computers allow bit fields to cross word boundaries, whereas others do not. |
Common Programming Error 22.6
Attempting to access individual bits of a bit field with subscripting as if they were elements of an array is a compilation error. Bit fields are not "arrays of bits." |
Common Programming Error 22.7
Attempting to take the address of a bit field (the & operator may not be used with bit fields because a pointer can designate only a particular byte in memory and bit fields can start in the middle of a byte) is a compilation error. |
Performance Tip 22.3
Although bit fields save space, using them can cause the compiler to generate slower-executing machine-language code. This occurs because it takes extra machine-language operations to access only portions of an addressable storage unit. This is one of many examples of the space-time trade-offs that occur in computer science. |