Style guide
Course Assignment Style Guide
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. Good style will help you to debug your programs more easily, and also make them more easily readable by your instructor and the grader.
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 is written, it is seldom read from top to bottom. While debugging or modifying a program, programmers often skip large blocks of text 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 style, as is this article. 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 in will begin to pay dividends as you debug your code.
Indentation
Indentation is used to enable a reader to determine the nesting level of a statement at a glance. In order to be useful, indentation must be consistent - the number of spaces used per indentation level should be between 3 and 5 - and the same style of indentation should be used throughout the program. Proper indentation makes your program much easier to debug.
Spaces
Normally in programming the standard for the use of spaces is that you follow normal English rules. This means that:
Most basic symbols in C++ (e.g., “=”, “+”, etc.) should have at least one space before and one space after them, with the following notable exceptions:
- No space appears before a comma or a semicolon.
- No space appears before or after a period.
- No space appears between unary operators and their operands (e.g. "++").
Code Snippet 1: An Example of Good Spacing Practices:
Blank Lines
Blank lines should be used to separate long, logically related blocks of code. Specifically:
- In the global section of a compilation unit, the include, const, typedef, and variable declaration sections should be separated by at least one blank line.
- Within a long piece of code, 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.
Refer to the example in Section A.6 for an illustration of these guidelines.
Statements
- Each statement should appear on a separate line.
- The opening brace following a control statement such as if or while should appear on the same line as the if or while, and the closing brace should appear on its own line, lined up with the left of the control statement. As an example, see the for loop in Figure A.1. In contrast, both the opening and closing braces for a function should appear unindented on lines by themselves.
- The statements within a
{....}
pair are indented relative to the braces. Again, see Figure A.1 for an example. - Even if only a single statement falls in the body of a compound statement, it is indented on a separate line.
Declarations
- Variables should be listed one per line, with the type of the variable preceding every declaration. Do not put blank lines between identifiers being declared. The same rules apply to fields declared within a struct. As an example, refer to Figure A.2.
- Variables should be grouped functionally. In other words, related identifiers should be grouped together in the declaration section.
Code Snippet 2: An Example of Variable Declarations:
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 they are reading. 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 fall into one of the following groups:
- Function prologue comments
- Program prologue comments
- Declaration comments
- Sidebar comments
- In-line comments
Function Prologue
See Section User-defined Functions for a discussion of function prologues.
Program Prologue
The major function of a program prologue is to explain the purpose of the program. A program prologue is similar to a function prologue and includes the following sections, following the name of the file (the first three are particular to student projects):
- Your name.
- Date (or semester and year).
- Class and professor's name.
- Purpose: an explanation of what the program does.
- Algorithm: a general description or outline of the processing done.
- Program input.
- Program output.
- A description of the data structures used. 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 goes into production; thus it seldom appears in student programs.
Code Snippet 3: An Example of a Program Prologue:
Declaration Comments
- Constants and variables are always commented with short, precise comments stating their purpose. These comments normally follow the declaration on the same line and only rarely take more than a line or two. See Code Snippet 2 for some examples.
- It is usually not necessary to comment types, whether global or local (although they are commented in our example program). However, fields within a struct are always
Sidebar Comments
A sidebar comment is one which explains a single statement and should follow the statement on the same line. The comment should be brief, accurate and precise. See Figure A.4 for some examples. Such comments are common in assembly code.
Code Snippet 4: An Example of Sidebar Comments:
A sidebar comment should always be used after a closing brace to uniquely identify the compound statement which is being ended, and is essential in finding the matching opening brace. In the case of a function, the comment contains the name of the function.
In-line Comments
In-line comments explain a block of code. Such a comment should precede the code itself and should be indented the same as the block it describes. A blank line should be placed before the in-line comment, between the in-line comment and the block of code, and after the block of code to separate it from the next block of code. See Figure A.5 for an example of when to use in-line comments.
Code Snippet 5: An Example of In-line Comments:
Sidebar comments and in-line comments should be used sparingly. Before adding such comments you should first attempt to make the code itself more understandable by improving the identifier names used, replacing groups of statements with function calls, reducing the control complexity of the code, etc.
Naming & Identifiers
When choosing names/identifiers for items in your program, being descriptive is very important as it will help others to understand what the program is doing. In general, you should avoid using small non-descriptive identifiers such as i
, var
, etc. as these will make it difficult to interpret your program.
Function Identifiers
See the section on User-defined Functions for a discussion of function naming.
Variable Identifiers
In strongly typed languages, a variable is of a particular type. Therefore the structure "adjective + type name" for variable names is an obvious suggestion. (The name does not have to contain an adjective; alternatively, a name can take on the more general form "qualified type name".) Note that this naming convention is not always appropriate however, especially when variables have standard types. For instance, if your program contains an integer variable which stores the length of a list, it would be much better to choose listLength or just length over lengthInt! We use snake_case for variable names in this class, where the all letters are lowercase and words are separated by an underscore (_
).
Some Examples of good variable names: response
,length
,current_entry
,head_pointer
,current_symbol
.
Constant Identifiers
All non-trivial constant values in a program should be assigned names. Constants often describe a limit within a program. In these cases it is appropriate to use the prefix MAX or MIN in conjunction with the type name. Otherwise, name constants like variable names. Names for constants should be in all capital letters with underscores between words.
Some Examples of good constant names: MAX_FRIENDS
,PI
,BLANK
,MAX_LINE_LENGTH
,TAX_RATE
,LOWER_LIMIT
.
General Hints for Identifiers
The most important criterion when choosing a name is: how easily can another programmer (not just yourself) understand the program? If understanding a name was not important we could just name variables a, b, etc. Here are some additional pointers on how to choose names in a program.
- Names must be pronounceable. You should opt to use untruncated, long names over using names that are not pronounceable. As a "rule of mouth", if you cannot read the name out loud, it is not a good name. Examples:
group_id
instead ofgrp_id
,name_length
instead ofnam_ln
,powers_of_two
instead ofpwrs_of_2
. - Abbreviate with care. Abbreviations always carry the risk of being misunderstood. For example, does termProcess mean terminateProcess or terminalprocess? Abbreviations are usually also hard to pronounce (for example, nxtGrp). Use only commonly known abbreviations, like the ID in processID. As a general rule of thumb, you should only abbreviate a name if it saves more than three characters. *Examples:
error
instead oferr
,name
instead ofnam
, butmax_line_length
is probably better thanmaximum_line_length
. - Do not use names whose only difference is capitalization. C is case sensitive, so the name
groupID
is different from the namegroupId
. If two names in the same program only differ in capitalization, typographical mistakes can create errors that are very difficult to track down. - Boolean variable and function names 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 (queueIsEmpty) Insert(item);
- The more important (read global) an object is, the more care should go into choosing its name. In a short function, a variable like
ok
is probably fine since "what is OK" is probably easily determined from the context. However, this is most likely not the case with a global variable. Thus, the most care should be taken when naming global variables in a program, followed by field names within a record, and finally variables in a function.
User-defined Functions
In this section, we consolidate all the style guidelines relevant to function declarations. An example illustrating good formatting practices is shown in the code snippet below.
Code Snippet 6: An Example of a Well-Formatted Function Declaration:
Function Prologues
The major reason for a function prologue is to explain the purpose of the function. A function prologue should appear just before the implementation of the function and include the following sections:
- Function name and parameter list: just as it appears later in the actual code.
- Purpose: what the function does.
- Algorithm: how the function does what it does. If a standard algorithm such as Quicksort is used, a reference rather than an explanation is preferred.
- Input and Output: what the function will expect from the user and what the user will see on the screen.
- Precondition: what assumptions the routine makes about its data; under what conditions the routine fails to operate properly, etc.
- Postcondition: what should be true after the routine is finished. An explanation of the return value, if any, should also be included.
Any of the these items except the first two may be left out if they are inappropriate. For example, if the function does not expect any input, leave out the input section. See Code Snippet 6 for an example of a function prologue.
Function Names
Functions should be named differently depending upon whether they return a value. A void function is (literally) called by its name, which stands for a group of statements to be executed. Therefore the name of a void function should express the implied action ("do this") by including an imperative verb. Since functions operate on a specific type, the structure "verb + type name" is best suited for a function name. Function names should be capitalized like type names.
Examples of good function names: GetEntry
,DisplayError
,PrintAddress
,GetFirstElement
,FindName
.
Functions that return a value should contain nouns or adjectives. Again, since these types of functions operate on a specific type, the form "adjective + type name" or "noun + type name" are good choices.
Examples of good function names that return a value: GreatestItem
,CubeRoot
,LastNode
,HeadOfList
,IsEmpty
.
Formatting Function Declarations
The following guidelines apply to function declarations and prototypes. All should be illustrated in Code Snippet 6.
- When declaring functions, the leading parenthesis and first parameter (if any) are to be written on the same line as the function name. Then, each subsequent parameter should be listed on a separate line to allow for each to be commented.
- Each function parameter is always commented on the same line as the declaration.
- In function declarations and prototypes, a space should appear after the opening parenthesis beginning the parameter list and before the matching closing parenthesis. However, no such spaces should be used in function calls.
- Functions should be separated by at least two blank lines.
Miscellaneous Guidelines
- 1) The
main()
function should always be written in the following style:
Code Snippet 7: An Example of a Well-Formatted Main Function:
- Few constants should appear in your code, other than 0,1, and ‘ ‘. All other constants should be declared and named in a
const
declaration. - Use the operators ++ and -- only in statements, never as part of larger expressions.