31.5. Laying Out Individual Statements

 < Free Open Study > 

This section explains many ways to improve individual statements in a program.

Statement Length

A common and somewhat outdated rule is to limit statement line length to 80 characters. Here are the reasons:

Cross-Reference

For details on documenting individual statements, see "Commenting Individual Lines" in Section 32.5.


  • Lines longer than 80 characters are hard to read.

  • The 80-character limitation discourages deep nesting.

  • Lines longer than 80 characters often won't fit on 8.5" x 11" paper, especially when code is printed "2 up" (2 pages of code to each physical printout page).

With larger screens, narrow typefaces, and landscape mode, the 80-character limit appears increasingly arbitrary. A single 90-character-long line is usually more readable than one that has been broken in two just to avoid spilling over the 80th column. With modern technology, it's probably all right to exceed 80 columns occasionally.

Using Spaces for Clarity

Add white space within a statement for the sake of readability:

Use spaces to make logical expressions readable The expression

while(pathName[startPath+position]<>';') and    ((startPath+position)<length(pathName)) do

is about as readable as Idareyoutoreadthis.

As a rule, you should separate identifiers from other identifiers with spaces. If you use this rule, the while expression looks like this:

while ( pathName[ startPath+position ] <> ';' ) and    ( ( startPath + position ) < length( pathName )) do

Some software artists might recommend enhancing this particular expression with additional spaces to emphasize its logical structure, this way:

while ( pathName[ startPath + position ] <> ';' ) and    ( ( startPath + position ) < length( pathName ) ) do

This is fine, although the first use of spaces was sufficient to ensure readability. Extra spaces hardly ever hurt, however, so be generous with them.

Use spaces to make array references readable The expression

grossRate[census[groupId].gender,census[groupId].ageGroup]

is no more readable than the earlier dense while expression. Use spaces around each index in the array to make the indexes readable. If you use this rule, the expression looks like this:

grossRate[ census[ groupId ].gender, census[ groupId ].ageGroup ]

Use spaces to make routine arguments readable What is the fourth argument to the following routine?

ReadEmployeeData(maxEmps,empData,inputFile,empCount,inputError);

Now, what is the fourth argument to the following routine?

GetCensus( inputFile, empCount, empData, maxEmps, inputError );

Which one was easier to find? This is a realistic, worthwhile question because argument positions are significant in all major procedural languages. It's common to have a routine specification on one half of your screen and the call to the routine on the other half, and to compare each formal parameter with each actual parameter.

Formatting Continuation Lines

One of the most vexing problems of program layout is deciding what to do with the part of a statement that spills over to the next line. Do you indent it by the normal indentation amount? Do you align it under the keyword? What about assignments?

Here's a sensible, consistent approach that's particularly useful in Java, C, C++, Visual Basic, and other languages that encourage long variable names:

Make the incompleteness of a statement obvi Sometimes a statement must be broken across lines, either because it's longer than programming standards allow or because it's too absurdly long to put on one line. Make it obvious that the part of the statement on the first line is only part of a statement. The easiest way to do that is to break up the statement so that the part on the first line is blatantly incorrect syntactically if it stands alone. Some examples are shown in Listing 31-37:

Listing 31-37. Java examples of obviously incomplete statements
 while ( pathName[ startPath + position ] != ';' ) &&       <-- 1    ( ( startPath + position ) <= pathName.length() ) ... totalBill = totalBill + customerPurchases[ customerID ] +       <-- 2    SalesTax( customerPurchases[ customerID ] ); ... DrawLine( window.north, window.south, window.east, window.west,       <-- 3    currentWidth, currentAttribute ); ... 

(1)The && signals that the statement isn't complete.

(2)The plus sign (+) signals that the statement isn't complete.

(3)The comma (,) signals that the statement isn't complete.

In addition to telling the reader that the statement isn't complete on the first line, the break helps prevent incorrect modifications. If the continuation of the statement were deleted, the first line wouldn't look as if you had merely forgotten a parenthesis or semicolon it would clearly need something more.

An alternative approach that also works well is to put the continuation character at the beginning of the continuation line, as shown in Listing 31-38.

