Guide to a Successful Programming Style
Good style is like touch typing: it may seem counter-productive at first,
but the initial effort will pay enormous dividends. After a little while, the
elements of good style will be second nature. In addition to all the previously
mentioned attributes that accrue from using good style, you will find that
utilizing good (consistent) programming style will assist you in creating error-free
programs.
Readability
There are a number of stylistic elements which can make a program more readable,
including the use of horizontal and vertical spacing, the conventions used in
declarations, etc. Each of these elements will be discussed in turn.
Keep in mind that once a program (function) is written, it is seldom read
from top to bottom. Programmers in correcting or modifying a program often
skip large blocks of text/code in order to find what they are looking for.
A good analogy can be made to a dictionary. Imagine if the words in a dictionary
were written in normal English run-on (stream) prose style, as this handout
is. What if the dictionary were not alphabetized? What if the words being defined
did not appear in boldface?
As a good programmer you should strive to enhance the visual appearance of
the code you write. The effort you put into this will begin to pay dividends
as you eliminate errors from your code.
Indentation
Indentation is used to enable a reader to determine the statement nesting level
at a glance. In order to be useful, indentation must be consistent, the number
of spaces used per indentation level should be either 3, 4, or 5 spaces, and
the same style of indentation should be used throughout the program. Proper indentation
makes your program much easier to debug.
Fortunately the C++ emacs programming environment automatically handles indentation
for us. For good examples of the use of indentation, the interested reader
is referred to the sample lab solutions placed on the dept. server. (i.e. perfectNums.C)
Spaces
Normally in programming, the minimum acceptable standard for the use of spaces
is that you follow normal English rules. This means that most of the basic symbols
in C++ (e.g., ``*'', ``+'', etc.) should have at least one
space before and one space after them, with the exceptions being that no space
appears before a comma. More than one space may be used if you are aligning things
on adjacent lines.
Blank Lines
Blank lines should be used to separate long, logically related blocks. This means
that the variable initialization, module documentation, and main code sections
should be separated by at least one blank line. Functions should be separated
by at least two blank lines. Similarly, within a long function body, groups of
related statements may be separated from other groups by a blank line. To be
effective as an element of style, blank lines should be used consistently.
Statements
Each statement should appear on a separate line; with very long lines continued
on the next line indented appropriately.
Declarations
Variables should by accompanied by a documenting comment describing its use.
These comments should appear on the same line as the initial variable initializing
assignment statement.
Comments
In the real world both maintenance programmers and other members of a programming
team rely on comments to explain the program, function or code fragment that
they are reading. The comments should be brief, accurate and very precise.
If a comment and the code disagree, the comment is presumed to be correct, and
the code incorrect. Comments are used primarily to state what the code
is doing (its purpose), while the code itself describes how you are
doing it. Thus, it is only common sense that you should write the comment first
(i.e., define what you are doing) before you write the code.
Comments should always start and end on the same line. This makes it easier
to tell if a statement is commented out, and removes the problem of unclosed
comments. Also, never put a comment in the middle of a statement or declaration
unless you are commenting out a part of the statement or declaration.
Comments fall into one of the following groups:
- Program prologue comments.
- Function prologue comments.
- Declaration comments.
- Code or statement comments.
Each of these will be discussed in turn.
Program Prologue
The main goal of a program prologue is to explain the purpose of the
program/class. A program prologue is similar to a function prologue and includes
the following sections (the first three are particular to student projects):
- Your name.
- Date (or semester and year).
- Class and professor's name.
- Purpose of the program/class, i.e., an explanation of what the program/class
does.
- Program input.
- Program output.
- A description of what data structures are used. This section is optional,
depending on need.
- Algorithm: a general description or outline of the processing done. This
section is optional, depending on need.
- Limitations or restrictions: what assumptions are made about the input data;
under what conditions the program or unit fails to operate properly, etc.
- Modification history: who has modified the program, when, and why. This
section is normally started once the program or unit goes into production;
thus it seldom appears in student programs.
Function Prologue
The major reason for a function prologue is to explain the purpose of the function.
A prologue should normally include the following sections:
- Explanation of parameters: these normally appear immediately after the
parameter names or in the function prologue .
- Purpose of the function: i.e., what the function does.
- Algorithm: i.e., how the function does what it does. If a standard
algorithm such as Quicksort is used, a reference rather than an explanation
is preferred.
- Limitations or restrictions: what assumptions the subprogram makes about
its data; under what conditions the subprogram fails to operate properly, etc.
(A precise description of the preconditions and postconditions for the subprogram.)
void PrintMembers (int memberList[], int noOfMembers) {
/****************************************************************
Purpose: Prints a list of active club members to the screen.
Algorithm: For each item in memberList [0 .. (noOfMembers - 1)],
checks whether or not the current record represents an
active member. If active, then displays the member;
otherwise does nothing.
Preconditions: Describe appropriate preconditions for the
input parameters.
Postconditions: Describe appropriate postconditions for the
function.
Parameters: memberList: array of club members
noOfMembers: number of members
***************************************************************
}
Declaration Comments
Constants and variables are always commented with short, precise comments
giving their purpose. These comments normally follow the variable initialization
on the same line and only rarely take more than a line or two. Similarly, function
parameters are always commented.
Statement Comments
Statement comments generally fall into one of the following groups:
- Comments which explain a block of code: such a comment should precede
the code itself and should be indented one indentation level. The entire
block of code including the comment should be separated from other blocks
of code by a blank line.
- Comments which explain a statement: such a comment should follow the statement
on the same line. Such comments are common in assembly code.
-
end comments: such a comment uniquely identifies
the component which is being ended and is essential in finding the matching while,
if, for, or function. In the case of a function, the
comment contains the name of the handler or function.
The first two types of comments can obscure the code which is being commented
and if not used properly can have a negative effect on the readability of the
code. Thus, code block comments and statement comments should be created carefully.
Before adding such comments you should first attempt to make the code itself
more understandable, by improving the identifier names used, by replacing groups
of statements with subprogram calls, by reducing the control complexity of
the code, etc.
The Structure of Names
In a language like C++, names must be given to files, classes, functions, variables
and constants. Usually the sequence in which the names of various entities are
chosen is random. This should not be the case. There is a clear advantage in
choosing the names in a certain order. The remainder of this section discusses
the structure of names for the different objects. After that it will become clear
that the proposed sequence in naming is preferable.
Function names
A function is (literally) called by its name which stands for a group of statements
to be executed. Therefore the name should express the implied action (``Do This'')
by including a verb.
Function names should always be capitalized. (Which will differentiate them
from variable names.)
Examples: StoreWord, DisplayError, ShowPrinterStatus, PrintPage,
InvertMenuTitle, OpenWindow, DrawLine, PrintAddress, GetFirstElement,
CheckMachineState, FindName.
Variable names
Variable As with all identifiers, identifiers that are the concatenation of two
or more English words should have the first letter of all words after the first
capitalized.
Examples: firstState, nextState, lastElement, bigWindow, homeAddress,
runningTitle, headPointer, titlePage, endOfList, maxLength, currentSymbol,
optimalLevel, screenWidth.
Constants
Constants often describe a limit within a program. In these cases it is appropriate
to use the prefix ``Max'' in conjunction with the type name. Otherwise treat
the names for constants like variable names.
Constants should always appear in all capital letters.
Examples: MAXLINELENGTH, MAXLINESPERPAGE, MAXNOOFELEMENTS, MINWINDOWWIDTH,
FASTCLICK, SLOWCLICK, DEFAULTREPEATRATE.
General Hints on Naming
The most important criterion when choosing a name is: how easily another
programmer can understand the program (and not just yourself). If understanding
a name was not important we could name the variables a, b, i, j, x, y,
etc.
- Names must be pronounceable. Use long names without truncating the name.
As a ``rule of mouth'': if you cannot read the name out loud, it is not a
good name.
Examples: groupID instead of grpID, nameLength and
not namln, PowersOfTwo and not PwrsOf2, ResetPrinter and
not RstPrt.
- Use capitalization to mark the beginning of a new word within a name.
This makes the name easier to parse. (The use of underscores is an alternative
convention. Studies have also indicated that this style is slightly less
readable.)
Examples: latestEntry, nextState, topOfStack, or latest_entry,
next_state,
top_of_stack.
- Abbreviate with care. Abbreviations always carry the risk of being
misunderstood; does
termProcess mean terminateProcess or terminalProcess?
Usually they are also hard to pronounce (i.e.nxtGrp). Use only
commonly know abbreviations, like the ID in processID.
You should also only abbreviate a name if it saves more than three characters.
Examples: error and not err, name and not nam,
but maxLineLength is probably better than maximumLineLength.
- Boolean variables should state a fact that can be true or false.
This is easy to achieve with the inclusion of ``is'' in the name.
Examples: printerIsReady, queueIsEmpty, or simply done.
Note how naturally this reads:
if printerIsReady {
- The more important an object is, the more care should
go into choosing its name.
Miscellaneous C++ Style Conventions
Few numeric constants should
appear in your code, other than 0, 1, ' ',
and nil. (i.e. No magic numbers). All other constants should be declared and
named as a CONTSTANT.
All selection and iteration constructs should use a begin brace ({) and an
end brace (}) even if only one statement is placed inside the braces.