I l@ve RuBoard Previous Section Next Section

11.7 Setting, Clearing, and Testing Bits

A character contains eight bits.[1] Each of these can be treated as a separate flag. Bit operations can be used to pack eight single-bit values in a single byte. For example, suppose you are writing a low-level communications program. You are going to store the characters in an 8K buffer for later use. With each character, you will also store a set of status flags. The flags are listed in Table 11-8.

[1] This is true on every machine I know in use today, but there's nothing in the C++ standard that mandates how many bits must be in a character.

Table 11-8. Communications status values

Name

Description

ERROR

True if any error is set

FRAMING_ERROR

A framing error occurred for this character

PARITY_ERROR

Character had the wrong parity

CARRIER_LOST

The carrier signal went down

CHANNEL_DOWN

Power was lost on the communication device

You could store each flag in its own character variable. That would mean that for each character buffered, you would need five bytes of status storage. For a large buffer, that adds up. By instead assigning each status flag its own bit within an eight-bit status character, you cut storage requirements down to 1/5 of the original need.

You can assign the flags the bit numbers listed in Table 11-9.

Table 11-9. Bit assignments

Bit

Name

0

ERROR

1

FRAMING_ERROR

2

PARITY_ERROR

3

CARRIER_LOST

4

CHANNEL_DOWN

Bits are numbered 76543210 by convention. The constants for each bit are defined in Table 11-10.

Table 11-10. Bit values

Bit

Binary value

Hex constant

7

10000000

0x80

6

01000000

0x40

5

00100000

0x20

4

00010000

0x10

3

00001000

0x08

2

00000100

0x04

1

00000010

0x02

0

00000001

0x01

Here's one way we can define constants for the bits that make up the communication status values:

// True if any error is set
const int ERROR = 0x01;

// A framing error occurred for this character
const int FRAMING_ERROR = 0x02;    

// Character had the wrong parity
const int PARITY_ERROR = 0x04;    

// The carrier signal went down
const int CARRIER_LOST = 0x08;    

// Power was lost on the communication device
const int CHANNEL_DOWN = 0x10;    

This method of defining bits is somewhat confusing. Can you tell (without looking at the table) which bit number is represented by the constant 0x10? Table 11-11 shows how you can use the left shift operator (<<) to define bits.

Table 11-11. The left shift operator and bit definition

C++ representation

Base 2 equivalent

Result (base 2)

Bit number

1 << 0

000000012 << 0

000000012

Bit 0

1 << 1

000000012 << 1

000000102

Bit 1

1 << 2

000000012 << 2

000001002

Bit 2

1 << 3

000000012 << 3

000010002

Bit 3

1 << 4

000000012 << 4

000100002

Bit 4

1 << 5

000000012 << 5

001000002

Bit 5

1 << 6

000000012 << 6

010000002

Bit 6

1 << 7

000000012 << 7

100000002

Bit 7

Although it is hard to tell what bit is represented by 0x10, it's easy to tell what bit is meant by 1 << 4.

Here's another way of defining the constants for testing the communication status bits:

// True if any error is set 
const int ERROR =           (1 << 0);   

// A framing error occurred for this character  
const int FRAMING_ERROR =   (1 << 1);   

// Character had the wrong parity  
const int PARITY_ERROR =    (1 << 2);

// The carrier signal went down 
const int CARRIER_LOST =    (1 << 3);   

// Power was lost on the communication device 
const int CHANNEL_DOWN =    (1 << 4);  

Now that you have defined the bits, you can manipulate them. To set a bit, use the | operator. For example:

char    flags = 0;  // Start all flags at 0 

    flags |= CHANNEL_DOWN; // Channel just died 

To test a bit, use the & operator to "mask out" the bits:

    if ((flags & ERROR) != 0) 
        std::cerr << "Error flag is set\n"; 
    else 
        std::cerr << "No error detected\n"; 

Clearing a bit is a little harder. Suppose you want to clear the bit PARITY_ERROR. In binary this bit is 00000100. You want to create a mask that has all bits set except for the bit you want to clear (11111011). This is done with the NOT operator (~). The mask is then ANDed with the number to clear the bit.

PARITY_ERROR                  00000100
~PARITY_ERROR                 11111011
flags                         00000101
___________________________________________
flags & ~PARITY_ERROR         00000001

In C++ this is:

    flags &= ~PARITY_ERROR; // Who cares about parity

Question 11-1: In the following program, the HIGH_SPEED flag works, but the DIRECT_CONNECT flag does not. Why?

#include <iostream>

const int HIGH_SPEED = (1<<7);    /* modem is running fast */
                                 // we are using a hardwired connection 
const int DIRECT_CONNECT = (1<<8);   

char flags = 0;                  // start with nothing 

int main(  )
{
    flags |= HIGH_SPEED;         // we are running fast 
    flags |= DIRECT_CONNECT;     // because we are wired together 

    if ((flags & HIGH_SPEED) != 0) 
        std::cout <<"High speed set\n";

    if ((flags & DIRECT_CONNECT) != 0)
        std::cout <<"Direct connect set\n";
    return (0);
}
    I l@ve RuBoard Previous Section Next Section