Listing 31-38. Java examples of obviously incomplete statements alternate style
while ( pathName[ startPath + position ] != ';' )    && ( ( startPath + position ) <= pathName.length() ) ... totalBill = totalBill + customerPurchases[ customerID ]    + SalesTax( customerPurchases[ customerID ] );

While this style won't induce a syntax error with a hanging && or +, it does make it easier to scan for operators at the left edge of the column, where the text is aligned, than at the right edge, where it's ragged. It has the additional advantage of illuminating the structure of operations, as illustrated in Listing 31-39.

Listing 31-39. Java example of a style that illuminates complex operations
totalBill = totalBill    + customerPurchases[ customerID ]    + CitySalesTax( customerPurchases[ customerID ] )    + StateSalesTax( customerPurchases[ customerID ] )    + FootballStadiumTax()    - SalesTaxExemption( customerPurchases[ customerID ] );

Keep closely related elements together When you break a line, keep things together that belong together: array references, arguments to a routine, and so on. The example shown in Listing 31-40 is poor form:

Java example of breaking a line poorly

Listing 31-40.

customerBill = PreviousBalance( paymentHistory[ customerID ] ) +  LateCharge(    paymentHistory[ customerID ] );


Admittedly, this line break follows the guideline of making the incompleteness of the statement obvious, but it does so in a way that makes the statement unnecessarily hard to read. You might find a case in which the break is necessary, but in this case it isn't. It's better to keep the array references all on one line. Listing 31-41 shows better formatting:

Listing 31-41. Java example of breaking a line well
 customerBill = PreviousBalance( paymentHistory[ customerID ] ) +    LateCharge( paymentHistory[ customerID ] );

Indent routine-call continuation lines the standard amount If you normally indent three spaces for statements in a loop or a conditional, indent the continuation lines for a routine by three spaces. Some examples are shown in Listing 31-42:

Listing 31-42. Java examples of indenting routine-call continuation lines using the standard indentation increment
DrawLine( window.north, window.south, window.east, window.west,    currentWidth, currentAttribute ); SetFontAttributes( faceName[ fontId ], size[ fontId ], bold[ fontId ],    italic[ fontId ], syntheticAttribute[ fontId ].underline,    syntheticAttribute[ fontId ].strikeout );

One alternative to this approach is to line up the continuation lines under the first argument to the routine, as shown in Listing 31-43:

Listing 31-43. Java examples of indenting a routine-call continuation line to emphasize routine names
DrawLine( window.north, window.south, window.east, window.west,           currentWidth, currentAttribute ); SetFontAttributes( faceName[ fontId ], size[ fontId ], bold[ fontId ],                    italic[ fontId ], syntheticAttribute[ fontId ].underline,                    syntheticAttribute[ fontId ].strikeout );

From an aesthetic point of view, this looks a little ragged compared to the first approach. It is also difficult to maintain as routine names change, argument names change, and so on. Most programmers tend to gravitate toward the first style over time.

Make it easy to find the end of a continuation line One problem with the approach shown above is that you can't easily find the end of each line. Another alternative is to put each argument on a line of its own and indicate the end of the group with a closing parenthesis. Listing 31-44 shows how it looks.

Listing 31-44. Java examples of formatting routine-call continuation lines one argument to a line
DrawLine(    window.north,    window.south,    window.east,    window.west,    currentWidth,    currentAttribute ); SetFontAttributes(    faceName[ fontId ],    size[ fontId ],    bold[ fontId ],    italic[ fontId ],    syntheticAttribute[ fontId ].underline,    syntheticAttribute[ fontId ].strikeout );

Obviously, this approach takes up a lot of real estate. If the arguments to a routine are long object-field references or pointer names, however, as the last two are, using one argument per line improves readability substantially. The ); at the end of the block makes the end of the call clear. You also don't have to reformat when you add a parameter; you just add a new line.

In practice, usually only a few routines need to be broken into multiple lines. You can handle others on one line. Any of the three options for formatting multiple-line routine calls works all right if you use it consistently.

