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 20.1
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 fields—face
, suit
and color
—used 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 four bits, member suit
in 2 bits and member color
in one 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)—four 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)—two bits can store a value between 0
and 3
. Finally, member color
stores either 0
(Red) or 1
(Black)—one bit can store either 0
or 1
.
The program in Figs. 20.14–20.16 creates array deck
containing BitCard
structures (line 25 of Fig. 20.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 14–16 and 25–30 of Fig. 20.15). The member color
is included as a means of indicating the card color.
1 // Fig. 20.14: DeckOfCards.h
2 // Definition of class DeckOfCards that
3 // represents a deck of playing cards.
4 #include <array>
5
6 // BitCard structure definition with bit fields
7 struct BitCard
8 {
9 unsigned face : 4; // 4 bits; 0-15
10 unsigned suit : 2; // 2 bits; 0-3
11 unsigned color : 1; // 1 bit; 0-1
12 }; // end struct BitCard
13
14 // DeckOfCards class definition
15 class DeckOfCards
16 {
17 public:
18 static const int faces = 13;
19 static const int colors = 2; // black and red
20 static const int numberOfCards = 52;
21
22 DeckOfCards(); // constructor initializes deck
23 void deal() const; // deals cards in deck
24 private:
25 std::array< BitCard, numberOfCards > deck; // represents deck of cards
26 }; // end class DeckOfCards
1 // Fig. 20.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 <iostream>
5 #include <iomanip>
6 #include "DeckOfCards.h" // DeckOfCards class definition
7 using namespace std;
8
9 // no-argument DeckOfCards constructor intializes deck
10 DeckOfCards::DeckOfCards()
11 {
12 for ( size_t i = 0; i < deck.size(); ++i )
13 {
14 deck[ i ].face = i % faces; // faces in order
15 deck[ i ].suit = i / faces; // suits in order
16 deck[ i ].color = i / ( faces * colors ); // colors in order
17 } // end for
18 } // end no-argument DeckOfCards constructor
19
20 // deal cards in deck
21 void DeckOfCards::deal() const
22 {
23 for ( size_t k1 = 0, k2 = k1 + deck.size() / 2;
24 k1 < deck.size() / 2 - 1; ++k1, ++k2 )
25 cout << "Card:" << setw( 3 ) << deck[ k1 ].face
26 << " Suit:" << setw( 2 ) << deck[ k1 ].suit
27 << " Color:" << setw( 2 ) << deck[ k1 ].color
28 << " " << "Card:" << setw( 3 ) << deck[ k2 ].face
29 << " Suit:" << setw( 2 ) << deck[ k2 ].suit
30 << " Color:" << setw( 2 ) << deck[ k2 ].color << endl;
31 } // end function deal
1 // Fig. 20.16: fig20_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 } // end main
Card: 0 Suit: 0 Color: 0 Card: 0 Suit: 2 Color: 1
Card: 1 Suit: 0 Color: 0 Card: 1 Suit: 2 Color: 1
Card: 2 Suit: 0 Color: 0 Card: 2 Suit: 2 Color: 1
Card: 3 Suit: 0 Color: 0 Card: 3 Suit: 2 Color: 1
Card: 4 Suit: 0 Color: 0 Card: 4 Suit: 2 Color: 1
Card: 5 Suit: 0 Color: 0 Card: 5 Suit: 2 Color: 1
Card: 6 Suit: 0 Color: 0 Card: 6 Suit: 2 Color: 1
Card: 7 Suit: 0 Color: 0 Card: 7 Suit: 2 Color: 1
Card: 8 Suit: 0 Color: 0 Card: 8 Suit: 2 Color: 1
Card: 9 Suit: 0 Color: 0 Card: 9 Suit: 2 Color: 1
Card: 10 Suit: 0 Color: 0 Card: 10 Suit: 2 Color: 1
Card: 11 Suit: 0 Color: 0 Card: 11 Suit: 2 Color: 1
Card: 12 Suit: 0 Color: 0 Card: 12 Suit: 2 Color: 1
Card: 0 Suit: 1 Color: 0 Card: 0 Suit: 3 Color: 1
Card: 1 Suit: 1 Color: 0 Card: 1 Suit: 3 Color: 1
Card: 2 Suit: 1 Color: 0 Card: 2 Suit: 3 Color: 1
Card: 3 Suit: 1 Color: 0 Card: 3 Suit: 3 Color: 1
Card: 4 Suit: 1 Color: 0 Card: 4 Suit: 3 Color: 1
Card: 5 Suit: 1 Color: 0 Card: 5 Suit: 3 Color: 1
Card: 6 Suit: 1 Color: 0 Card: 6 Suit: 3 Color: 1
Card: 7 Suit: 1 Color: 0 Card: 7 Suit: 3 Color: 1
Card: 8 Suit: 1 Color: 0 Card: 8 Suit: 3 Color: 1
Card: 9 Suit: 1 Color: 0 Card: 9 Suit: 3 Color: 1
Card: 10 Suit: 1 Color: 0 Card: 10 Suit: 3 Color: 1
Card: 11 Suit: 1 Color: 0 Card: 11 Suit: 3 Color: 1
Card: 12 Suit: 1 Color: 0 Card: 12 Suit: 3 Color: 1
It’s 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 three-bit field as padding—nothing can be stored in those three 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 20.4
Bit-field manipulations are machine dependent. For example, some computers allow bit fields to cross word boundaries, whereas others do not.
Common Programming Error 20.5
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 20.6
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 20.2
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.
3.145.171.58