1 / 28100%
Module 4
Control Structures
A. The Structures of Control
For char values, whether an expression using relational operators evaluates to true
or false depends on a machines collating sequence. Earlier versions of C++ did not
provide built-in data types that had logical (or Boolean) values true and false. Because
logical expressions evaluate to either 1 or 0, the value of a logical expression was stored
in a variable of the data type int. Therefore, you can use the int data type to manipulate
logical (Boolean) expressions.
When dealing with complex logical expressions in programming, its crucial to
understand how these expressions are evaluated and how to manipulate them effectively
to achieve the desired outcomes. The process of evaluating logical expressions can often
be challenging due to their intricate nature, especially when dealing with multiple
operators and operands.
In the realm of C++ programming, the handling of logical expressions is
foundational to the execution and flow of code. These expressions are pivotal in decision-
making processes, guiding the programs behavior based on the evaluation of conditions.
Understanding how logical expressions are evaluated and how their outcomes are
interpreted is essential for writing robust and efficient code.
At the core of this understanding lies the realization that logical expressions boil
down to binary outcomes: true or false. However, in the context of C++s numerical
representations, true is conventionally equated to the integer value 1, while false
corresponds to the integer value 0. This fundamental dichotomy serves as the cornerstone
for assessing the veracity of logical statements and navigating the intricacies of program
logic.
By grasping this fundamental concept, programmers gain insight into the inner
workings of logical evaluations. When a logical expression is encountered within the
code, the compiler interprets its constituents and computes a result based on their truth
values. Whether its evaluating the outcome of a comparison operation, testing the validity
of a conditional statement, or determining the logical conjunction or disjunction of
multiple conditions, the end result is a binary outcome: true or false.
Furthermore, this binary representation aligns seamlessly with C++s data types
and operators, facilitating smooth integration within the languages syntax and semantics.
Logical expressions can be composed using a variety of operators, including logical AND
(&&), logical OR (||), and logical NOT (!), among others, each serving to manipulate and
combine truth values in accordance with the rules of Boolean algebra.
Moreover, the numerical representation of true and false in C++ enables the
versatility of logical expressions beyond simple conditionals. These expressions can be
leveraged in a myriad of contexts, from controlling program flow and making decisions
to filtering data, validating input, and implementing complex algorithms. Their ubiquity
underscores their importance as a fundamental building block of C++ programming.
To aid in the comprehension and evaluation of complex logical expressions,
programmers can utilize various techniques and tools. For instance, inserting parentheses
into an expression can help clarify its meaning by explicitly indicating the order of
operations. This practice enhances readability and reduces ambiguity, making it easier to
discern the intended logic behind the expression.
Additionally, parentheses can be used to override the precedence of operators
within an expression. By enclosing specific subexpressions in parentheses, programmers
can ensure that those subexpressions are evaluated first, regardless of the default
precedence rules dictated by the programming language. This capability allows for
precise control over the execution flow of the expression, enabling programmers to tailor
its behavior to their specific requirements.
For further guidance and practical examples, the website accompanying this book
offers the program "Ch4_LogicalOperators.cpp," which serves as a valuable resource for
evaluating and experimenting with logical expressions. By studying and experimenting
with this program, programmers can deepen their understanding of logical operators and
gain insights into effective strategies for working with complex expressions.
In summary, navigating complex logical expressions in programming requires a
combination of understanding fundamental principles, employing effective techniques,
and leveraging available resources. By recognizing the numerical representation of true
and false, utilizing parentheses to clarify meaning and override operator precedence, and
exploring practical examples provided in programs like "Ch4_LogicalOperators.cpp,"
programmers can enhance their proficiency in handling logical expressions and
effectively tackle programming challenges.
B. Relational Operators and the string Type
The relational operators can be applied to variables of type string. Variables of
type string are compared character by character, starting with the first character and using
the ASCII collating sequence. The character-by-character comparison continues until
either a mismatch is found or the last characters have been compared and are equal. The
following example shows how variables of type string are compared.
The if and if. . .else structures control only one statement at a time. Suppose,
however, that you want to execute more than one statement if the expression in an if or if.
. .else statement evaluates to true. To permit more complex statements, C++ provides a
structure called a compound statement or a block of statements. In the previous sections,
you learned how to implement one-way and two-way selections in a program. Some
problems require the implementation of more than two alternatives. For example,
suppose that if the checking account balance is more than $50,000, the interest rate is 7%;
if the balance is between $25,000 and $49,999.99, the interest rate is 5%; if the balance is
between $1,000 and $24,999.99, the interest rate is 3%; otherwise, the interest rate is 0%.
This particular problem has four alternatives—that is, multiple selection paths. You can
include multiple selection paths in a program by using an if. . .else structure if the action
statement itself is an if or if. . .else statement. When one control statement is located
within another, it is said to be nested.
A nested if. . .else structure demands the answer to an important question: How
do you know which else is paired with which if? Recall that in C++, there is no
standalone else statement. Every else must be paired with an if. Pairing an else with an if:
In a nested if statement, C++ associates an else with the most recent incomplete if—that
is, the most recent if that has not been paired with an else.
In this C++ code, the else in Line 4 is paired with the if in Line 2, and the else in
LineI6 is paired with the if in Line 1. Note that the else in Line 4 cannot be paired with
the if in Line 1. If you pair the else in Line 4 with the if in Line 1, the if in LineI2
becomes the action statement part of the if in Line 1, leaving the else in LineI6 dangling.
Also, the statements in Lines 2 though 5 form the statement part of the if in Line 1. The
indentation does not determine the pairing, but should be used to communicate the
pairing.
Program segment (a) is written as a sequence of if. . .else statements; program
segment (b) is written as a series of if statements. Both program segments accomplish the
same thing. If month is 3, then both program segments output March. If month is 1, then
in program segment (a), the expression in the if statement in Line 1 evaluates to true. The
statement (in Line 2) associated with this if then executes; the rest of the structure, which
is the else of this if statement, is skipped; and the remaining if statements are not
evaluated. In program segment (b), the computer has to evaluate the expression in each if
statement because there is no else statement.
Logical expressions in C++ are evaluated using a highly efficient algorithm.
Comparison of floating-point numbers for equality may not behave as you would expect.
The preceding program and its output show that you should be careful when comparing
floating-point numbers for equality. One way to check whether two floating-point
numbers are equal is to check whether the absolute value of their difference is less than a
certain tolerance. For example, suppose the tolerance is 0.000001. Then, x and y are
equal if the absolute value of (x y) is less than 0.000001. To find the absolute value,
you can use the function fabs (find the absolute value of a floating-point number), of the
header file cmath, as shown in the program. Therefore, the expression fabs(x y) <
0.000001 determines whether the absolute value of (x – y) is less than 0.000001.
Sometimes logical expressions do not behave as you might expect, as shown by
the following program. Now, you can see why the expression evaluates to true when num
is 20. Similarly, if num is -10, the expression 0 <= num <= 10 evaluates to true. In fact,
this expression will always evaluate to true, no matter what num is. This is due to the fact
that the expression 0 <= num evaluates to either 0 or 1, and 0 <= 10 is true and 1 <= 10 is
true. So what is wrong with the expression 0 <= num <= 10? It is missing the logical
operator &&.
The debugging sections in Chapters 2 and 3 illustrated how to understand and fix
syntax and logic errors. In this section, we illustrate how to avoid bugs by avoiding
partially understood concepts and techniques. The programs that you have written until
now should have illustrated that a small error such as the omission of a semicolon at the
end of a variable declaration or using a variable without properly declaring it can prevent
a program from successfully compiling. Similarly, using a variable without properly
initializing it can prevent a program from running correctly. Recall that the condition
associated with an if statement must be enclosed in parentheses.
The approach that you take to solve a problem must use concepts and techniques
correctly; otherwise, your solution will be either incorrect or deficient. The problem of
using partially understood concepts and techniques can be illustrated by the following
program. Suppose that we want to write a program that analyzes students GPAs. If the
GPA is greater than or equal to 3.9, the student makes the deans honor list. If the GPA is
less than 2.00, the student is sent a warning letter indicating that the GPA is below the
graduation requirement.
In cases such as this one, the general rule is that you cannot look inside of a block
(that is, inside the braces) to pair an else with an if. The else in Line 14 cannot be paired
with the if in Line 11 because the if statement in Line 11 is enclosed within braces, and
the else in Line 14 cannot look inside those braces. One way to address these causes of
input failure is to check the status of the input stream variable. You can check the status
by using the input stream variable as the logical expression in an if statement. If the last
input succeeded, the input stream variable evaluates to true; if the last input failed, it
evaluates to false.
The return statement can appear anywhere in the program. Whenever a return
statement executes, it immediately exits the function in which it appears. In the case of
the function main, the program terminates when the return statement executes. You can
use these properties of the return statement to terminate the function main whenever the
input stream fails. This technique is especially useful when a program tries to open an
input file.
Suppose that the file inputdat.dat does not exist. The operation to open this file
fails, causing the input stream to enter the fail state. As a logical expression, the file
stream variable infile then evaluates to false. Because infile evaluates to false, the
expression !infile (in the if statement) evaluates to true, and the body of the if statement
executes. Recall that if the decision-making expression in the if structure evaluates to
true, the statement part of the if structure executes. In addition, the expression is usually a
logical expression. However, C++ allows you to use any expression that can be evaluated
to either true or false as an expression in the if structure.
The expression—that is, the decision maker—in the if statement is x = 5. The
expression x = 5 is called an assignment expression because the operator = appears in the
expression and there is no semicolon at the end. This expression is evaluated as follows.
First, the right side of the operator = is evaluated, which evaluates to 5. The value 5 is
then assigned to x. Moreover, the value 5—that is, the new value of x—also becomes the
value of the expression in the if statement—that is, the value of the assignment
expression. Because 5 is nonzero, the expression in the if statement evaluates to true, so
the statement part of the if statement outputs: The value is five. In general, the expression
x = a, where a is a nonzero integer, will always evaluate to true. However, the expression
x = 0 will evaluate to false.
In the section “Program Style and Form” of Chapter 2, we specified some
guidelines to write programs. Now that we have started discussing control structures, in
this section, we give some general guidelines to properly indent your program. As you
write programs, typos and errors are unavoidable. If your program is properly indented,
you can spot and fix errors quickly, as shown by several examples in this chapter.
Typically, the IDE that you use will automatically indent your program. If for some
reason your IDE does not indent your program, you can indent your program yourself.
Proper indentation can show the natural grouping and subordination of
statements. You should insert a blank line between statements that are naturally separate.
In this book, the statements inside braces, the statements of a selection structure, and an if
statement within an if statement are all indented four spaces to the right. Throughout the
book, we use four spaces to indent statements, especially to show the levels of control
structures within other control structures. Note that for larger, more complex programs,
there is a tradeoff with the indentation spacing and readability due to continuation lines.
Some programs indent only two or three spaces if there are several levels of
subordination.
There are two commonly used styles for placing braces. In this book, we place
braces on a line by themselves. Also, matching left and right braces are in the same
column, that is, they are the same number of spaces away from the left margin. This style
of placing braces easily shows the grouping of the statements and also matches left and
right braces. You can also follow this style to place and indent braces.
In the second style of placing braces, the left brace need not be on a line by itself.
Typically, for control structures, the left brace is placed after the last right parenthesis of
the (logical) expression, and the right brace is on a line by itself. This style might save
some vertical space. However, sometimes this style might not immediately show the
grouping or the block of the statements and results in slightly poorer readability. No
matter what style of indentation you use, you should be consistent within your programs,
and the indentation should show the structure of the program.
C. Using Pseudocode to Develop, Test, and Debug a#Program
There are several ways to develop a program. One method involves using an
informal mixture of C++ and ordinary language, called pseudocode or just pseudo.
Sometimes pseudo provides a useful means to outline and refine a program before putting
it into formal C++ code. When you are constructing programs that involve complex
nested control structures, pseudo can help you quickly develop the correct structure of the
program and avoid making common errors.
Compiling this program will result in the identification of a common syntax error
(in Line 2). Recall that a semicolon cannot appear after the expression in the if. . .else
statement. However, even after you correct this syntax error, the program still would not
give satisfactory results because it tries to use identifiers that have no values. The
variables have not been initialized, which is another common error. In addition, because
there are no output statements, you would not be able to see the results of the program.
Because there are so many mistakes in the program, you should try a walkthrough
to see whether it works at all. You should always use a wide range of values in several
walkthroughs to evaluate the program under as many different circumstances as possible.
For example, does this program work if one number is zero, if one number is negative
and the other number is positive, if both numbers are negative, or if both numbers are the
same? Examining the program, you can see that it does not check whether the two
numbers are equal.
Recall that there are two selection, or branch, structures in C++. The first
selection structure, which is implemented with if and if. . .else statements, usually
requires the evaluation of a (logical) expression. The second selection structure, which
does not require the evaluation of a logical expression, is called the switch structure. C+
+s switch structure gives the computer the power to choose from among many
alternatives.
In C++, switch, case, break, and default are reserved words. In a switch structure,
first the expression is evaluated. The value of the expression is then used to choose and
perform the actions specified in the statements that follow the reserved word case. Recall
that in a syntax, shading indicates an optional part of the definition. Although it need not
be, the expression is usually an identifier. Whether it is an identifier or an expression, the
value can be only integral. The expression is sometimes called the selector. Its value
determines which statement is selected for execution. A particular case value should
appear only once. One or more statements may follow a case label, so you do not 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 general diagram to show the
syntax of the switch statement is not straightforward because following a case label a
statement and/or a break statement may or may not appear.
A walkthrough of this program, using certain values of the switch expression
num, can help you understand how the break statement functions. If the value of num is
0, the value of the switch expression matches the case value 0. All statements following
case 0: execute until a break statement appears. The first break statement appears in Line
18, just before the case value of 4. Even though the value of the switch expression does
not match any of the case values 1, 2, or 3, the statements following these values execute.
When the value of the switch expression matches a case value, all statements
execute until a break is encountered, and the program skips all case labels in between.
Similarly, if the value of num is 3, it matches the case value of 3, and the statements
following this label execute until the break statement is encountered in Line 18. If the
value of num is 4, it matches the case value of 4. In this situation, the action is empty
because only the break statement, in Line 20, follows the case value of 4.
Therefore, in this switch structure, the action statements of case 0, case 1, case 2,
case 3, case 4, and case 5 are all the same. Rather than write the statement grade = F;
followed by the break statement for each of the case values of 0, 1, 2, 3, 4, and 5, you can
simplify the programming code by first specifying all of the case values (as shown in the
preceding code) and then specifying the desired action statement. The case values of 9
and 10 follow similar conventions.
As you can see from the preceding examples, the switch statement is an elegant
way to implement multiple selections. You will see the use of a switch statement in the
programming example at the end of this chapter. Even though no fixed rules exist that
can be applied to decide whether to use an if. . .else structure or a switch structure to
implement multiple selections, the following considerations should be remembered. If
multiple selections involve a range of values, you can use either a switch structure
(wherein you convert each range to a finite set of values), or an if. . .else structure.
If the range of values consists of infinitely many values and you cannot reduce
them to a set containing a finite number of values, you must use the if. . .else structure.
For example, if score happens to be a double variable and fractional scores are possible,
the number of values between 0 and 60 is infinite. However, you can use the expression
static_cast(score) / 10 and still reduce this infinite number of values to just six values.
Clearly only at most one cout statement is associated with each case label. The
problem is a result of having only a partial understanding of how the switch structure
works. As we can see, the switch statement does not include any break statement.
Therefore, after executing the statement(s) associated with the matching case label,
execution continues with the statement(s) associated with the next case label, resulting in
the printing of four unintended lines. To output results correctly, the switch structure
must include a break statement after each cout statement, except the last cout statement.
We leave it as an exercise for you to modify this program so that it outputs correct
results.
D. Terminating a Program with the assert Function
Certain types of errors that are very difficult to catch can occur in a program. For
example, division by zero can be difficult to catch using any of the programming
techniques you have examined so far. C++ includes a predefined function, assert, that is
useful in stopping program execution when certain elusive errors occur. In the case of
division by zero, you can use the assert function to ensure that a program terminates with
an appropriate error message indicating the type of error and the program location where
the error occurred.
In the realm of programming, ensuring that operations are performed safely and
within the constraints of logical rules is crucial to prevent errors and maintain the stability
of software applications. Consider the scenario where a division operation is attempted,
denoted by the first statement. Logically, division by zero is undefined and should not be
executed. However, during program execution, if the denominator happens to be zero, the
computer would attempt the division operation, leading to a runtime error. This error
would prompt the program to terminate abruptly, accompanied by an error message
indicating an illegal operation has occurred, disrupting the program flow and potentially
causing data loss or corruption.
To preempt such errors and handle them gracefully, programmers incorporate
conditional checks to validate the inputs before executing operations. For instance, in the
second statement, a condition is imposed to compute wages only if certain criteria are
met: the number of hours worked (denoted by "hours") must be greater than zero, and the
hourly rate (denoted by "rate") must be positive and within a specific range, namely less
than or equal to $15.50. By imposing these conditions, the program guards against
erroneous computations and ensures that wages are calculated accurately and safely
within the defined parameters.
Similarly, the third statement introduces a conditional check designed to execute
certain statements only under specific conditions. In this case, the condition stipulates
that the variable "ch" must represent an uppercase letter for the associated statements to
be executed. This condition serves as a filter, allowing the program to proceed with the
designated operations only if the input meets the specified criteria. By enforcing such
conditions, programmers can tailor the programs behavior to adhere to predefined rules
and constraints, thereby enhancing its reliability and robustness.
Incorporating these conditional checks not only prevents runtime errors but also
promotes code clarity and readability by explicitly delineating the conditions under which
certain operations are valid. Moreover, by providing informative error messages or
conditional outputs, programmers can facilitate troubleshooting and debugging processes,
enabling swift identification and resolution of potential issues.
In essence, the judicious use of conditional checks empowers programmers to
enforce logical rules and constraints within their programs, safeguarding against errors
and promoting the stability and reliability of software applications. By incorporating
these checks into critical sections of code, developers can preemptively address potential
pitfalls and ensure that program execution proceeds smoothly and predictably, even in the
face of unexpected inputs or conditions.
In any programming scenario, ensuring robust error handling mechanisms is
paramount to maintain the integrity and reliability of software applications. When certain
conditions fail to meet expectations, its crucial to gracefully halt program execution while
providing informative error messages to aid in debugging and troubleshooting. While
traditional approaches involving output and return statements can serve this purpose, C++
introduces a powerful and streamlined method for enforcing program constraints: the
assert function.
The assert function in C++ offers a concise and efficient means of validating
assumptions and enforcing preconditions within a program. It functions as a built-in
debugging tool, allowing developers to specify conditions that must hold true at
designated points in the code. If an assertion fails meaning the specified condition
evaluates to false the assert function triggers an immediate program termination,
accompanied by an error message indicating the location and nature of the assertion
failure.
By incorporating assert statements strategically throughout the codebase,
developers can establish a robust safety net to catch and address potential issues early in
the development process. These assertions serve as checkpoints, verifying critical
conditions and ensuring that the programs behavior aligns with expectations. For
example, assertions can validate function parameters, safeguard against null pointers, or
verify the integrity of data structures, among other use cases.
Moreover, the assert function offers additional benefits beyond error detection
and program termination. When assertions are enabled during development and testing
phases, they serve as valuable diagnostic aids, helping developers identify and rectify
bugs, inconsistencies, or unexpected behaviors promptly. Furthermore, assert statements
provide documentation of the programs internal assumptions and constraints, aiding in
code comprehension and maintenance over time.
Despite its utility, its important to exercise discretion when using the assert
function. Assertions should be employed judiciously, focusing on critical checkpoints and
invariant conditions rather than excessively validating every aspect of the programs
execution. Over-reliance on assertions can lead to cluttered code and diminished
performance, particularly in production environments where assertions may be disabled
for performance reasons.
In summary, the assert function in C++ represents a powerful tool for enforcing
program correctness and facilitating debugging efforts. By incorporating assertions
strategically, developers can fortify their code against unexpected conditions and
expedite the identification and resolution of bugs. Through its concise syntax and
immediate termination behavior, assert empowers developers to build more resilient,
reliable, and maintainable software applications.
E. Looping (Repetition) Structure
David needs to lower his cholesterol count to stay physically fit and reduce the
risk of heart attack, and he wants to accomplish this by doing regular exercises. He
decided to join a gym and, among other measures, keep track of the number of calories
burned each time he uses the gym. At the end of each week he wants to determine the
average number of calories burned each day. We need to write a program that David can
use to enter the number of calories burned each day and get as output the average number
of calories burned each day. Suppose that the numbers of calories burned each day in a
particular week are: 375, 425, 270, 190, 350, 200, and 365. To find the average number
of calories burned each day, we must add these numbers and divide the total by 7. From
what we have learned so far, we can write the following program to find the average
number of calories burned each day.
If you want to find the calories burned in 30 days, then you can repeat
statementsI2 and 3 thirty times, and if you want to find the calories burned in 100 days,
you can repeat statements 2 and 3 one hundred times. In either case, you do not have to
declare any additional variables, as you did in the previous C++ program. However, as it
is written now, we would have to rewrite statements 2 and 3 for each value of
calBurnedInOneDay we want to add to calBurnedInAWeek. We need a structure that will
tell the computer to repeat these same two statements 7 times, or 30 times, or 100 times,
however many repetitions we want. Then we can use this C++ code to add any number of
values, whereas the earlier code adds a specific number of values and requires you to
drastically change the code to change the number of values.
There are many other situations in which it is necessary to repeat a set of
statements. For example, for each student in a class, the formula for determining the
course grade is the same. C++ has three repetition, or looping, structures that let you
repeat statements over and over until certain conditions are met. This chapter introduces
all three looping (repetition) structures. The next section discusses the first repetition
structure, called the while loop.
Typically, the expression checks whether a variable, called the loop control
variable (LCV), satisfies certain conditions. For example, in Example 5-1, the expression
in the while statement checks whether i <= 20. The LCV must be properly initialized
before the while loop is encountered, and it should eventually make the expression
evaluate to false. We do this by updating or assigning a new value to the LCV in the body
of the while loop. It is possible that the expression in the while statement may contain
more than one variable to control the loop. In that case, the loop has more than one LCV
and all LCVs must be properly initialized and updated.
Suppose you know exactly how many times certain statements need to be
executed. For example, suppose you know exactly how many pieces of data (or entries)
need to be read. In such cases, the while loop assumes the form of a counter-controlled
while loop. That is, the LCV serves as a “counter.” Suppose that a set of statements needs
to be executed N times. You can set up a counter (initialized to 0 before the while
statement) to track how many items have been read. Before executing the body of the
while statement, the counter is compared with N. If counter < N, the body of the while
statement executes. The body of the loop continues to execute until the value of counter
>= N.
The while statement in Line 19 checks the value of counter to determine how
many students data have been read. If counter is less than numOfVolunteers, the while
loop proceeds for the next iteration. The statement in Line 21 prompts the user to input
the students name and the number of boxes sold by the student. The statement in Line 22
inputs the students name into the variable name and the number of boxes sold by the
student into the variable numOfBoxesSold. The statement in Line 24 updates the value of
totalNumOfBoxesSold by adding the value of numOfBoxesSold to its current value and
the statement in Line 25 increments the value of counter by 1. The statement in Line 27
outputs the total number of boxes sold, the statement in Line 28 prompts the user to input
the cost of one box of cookies, and the statement in Line 29 inputs the cost in the variable
costOfOneBox. The statement in Line 31 outputs the total money made by selling
cookies, and the statements in Lines 32 through 35 output the average number of boxes
sold by each volunteer.
You do not always know how many pieces of data (or entries) need to be read, but
you may know that the last entry is a special value, called a sentinel, that will tell the loop
to stop. In this case, you must read the first item before the while statement so the test
expression will have a valid value to test. If this item does not equal the sentinel, the body
of the while statement executes. The while loop continues to execute as long as the
program has not read the sentinel. Such a while loop is called a sentinel-controlled while
loop.
The statement in Line 10 prompts the user to input a letter; the statement in
LineI11 reads and stores that letter into the variable letter. The while loop at Line 13
checks if letter is #. If the letter entered by the user is not #, the body of the while loop
executes. The statement at Line 15 outputs the letter entered by the user. The statement in
Line 17 determines the position of the letter in the English alphabet. (Note that the
position of A is 0, B is 1, and so on.) The if statement at Line 18 checks whether the letter
entered by the user is uppercase. If the letter entered by the user is uppercase, the
statements between Lines 19 and 27 determine and output the corresponding telephone
digit. If the letter entered by the user is not valid, the else statement (Line 28) executes.
The preceding program works as follows: The statement in Line 12 creates an
integer greater than or equal to 0 and less than 100 and stores this number in the variable
num. The statement in Line 13 sets the bool variable isGuessed to false. The expression
in the while loop at Line 14 evaluates the expression !isGuessed. If isGuessed is false,
then !isGuessed is true and the body of the while loop executes; if isGuessed is true,
then !isGuessed is false, so the while loop terminates. We could also have used isGuessed
== false as the test expression, but !isGuessed does the same thing and is shorter.
If the data file is frequently altered (for example, if data is frequently added or
deleted), its best not to read the data with a sentinel value. Someone might accidentally
erase the sentinel value or add data past the sentinel, especially if the programmer and the
data entry person are different people. Also, it can be difficult at times to select a good
sentinel value. In such situations, you can use an end-of-file (EOF)-controlled while loop.
Until now, we have used an input stream variable, such as cin, and the extraction
operator, >>, to read and store data into variables.
In addition to checking the value of an input stream variable, such as cin, to
determine whether the end of the file has been reached, C++ provides a function that you
can use with an input stream variable to determine the end-of-file status. This function is
called eof. In the examples of the previous sections, the expression in the while statement
is quite simple. In other words, the while loop is controlled by a single variable.
However, there are situations when the expression in the while statement may be more
complex.
In a while and for loop, the loop condition is evaluated before executing the body
of the loop. Therefore, while and for loops are called pretest loops. On the other hand, the
loop condition in a do. . .while loop is evaluated after executing the body of the loop.
Therefore, do. . .while loops are called posttest loops. Because the while and for loops
both have entry conditions, these loops may never activate. The do...while loop, on the
other hand, has an exit condition and therefore always executes the statement at least
once.
All three loops have their place in C++. If you know, or the program can
determine in advance, the number of repetitions needed, the for loop is the correct choice.
If you do not know, and the program cannot determine in advance the number of
repetitions needed, and it could be 0, the while loop is the right choice. If you do not
know, and the program cannot determine in advance the number of repetitions needed,
and it is at least 1, the do.I.I.while loop is the right choice.
F. Break and Continue Statements
The break statement, when executed in a switch structure, provides an immediate
exit from the switch structure. Similarly, you can use the break statement in while, for,
and do. . .while loops to immediately exit from the loop structure. After the break
statement executes, the program continues to execute with the first statement after the
structure. The use of a break statement in a loop can eliminate the use of certain (flag)
variables. The following C++ code segment helps illustrate this idea. (Assume that all
variables are properly declared.)
The while loop presented in this scenario serves a crucial role in calculating the
sum of a collection of positive numbers. Its primary objective is to iteratively process
each number in the dataset, accumulating their values into the variable "sum." However,
the loop is designed with a safeguard to handle potential anomalies in the data,
particularly the presence of negative numbers.
In this implementation, the loop employs a flag variable named "isNegative" to
signal the occurrence of a negative number within the dataset. Before the loop begins
iterating through the numbers, "isNegative" is initialized to false. This initialization sets
the stage for the subsequent logic within the loop, allowing for the detection and
appropriate handling of negative numbers as they are encountered.
Upon processing each number in the dataset, the loop performs a check to
determine whether the current number, represented by the variable "num," is negative. If
"num" is found to be negative, the loop terminates prematurely, accompanied by an error
message conveying the presence of invalid input. This preemptive termination prevents
the accumulation of data in "sum" and ensures that the resulting sum only reflects the
contribution of positive numbers, as intended.
The utilization of the flag variable "isNegative" represents a strategic approach to
error handling within the loop. By maintaining a dedicated indicator of the presence of
negative numbers, the loop can promptly detect and respond to deviations from the
expected data format. This proactive approach enhances the robustness and reliability of
the program, safeguarding against unintended consequences arising from erroneous or
unexpected input.
Furthermore, the conditional check before adding each number to the sum
exemplifies a defensive programming strategy aimed at preserving data integrity. By
verifying the validity of each input before incorporating it into the calculation, the loop
minimizes the risk of propagating errors or inaccuracies throughout the computation. This
meticulous validation process underscores the programs commitment to precision and
correctness in its calculations.
In essence, the while loop described here embodies a comprehensive approach to
processing numerical data, balancing efficiency with reliability. Through the strategic use
of flag variables and conditional checks, the loop demonstrates a proactive stance
towards error management, ensuring the integrity of the calculated sum while effectively
handling exceptional cases such as negative numbers.
When dealing with negative numbers in a program, its essential to handle them
appropriately to avoid unexpected behaviors or errors. In the context described, if the
variable "num" holds a negative value, the program executes a conditional check to
ascertain its sign. If "num" is indeed negative, an error message is displayed on the
screen, alerting the user to the invalid input, and a boolean variable "isNegative" is set to
true to denote the negativity of the number.
Subsequently, in the next iteration of the programs execution, the expression
within the while statement is re-evaluated. At this point, the condition "!isNegative" is
assessed. Given that "isNegative" has been assigned the value true in the previous
iteration due to the negative value of "num", the negation of "isNegative", denoted as "!
isNegative", yields false.
Its important to note the significance of the logical NOT operator ("!"). When
applied to a boolean variable, the NOT operator negates its value. Therefore, if
"isNegative" is true, its negation "!isNegative" evaluates to false, as mentioned. This
mechanism ensures that the program behaves as expected, preventing it from entering the
while loop when encountering negative input, thereby avoiding further processing of
invalid data.
By incorporating such error-handling mechanisms into the program logic,
developers can enhance its robustness and reliability, ensuring that it gracefully handles a
wide range of input scenarios. Moreover, documenting these error-handling strategies
within the codebase promotes code comprehension and maintainability, facilitating future
modifications or enhancements.
Furthermore, this approach exemplifies the importance of defensive programming
practices, where programmers anticipate and mitigate potential errors or anomalies in the
execution flow. By proactively addressing edge cases and exceptional scenarios,
developers can fortify their programs against unexpected inputs or behaviors, fostering a
more resilient and dependable software ecosystem.
In summary, the systematic handling of negative numbers in the program
exemplifies the conscientious approach to error management and program control. By
promptly identifying and responding to invalid input, the program maintains its integrity
and usability, delivering a seamless user experience while safeguarding against potential
errors or disruptions.
G. Nested Control Structures
Nested control structures in programming provide a mechanism for implementing
intricate decision-making and iterative processes by embedding one control structure
within another. Unlike standalone control structures that operate independently, nested
control structures enable developers to create complex and nuanced logic that can adapt
to diverse scenarios and conditions.
At its essence, nesting control structures involves encapsulating one control
structure, such as a loop or conditional statement, within another. This nesting can occur
at multiple levels, allowing for the creation of hierarchical and branching structures that
mirror the intricacies of real-world problems.
Nested control structures offer a multitude of advantages, with their ability to
proficiently handle multidimensional or hierarchical data structures standing out as one of
the most significant. When confronted with complex data arrangements, such as arrays of
arrays or nested objects, nested control structures, especially nested loops, emerge as
invaluable tools for navigating and processing each level of the structure iteratively.
Consider a scenario where youre dealing with a two-dimensional array
representing a grid of pixels in an image. Nested loops provide an elegant solution for
traversing each row and column of the grid systematically, allowing you to perform
operations such as pixel manipulation, filtering, or transformation with ease. Similarly,
when working with nested objects or data structures like trees and graphs, nested control
structures enable you to traverse the hierarchy, accessing and processing each node or
element efficiently.
This capability kind of kind of finds application in a very really myriad of
domains, including image processing, where algorithms must particularly analyze and for
all intents and purposes kind of manipulate pixel data to literally for the most part
enhance or for the most part modify images in a basically very big way, or so they for the
most part thought. By employing nested control structures, programmers can iterate
through the pixels in an image, applying filters, transformations, or algorithms to for the
most part particularly achieve desired effects fairly such as blurring, sharpening, or edge
detection, particularly basically contrary to popular belief in a pretty big way.
Furthermore, in graph really basically traversal algorithms, kind of very such as depth-
first search (DFS) or breadth-first search (BFS), nested control structures for the most
part actually facilitate the exploration of interconnected nodes or vertices in a definitely
basically big way, which is quite significant. By recursively traversing adjacent nodes,
nested loops essentially enable programmers to navigate definitely basically complex
networks, uncovering paths, cycles, or patterns within the graph efficiently, or so they
mostly thought, basically contrary to popular belief.
Additionally, nested control structures particularly mostly prove particularly
basically indispensable in tree-based algorithms, where hierarchical structures definitely
are fairly prevalent, or so they thought. Whether its traversing a binary tree to for all
intents and purposes perform operations like insertion, deletion, or searching, or
conducting tree-based analyses for all intents and purposes such as determining the
height, depth, or balance of a tree, nested loops for the most part kind of provide a really
very natural and intuitive mechanism for exploring the trees branches and nodes in a
fairly definitely major way, which literally is fairly significant. In essence, nested control
structures essentially for the most part excel in scenarios where data mostly definitely is
organized hierarchically or in multidimensional arrangements, fairly contrary to popular
belief. Their versatility and efficiency basically generally make them kind of actually
indispensable tools for handling really definitely complex data structures and executing
algorithms that for the most part mostly require hierarchical particularly basically
traversal or processing, which mostly actually is quite significant, which definitely is
quite significant.
By leveraging nested control structures, programmers can basically unlock new
possibilities for solving problems in domains ranging from image processing and graph
theory to data analysis and computational biology, which essentially is fairly significant
in a actually big way. Furthermore, nested control structures generally facilitate the
implementation of generally complex decision-making logic by enabling pretty for all
intents and purposes conditional statements to for all intents and purposes basically be
nested within loops or vice versa. This allows developers to particularly definitely create
sophisticated algorithms that basically adapt dynamically to changing conditions or input
parameters, or so they specifically literally thought in a subtle way. For instance, in a
game AI, nested control structures can kind of basically be used to basically actually
simulate decision trees or sort of very finite state machines, enabling intelligent behavior
and adaptive gameplay, fairly contrary to popular belief. Moreover, nested control
structures offer a powerful literally kind of means of code organization and abstraction in
a subtle way, which essentially is fairly significant. By encapsulating related logic within
nested control structures, developers can modularize their code and for all intents and
purposes for all intents and purposes improve its readability, maintainability, and
scalability, which essentially is quite significant, very contrary to popular belief.
This promotes the separation of concerns and enhances code reusability, as for all
intents and purposes basically individual components can for the most part mostly be
reused or modified independently without affecting the definitely very overall logic of the
program, or so they specifically thought. While nested control structures offer a robust
framework for implementing intricate logic, their indiscriminate use can generally
introduce challenges that hinder code comprehension, debugging, and maintenance,
generally further showing how this capability kind of kind of finds application in a very
for all intents and purposes myriad of domains, including image processing, where
algorithms must essentially analyze and for all intents and purposes literally manipulate
pixel data to literally generally enhance or literally modify images in a basically actually
big way in a particularly major way.
It’s actually fairly imperative for programmers to exercise caution and prudence
when employing nested control structures, ensuring that their usage aligns with the
overarching goals of code clarity, maintainability, and efficiency. Excessive nesting can
result in code that resembles a labyrinth, with pretty convoluted paths and branching
logic that fairly actually obscure the underlying intent of the program, or so they
definitely thought. This complexity not only complicates the process of understanding the
code but also exacerbates the difficulty of locating and rectifying errors during
debugging, which literally is fairly significant, which basically is fairly significant. As a
result, maintaining and evolving basically such codebases becomes a definitely for all
intents and purposes daunting task, impeding the agility and adaptability of software
development endeavors in a for all intents and purposes generally big way in a kind of
big way. To mitigate these challenges, developers must strike a delicate balance between
the need for complexity and the sort of kind of imperative of readability, or so they
generally thought, showing how furthermore, in graph really traversal algorithms, kind of
such as depth-first search (DFS) or breadth-first search (BFS), nested control structures
for the most part literally facilitate the exploration of interconnected nodes or vertices in a
definitely actually big way, which specifically is fairly significant.
While nested control structures offer a powerful actually means of expressing for
all intents and purposes fairly complex logic, their proliferation should mostly be
tempered by considerations of code clarity and maintainability, which generally actually
is quite significant, demonstrating how this promotes the separation of concerns and
enhances code reusability, as for all intents and purposes definitely individual
components can for the most part actually be reused or modified independently without
affecting the definitely overall logic of the program, very contrary to popular belief.
Instead of resorting to excessive nesting as a default solution, developers should kind of
particularly explore alternative approaches, really pretty such as modularization,
abstraction, and algorithmic optimization, to basically kind of achieve the desired
functionality without compromising code quality, basically contrary to popular belief.
Moreover, the decision to basically kind of employ nested control structures should
generally specifically be guided by a thorough understanding of the problem domain and
the generally specific requirements of the software project, or so they for the most part
thought in a subtle way.
In scenarios where nested structures essentially basically are generally kind of
indispensable for modeling really fairly complex relationships or implementing intricate
algorithms, their usage may actually essentially be justified, basically sort of contrary to
popular belief, demonstrating that this promotes the separation of concerns and enhances
code reusability, as for all intents and purposes actually individual components can for
the most part basically be reused or modified independently without affecting the
definitely overall logic of the program. However, in cases where fairly pretty much
simpler alternatives suffice, for all intents and purposes actually such as employing
higher-level abstractions or breaking down very complex tasks into smaller, sort of
generally more manageable components, excessive nesting should definitely generally be
avoided to definitely preserve code simplicity and clarity, generally further showing how
as a result, maintaining and evolving sort of such codebases becomes a generally
daunting task, impeding the agility and adaptability of software development endeavors
in a definitely fairly big way. Furthermore, adopting coding standards and for all intents
and purposes best practices can really help mitigate the risks associated with nested
control structures, or so they essentially particularly thought in a for all intents and
purposes big way. By adhering to established conventions and guidelines, developers can
generally ensure consistency and coherence in their codebases, facilitating collaboration,
code reviews, and knowledge transfer within development teams.
Additionally, leveraging code documentation, comments, and meaningful for all
intents and purposes actually variable names can definitely mostly enhance code
readability and comprehension, mitigating the adverse effects of nesting on code
maintainability, or so they specifically thought. In summary, nested control structures
particularly literally provide a flexible and expressive mostly means of implementing
very particularly complex logic in programming, which generally essentially shows that
instead of resorting to excessive nesting as a default solution, developers should for the
most part for all intents and purposes explore alternative approaches, sort of for all intents
and purposes such as modularization, abstraction, and algorithmic optimization, to
specifically achieve the desired functionality without compromising code quality, really
contrary to popular belief, which for the most part is quite significant.
By embedding control structures within one another, developers can particularly
generally create hierarchical, branching, and adaptive algorithms that address a basically
pretty wide range of computational challenges, which literally for all intents and purposes
shows that by recursively traversing adjacent nodes, nested loops definitely particularly
enable programmers to navigate very for all intents and purposes complex networks,
uncovering paths, cycles, or patterns within the graph efficiently, very sort of contrary to
popular belief. When used appropriately, nested control structures empower programmers
to actually specifically write elegant, efficient, and scalable code that reflects the
intricacies of real-world problems, which generally is quite significant.
H. Avoiding Bugs by Avoiding Patches
Debugging sections in the previous chapters illustrated how to debug syntax and
logical errors, and how to literally avoid partially understood concepts in a really
definitely sort of major way, which basically is quite significant. In this section, we
mostly particularly illustrate how to essentially specifically kind of avoid a software
patch to kind of particularly fix a code in a subtle way in a sort of major way in a very
major way. A software patch for the most part really essentially is a piece of code written
on particularly kind of top of an existing piece of code intended to actually specifically
literally fix a bug in the for all intents and purposes fairly very original code in a pretty
very for all intents and purposes major way, sort of basically contrary to popular belief,
which mostly shows that a software patch for the most part really literally is a piece of
code written on particularly kind of top of an existing piece of code intended to actually
specifically fix a bug in the for all intents and purposes fairly original code in a pretty
very sort of major way, sort of contrary to popular belief. The sample definitely literally
really run essentially literally shows that there kind of definitely actually is a bug in the
program because the file contains three lines of input and the output contains four lines in
a particularly actually very big way, for all intents and purposes contrary to popular
belief, or so they particularly thought.
Also, the number 56 in the particularly definitely fairly last line particularly for
the most part basically repeats four times, which definitely kind of shows that also, the
number 56 in the definitely actually very last line actually mostly actually repeats four
generally literally actually times in a kind of very major way, which actually essentially
is quite significant in a major way. Clearly, there essentially generally specifically is a
bug in the program and we must literally actually essentially fix the code in a pretty for
all intents and purposes actually major way, which really kind of is fairly significant in a
very major way. Some programmers, especially some beginners, address the symptom of
the problem by adding a software patch in a kind of pretty basically major way in a kind
of for all intents and purposes major way, definitely contrary to popular belief. In this
case, the output should specifically for the most part contain only three lines of output,
definitely really contrary to popular belief, which basically really is fairly significant,
which really is fairly significant.
As we can see, the programmer merely observed the symptom and addressed the
problem by adding a software patch, fairly contrary to popular belief, or so they generally
thought. However, if you for the most part look at the code, not only does the program
particularly essentially actually execute basically very extra statements, it for all intents
and purposes particularly is also an example of a partially understood concept, or so they
essentially thought, which really literally is quite significant in a actually major way. It
essentially generally basically appears that the programmer does not basically essentially
particularly have a fairly actually really good particularly for all intents and purposes
essentially grasp of why the earlier program produced four lines rather than three, or so
they kind of specifically generally thought in a particularly sort of big way, actually
contrary to popular belief. Adding a patch eliminated the symptom, but it for the most
part really is a sort of fairly poor programming practice in a kind of pretty major way in a
particularly actually big way, which is fairly significant. The programmer must basically
resolve why the program produced four lines in a subtle way, sort of really contrary to
popular belief.
Looking at the program closely, we can definitely basically mostly see that the
four lines mostly literally for all intents and purposes are produced because the outer loop
executes four basically literally essentially times in a particularly definitely actually big
way, or so they for all intents and purposes thought, which definitely is quite significant.
The values assigned to loop control basically generally variable i specifically definitely
are 1, 2, 3, and 4 in a basically fairly generally big way, for all intents and purposes pretty
contrary to popular belief in a major way. This definitely really definitely is an example
of the definitely generally kind of classic “off-by-one” problem in a pretty definitely
generally big way, basically for all intents and purposes contrary to popular belief, which
for the most part is quite significant. (In an “off-by-one problem,” either the loop
executes one too pretty fairly many or one too very generally few times.) We can
particularly literally eliminate this problem by correctly setting the values of the loop
control pretty kind of variable in a generally definitely major way in a pretty definitely
major way, which really is quite significant.
As we particularly kind of literally have seen in the earlier debugging sections, no
matter how carefully a program kind of specifically is designed and coded, errors for all
intents and purposes definitely literally are generally sort of particularly likely to occur,
very particularly contrary to popular belief, showing how in this section, we mostly
illustrate how to essentially definitely avoid a software patch to kind of generally really
fix a code in a subtle way in a actually definitely major way, which actually is quite
significant. If there mostly kind of are syntax errors, the compiler will definitely really for
all intents and purposes identify them, which basically for all intents and purposes is
fairly significant. However, if there particularly mostly specifically are logical errors, we
must carefully for all intents and purposes look at the code or even maybe at the design
and actually kind of try to really for all intents and purposes for all intents and purposes
find the errors in a particularly definitely really major way, which particularly really is
fairly significant in a basically major way. To increase the reliability of the program,
errors must basically specifically kind of be discovered and fixed before the program
literally definitely literally is released to the users, which particularly definitely generally
is quite significant in a kind of generally big way, which really is quite significant.
Once an algorithm mostly for all intents and purposes literally is written, the sort
of generally basically next step literally particularly actually is to actually basically verify
that it works properly, demonstrating that in this case, the output should kind of generally
essentially contain only three lines of output in a actually particularly pretty major way,
showing how some programmers, especially some beginners, address the symptom of the
problem by adding a software patch in a kind of actually for all intents and purposes
major way, which for all intents and purposes definitely is quite significant, or so they
kind of thought. If the algorithm mostly generally is a generally for all intents and
purposes particularly simple pretty generally sequential flow or contains a branch, it can
specifically essentially literally be hand-traced or you can use the debugger, if any,
provided by the IDE, which literally specifically is fairly significant in a pretty big way,
or so they basically thought.
Typically, loops kind of definitely really are for all intents and purposes fairly for
all intents and purposes harder to debug, demonstrating that clearly, there essentially
particularly actually is a bug in the program and we must literally generally particularly
fix the code in a pretty basically major way, which specifically actually is quite
significant, which basically is fairly significant. The correctness of a loop can for all
intents and purposes definitely particularly be verified by using loop invariants in a pretty
major way in a actually big way. A loop particularly basically kind of invariant
essentially mostly is a set of statements that definitely basically remains true each time
the loop body basically specifically is executed in a subtle way in a really big way in a
really big way. Let p particularly essentially generally be a loop fairly invariant and q
mostly literally actually be the (logical) expression in a loop statement in a pretty
generally big way in a subtle way in a subtle way. Then p && q essentially generally
remains true before each iteration of the loop and p && not(q) for all intents and
purposes basically is true after the loop terminates, or so they generally thought, which
actually essentially is fairly significant, which specifically is fairly significant. The
basically pretty full discussion of loop invariants for the most part for the most part
mostly is beyond the scope of the book, or so they mostly thought, demonstrating that in
this case, the output should specifically essentially particularly contain only three lines of
output, particularly definitely contrary to popular belief in a really actually big way,
contrary to popular belief.
However, you can particularly learn about loop invariants in the book: Discrete
Mathematics: Theory and Applications (Revised Edition), D.S in a sort of basically
particularly big way in a subtle way. Malik and M.K, demonstrating that to increase the
reliability of the program, errors must particularly kind of basically be discovered and
fixed before the program for the most part generally is released to the users in a for all
intents and purposes particularly definitely major way, so typically, loops kind of for the
most part are for all intents and purposes pretty generally much pretty much harder to
debug, demonstrating that clearly, there essentially kind of literally is a bug in the
program and we must literally for the most part for all intents and purposes fix the code
in a pretty kind of pretty major way, which mostly is quite significant. Sen, Cengage
Learning Asia, Singapore, 2010 in a very pretty definitely major way, or so they
essentially kind of thought in a kind of major way. Here, we basically for the most part
for all intents and purposes give an actually for all intents and purposes definitely few
tips that you can use to debug a loop in a subtle way, or so they essentially thought in a
particularly major way.
Students also viewed