5.8. Logical Operators

So far we have studied only simple conditions, such as counter <= 10, total > 1000 and number != sentinelValue. We expressed these conditions in terms of the relational operators >, <, >= and <=, and the equality operators == and !=. Each decision tested precisely one condition. To test multiple conditions while making a decision, we performed these tests in separate statements or in nested if or if...else statements.

C++ provides logical operators that are used to form more complex conditions by combining simple conditions. The logical operators are && (logical AND), || (logical OR) and ! (logical NOT, also called logical negation).

Logical AND (&&) Operator

Suppose that we wish to ensure that two conditions are both true before we choose a certain path of execution. In this case, we can use the && (logical AND) operator, as follows:

if ( gender == 1 && age >= 65 )
   seniorFemales++;

This if statement contains two simple conditions. The condition gender == 1 is used here to determine whether a person is a female. The condition age >= 65 determines whether a person is a senior citizen. The simple condition to the left of the && operator evaluates first. If necessary, the simple condition to the right of the && operator evaluates next. As we'll discuss shortly, the right side of a logical AND expression is evaluated only if the left side is true. The if statement then considers the combined condition

gender == 1 && age >= 65

This condition is true if and only if both of the simple conditions are true. Finally, if this combined condition is indeed true, the statement in the if statement's body increments the count of seniorFemales. If either of the simple conditions is false (or both are), then the program skips the incrementing and proceeds to the statement following the if. The preceding combined condition can be made more readable by adding redundant parentheses:

( gender == 1 ) && ( age >= 65 )

Common Programming Error 5.13

Although 3 < x < 7 is a mathematically correct condition, it does not evaluate as you might expect in C++. Use ( 3 < x && x < 7 ) to get the proper evaluation in C++.


Figure 5.15 summarizes the && operator. The table shows all four possible combinations of false and true values for expression1 and expression2. Such tables are often called truth tables. C++ evaluates to false or true all expressions that include relational operators, equality operators and/or logical operators.

Fig. 5.15. && (logical AND) operator truth table.
expression1 expression2 expression| && expression2
false false false
false true false
true false false
true true true


Logical OR (||) Operator

Now let us consider the || (logical OR) operator. Suppose we wish to ensure at some point in a program that either or both of two conditions are true before we choose a certain path of execution. In this case, we use the || operator, as in the following program segment:

if ( ( semesterAverage >= 90 ) || ( finalExam >= 90 ) )
   cout << "Student grade is A" << endl;

This preceding condition also contains two simple conditions. The simple condition semesterAverage >= 90 evaluates to determine whether the student deserves an "A" in the course because of a solid performance throughout the semester. The simple condition finalExam >= 90 evaluates to determine whether the student deserves an "A" in the course because of an outstanding performance on the final exam. The if statement then considers the combined condition

( semesterAverage >= 90 ) || ( finalExam >= 90 )

and awards the student an "A" if either or both of the simple conditions are true. Note that the message "Student grade is A" prints unless both of the simple conditions are false. Figure 5.16 is a truth table for the logical OR operator (||).

Fig. 5.16. || (logical OR) operator truth table.
expression1 expression2 expression1 || expression2
false false false
false true true
true false true
true true true


The && operator has a higher precedence than the || operator. Both operators associate from left to right. An expression containing && or || operators evaluates only until the truth or falsehood of the expression is known. Thus, evaluation of the expression

( gender == 1 ) && ( age >= 65 )

stops immediately if gender is not equal to 1 (i.e., the entire expression is false) and continues if gender is equal to 1 (i.e., the entire expression could still be true if the condition age >= 65 is true). This performance feature for the evaluation of logical AND and logical OR expressions is called short-circuit evaluation.

Performance Tip 5.6

In expressions using operator &&, if the separate conditions are independent of one another, make the condition most likely to be false the leftmost condition. In expressions using operator ||, make the condition most likely to be true the leftmost condition. This use of short-circuit evaluation can reduce a program's execution time.