Indent control-statement continuation lines the standard amount If you run out of room for a for loop, a while loop, or an if statement, indent the continuation line by the same amount of space that you indent statements in a loop or after an if statement. Two examples are shown in Listing 31-45:

Listing 31-45. Java examples of indenting control-statement continuation lines
 while ( ( pathName[ startPath + position ] != ';' ) &&    ( ( startPath + position ) <= pathName.length() ) ) {       <-- 1    ... } for ( int employeeNum = employee.first + employee.offset;    employeeNum < employee.first + employee.offset + employee.total;       <-- 2    employeeNum++ ) {       <-- 2    ... } 

(1)This continuation line is indented the standard number of spaces…

(2)…as is this one.

This meets the criteria set earlier in the chapter. The continuation part of the statement is done logically it's always indented underneath the statement it continues. The indentation can be done consistently it uses only a few more spaces than the original line. It's as readable as anything else, and it's as maintainable as anything else. In some cases you might be able to improve readability by fine-tuning the indentation or spacing, but be sure to keep the maintainability tradeoff in mind when you consider fine-tuning.

Cross-Reference

Sometimes the best solution to a complicated test is to put it into a boolean function. For examples, see "Making Complicated Expressions Simple" in Section 19.1.


Do not align right sides of assignment statements In the first edition of this book I recommended aligning the right sides of statements containing assignments as shown in Listing 31-46:

Listing 31-46. Java example of endline layout used for assignment-statement continuation bad practice
customerPurchases = customerPurchases + CustomerSales( CustomerID ); customerBill      = customerBill + customerPurchases; totalCustomerBill = customerBill + PreviousBalance( customerID ) +                     LateCharge( customerID ); customerRating    = Rating( customerID, totalCustomerBill );

With the benefit of 10 years' hindsight, I have found that, while this indentation style might look attractive, it becomes a headache to maintain the alignment of the equals signs as variable names change and code is run through tools that substitute tabs for spaces and spaces for tabs. It is also hard to maintain as lines are moved among different parts of the program that have different levels of indentation.

For consistency with the other indentation guidelines as well as maintainability, treat groups of statements containing assignment operations just as you would treat other statements, as Listing 31-47 shows:

Listing 31-47. Java example of standard indentation for assignment-statement continuation good practice
customerPurchases = customerPurchases + CustomerSales( CustomerID ); customerBill = customerBill + customerPurchases; totalCustomerBill = customerBill + PreviousBalance( customerID ) +    LateCharge( customerID ); customerRating = Rating( customerID, totalCustomerBill );

Indent assignment-statement continuation lines the standard amount In Listing 31-45, the continuation line for the third assignment statement is indented the standard amount. This is done for the same reasons that assignment statements in general are not formatted in any special way: general readability and maintainability.

Using Only One Statement Per Line

Modern languages such as C++ and Java allow multiple statements per line. The power of free formatting is a mixed blessing, however, when it comes to putting multiple statements on a line. This line contains several statements that could logically be separated onto lines of their own:

i = 0; j = 0; k = 0; DestroyBadLoopNames( i, j, k );

One argument in favor of putting several statements on one line is that it requires fewer lines of screen space or printer paper, which allows more of the code to be viewed at once. It's also a way to group related statements, and some programmers believe that it provides optimization clues to the compiler.

These are good reasons, but the reasons to limit yourself to one statement per line are more compelling:

  • Putting each statement on a line of its own provides an accurate view of a program's complexity. It doesn't hide complexity by making complex statements look trivial. Statements that are complex look complex. Statements that are easy look easy.

  • Putting several statements on one line doesn't provide optimization clues to modern compilers. Today's optimizing compilers don't depend on formatting clues to do their optimizations. This is illustrated later in this section.

    Cross-Reference

    Code-level performance optimizations are discussed in Chapter 25, "Code-Tuning Strategies," and Chapter 26, "Code-Tuning Techniques."


  • With statements on their own lines, the code reads from top to bottom, instead of top to bottom and left to right. When you're looking for a specific line of code, your eye should be able to follow the left margin of the code. It shouldn't have to dip into each and every line just because a single line might contain two statements.

  • With statements on their own lines, it's easy to find syntax errors when your compiler provides only the line numbers of the errors. If you have multiple statements on a line, the line number doesn't tell you which statement is in error.

  • With one statement to a line, it's easy to step through the code with line-oriented debuggers. If you have several statements on a line, the debugger executes them all at once and you have to switch to assembler to step through individual statements.

  • With one to a line, it's easy to edit individual statements to delete a line or temporarily convert a line to a comment. If you have multiple statements on a line, you have to do your editing between other statements.

