The C++ programming language offers a number of ways to format code. Many programmers abuse this freedom and write unreadable (and thus incomprehensible and unmaintainable) code. While there is more than one way to properly format code, here is a set of guidelines which have been found useful in practice. Note that marks will be taken off for poor formatting (more marks deducted as the term goes on). Some of these guidelines may seem amazingly anal, but they really make a difference when reading code. Remember: you are writing code not just for the compiler, but for other people to read as well. The other person reading your code will most likely be you six months from now, so making sure that your code is readable is extremely important.
In the following, each item has a code associated with it to the left of the description of the item. This code will be used to specify the problem when correcting your code. It is up to you to match the code with the item. Hopefully this will encourage you to read this style guide :-) As a general rule, the earlier items in a section are more important than the later ones and/or represent more common errors.
In order to make life easier on you, I am supplying you with an automatic style checker. It won't catch all of these errors by any means, but it will catch a lot of them. You will be required to run your code through the style checker before you submit it; if it fails, I won't grade it (unless you can convince me that the style checker itself has a bug, which is possible). The C style checker is written in python, which is available on both the UGCS and CS clusters. You should download it, put it in your "~/bin" directory (create one and put it in your path if you haven't done so already) and then do:
% chmod +x ~/bin/c++_style_check
Then, to style-check your file "foo.c", do:
% c++_style_check foo.c
("%" is the unix prompt here.) Don't be alarmed if there are a lot of errors
reported; just go through the file and fix them. Some lines will probably
have multiple style violations; you should fix all of them. You'll probably
hate me for writing this program at first, but your code will become much
more readable as a result. If you think you've found a bug in it, let me
know at once; this is a work in progress. Note that the style checker will
sometimes be too stupid to know when it's in the middle of a comment or a
literal string, so it may report errors that aren't really errors in those
cases. If so, just disregard them.
Never, ever, ever use the tab character (ascii 0x9)! Different people use different tab settings, and code that looks just fine with a tab width of 2 becomes illegible with a tab width of 8. Unfortunately, many text editing programs will stick in tab characters without making it obvious that they're doing it. If you use emacs for text editing (which I recommend) put this in your .emacs file:
(setq c-mode-hook
'(lambda ( )
(progn
(set-variable 'indent-tabs-mode nil)
;; other customizations, if any, go here
)))
When you hit the tab key while editing C++ code in emacs, it will
automatically indent the code to a reasonable point on the line. When you
put the preceding code (which is emacs-lisp code, but never mind that) into
your .emacs file, it will make sure that hitting the tab key doesn't actually
put any tab characters in your code, but instead just puts in spaces.
Use a single space to separate variable names from operators, i.e. write
a = b + c * d;
instead of
a=b+c*d;
The only exception I have for this rule is for array subscripts e.g.
b = a[i-1] // not a[i - 1]
but you can put the spaces in here too if you want. Unfortunately the style
checker currently complains if you don't put the spaces in. Don't worry
about the warning in this case.
Always put a space after a comma. I know of no exceptions to this rule.
If you are using a formatting style where the opening curly brace of a block is on the same line as an if, while, or for statement (which I discourage), make sure that there is a space between the close paren on the line and the open curly brace, e.g. do
for (i = 0; i < n; i++) {
// code goes here
}
instead of:
for (i = 0; i < n; i++){
// code goes here
}
because the latter is hard to read. Similarly, leave a space between an
else keyword and an opening curly brace if they're on the same
line.
Don't write lines that are longer than 78 characters long. Long lines tend to be wrapped, or worse, to be truncated when printing out the source. Printing out source code is a valuable way to review your code. It is almost never necessary to have long lines, even for long strings; you can always break up a string like this:
cout << "this is a really, really, really, really, really, really, "
"really, really, really, really, really, really long string.\n";
and the two strings will be concatenated together. This will work for any
number of consecutive strings. Note that this trick only works for literal
strings, not for variables which contain (point to) strings.
Avoid putting a large number into a file which has no obvious relevance to the surrounding code. This is known as a "magic number" and is often found when setting the size of arrays, e.g.:
int my_array[4096]; // 4096 is a magic number
The reason for avoiding this is twofold:
The right thing to do is this:
const int BUFSIZE = 4096; // size of buffer
...
int my_array[BUFSIZE];
Do not put in code that has no function or no effect. If it is debugging code, it should be clearly marked as such. It's OK to leave debugging code in, but please comment it out.
If a program is called with incorrect arguments, it should detect that and print a usage statement to the terminal. The usage statement should include the program name.
if (argc != 2)
{
cerr << "myprog filename\n";
exit(1);
}
Note that the arguments have mnemonic names. Don't write the usage message
multiple times. If necessary, you can define a usage function:
void usage(void)
{
cerr << "usage: myprog input_filename output_filename\n";
}
and then call it like this:
int main(int argc, char **argv)
{
// code omitted
if (/* arguments are incorrect */)
{
usage();
return 1;
}
// more code omitted
return 0;
}
Also, make sure that you use cerr (the error output stream) instead of
using cout, which prints to stdout (the normal output stream).
As a general rule, any error that involves the user supplying invalid command-line arguments should give rise to a usage statement like the ones described above. You should try to make your usage statements comprehensive enough so that one statement will work for all such errors.
Never put more than one statement on a line. It makes for unreadable code.
Use parentheses to show operator precedence in all cases except that of multiplication/division over addition/subtraction and assignment statements.
Do not put large numbers of empty lines (> 2) between code sections unless there is a clear need to distinguish different sections of the code. Conversely, do put an empty line between logical sections in a single function. An example of this is between the type declarations and the first line of actual code. Another example is at the end of a block in curly braces (though this is a judgment call). Long functions that have no blank lines in them are really hard to read.
Use curly braces for the body of all if statements, even if the body is only a single statement. Do the same for else, else if, for, and while statements. The reason for this is twofold: first, it makes the code more readable, and second, it makes it easier to add printf statements for debugging in the body of the expression (which you will frequently have to do).
If you are using a formatting style where the curly braces of a block are on a separate line (which I encourage), make sure that the column of the curly braces match e.g. do this:
for (i = 0; i < n; i++)
{
// code goes here
}
instead of:
for (i = 0; i < n; i++)
{
// code goes here
} // braces don't line up
Don't put code on the same line as an open curly brace. For instance, this is bad:
if (a != 0)
{ a = b + c; // ugly
cout << "a is now: " << a << "\n";
}
Keep the curly braces on their own lines; this makes the code easier to
read. Unfortunately, you often see code written like that in books about
programming; the reason is that they have to cram as much code as possible
onto a single page. You don't. Instead, write this as:
if (a != 0)
{
a = b + c;
cout << "a is now: " << a << "\n";
}
Do not put an entire block on a single line, and most especially do not put it on the same lines as an if, while, for etc. E.g. change
if (i < 10) { break; }
to
if (i < 10)
{
break;
}
Lines within a block should be indented relative to lines outside a block.
Lines at the same level of a block should start at the same column.
Do not try to do complex calculations in the testing or increment parts of for loops. Don't try to impress me with how clever you are; clever code is a maintenance disaster.
Make variable names descriptive as much as possible; avoid one or two character names unless it's for something trivial like a loop index. It's perfectly OK (and usually desirable) to have longer descriptive names for variables. When you do this with names that are actually multiple words, use one of two conventions:
Either convention is OK as long as you're consistent. I prefer the underscore convention, but that's just personal preference.
Avoid using implicit int-to-float or int-to-double conversions (or vice-versa) as much as possible. It's hard to keep track of the types of the results otherwise. Instead, use explicit type casts when you want to convert an int to a double etc. For instance, this:
int a = 10;
double b;
b = a; // implicit conversion
should be written as:
int a = 10;
double b;
b = double(a); // explicit conversion
Yes, it's a bit more verbose, but it's absolutely unambiguous.
This is the single most common style mistake. If a comment is a phrase or sentence, its first word should be capitalized, unless it is an identifier that begins with a lower case letter (never alter the case of identifiers!), and it should end in a period. I prefer comments that are complete sentences. You should use two spaces after a sentence-ending period.
Bad:
// go through the loop and make sure that all the array elements
// have been set to zero
Good:
// Go through the loop and make sure that all the array elements
// have been set to zero.
That wasn't so hard, was it?
When you need to refer to identifiers, put them in surrounding single quotes, e.g.
// The variable 'nitems' represents the number of items in the stack.
If a comment is very short, it doesn't have to be a full sentence or end in a period e.g.
i = 1; // loop index
This is called an "inline comment". Use these only when describing something
i.e. in the above code snippet you're saying "The variable 'i'
represents a loop index."
Comments should be grammatically correct. In particular, incorrect spelling is unacceptable. I hate to sound like your high school English teacher, but it's a pain to read code with tons of spelling mistakes. Use a spell checker if you have to.
Put a space after the open-comment symbol and before the close-comment symbol i.e. do this:
// This is a comment that is easy to read.
and not this:
//This is a comment that is harder to read.
Do not use C++-style comments i.e. comments that start with // and go to the end of the line. It is true that most C compilers (including gcc) accept them, and they will be part of the C standard soon. But for now, it's a non-portable feature.
Block comments generally apply to some (or all) code that follows them, and are indented to the same level as that code. Each line of a block comment starts with a * and a single space (unless it is indented text inside the comment). Paragraphs inside a block comment are separated by a line containing a single *. Block comments are best surrounded by a blank line above and below them (or two lines above and a single line below for a block comment at the start of a a new section or function definition). I always like to start and end a block comment with a line containing a single *; this is a matter of personal preference. In other words, a block comment looks like this:
//
// The first line comes after an empty line.
//
// Separate paragraphs are also separated by an empty line,
// and there's an empty line at the end.
//
Write comments for anything that isn't completely obvious from the context. In particular, write comments for any tricky algorithm or code you are using. When in doubt, comment more rather than less.
Conversely, don't make completely redundant comments, e.g.
i = 1; // Set i to 1.
What constitutes redundancy is often a judgment call. If in doubt, comment
more rather than less.
Don't make meaningless comments e.g.
// i
i = 1;
Don't laugh; I've actually seen this sort of thing.
You should almost always put a comment at the beginning of each function describing what it does. The only exception I can think of is when you have a series of very similar functions which are written out one after another, and where the first comment applies (suitably modified) to all of them. This kind of "header comments" (not to be confused with header files) are by far the most important kind of comments, because even if the person reading your code has no idea how a given function works, the header comment will at least tell him what it does and how to use it. You should state what each of the arguments represents and what the function returns. You may also want to describe the algorithm used, its efficiency, and any other relevant factoids. An example:
//
// bubble_sort:
// This function takes an array and sorts it in-place using the bubble
// sort algorithm. This algorithm has a time complexity of O(n^2)
// where 'n' is the size of the array, which is not very efficient.
// Therefore, for large arrays use a more efficient algorithm such as
// quicksort.
//
// Arguments:
// -- arr: the array to be sorted
// -- size: the length of the array to be sorted
//
// Return value: none.
//
void bubble_sort(int arr[], int size)
{
// code
}
Comments that contradict the code are worse than no comments. ALWAYS MAKE A PRIORITY OF KEEPING THE COMMENTS UP-TO-DATE WHEN THE CODE CHANGES!
Always indent your comments to the same degree as the surrounding code.
Try to line up inline comments where convenient. In other words, don't do this:
x = x + 1 // some cool comment about x
y = y + 1 // some even cooler comment about y
Instead, do this:
x = x + 1 // some cool comment about x
y = y + 1 // some even cooler comment about y
I like to line up the close-comment token as well, but then, I'm really anal
(as if you didn't know that by now ;-)).
Do not write comments that apply to the preceding code if you can possibly avoid it. Try to write comments that refer to the current line of code or to the lines of code which immediately follow. For instance, this is bad:
int res;
// 'res' contains the result of the program. It will normally be 0,
// unless an error occurs, in which case it will be 1.
and this is good:
// 'res' contains the result of the program. It will normally be 0,
// unless an error occurs, in which case it will be 1.
int res;
Always write function prototypes at the top of a file for every function whose definition is in that file. This is not only good documentation, it enables you to use these functions anywhere in the file without having to worry about putting them in strict order of definition. In case you don't know already, this is a function prototype:
int foo(double bar, string baz);
It's just a function without a body.
Please separate your function definitions by at least two blank lines. Otherwise it's hard to find where a function definition begins.
Don't hesitate to decompose your functions into lots of smaller functions. Many people seem to think that calling a function is an incredibly expensive operation. That was true for some languages and some compilers a long time ago, but now almost any C compiler (at least) can "inline" little functions so that there is no calling overhead. Some compilers, such as gcc, even allow you to declare functions as inline, although this isn't in the C standard yet (but it soon will be). I have seen and worked on code that had single functions that were twenty or more pages long, with no comments. Don't do it!
Start the line that begins a function in column 0 (the leftmost column).