Basic components of ‘C’ programming language

Lesson 8

OPERATORS AND EXPRESSIONS – PART – II
(Logical and Bitwise Operators)

8.1  Logical Operators

Besides the relational and equality operators, ‘C’ contains two logical operators (also known as logical connectives) as shown in Table-8.1:

Table 8.1 ‘C’ Logical operators 

Serial Number

Operator

Description

  1.  

&&

AND

  1.  

||

OR

 

These operators as mentioned at serial numbers 1 and 2 in Table-8.1 are known as ‘logical AND’ and ‘logical OR’, respectively. These operators are employed on logical expressions to combine the individual logical expressions in order to form compound conditions that come out to be either TRUE or FALSE. The outcome of a logical AND operation will be TRUE only if both the operands are TRUE, whereas that of a logical OR operation will be TRUE if either of the two operands is TRUE or if both the operands are TRUE. Thus, the result of the logical OR is FALSE only if both the operands are FALSE. Please mind it carefully that any non-zero value (not restricted to the truth value of 1 only), is considered as TRUE in such cases. 

Example 1:

Suppose that i is an integer variable containing value as 7; f  is a floating-point variable having value as 5.5; and c is a character type variable that represents the character ‘w’. Several complex logical expressions based on these variables along with their resultant values are given below.

Expression

Description

Resultant Value

true

1

true

1

false

0

true

1

 

The logical operators fall into different precedence groups. Logical AND has a higher precedence than logical OR. However, the logical operators have lower precedence than the equality operators. The associativity is from left to right.    

‘C’ also includes the unary operator ‘!’ that negates the value of a logical expression, i.e., it causes an expression that is originally TRUE to become FALSE and vice versa. This operator is referred to as the logical negation or logical NOT operator.

Example 2:

Suppose that i is an integer variable containing value as 7; and f is a floating-point variable having value as 5.5. Several logical expressions illustrating the use of these variables and the negation operator along with their resultant values are given below.                            

                                                                                                                                                                                                                                                                                                                                         

Expression

Description

Resultant Value

true

1

false

0

false

0

true

1

true

1

false

0

 

The hierarchy of operators’ precedence (highest to lowest) covering arithmetic, assignment, relational and logical operators is summarised in Table-7.2 given in Lesson 7.

Example 3:

Suppose that i is an integer variable containing value as 7; f is a floating-point variable having value as 5.5; and c is a character type variable that represents the character ‘w’. Several complex logical expressions based on these variables along with their resultant values are given below.

 

Expression

Description

Resultant Value

false

0

true

1

true

1

 

Note that these expressions have already been presented earlier within this Section but with pair of parentheses. However, these parentheses are not necessary because of the natural precedence of operators. Thus, the arithmetic operations will automatically be performed prior to the relational or equality operations. Similarly, the relational and equality operations will automatically be accomplished earlier than the logical connectives. For instance, consider the last expression, i.e., c ! = ʹ pʹ || i + f <= 10. At first, the addition operation, i.e.,i +f is performed followed by the relational comparison, i + f <= 10 then, the equality comparison, i.e., c ! = ʹ pʹ is carried out; and finally, the logical OR condition is accomplished.

Complex logical expressions comprising of different logical expressions joined together by the logical operators, && and || are evaluated left to right, but only until the overall truth value (TRUE/FALSE) can be established. Hence, a complex logical expression will not be evaluated in its entirety if its value can be established from its constituent operands. For instance, consider a complex logical expression,

                error > .0001 && count < 100

If the expression error > .0001 is FALSE, then the second operand count < 100 will not be evaluated, because the entire expression will be considered as FALSE.

On the other hand, suppose the expression is

error > .0001 || count < 100

If the expression error > .0001 is TRUE, then the entire expression will be TRUE. Hence, the second operand will not be evaluated. However, if the expression error > .0001 is FALSE, then the second expression must be evaluated to determine the final truth value of the whole expression.  

8.2  Bitwise Operators