In C++, avoid using multiple operations per line (side effects) Side effects are consequences of a statement other than its main consequence. In C++, the ++ operator on a line that contains other operations is a side effect. Likewise, assigning a value to a variable and using the left side of the assignment in a conditional is a side effect.

Side effects tend to make code difficult to read. For example, if n equals 4, what is the printout of the statement shown in Listing 31-48?

Listing 31-48. C++ example of an unpredictable side effect
PrintMessage( ++n, n + 2 );

Is it 4 and 6? Is it 5 and 7? Is it 5 and 6? The answer is "None of the above." The first argument, ++n, is 5. But the C++ language does not define the order in which terms in an expression or arguments to a routine are evaluated. So the compiler can evaluate the second argument, n + 2, either before or after the first argument; the result might be either 6 or 7, depending on the compiler. Listing 31-49 shows how you should rewrite the statement so that the intent is clear:

Listing 31-49. C++ example of avoiding an unpredictable side effect
++n; PrintMessage( n, n + 2 );

If you're still not convinced that you should put side effects on lines by themselves, try to figure out what the routine shown in Listing 31-50 does:

Listing 31-50. C example of too many operations on a line
strcpy( char * t, char * s ) {    while ( *++t = *++s )       ; }

Some experienced C programmers don't see the complexity in that example because it's a familiar function. They look at it and say, "That's strcpy()." In this case, however, it's not quite strcpy(). It contains an error. If you said, "That's strcpy()" when you saw the code, you were recognizing the code, not reading it. This is exactly the situation you're in when you debug a program: the code that you overlook because you "recognize" it rather than read it can contain the error that's harder to find than it needs to be.

The fragment shown in Listing 31-51 is functionally identical to the first and is more readable:

Listing 31-51. C example of a readable number of operations on each line
strcpy( char * t, char * s ) {    do {       ++t;       ++s;       *t = *s;    }    while ( *t != '\0' ); }

In the reformatted code, the error is apparent. Clearly, t and s are incremented before *s is copied to *t. The first character is missed.

The second example looks more elaborate than the first, even though the operations performed in the second example are identical. The reason it looks more elaborate is that it doesn't hide the complexity of the operations.

Improved performance doesn't justify putting multiple operations on the same line either. Because the two strcpy() routines are logically equivalent, you would expect the compiler to generate identical code for them. When both versions of the routine were profiled, however, the first version took 4.81 seconds to copy 5,000,000 strings and the second took 4.35 seconds.

Cross-Reference

For details on code tuning, see Chapter 25, "Code-Tuning Strategies," and Chapter 26, "Code-Tuning Techniques."


In this case, the "clever" version carries an 11 percent speed penalty, which makes it look a lot less clever. The results vary from compiler to compiler, but in general they suggest that until you've measured performance gains, you're better off striving for clarity and correctness first, performance second.

Even if you read statements with side effects easily, take pity on other people who will read your code. Most good programmers need to think twice to understand expressions with side effects. Let them use their brain cells to understand the larger questions of how your code works rather than the syntactic details of a specific expression.

Laying Out Data Declarations

Use only one data declaration per line As shown in the previous examples, you should give each data declaration its own line. It's easier to put a comment next to each declaration if each one is on its own line. It's easier to modify declarations because each declaration is self-contained. It's easier to find specific variables because you can scan a single column rather than reading each line. It's easier to find and fix syntax errors because the line number the compiler gives you has only one declaration on it.

Cross-Reference

For details on documenting data declarations, see "Commenting Data Declarations" in Section 32.5. For aspects of data use, see Chapters 10 through 13.


Quickly in the data declaration in Listing 31-52, what type of variable is currentBottom?

