The smallest element in memory on which we are able to operate as yet is a bit. A bit is the building block of any data. In this section, we shall see how to operate on bits. Being able to operate on a bit level can be very important in programming, especially when a program must interact directly with the hardware. This is because the programming languages are byte-oriented, whereas hardware tends to be bit-oriented.
Before delving into bitwise operators, it is important to understand the bit numbering scheme in integers and characters.
Bit numbering scheme
Bits are numbered from zero onwards, increasing from left to right. This is shown below:
showbits()
This is a function which will be used throughout the section. We shall see the internal implementation of this function at a later part of the notes. For now, assume that the function prints the bits (binary format) of the number sent as an argument. For example:
int main()
{
for ( int j = 0 ; j <= 5 ; j++ ) {
printf ( "\nDecimal %d is same as binary ", j ) ;
showbits ( j ) ;
}
return 0;
}
Should produce the following output:
Decimal 0 is same as binary 0000000000000000
Decimal 1 is same as binary 0000000000000001
Decimal 2 is same as binary 0000000000000010
Decimal 3 is same as binary 0000000000000011
Decimal 4 is same as binary 0000000000000100
Decimal 5 is same as binary 0000000000000101
Bitwise operators
Operator | Description |
~ >> << & | ^ | One’s Complement Right shift Left shift Bitwise AND Bitwise OR Bitwise XOR |
The above table contains one of the C’s powerful features: a set of bit manipulation operators. These permit the programmer to access and manipulate individual bits within a piece of data. These operators can operate on all primitive data types available in C.
One’s Complement
On taking one’s complement of a number, all 1’s present in the number is changed to 0’s and all 0’s are changed to 1’s. Examples:
- One’s complement of 1010 is 0101.
- One’s complement of 1111 is 0000.
- One’s complement of 65 is -66
- The binary equivalent of 65 is 0000 0000 0100 0001.
- One’s complement of the above binary is 1111 1111 0100 0001.
- The compiler always displays the two’s complement of a negative number. The decimal equivalent of the two’s complement of the above binary is -66.
A program example:
#include <stdio.h>
int main()
{
int i = 5;
while (i--) {
printf ( "\nDecimal: %d\tBinary:\t", i ) ;
showbits ( i ) ;
printf ( "\nOne’s complement: ") ;
showbits ( ~i ) ;
}
return 0;
}
Output:
Decimal: 4 Binary: 0000000000000100
One’s complement: 1111111111111011
Decimal: 3 Binary: 0000000000000011
One’s complement: 1111111111111100
Decimal: 2 Binary: 0000000000000010
One’s complement: 1111111111111101
Decimal: 1 Binary: 0000000000000001
One’s complement: 1111111111111110
Decimal: 0 Binary: 0000000000000000
One’s complement: 1111111111111111
Right shift operator
The right shift operator is represented by >>. It needs two operands. It shifts each bit in its left operand to the right. The number of places the bits are shifted depends on the integer following the operator (i.e. its right operand)
Example:
- val >> 2 would shift all bits in val two places to the right.
- If val is 5, then:
- Val initially contains 0000 0101
- The right shift results in the bits moving towards the right: 0000 0001.
- If val is 5, then:
- val >> 5 would shift all bits 5 places to the right.
Points to note
- Note that as the bits are shifted to the right, blanks are created on the left. These blanks must be filled somehow. They are filled with zeros.
- Note that right shifting once is the same as dividing it by 2 and ignoring the remainder. Thus,
- 64 >> 1 gives 32
- 64 >> 2 gives 16
- 128 >> 2 gives 32
- 27 >> 1 gives 13
- 49 >> 1 gives 24
- In the explanation a >> b if b is negative the result is unpredictable.
- If a is negative then its leftmost bit (sign bit) would be 1. On some computers, right shifting would result in extending the sign bit.
- For example, if a contains -1, its binary representation would be
1111111111111111Without sign extension, the operation a >> 4 would be 0000111111111111.
- For example, if a contains -1, its binary representation would be
#include <stdio.h>
int main()
{
int i = 1234;
printf ( "\nDecimal %d is same as binary ", i ) ;
showbits ( i ) ;
for ( int j = 0 ; j <= 5 ; j++ ) {
printf ( "\n%d right shift %d gives ", i, j ) ;
showbits ( i >> j ) ;
}
return 0;
}
Output
Decimal 1234 is same as binary 0000 0100 1101 0010
1234 right shift 0 gives 0000 0100 1101 0010
1234 right shift 1 gives 0000 0010 0110 1001
1234 right shift 2 gives 0000 0001 0011 0100
1234 right shift 3 gives 0000 0000 1001 1010
1234 right shift 4 gives 0000 0000 0100 1101
1234 right shift 5 gives 0000 0000 0010 0110
Left shift operator
The left shift operator is represented by <<. It needs two operands. It shifts each bit in its left operand to the left. The number of places the bits are shifted depends on the integer following the operator (i.e. its right operand) Example:
- val << 2 would shift all bits in val two places to the left.
- If val is 5, then:
- Val initially contains 0000 0101
- The left shift results the bits moved towards left: 0001 0100
- If val is 5, then:
- val << 5 would shift all bits 5 places to the left.
Points to note
- Note that as the bits are shifted to the left, blanks are created on the right. These blanks must be filled somehow. They are filled with zeros.
- Note that left shifting once is the same as multiplying it by 2. Thus,
- – 64 << 1 gives 128
- – 64 << 2 gives 256
- In the explanation a << b if b is negative the result is unpredictable.
A program example:
#include <stdio.h>
int main()
{
int i = 1234;
printf ( "\nDecimal %d is same as binary ", i ) ;
showbits ( i ) ;
for ( int j = 0 ; j <= 5 ; j++ )
{
printf ( "\n%d left shift %d gives ", i, j ) ;
showbits ( i << j ) ;
}
return 0;
}
Output
Decimal 1234 is same as binary 0000 0100 1101 0010
1234 left shift 0 gives 0000 0100 1101 0010
1234 left shift 1 gives 0000 1001 1010 0100
1234 left shift 2 gives 0001 0011 0100 1000
1234 left shift 3 gives 0010 0110 1001 0000
1234 left shift 4 gives 0100 1101 0010 0000
1234 left shift 5 gives 1001 1010 0100 0000
Bitwise AND Operator
This operator is represented as & (not to be confused with &&, the logical AND operator). The & (an ampersand) operator operates on two operands and it is associative, that is, the order of the operands doesn’t change the result. While operating upon these two operands they are compared on a bit-by-bit basis. Hence both the operands must be of the same type. The second operand is often called an AND mask. The & operator operates on a pair of bits to yield a resultant bit. The rules (also called as truth table) that decide the value of the resultant bit are shown below:
+-----------+-----------+--------+
| Operand 1 | Operand 2 | Result |
+-----------+-----------+--------+
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
+-----------+-----------+--------+
The easier way to remember the truth table is that if both the operands are 1 then and then only the result will be 1, else 0.
Example:
+-----------+---+---+---+---+---+---+---+---+
| Operand 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
+-----------+---+---+---+---+---+---+---+---+
| Operand 2 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 |
+-----------+---+---+---+---+---+---+---+---+
| Result | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
+-----------+---+---+---+---+---+---+---+---+
Thus, it must be clear that the operation is being performed on individual bits, and the operation performed on one pair of bits is completely independent of the operation performed on the other pairs.
The best use-case example of the AND operator is to check whether a particular bit or a set of bits of an operand is ON or OFF. Consider an operand whose 0th bit needs to be inspected to be 1 or not. All we need to do is AND the operand with another operand (the bitmask) whose 0th bit is set to 1, that is 00000001. If the result is equal to the bitmask, then the operand has the 0th bit set to 1. If the result is 0, then the 0th bit of the operator is set to 0. It is illustrated as follows:
+----------+---+---+---+---+---+---+---+---+
| Operand | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
+----------+---+---+---+---+---+---+---+---+
| Bit mask | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
+----------+---+---+---+---+---+---+---+---+
| Result | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+----------+---+---+---+---+---+---+---+---+
As the result is 0, the 0th bit of the operand is set to 0. A program example:
#include <stdio.h>
int main() {
int i = 65, j ;
printf ( "i: %d", i ) ;
j = i & 32 ;
/*
+---------------------------+
| Binary Representation |
+---------------+---+---+---+---+---+---+---+---+
| i = 65 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
+---------------+---+---+---+---+---+---+---+---+
| Bit mask = 32 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
+---------------+---+---+---+---+---+---+---+---+
| Result | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+---------------+---+---+---+---+---+---+---+---+
*/
if ( j == 0 )
printf ( "\nFifth bit is off" ) ;
else
printf ( "\nFifth bit is on" ) ;
j = i & 64 ;
/*
+---------------------------+
| Binary Representation |
+---------------+---+---+---+---+---+---+---+---+
| i = 65 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
+---------------+---+---+---+---+---+---+---+---+
| Bit mask = 64 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
+---------------+---+---+---+---+---+---+---+---+
| Result | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
+---------------+---+---+---+---+---+---+---+---+
*/
if ( j == 0 )
printf ( "\nSixth bit is off" ) ;
else
printf ( "\nSixth bit is on" ) ;
return 0;
}
Output:
i: 65
Fifth bit is off
Sixth bit is on
Bitwise AND can also be used to turn off a particular bit in a given number. To do this, all the bits in the bitmask need to be set to 1 except for the bit position which we need to set 0 in the operand. For example, if the operand is 01000001 and we need to set the bit in the 0th
position to 0, the bitmask should be 11111110. The result of the AND operation of these two will give the desired result: 01000000.
Bitwise OR operator
Bitwise OR operator is represented as | (a pipe). The truth table of OR is as follows:
+-----------+-----------+--------+
| Operand 1 | Operand 2 | Result |
+-----------+-----------+--------+
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
+-----------+-----------+--------+
Notice that if any one of the operands of the OR operator is 1, the result is 1. Example:
+-----------+---+---+---+---+---+---+---+---+
| Operand 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
+-----------+---+---+---+---+---+---+---+---+
| Operand 2 | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 |
+-----------+---+---+---+---+---+---+---+---+
| OR Result | 1 | 0 | 1 | 1 | 1 | 0 | 1 | 1 |
+-----------+---+---+---+---+---+---+---+---+
Bitwise OR operator is usually used to put ON a particular bit in a number. To do this, all the bits in the second operand need to be set to 0 except for the bit position which we need to set 1 in the first operand. For example, if the operand is 01000001 and we need to set the bit in
the 2nd position to 1, the bitmask should be 00000010. The result of the OR operation of these two will give the desired result: 01000011.
Bitwise XOR operator
The XOR operator is represented as ^ and is also called an Exclusive OR Operator. XOR returns 1 only if one of the two bits is 1. The truth table for the XOR operator is given below.
+-----------+-----------+------------+
| Operand 1 | Operand 2 | XOR Result |
+-----------+-----------+------------+
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
+-----------+-----------+------------+
XOR operator is used to toggle a bit ON or OFF. A number XORed with another number twice gives the original number. This is shown in the following program.
#include <stdio.h>
int main() {
int b = 45 ;
b = b ^ 6 ;
/*
```
+-----------+---+---+---+---+---+---+---+
| Operand 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
+-----------+---+---+---+---+---+---+---+
| Operand 2 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
+-----------+---+---+---+---+---+---+---+
|XOR Result | 0 | 1 | 0 | 1 | 0 | 1 | 1 |
+-----------+---+---+---+---+---+---+---+
```
*/
printf ( "\n%d", b ) ; /* this will print 43 */
b = b ^ 12 ;
printf ( "\n%d", b ) ; /* this will print 39 */
return 0;
}
The showbits() function
We have been using the showbits() function throughout the text and now, as we have sufficient knowledge of bitwise operators, let’s have a look at the definition of this mighty function which is very useful and important:
void showbits(int number)
{
for (int i = 15 ; i >= 0 ; i--)
{
printf("%d", (number >> i) & 1);
if (i % 4 == 0)
printf(" ");
}
}
The given number is shifted right bit by bit and a bitwise AND operation is performed with 1. This will print 1 if the bit is set to 1 at a location in number, else 0. Trace the program for better understanding.
Exercises
- Write a C program to count trailing zeros in a binary number.
- Write a C program to flip bits of a binary number using the bitwise operator.
- Write a C program to count total zeros and ones in a binary number.
- Write a C program to rotate bits of a given number.
- Modify showbits() function to return an integer containing the binary number of the input number.
- Write a C program to swap two numbers using the bitwise operator.
- Write a C program to check whether a number is even or odd using a bitwise operator.
- Write a C program to get the nth bit of a number