Computer Science 111, Assignment 3
Tutorial on if, if/else, program design, testing, debugging, and garbage values
- Preliminaries
- Introduction to decision-making in programs: The bus problem
- More about assignment statements
- Program development and testing
- Another program development example: Odd or even?
- if/else statements
- if and if/else statements with dependent blocks, and more about return statements in the main function
- The bool data type and a common C++ error
- How to generate line-numbered copies of files, to help you debug
- Program development, testing, and debugging example: Maximum of 3 numbers
- Brief introduction to boolean (logical) operators
- Nested if/else
- More about avoidance of garbage values
- More about indentation
First, to ensure that you have enough disk space, please be sure to get rid of all a.out, l.out, and core files before you begin. To find where they are, type the following at the "forbin>" prompt, in your home directory:
- Preliminaries.
find . | grep a.out find . | grep l.out find . | grep coreThen go to the indicated directories and remove the files.
Then, if you have not done so already, create a directory named hw03 inside your homework directory. Then change your present working directory to hw03 and copy into it the example files for Assignment 3, as follows:
cp ~nixon/cs111/hw03/* .Let's think about how to write a program which calculates how many charter busses are needed to transport a given number of people, with a given number of seats per bus. Our program should do the following:
- Introduction to decision-making in programs: The bus problem.
- Output a user-friendly introductory message.
- Input the total number of people and the number of seats per bus.
- Calculate how many busses are needed.
- Output the result.
By now, you should know how to do steps 1, 2, and 4. But you haven't yet learned how to do step 3. Let's think about what is needed.
Will integer division do the job? Almost, but not quite. Suppose we want to transport 80 people, and our charter busses have 40 seats each. If we do integer division of 80 by 40, our quotient is 2, which is indeed the number of busses we need to transport 80 people. However, suppose we want to transport 110 people using 40-seat busses. In that case, we will need 3 busses, not just 2, because there will be 30 people left over after the first 2 busses are filled.
So, in addition to finding the integer quotient, our program must also check whether there are any people left over, i.e. whether the remainder is nonzero, and then make a decision about whether or not to add 1 to the quotient. If the remainder is nonzero, the program must add 1 to the quotient to get the number of busses needed. On the other hand, if the remainder is zero, then the number of busses needed is just our integer quotient, and we don't need to do anything else.
Example program busses.cpp first does the following integer division:
int bussesNeeded = totalPeople / seatsPerBus; // initial estimateThen, to make the needed decision, our program contains the following if statement:
if ( totalPeople % seatsPerBus > 0 ) bussesNeeded = bussesNeeded + 1;An if statement consists of the word if followed by an expression in parentheses, followed by a statement or block after the right parenthesis. The expression inside the parentheses after the word if is known as a condition. A condition must be an expression which is either true or false. The statement or block after the right parenthesis is executed only if the condition is true.
In C++, the word if is a reserved word, meaning that you cannot use it as the name of a variable or function. For a complete list of C++ reserved words, see Skansholm, Appendix A.
Observe that the statement whose execution is subject to the condition, in this case the assignment statement which increases the value of bussesNeeded, is indented one level further than the if clause and the rest of the statements in the main function. The compiler does not care about indentation. However, human programmers do care about indentation, because good indentation makes a program more readable.
Now take a closer look at the statement dependent on the if clause in busses.cpp:
- More about assignment statements.
bussesNeeded = bussesNeeded + 1;As an algebraic equation, the above statement would not make sense. No number equals itself plus one. However, the above statement is not an equation. It's an assignment statement. Remember what an assignment statement does. It evaluates the expression on the right-hand side of the equals sign, and then puts that value in the memory location named by the variable on the left-hand side of the equals sign. In this case, the expression on the right-hand side takes the value PREVIOUSLY stored in bussesNeeded and adds 1 to it. Then, because bussesNeeded is the variable on the left-hand side, this new value is stored in bussesNeeded.
In C and C++, it is possible to do more than one assignment in one statement. For example:
y = x = 1;where x and y have previously been declared as variables of some numeric data type. This nested assignment statement is executed as follows: First, the variable x is assigned the value 1. Then the entire assignment expression x = 1 is itself treated as the right-hand side of an assignment statement which assigns a value to the variable y. In order for this to work, the assignment x = 1 must not only assign 1 to x, but must also be, itself, an expression with a value that can be assigned to y. The value of an assignment expression is equal to the value of its right-hand side. So, the value of the expression x = 1 is 1.
When writing a program, you should first plan out, informally on paper, the sequence of steps needed to accomplish your aims. Such a sequence of steps is known as an algorithm.
- Program development and testing.
In planning our algorithm for busses.cpp above, we first wrote an outline of a series of steps, then planned out one of the steps in more detail. This approach, first outlining a general series of steps, then refining each step, if necessary, into a series of sub-steps, is known as functional decomposition.
Let's now test busses.cpp to make sure it works.
For our bus program, suitable boundary cases would be:
- When the total number of people is evenly divisible by the number of seats per bus.
- When the total number of people is one greater than a number evenly divisible by the number of seats per bus.
- When the total number of people is one less than a number evenly divisible by the number of seats per bus.
Test our bus program using inputs that fit each of the above three boundary cases.
Let's now think about how to write a program which determines whether a number is odd or even. Our program should do the following:
- Another program development example: Odd or even?
- Output a user-friendly introductory message (as appropriate, depending on the kind of input).
- Input the number to be tested.
- Test whether the number is odd or even.
- Output the result of our test.
Steps 3 and 4 can be thought of as follows:
if ( number is even ) say that the number is even; if ( number is odd ) say that the number is odd;How do we determine whether a number is even? Let's think about what it means for a number to be even. It means that the number is evenly divisible by 2. In other words, the remainder of integer division by 2 is zero. On the other hand, if the number of odd, then the remainder of integer division by 2 is 1.
So then, our program should do the following:
if ( number % 2 == 0 ) // i.e. if number is even cout << number << " is even." << endl; if ( number % 2 == 1 ) // i.e. if number is odd cout << number << " is odd." << endl;Compile oddOrEven1.cpp and run it, and observe that it implements the algorithm we just now developed.
This program is relatively simple to test. It should be tested for an even number and an odd number. Try also testing it for negative numbers and zero as well as positive numbers.
Note the use of the double equals sign ("==") when comparing two expressions to determine whether they have equal value. Do not confuse the double equals sign (used in equality comparisons) with the single equals sign (used in assignment statements).
For a complete list of comparison operators that can be used in conditions, see Skansholm, p. 45.
In oddOrEven1.cpp, observe that the program makes two separate tests to determine whether the number is odd or even:
- if/else statements.
if ( number % 2 == 0 ) // i.e. if number is even cout << number << " is even." << endl; if ( number % 2 == 1 ) // i.e. if number is odd cout << number << " is odd." << endl;The program could make the same decisions more efficiently by checking the value of number % 2 only once, as as we have done in oddOrEven2.cpp:
if ( number % 2 == 0 ) // i.e. if number is even cout << number << " is even." << endl; else cout << number << " is odd." << endl;An if/else statement consists of the reserved word if followed by a condition in parentheses, followed by a statement or block which is executed if, and only if, the condition is true; and then the reserved word else followed by a statement or block which is executed if, and only if, the condition after the word if is false.
An if/else statement is preferable to an if statement when the program needs to choose between two courses of action.
On the other hand, an if statement is preferable when there is nothing to be done in one of the two cases. For example, in busses.cpp, no change needs to be made to the value of bussesNeeded if the remainder is zero. It needs to be changed only if the remainder is nonzero.
In each of the previous examples, there was only one statement to be executed when the condition was true and, in the if/else example, only one statement to be executed when the condition was false. If we want more than one consecutive statement to be executed subject to a condition, then the statements must be placed in a block, enclosed in curly braces.
- if and if/else statements with dependent blocks, and more about return statements in the main function.
See, for example, passOrFail1.cpp. Compile it and run it for the boundary cases of 101, 100, 60, 59, 0, and -1. Also, try it for obviously absurd inputs like 5000 and -457. In these cases, it prints an error message, but then it ALSO states whether your absurd input represents a passing or failing grade. We would like the program to just quit after printing the error message, and NOT state whether an out-of-range input represents a passing or failing grade.
To that end, we can use return statements in the two blocks which check whether the input is too large or too small, as we have done in passOrFail2.cpp. For example:
// Check if score is too low to be a valid score: if ( score < 0 ) { cout << "You entered " << score << " is less than 0." << endl; cout << "It must be in range [0, 100]." << endl; return 1; } // ifA return statement before the end of a function makes the function quit at that point. In passOrFail2.cpp, if score is less than zero, the return statement in the above block makes the program quit after printing out an error message.
What about the value after the word return? Recall that the return 0 statement at the end of the main function indicates, to the operating system, that the program finished successfully. If the main function returns with a nonzero value, as in the above block, this means that the program is being terminated abnormally due to an error.
We have said that a condition is an expression which is either true or false. It is an expression with a value which is either true or false.
- The bool data type and a common C++ error.
Most programming languages have a boolean data type which has two possible values: true and false. In older versions of C++, and in the language C, from which C++ is derived, there was no boolean data type. Instead, int values were used to do the job of booleans, with zero taken to mean false, and all nonzero values taken to mean true. For example, to test whether a remainder is nonzero, you could write:
if ( dividend % divisor ) cout << "Remainder is nonzero." << endl;which means the same thing as:
if ( dividend % divisor != 0 ) cout << "Remainder is nonzero." << endl;In more recent versions of C++, there is now a data type bool. However, for backward compatibility with older C and C++ programs, C++ still allows very easy, automatic conversion (without need for casting) between bool and other data types such as int.
Such easy data type conversion, combined with another C/C++ feature -- assignments as expressions with values -- is a common source of hard-to-debug programmer errors. For example, it is possible to confuse the assignment operator ("=") with the equality operator ("==") without the compiler complaining about it, as follows:
if ( n = 3 ) cout << n << endl;The condition above is always true, no matter what the value of x was before the condition, because the expression x = 3 is an assignment which sets x equal to 3 and has the value 3, which is nonzero and thus interpreted as meaning true.
When you are first learning how to programming, one of your most valuable debugging techniques is a hand-written code trace. To write such a code trace, it helps to have a printout with line numbers. So that you can easily generate line-numbered copies of your source code files, copy the file LineNumberer.class from Ms. Nixon's account into your present working directory, as follows:
- How to generate line-numbered copies of files, to help you debug.
cp ~nixon/java/LineNumberer.class .This is a Java program, not a C++ program, to save disk space. The Java byte code file LineNumberer.class takes up much less disk space than any of the machine code files you've generated for even the simplest "Hello world" C++ program. To run this program, type:
java LineNumberer myProgram.cpp 2replacing myProgram.cpp with the name of the actual source code file of which you want to generate a line-numbered copy. Then the number you type after the source code file should be equal to the number of digits of the largest expected line number. This number is 2 if your source code file has 99 or fewer lines.
Note that a line-numbered copy CANNOT compile. It is useful only for generating code traces, which can be helpful in debugging.
The line-numbered copy will have the filename myProgram.cpp.txt.
Let's now think about how to write a program which finds the largest of three floating-point numbers. This program should:
- Program development, testing, and debugging example: Maximum of 3 numbers.
- Output a user-friendly introductory message (as appropriate, depending on the kind of input).
- Input three floating-point numbers.
- Determine which of the three numbers is the largest.
- Output the largest number.
We will need to develop step 3 in more detail.
To find the largest of 3 numbers, we could first find the largest of the first two numbers, and then compare the largest of the first two numbers to the third number. If the third number is larger than the largest of the first two numbers, then the third number is the largest of all three. Otherwise, if the largest of the first two numbers is larger than the third number, then the largest of the first two numbers is also the largest of all three.
In our example program compare3.cpp, the variable largest is first given the value of the largest of the first two numbers:
float largest; if ( number1 > number2 ) largest = number1; if ( number1 < number2 ) largest = number2;Then, largest is compared to the third number, and the value of largest is then changed, if necessary, so that it is now the largest of all three numbers:
if ( number3 > largest ) largest = number3;To test this program adequately, we will need to test it for valid inputs in all the following cases, in addition to invalid inputs:
- First number largest, third number smallest, second number in between.
- First number largest, second number smallest, third number in between.
- Second number largest, third number smallest, first number in between.
- Second number largest, first number smallest, third number in between.
- Third number largest, first number smallest, second number in between.
- Third number largest, second number smallest, first number in between.
- First number equal to second number, both larger than third number.
- First number equal to second number, both smaller than third number.
- First number equal to third number, both larger than second number.
- First number equal to third number, both smaller than second number.
- Second number equal to third number, both larger than first number.
- Second number equal to third number, both smaller than first number.
If you were to test this program, you would PROBABLY discover that it does not work in case 7 above, and it might not work in case 8 either. However, results are unpredictable in these two cases. Why? Look again at how the first two numbers are compared:
float largest; if ( number1 > number2 ) largest = number1; if ( number1 < number2 ) largest = number2;Here, observe that largest is assigned a value when number1 is either greater than or less than number2, but largest is NOT assigned a value when number1 is equal to number2, If largest is not assigned a value, then the memory location named by that variable contains garbage, i.e. it contains whatever sequence of 1's and 0's happened to be there already before the program was executed. Thus, its contents are unpredictable.
So, if number1 is equal to number2, the resulting garbage value of largest is then compared to number3:
if ( number3 > largest ) largest = number3;with unpredictable results.
This problem could be discovered by doing a code trace, as discussed in lecture. Here are code traces of a line-numbered copy of compare3.cpp for several sample inputs. Observe that the last set of sample inputs satisfies case 7, above. Using pen and paper, do a similar code trace for sample inputs that satisfy case 8.
To fix our problem, we need to make sure that largest does not have a garbage value when it is compared to number3. We need to make sure that largest is assigned a value, in ALL possible cases, before anything is done which assumes that largest already has a value, such as comparing it to another number.
One way to ensure that largest is assigned a value in all possible cases would be as follows:
float largest; if ( number1 > number2 ) largest = number1; if ( number1 <= number2 ) largest = number2;Thus, the equality case is covered by the less-than-or-equals case. Note that we now have two separate if clauses, one of which has a condition that is the exact negation of the other condition. Thus, the second if clause can be replaced by an else, as follows:
float largest; if ( number1 > number2 ) largest = number1; else largest = number2;The else is more efficient, because the machine does one less comparison. An if/else statement is also less error-prone than two separate if statements as a means of ensuring that a value is assigned to largest in every possible case, because an if/else automatically covers all possible cases by covering the two complementary cases of when the condition is true and when the condition is false.
The comparison of largest to number3 will now have predictable results in all cases:
if ( number3 > largest ) largest = number3;Verify this via our code trace for test case 7.
Compile multibranchIfDemo.cpp and run it. This program determines the letter grade corresponding to a percentage score, if the score is in the range [0, 100]. Otherwise, it prints an error message.
- Brief introduction to boolean (logical) operators.
Look now at the source code. After inputting a value into the variable score, the program first checks whether the value is out of range, either less than 0 or greater than 100:
if ( score < 0 || score > 100 ) { cout << "You entered " << score << ", must be in range [0, 100]." << endl; return 1; } // ifThe double vertical bar ("||") means "or."
The program then determines the letter grade as follows:
char grade; if ( score >= 90 ) grade = 'A'; if ( score >= 80 && score < 90 ) grade = 'B'; if ( score >= 70 && score < 80 ) grade = 'C'; if ( score >= 60 && score < 70 ) grade = 'D'; if ( score < 60 ) grade = 'F';The double ampersand ("&&") means "and."
For more about logical operators, see Skansholm, section 2.4, p. 46.
The statement or block whose execution depends on an if clause or an else can be any kind of statement or block, including another if/else statement. Thus we can nest one if/else statement inside another. We will now look at sevaral programs which all use nested if/else in place of the multiple separate if statements in multibranchIfDemo.cpp.
- Nested if/else.
First, let's look at nestedIfElseDemo1.cpp, which uses one big nested if/else to cover all the decisions made by the program. The outermost if/else statement has the following if clause:
if ( score < 0 || score > 100 ) // range checkwhich does the error-checking. Note that, in this program, there is no return statement subject to the if clause. Instead, the entire rest of the program is subject to the else corresponding to the above if clause.
Inside that else block, the next outermost if/else statement has the following if clause:
if ( score >= 60 )Subject to this if clause are further nested if/else statements, where the if clauses at each level of nesting further test the value of score.
Observe the absencs of curly braces in all the nested if/else statements except the outermost one. Curly braces are not needed when there is only one statement dependent on the if clause or the else. Note that an if/else statement is, itself, considered a single statement and thus does not need curly braces surrounding it when it is the ONLY statement dependent on another if clause or another else.
On the other hand, there is more than one statement dependent on the outermost else in this progra;m. Thus, curly braces are needed there.
Curly braces are not required when there is only one dependent statement, but they are permitted in that case. Some programmers prefer always to use curly braces no matter how many dependent statements there are, to avoid forgetting the curly braces in case they need to add another statement later.
Observe how the indentation helps you, the human programmer, keep track of which if/else statement is subject to which other if or else clause, and to keep track of which else corresponds to which if. However, the compiler does NOT use indentation to keep track of these. Instead, the compiler matches each else to the most recent if that is not enclosed in a more deeply nested pair of curly braces.
Look now at nestedIfElseDemo2.cpp, another program containin a nested if/else equivalent of the separate if statements in multibranchIfDemo.cpp, but with the branches reorganized in the part of the program that determinse the grade:
char grade; if ( score >= 90 ) grade = 'A'; else if ( score >= 80 ) grade = 'B'; else if ( score >= 70 ) grade = 'C'; else if ( score >= 60 ) grade = 'D'; else // if ( score < 60 ) grade = 'F';This way of organizing a nested if/else is slightly less efficient than what we saw in nestedIfElseDemo1.cpp,
because, in the nestedIfElseDemo1.cpp version, at most three comparisons are done for any branch of the nested if/else, whereas the version in nestedIfElseDemo2.cpp does four comparisons for the D and F cases. This difference would be even greater for a larger number of cases.Nevertheless, the nestedIfElseDemo2.cpp version, above, is a more readable and much more commonly-used way of organizing a nested if/else statement which is being used to select one of a number of alternatives.
In nestedIfElseDemo2.cpp, the branches of the nested if/else are indented so as to show the level of nesting for each branch. Thus we have rather extreme indentation for the very last branch. With so many levels of indentation, there is danger of running out of room at the deeper levels. This problem is known as "right-crawl." To avoid right-crawl, when a nested if/else is used to select among multiple alternatives, it is most common to indent it in such a way that all the alternatives have equal indentation, as is done in nestedIfElseDemo3.cpp:
char grade; if ( score >= 90 ) grade = 'A'; else if ( score >= 80 ) grade = 'B'; else if ( score >= 70 ) grade = 'C'; else if ( score >= 60 ) grade = 'D'; else // if ( score < 60 ) grade = 'F';To further reduce right-crawl, nestedIfElseDemo3.cpp uses a return statement to make the program quit if the error condition is true, rather than enclosing the entire rest of the program in an else block as is done in nestedIfElseDemo1.cpp and nestedIfElseDemo2.cpp. Also, the return allows the main function to return a nonzero value, thereby informing the operating system that the program terminated abnormally.
It is common, and considered good programming practice, to use return instead of else in cases where the statement or block dependent on the else would encompass the entire rest of the function.
To avoid using garbage values, with unpredictable results, you need to make sure that every variable is given a value, in all possible cases, before it is used in any way which assumes it already has a value. Otherwise, the resulting run-time errors can be extremely difficult to debug.
- More about avoidance of garbage values.
This is one of the reasons why it's best NOT to declare all variables at the top, but rather to declare them as late in your program as possible, no sooner than needed. Then, immediately after any variable is declared, it can be given a value in all possible cases.
When appropriate, it is a good idea to initialize variables, i.e. to combine declarations with assignment statements. If a variable is initialized, it never has a garbage value. However, initialization is not always appropriate.
Note: The word "initialization" means something a bit different in C++ from what it means in other program languages. In C++, "initialization" refers, specifically, to the combination of a variable declaration and an assignment statement. On the other hand, in other programming languages such as Java, to "initialize" a variable means simply to give the variable its first value, regardless of whether this is done in the same statement as the declaration of that variable.
Furthermore, in other programming languages such as Java, the compiler checks to see whether a variable has been initialized (in the sense of being given its first value, in whatever manner) before the variable is used in any way which assumes it has a value. In Java, if the compiler detects a possible case in which a variable has not yet been initialized (in the Java sense) before its assumed value is used, the program will not compile. Thus, in Java, a program which uses garbage values is impossible.
Alas, C++ compilers are not nearly so fussy. Thus, you can indeed write, compile, and run C++ programs which use garbage values, with unpredictable results. In C++, it is your job, not the compiler's job, to make sure that every variable is somehow given an initial value in all possible cases before it used in any other way. The simplest and surest way to accomplish this is via initialization (in the C++ sense). When a variable is not initialized, we need to check the statements immediately following the declaration.
In our corrected version of compare3.cpp, the variable largest receives its first value conditionally, in both branches of an if/else statement. Such a conditional first assignment cannot be combined with a declaration.
In our corrected version of compare3.cpp, try commenting out the declaration before the if/else structure and, instead, declare the variable together with the initial assignments inside the if/else structure, as follows:
// float largest; if ( number1 > number2 ) float largest = number1; else float largest = number2;Try to compile it. As you can see, the compiler does not like this at all. Look carefully at the error message. To find out which line it is complaining about, type something like:
vi +n compare3.cppwith the actual line number instead of n. This opens vi with the cursor at the specified line.
If we declare a variable within a branch of the if/else, then the variable exists only within that branch. After the branch has finished being executed, the variable will no longer exist. So, if we then try to use the variable AFTER the if/else, it is as if the variable was never declared.
In order to use the variable largest after the if/else structure, we must declare it OUTSIDE the if/else structure, not within one or both branches. In order to assign an initial value to the variable largest within each branch of the if/else structure and then use the value afterward, we must declare the variable BEFORE the if/else structure. So, in order for our program to compile, we cannot combine the declaration with the two initial assignments.
When a variable is declared before a branching structure (if, if/else, or nested if/else) and then receives its first value within that structure, we must make sure the variable receives a value in every possible case. One simple way to ensure this is by making sure that, in any branching structure which gives a first value to a variable declared before that structure, (1) the variable is given a value in every possible branch and (2) there is an else for every if in the branching structure, thereby ensuring that the branching structure covers every possible path of execution.
Look now at the following three programs: nestedIfElseDemo2.cpp, nestedIfElseDemo3.cpp, and nestedIfElseDemo4.cpp. Each of these programs uses a nested if/else to determine the letter grade corresponding to a percentage score.
The very last branch handles the case when the score is less than 60. In nestedIfElseDemo2.cpp and nestedIfElseDemo3.cpp, there is no if for this final branch, just an else. (There is a commented-out if to indicate to the human reader which case is handled by the else, but the compiler sees only the else..) Observe that the absence of an if on the last branch ensures that our nested if/else has an else for every if, thereby ensuring that every possible path of execution is covered.
In nestedIfElseDemo4.cpp, the last branch has an explicit if, seen by the compiler. Hence the number of ifs in our nested if/else is one greater than the number of elses. In Java, the nested if/else in nestedIfElseDemo3.cpp would not compile. A Java compiler would complain that the variable grade "may not have been initialized," i.e. there might be some possible path of execution in which grade does not receive a value before being used later. It so happens that, in this particular example, every possible path of execution IS covered anyway, but to determine whether this is true or not would require a much more detailed inspection of the program than a Java compiler performs. So a Java compiler plays it safe, refusing to compile programs which MIGHT result in use of garbage values, according the simplistic analysis which the compiler performs.
Our C++ compiler will NOT reject nestedIfElseDemo4.cpp. But you too should play it safe and make sure you have an else for every if in any branching structure which gives an initial value to a variable which was declared before the branching structure.
Note that it is not necessary, or even desirable, to have an else for every if in EVERY branching structure, but only in those branching structures in which there is something that needs to be done in every possible path of execution, such as giving a first value to a variable declared before the branching structure. There are other kinds of situations where, in some of the possible paths of execution, there is nothing that needs to be done. In such situations, there should NOT be an else for every if.
For example, in compare3.cpp, nothing needs to be done when largest is already greater than or equal to number3. Hence the comparison of largest to number3 is handled by an if, not an if/else,
- More about indentation.
Be careful about indentation! You may lose up to 50 % of your homework grade for lousy indentation, depending on how bad it is.The compiler does not care about indentation. But you should NOT regard indentation as just a minor detail, to be attended to only when you are otherwise finished with your program. Use proper indentation from the very beginning, as you are writing your program. Doing so will make it MUCH easier for you to keep track of what you are doing.
For ease in keeping track of your levels of indentation, you may find it helpful to type each closing curly brace IMMEDIATELY after typing the corresponding opening curly brace, and THEN type the statements that are supposed to go in between the curly braces, rather than waiting to type the closing curly brace until after you have finished typing the statements in between.
So that your program will print out properly, use no more than 68 characters per line. If a line is too long, it will be extended onto the next line in your printout, beginning the extreme left. This looks very ugly.
In your program, if you need to continue a statement onto the next line, in order fit within 68 characters per line, the continuation should be indented to a more extreme degree than what would otherwise be the next level of indentation, to indicate that the continuation is actually part of the preceding statement, rather than a separate indented statement. For examples, see the long cout statements in nearly all of our example programs.
Do NOT use both tabs and spaces for indentation. If you do, you will end up with a program which may look OK in one text editor, but lousy in another, because the number of spaces equal to one tab stop is not the same for all text editors. So, use either tabs or spaces, but NOT BOTH, in one program. Using tabs will save you keystrokes, but will give you a severe right-crawl problem in a program with more than two levels of indentation. So, it is generally better to use spaces. Use two or three spaces -- NOT just one space -- per level of indentation.
Back to: