MY PROJECTS
robotics - electronics - machining
  Sunday December 22nd 2024 12:52:14 PM

C Bit Manipulation

Posted: Apr 22, 2006 - 02:26 PM  on avrfreaks.net – reposted here for convenience

The following text is a slightly reformatted version of a post made by Eric quite a while ago. It has been referred to by so many users (it's now the post for bit-manipulations), and has helped so many that I have reposted it here so it is more easily found.

Programming 101 - By Eric Weddington

To really understand what's going, it's best to learn C languages bit operators and about truth tables.

| bit OR
& bit AND
~ bit NOT
^ bit EXLUSIVE OR (XOR)
<< bit LEFT SHIFT
>> bit RIGHT SHIFT


These operators work on bits and not logical values. Take two 8 bit bytes, combine with any of these operators, and you will get another 8 bit byte according to the operator's function. These operators work on the individual bits inside the byte.

A truth table helps to explain each operation. In a truth table, a 1 bit stands for true, and a 0 stands for false.

The OR operation truth table:

0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1


The AND operation truth table:

0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1


The XOR operation truth table:

0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0


The NOT operator inverts the sense of the bit, so a 1 becomes a 0, and a 0 becomes a 1.

So let's say I have a byte foo that is initialized to 0:

Code:

unsigned char foo = 0;


To set bit 0 in foo and then store the result back into foo:

Code:

foo = foo | 0x01;


The OR operation is used between the variable that we want to change and a constant which is called a BIT MASK or simply the MASK. The mask is used to identify the bit that we want changed.

Remember that we write the constants in hexadecimal because it's shorter than writing it in binary. It is assumed that the reader knows how to convert back and forth between hex and binary. 

Usually, though the statement is made shorter in real programming practice to take advantage of C's compound assignment:

Code:

foo |= 0x01;


This is equivalent to the statement above.
To clear bit 0 in foo requires 2 bit operators:

Code:

foo = foo & ~0x01;


This uses the AND operator and the NOT operator. Why do we use the NOT operator? Most programmers find it easier to specify a mask wherein the bit that they are interested in changing, is set. However, this kind of mask can only be used in setting a bit (using the OR operator). To clear a bit, the mask must be inverted and then ANDed with the variable in question. It is up to the reader to do the math to show why this works in clearing the desired bit.

Again, the statement is made shorter with a compound assignment:

Code:

foo &= ~0x01;


To see if a bit is set or clear just requires the AND operator, but with no assignment. To see if bit 7 is set in the variable foo:

Code:

if(foo & 0x80)
{
}


The condition will be zero if the bit is clear, and the condition will be non-zero if the bit is set. NOTE! The condition will be NON-ZERO when the bit is set. But the condition will not NECESSARILY BE ONE. It is left to the reader to calculate the value of the condition to understand why this is the case.

There is another useful tool that is not often seen, and that is when you want to flip a bit, but you don't know and you don't care what state the bit is currently in. Then you would use the XOR operator:

Code:

foo = foo ^ 0x01;

Or the shorter statement:

Code:

foo ^= 0x01;


A lot of times the bit mask is built up dynamically in other statements and stored in a variable to be used in the assignment statement:

Code:

foo |= bar;


Sometimes, a programmer wants to specify the bit NUMBER that they want to change and not the bit MASK. The bit number always starts at 0 and increases by 1 for each bit. An 8 bit byte has bit numbers 0-7 inclusive. The way to build a bit mask with only a bit number is to LEFT SHIFT a bit by the bit number. To build a bit mask that has bit number 2 set:

Code:

(0x01 << 2)


To build a bit mask that has bit number 7 set:

Code:

(0x01 << 7)


To build a bit mask that has bit number 0 set:

Code:

(0x01 << 0)


Which ends up shifting the constant 0 bytes to the left, leaving it at 0x01.


MACROS

Because there are a number of programmers who don't seem to have a familiarity with bit flipping (because they weren't taught it at school, or they don't need to know it because of working on PCs), most programmers usually write macros for all of these operations. Also, it provides a fast way of understanding what is happening when reading the code, or it provides additional functionality.

Below is a set of macros that works with ANSI C to do bit operations:

Code:

#define bit_get(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define bit_flip(p,m) ((p) ^= (m))
#define bit_write(c,p,m) (c ? bit_set(p,m) : bit_clear(p,m))
#define BIT(x) (0x01 << (x))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))


To set a bit:

Code:

bit_set(foo, 0x01);


To set bit number 5:

Code:

bit_set(foo, BIT(5));


To clear bit number 6 with a bit mask:

Code:

bit_clear(foo, 0x40);


To flip bit number 0:

Code:

bit_flip(foo, BIT(0));


To check bit number 3:

Code:

if(bit_get(foo, BIT(3)))
{
}


To set or clear a bit based on bit number 4:

Code:

if(bit_get(foo, BIT(4)))
{
    bit_set(bar, BIT(0));
}
else
{
    bit_clear(bar, BIT(0));
}



To do it with a macro:

Code:

bit_write(bit_get(foo, BIT(4)), bar, BIT(0));


If you are using an unsigned long (32 bit) variable foo, and have to change a bit, use the macro LONGBIT which creates un unsigned long mask. Otherwise, using the BIT() macro, the compiler will truncate the value to 16-bits.




Copyright © Tom_L 2009-2020