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 |
|
&& |
AND |
|
|| |
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!
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 */
}
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
#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