Text: C++ Programming: From Problem Analysis to Program Design, 2nd Ed. by D. S. Malik
Charles E. Oyibo
A computer can process a program in one of three ways:
Control structures provide an alternative to sequential program execution and are used to alter the sequential flow of execution. The two most common control structures are selection (where the program executes particular statements depending on some condition(s)); and repetition (where the program repeats particular statements a certain number of times depending on some condition(s)).
Statements that are executed in a program only if certain conditions are met are called conditional statements. A condition is met if it evaluates to true.
Certain items are compared for equality against a particular condition; others are compared for inequality--greater than or less than--against a particular condition. In C++, a condition is represented by a logical (Boolean) expression (i.e. an expression that has a value of, or evaluates to either true or false), and relational operators allow us to make comparisons in programs.
C++ includes six relational operators:
| Operator | Description |
|---|---|
| == | equal to |
| != | not equal to |
| < | less than |
| <= | less than or equal to |
| > | greater than |
| >= | greater than or equal to |
Note the distinction between that the assignment operator (=), which assigns the value of an expression to a variable, and the equality operator (==), which determines whether two expressions are equal.
Each of the relational operators is a binary operator as it requires two operands, and expressions using those operators evaluate to true or false. The identifier true is set to 1, and the identifier false is set to 0.
Comparing values of different data types may product unpredictable results.
For char values, whether an expression evaluates to true or false depends on a machine (ASCII) collating sequence.
The relational operators can be applied to variable to the type string. Variable of the type string are compare character-by-character, starting with the first character using the ASCII collating sequence. The character-by-character matching continues until either a mismatch is found or the last characters have been compared and are equal.
If two strings of different lengths are compared and the character-by-character comparison is equal until it reaches the last character of the shorter string, the shorter string (e.g. Bill) is evaluated to as less than the larger string (e.g. Billy).
Logical (Boolean) operators allow us to combine logical expressions. C++ has three logical (Boolean) operators:
| Operator | Description |
|---|---|
| ! | not |
| && | and |
| || | or |
Logical operators take only logical values as operands and yield only logical values as results. The operator ! is unary, so it has only one operand. The operators && and || are binary operators.
| Expression | !(Expression) |
|---|---|
| true (nonzero) | false (0) |
| false (0) | true (1) |
| Expression1 | Expression2 | Expression1 && Expression2 |
|---|---|---|
| true (nonzero) | true (nonzero) | true (1) |
| true (nonzero) | false (0) | false (0) |
| false (0) | true (nonzero) | false (0) |
| false (0) | false (0) | false (0) |
| Expression1 | Expression2 | Expression1 || Expression2 |
|---|---|---|
| true (nonzero) | true (nonzero) | true (1) |
| true (nonzero) | false (0) | true (1) |
| false (0) | true (nonzero) | true (1) |
| false (0) | false (0) | false (0) |
| Operators | Precedence |
|---|---|
| !, +, - (unary operators) | first |
| *, /, % | second |
| +, - | third |
| <, <=, >=, > | fourth |
| ==, != | fifth |
| && | sixth |
| || | seventh |
| = (assignment operator) | last |
With short-circuit evaluation, the computer evaluates the logical expression from left to right; as soon as the value of the entire logical expression is known, the evaluation stops. Consider the following statement for example:
(x > y) || (x == 5) // line1
(a == b) && (x >= 7) // line 2
If the operand (x > y) in statement one evaluates to true, then the entire expression evaluate to true because true || true is true and true || false is true. Therefore the value of the operand (x==5) has no bearing on the final outcome.
Similarly, if the operand (a==b) in the statement in line 2 evaluates to false, then the entire statement evaluates to false because false && true is false and false && false is false.
A short-circuit evaluation (of a logical expression), then, is a process by which the computer evaluates a logical expression from left to right and stops as soon as the value of the expression is known.
In C++, logical (Boolean) expressions can be manipulated or processed in either of two ways: by using int variables or by using bool variables. We go to these next.
Because logical expressions evaluate to either 1 or 0, the value of a logical expression could be stored in a variable of the type int. Therefore, it is possible to use the int data type to manipulate logical (Boolean) expressions. Observe:
int legalAge;
int age;
legalAge = 21; // the value of legalAge assigned
by this statement is true (recall that nonzero values are treated as true).
legalAge = (age >= 21); // assigns the value
of 1 to legalAge if the value of age is greater than or equal to 21, and assigns
a value of 0 if the value is less than 21.
More recent versions of C++ contain a built-in data type, bool, that has the logical (Boolean) values true and false. Therefore, one could manipulate logical (Boolean) expression using the bool data type. Consider the following declaration:
bool legalAge;
int Age;
legalAge = true //
sets the value of the variable legalAge to true
legalAge = (age >= 21); // assigns the value
true to legalAge if the value of age is greater than or equal to 21, and assigns
the value of false to legalAge if the value of age is less than 21.
Note: The correct way to represent the expression 0 <= x <= 10 in C++ is:
0 <= x && num <= 10
or
num >= 0 && num <= 10
This highlights the need for caution when formulating logical expressions. When creating complex logical expressions, one must use the proper logical operators.
In C++, there are two selection, or branch control structure: if statements and the switch structure.
These are examples of one-way selection. In C++, one-way selections are incorporated using the if statement:
if (expression)
statement
The expression contained with the parenthesis is usually a logical expression. If the value of the expression is true, the statement executes. If the value is false, the statement does not execute and the computer goes on to the next statement in the program.
Note: Putting a semicolon after the parenthesis following the expression in an if statement (that is, before the statement) is a semantic error. If the semicolon immediately follows the closing parenthesis, the if statement will be an empty statement.
if (expression)
statement1
else
statement2
In a two-way selection, if the value of the expression is true, statement1 executes. If the value of the expression is false, statement2 executes.
As with a one-way selection, putting a semicolon after the expression and before statement1 creates a syntax error. Semicolons should come only at the end of statements.
Suppose that we would like to execute more than one statement if the expression in an if or if...else statement evaluates to true, we could use a structure that C++ provides, called a compound statement or a block of statements:
{
statement1
statement2
...
statementn
}
That is, a compound statement consists of a sequence of statements enclosed in curly braces, {...}.
This problem has four alternative--that is, four multiple selection paths. We could nest multiple selection paths in a program by using an if or if...else statement thus:
if (balance > 50000.00)
interestRate = 0.07;
else
if (balance >= 25000.00)
interestRate = 0.05;
else
if (balance >= 1000.00
interestRate = 0.03
else
interestRate = 0.00;
Pairing an else with an if: In a nested if statement, C++ associates an else with the most recent incomplete if. There is however one general rule: you cannot look inside a block (that is inside braces, { ... }) to pair an else with an if.
In Chapter 3: Input/Output, we saw that an attempt to read invalid data causes the input stream to enter a fail state, all subsequent statements associated with that input stream are ignored, and the computer continues to execute the program, which produces erroneous results.
In addition to reading invalid data, two additional common causes of input failure are:
We could use the if statement to check the status of an input statement variable and, if the input stream enters a fail state, include instructions that stop program execution. We do this by using the input statement variable as a logical expression in an if statement. When the input stream variable is used as an expression in an if statement, it evaluates to true if the last input succeeded and to false if the last input failed.
The statement:
if (cin)
cout << "Input is OK." << endl;
prints:
Input is OK.
if the last input from the standard input device succeeded. Similarly, if infile is an ifstream variable, the statement:
if (!infile)
cout << "Input failed." << endl;
prints
Input failed.
if the last input associated with the stream variable infile failed.
... discussion of Input failure continues here...
Suppose that the discount on a car insurance policy is based on the insured's driving record. A driving record of 1 means that the driver is accident-free and receives a 25% discount on the policy. The statements:
if (drivingCode == 1)
cout << "The discount policy on the policy is 25%."
<< endl;
outputs:
The discount policy is 25%
only if the value of drivingCode is 1. However, the statement:
if (drivingCode = 1)
cout << "The discount policy on the policy is 25%."
<< endl;
always outputs:
The discount policy is 25%
because the right side of the assignment expression evaluates to 1, which is nonzero and so evaluates to true. Therefore, the expression on the if statement always evaluates to true. Also, the value 1 is assigned to the variable drivingCode. Suppose that before the if statement, the value of the variable drivingCode is 4. After the if statement executes, not only is the output wrong, but the new value also replaces the old driving code!
The appearance of = in place of == in a program is something of a silent killer. It is not a syntax error, so the compiler does not warn you of an error. Rather it is a logical error.
Moreover, the appearance of the equality operator in place of the assignment operator can also cause errors in a program. Suppose, for example, that x, y, and z are int variables. The statement:
x = y + z;
assigns the value of the expression y + z to x. The statement
x == y +z;
compares the value of the expression y + z with the value of x; the value of x remains the same, however. If elsewhere in the program you are counting on the value of x being y + z, a logic error will occur: the program would be incorrect, but you would receive no warning of this from the compiler. The compiler provides feedback only about syntax errors, not logic errors.
...discussion of the conditional operator (?:)...
While the if and if...else selection or branch structures require the evaluation of a (logical) expression, C++'s switch structure gives the computer the power to choose from among many alternatives.
The general structure of the switchs statement is:
switch (expression)
{
case value1: statements1
break;
case value2: statements2
break;
...
case valuen: statementsn
break;
default: statements
}
First the expression is evaluated. The value of the expression is the used to perform the action specified in the statements that follow the reserved word case.
The expression, (also called the selector) is usually an identifier and can be only integral. Its value determines which statement is selected for execution. A particular case should appear only once, and may hold multiple statements. There is no need to use braces to turn multiple statements into a single compound statement. The break statement may or may not appear after each statement.
The switch statements executes according to the following rules:
Example:
switch ( grade )
{
case 'A': cout << "Grade: A";
break;
case 'B': cout << "Grade: B ";
break;
case 'C': cout << "Grade: C ";
break;
case 'D': cout << "Grade: D ";
break;
case 'F': cout << "Grade: F ";
break;
default: cout << "Invalid Grade ";
break;
}
Here, the expression in the switch statement is a variable identifier (grade) of the data type char, which is an integral type.
Note: When the value of a switch expression matches a case value, all statements execute until a break is encountered, and the program skips all case labels in between.
In addition to being a variable identifier or a complex expression, the switch expression can evaluate to a logical value:
switch (age >= 18)
{
case 1: cout << "Old enough to be drafted." << endl;
cout << "Old enough
to vote." << end;
break;
case 0: cout << Not old enough to be drafted." << endl;
cout << "Not old
enough to vote." << end;
}
If the value of age is greater than or equal to 18, the expression evaluates to 1--that is, true, and the statements following the case label 1 execute. If the value of age is less than 18, the expression evaluates to 0--that is, false, and the statements following the case label 0 execute.
We can also use true and false instead of 1 and 2 in the preceding switch statement:
switch (age >= 18)
{
case true: cout << "Old enough to be
drafted." endl;
cout << "Old enough
to vote." end;
break;
case false: cout << Not old enough to be
drafted." endl;
cout << "Not old
enough to vote." end;
}
Though there aren't any hard and fast rules in this matter, the following considerations should be remembered when deciding whether to use an if...else structure or a switch structure.
C++ includes a predefined function, assert, that is useful in stopping program execution when certain elusive errors occur. In program execution situations in which certain conditions are not met, it would be useful to halt the program execution with a message indicating where in the program the error occured. We handle these types of situations by including output and return statements in our program.
The syntax of the assert function is:
assert (expression);
Here, expression is any logical expression. If the expression evaluates to true, the next statement executes; if the expression evaluates to false, the program terminates and indicates where the program error occured.
The specification for the assert function is found in the header file cassert. Hence, for a program to use the assert function, it must include the statement:
#include <cassert>
Example 1:
assert (denominator)
quotient = numerator / denominator
Here, if denominator is 0, the assert statement halts the execution of the program with an error message because 0 evaluates to false.
Example 2 (demonstrates the use of an explicit logical expression):
assert (hours > 0 && (0 < rate && rate
<= 15.50 ));
if (hours > 0 && (0 < rate && rate <= 15.50 ))
wages = rate * hours;
assert statements can be disabled in programs that have been developed, tested, and are ready to ship (to prevent end-users from seeing error messages that might be unintelligible to them). We disable assert statements by using the preprocessor directive:
#define NDEBUG
The preprocessor directive #define NDEBUG must be placed before the directive #include <cassert>.
Page Last Updated: Saturday February 12, 2005 10:21 AM