Logical Negation (!) Operator

C++ provides the ! (logical NOT, also called logical negation) operator to enable a programmer to "reverse" the meaning of a condition. Unlike the && and || binary operators, which combine two conditions, the unary logical negation operator has only a single condition as an operand. The unary logical negation operator is placed before a condition when we are interested in choosing a path of execution if the original condition (without the logical negation operator) is false, such as in the following program segment:

if ( !( grade == sentinelValue ) )
   cout << "The next grade is " << grade << endl;

The parentheses around the condition grade == sentinelValue are needed because the logical negation operator has a higher precedence than the equality operator.

In most cases, you can avoid using logical negation by expressing the condition with an appropriate relational or equality operator. For example, the preceding if statement also can be written as follows:

if ( grade != sentinelValue )
   cout << "The next grade is " << grade << endl;

This flexibility often can help a programmer express a condition in a more "natural" or convenient manner. Figure 5.17 is a truth table for the logical negation operator (!).

Fig. 5.17. ! (logical negation) operator truth table.
expression !expression
false true
true false


Logical Operators Example

Figure 5.18 demonstrates the logical operators by producing their truth tables. The output shows each expression that is evaluated and its bool result. By default, bool values true and false are displayed by cout and the stream insertion operator as 1 and 0, respectively. We use stream manipulator boolalpha (a sticky manipulator) in line 11 to specify that the value of each bool expression should be displayed as either the word "true" or the word "false." For example, the result of the expression false && false in line 12 is false, so the second line of output includes the word "false." Lines 11–15 produce the truth table for &&. Lines 18–22 produce the truth table for ||. Lines 25–27 produce the truth table for !.

Fig. 5.18. Logical operators.

 

 1   // Fig. 5.18: fig05_18.cpp
 2   // Logical operators.
 3   #include <iostream>
 4   using std::cout;
 5   using std::endl;
 6   using std::boolalpha; // causes bool values to print as "true" or "false"
 7
 8   int main()
 9   {
10      // create truth table for && (logical AND) operator
11      cout << boolalpha << "Logical AND (&&)"
12         << "\nfalse && false: " << ( false && false )
13         << "\nfalse && true: " << ( false && true )
14         << "\ntrue && false: " << ( true && false )
15         << "\ntrue && true: " << ( true && true ) << "\n\n";
16
17      // create truth table for || (logical OR) operator
18      cout << "Logical OR (||)"
19         << "\nfalse || false: " << ( false || false )
20         << "\nfalse || true: " << ( false || true )
21         << "\ntrue || false: " << ( true || false )
22         << "\ntrue || true: " << ( true || true ) << "\n\n";
23
24      // create truth table for ! (logical negation) operator
25      cout << "Logical NOT (!)"
26         << "\n!false: " << ( !false )
27         << "\n!true: " << ( !true ) << endl;
28      return 0; // indicate successful termination
29   } // end main

					  

Logical AND (&&)
false && false: false
false && true: false
true && false: false
true && true: true

Logical OR (||)
false || false: false
false || true: true
true || false: true
true || true: true

Logical NOT (!)
!false: true
!true: false


Summary of Operator Precedence and Associativity

Figure 5.19 adds the logical operators to the operator precedence and associativity chart. The operators are shown from top to bottom, in decreasing order of precedence.

Fig. 5.19. Operator precedence and associativity.
Operators           Associativity Type
::           left to right scope resolution
()           left to right parentheses
++ -- static_cast< type >() left to right unary (postfix)
++ -- + - !   right to left unary (prefix)
* / %       left to right multiplicative
+ -         left to right additive
<< >>         left to right insertion/extraction
< <= > >=     left to right relational
== !=         left to right equality
&&           left to right logical AND
||           left to right logical OR
?:           right to left conditional
= += -= *= /= %= right to left assignment
,           left to right comma