‘C’ includes bitwise operators to manipulate memory at the bit level. These operators are especially useful for writing low level hardware or operating system code where the ordinary abstractions of numbers, characters, pointers, etc., are insufficient. Bit manipulation code tends to be less portable. Recall that a code is said to be portable if it compiles and runs correctly on different types of computers without any modification (i.e., without a programmer’s intervention). The bitwise operations are typically used with unsigned types, int and char. However, ‘C’ does not specify the difference between a short int, an int and a long int, except to state that:

sizeof(short int) sizeof(int) sizeof(long).

You will find that these sizes vary from computer to computer, and possibly even compiler to compiler. The sizes do not have to be distinct. That means all the three sizes could be the same, or two of the three could be the same, provided that the above restrictions are held.

Bitwise operators fall into two categories: binary bitwise operators and unary bitwise operators. Binary operators take two operands, while unary operators take only one operand. Bitwise operators, like arithmetic operators, do not change the value of the arguments. Instead, a temporary value is created. This can then be assigned to a variable.  

These operators are quite useful for developing embedded software with application to process control and plant automation (since you often need to manipulate individual bits to turn features on or off or to configure your peripherals). As such, it’s very important that you understand what these different operators mean and how they function!

8.2.1  Bitwise AND, OR and XOR operators

These operators require two operands and perform bit comparisons. The operator AND (&) will copy a bit to the result if it exists in both the operands. The following code illustrates this:

main()
{
unsigned int a = 60;    /* (60)10 ≡ (0011 1100)2 */
unsigned int b = 13;    /* (13)10 ≡ (0000 1101)2 */
unsigned int c = 0;
c = a & b;               /* (12)10 ≡ (0000 1100)2 */
}

Note that the comments in above code are enclosed as /* . . . */. (60)10 denotes that 60 is a decimal number; and (0011 1100)2 represents the binary number equivalent to 60. This notion is applicable throughout this section.  Also, you should not confuse with the logical AND and bitwise AND operators, etc. These are different.

Similarly, bitwise OR (|) operator will copy a bit if it exists in either operand. It is illustrated with the following code:

main()
{
unsigned int a = 60;    /* 60 = 0011 1100 */
unsigned int b = 13;    /* 13 = 0000 1101 */
unsigned int c = 0;
c = a | b;               /* 61 = 0011 1101 */
}

Also, bitwise XOR (^) operator copies the bit if it is set in one operand (but not both). It is illustrated with the following code:

main()
{
unsigned int a = 60;    /* 60 = 0011 1100 */
unsigned int b = 13;    /* 13 = 0000 1101 */
unsigned int c = 0;
c = a ^ b;               /* 49 = 0011 0001 */
}

8.2.2  Bit shift operators

The bit shift operators, <<, >>, <<=, and >>= can be used for shifting bits left or right. The left operand’s value is moved left or right by the number of bits specified by the right operand. For example:

main()

{

    unsigned int Value=4;          /*  4 = 0000 0100 */ 

    unsigned int Shift=2;

    Value = Value << Shift;        /* 16 = 0001 0000 */ 

    Value <<= Shift;               /* 64 = 0100 0000 */ 

    printf("%d\n", Value);        /* Prints 64      */ 

    return 0;

}

Example 1:

Write a ‘C’ programme using bitwise operator ^ (XOR) to swap contents of two variables x1 and x2 without the use of a temporary variable.

#include <stdio.h>
void main()
{
int x1 = 50;
int x2 = 52;
printf("\nFirst Number = %d\nSecond Number = %d\n", x1, x2);
x1 ^= x2;
x2 ^= x1;
x1 ^= x2;
printf("\nFirst Number = %d\nSecond Number = %d\n", x1, x2);
}

Output:

First Number = 50

Second Number = 52

 

First Number = 52

Second Number = 50

Example 2:

Write a ‘C’ programme that multiplies any given number by 4 using bitwise operators. [Note: Bitwise operators allow changing the specific bits within an integer. The following programme multiplies any given number by 4 using left shift (<<) bitwise operator.

#include <stdio.h>

void main()

{

long number, tempnum;

printf("Enter an integer: ");

scanf("%ld",&number);

tempnum = number;

number = number << 2;   /*left shift by two bits*/

printf("%ld x 4 = %ld\n", tempnum, number);

}

Output:

Enter an integer: 2

2 x 4 = 8