C++ example of crowding more than one variable declaration onto a line

Listing 31-52.

int rowIndex, columnIdx; Color previousColor, currentColor,  nextColor; Point previousTop, previousBottom, currentTop, currentBottom, nextTop,  nextBottom; Font previousTypeface, currentTypeface, nextTypeface; Color choices[  NUM_COLORS ];


This is an extreme example, but it's not too far removed from a much more common style shown in Listing 31-53:

C++ example of crowding more than one variable declaration onto a line

Listing 31-53.

int rowIndex, columnIdx; Color previousColor, currentColor, nextColor; Point previousTop, previousBottom, currentTop, currentBottom,  nextTop, nextBottom; Font previousTypeface, currentTypeface, nextTypeface; Color choices[ NUM_COLORS ];


This is not an uncommon style of declaring variables, and the variable is still hard to find because all the declarations are jammed together. The variable's type is hard to find, too. Now, what is nextColor's type in Listing 31-54?

Listing 31-54. C++ example of readability achieved by putting only one variable declaration on each line
int rowIndex; int columnIdx; Color previousColor; Color currentColor; Color nextColor; Point previousTop; Point previousBottom; Point currentTop; Point currentBottom; Point nextTop; Point nextBottom; Font previousTypeface; Font currentTypeface; Font nextTypeface; Color choices[ NUM_COLORS ];

The variable nextColor was probably easier to find than nextTypeface was in Listing 31-53. This style is characterized by one declaration per line and a complete declaration, including the variable type, on each line.

Admittedly, this style chews up a lot of screen space 20 lines instead of the three in the first example, although those three lines were pretty ugly. I can't point to any studies that show that this style leads to fewer bugs or greater comprehension. If Sally Programmer, Jr., asked me to review her code, however, and her data declarations looked like the first example, I'd say "No way too hard to read." If they looked like the second example, I'd say "Uh…maybe I'll get back to you." If they looked like the final example, I would say "Certainly it's a pleasure."

Declare variables close to where they're first used A style that's preferable to declaring all variables in a big block is to declare each variable close to where it's first used. This reduces "span" and "live time" and facilitates refactoring code into smaller routines when necessary. For more details, see "Keep Variables "Live" for as Short a Time as Possible" in Section 10.4.

Order declarations sensibly In Listing 31-54, the declarations are grouped by types. Grouping by types is usually sensible since variables of the same type tend to be used in related operations. In other cases, you might choose to order them alphabetically by variable name. Although alphabetical ordering has many advocates, my feeling is that it's too much work for what it's worth. If your list of variables is so long that alphabetical ordering helps, your routine is probably too big. Break it up so that you have smaller routines with fewer variables.

In C++, put the asterisk next to the variable name in pointer declarations or declare pointer types It's common to see pointer declarations that put the asterisk next to the type, as in Listing 31-55:

Listing 31-55. C++ example of asterisks in pointer declarations
EmployeeList* employees; File* inputFile;

The problem with putting the asterisk next to the type name rather than the variable name is that, when you put more than one declaration on a line, the asterisk will apply only to the first variable even though the visual formatting suggests it applies to all variables on the line. You can avoid this problem by putting the asterisk next to the variable name rather than the type name, as in Listing 31-56:

Listing 31-56. C++ example of using asterisks in pointer declarations
EmployeeList *employees; File *inputFile;

This approach has the weakness of suggesting that the asterisk is part of the variable name, which it isn't. The variable can be used either with or without the asterisk.

The best approach is to declare a type for the pointer and use that instead. An example is shown in Listing 31-57:

Listing 31-57. C++ example of good uses of a pointer type in declarations
EmployeeListPointer employees; FilePointer inputFile;

The particular problem addressed by this approach can be solved either by requiring all pointers to be declared using pointer types, as shown in Listing 31-57, or by requiring no more than one variable declaration per line. Be sure to choose at least one of these solutions!

 < Free Open Study > 


Code Complete
Code Complete: A Practical Handbook of Software Construction, Second Edition
ISBN: 0735619670
EAN: 2147483647
Year: 2003
Pages: 334

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net