Logical expressions are a fundamental part of control statements, and programmers form them with combinations of two kinds of operators:
It's not possible to understand or to effectively use control statements without a thorough understanding of logical expressions and the operators that form them.
Relational operators form complex, Boolean-valued expressions by comparing or relating two numeric sub-expressions. Being Boolean-valued means that the result of the expression is constrained to one of two values: true or false. The following table lists all the relational operators.
Operator | Meaning |
---|---|
== |
Equal to |
!= |
Not equal to |
< |
Less than |
<= |
Less than or equal to |
> |
Greater than |
>= |
Greater than or equal to |
As relational operators compare two values, they are all binary (i.e., they require two operands), and, like most binary operators, they are left-associative (evaluated left to right). Each operand is an expression, a simple or arbitrarily complex one. However, "testing" the relation between two constants has little value in a program, so at least one operand is usually a variable or a more complex expression.
Programmers typically use relational operators in control statements (like loops and if-statements), which are the subjects of the following sections. C++ control statements are much the same as those you are familiar with in Java from your earlier studies.
C++ and Java both support two kinds of Boolean operators: logical and bitwise. The nature of programming can cause programmers to become pathologically precise: they take great care to avoid confusion by writing "logical-AND" or "bitwise-AND" to distinguish between the two kinds of operators. The bitwise operators were introduced in the supplemental section of Chapter 2, while the logical operators are the subject of the following section. For simplicity, the words AND, OR, or NOT, written with all upper case letters, refer to the logical Boolean operators.
C++ provides the same three logical operators as Java; two of the operators are binary, while one is unary. The operands for all three operators are Boolean, and the result of the operation is a Boolean value. All of the operators are left-associative.
Operator | Meaning | Alternate1 |
---|---|---|
! |
Logical NOT | not |
&& |
Logical AND | and |
|| |
Logical OR | or |
1 The alternative versions (which are case-sensitive) were added to support small character sets that do not include the traditional characters. Some versions of C++ provide alternative versions of the relational operators. See Alternative operators for more information.
Although the acceptance of the alternative operator representations is wide and growing, the alternatives are NOT universally supported by all compilers (specifically, Visual Studio). I recommend using only the operator versions generally and the alternatives only when necessary.
The logical operators have a limited domain (input) and range (output), which makes it easy for us to enumerate all the possible combinations of inputs and outputs. Summarizing all possible combinations in a truth table is common.
|
|
|
||||||||||||||||||||||||||||||||||||
(a) | (b) | (c) |
m / m > x * y
Knowing how to interpret the results of these operators is essential to understanding the behavior of the logical operators as they appear in expressions, and understanding the behavior of the expressions is fundamental to correctly forming even the most basic control statements. So, if you have not memorized these truth tables, you should do so immediately.
From the tables above, we see that AND and OR produce TRUE in only one case each. We can use these observations to form clear, efficient control statements. The AND operation has a TRUE value only when both operands are TRUE. So, if the left-hand operand evaluates to FALSE, the value of the right-hand operand makes no difference. Similarly, OR produces a FALSE value only when both operands are FALSE. So, if the left-hand operand evaluates to TRUE, then the value of the right-hand operand is irrelevant.
E1 && E2 | E3 || E4 | ||||
---|---|---|---|---|---|
E1 | E2 Evaluated | E3 | E4 Evaluated | ||
True | Yes | True | No | ||
False | No | False | Yes |
a == b
, a < b
, or x + y > 10
.
if (n != 0 && 100 / n > min) ... |
if (str != nullptr && str.length() > 0) ... |
(a) | (b) |
(As an aside, Java defines two logical AND and OR operator versions. One version provides short circuit evaluation while the other does not. However, I've never seen a textbook use the non-short circuit version beyond its demonstration; more importantly, I've never seen the non-short circuit version used in production code!)
The C programming language, the predecessor of both C++ and Java, does not define a Boolean data type, but C++ and Java do. C++ adds the bool
type, and Java refines it and names it boolean
(which is distinct from the Java wrapper class named Boolean
). C++ and Java also add two related keywords: true
and false
. The discussions of the relational and logical operators above also apply to C, but how does C manage if it doesn't have a Boolean data type or the keywords to represent the two possible Boolean values? The answer to that question is at the center of an essential difference between C++ and Java.
In Java, boolean
is a distinct data type that is unrelated to any numeric type. That means that true
and false
are unorderable and without a numeric representation. However, in C++, bool
is just a syntactic candy coating (i.e., a synonym) for an int
! Furthermore, true
and false
are just aliases for 1 and 0 respectively. Dealing with Boolean operations and values this way has some unexpected consequences.
|
if (n % 2) cout << "n is odd\n"; else cout << "n is even\n"; |
(a) | (b) |
nullptr
to a pointer variable (Chapter 4) and then logically treat it as 0. Being able to order the Boolean constants is irrelevant.Although using an arbitrary expression to control if-statements and loops is convenient, it also allows programmers to make an easy programming mistake.
int counter;
. . .
if (counter == 10)
. . .; |
int counter;
. . .
if (counter = 10)
. . .; |
(a) | (b) |