Keith Trnka's C++ (220) Style Guide

This style guide is a modification of an old C style guide. It should give you clear examples for getting full style/readability credit in 220. The difference from lower level style requirements is that you're expected to be able to program.

The most basic requirement of a program is that it works as intended. By programming with a consistent style, it'll help you code faster and make it easier for others to help or evaluate your code.

The following are general goals of good programming:

Performance

CPU and memory performance are essential in 220. The first, most basic requirement is that the runtime and memory usage of your program should match any specified complexity bounds. In general, a function's upper bound should be the best possible. For example, if you're asked to code an array append and you can do it in O(n) or O(n^2), you must do it in O(n) for full credit.

Memory usage requirements depend on the assignment. In general, if you want to be sure of full credit, a function should not use O(n) additional memory if it can be implemented with O(1) additional memory.

Beyond optimizing the Big-Oh of a function, the function should run as fast as possible whlie still being readable/extensible. This means that you shouldn't do unnecessary processing, especially inside loops.

Readability

Readability is probably the most commonly recognized aspect of style. There are a few different areas of style for readability, covered below.

Variable and function names

Pick names for variables that are descriptive. Unless it's obvious, don't use abbreviations. The same goes for functions. The best test for functions is that you should know what the function does by the function name alone. Here are some examples:
Variables
Good Bad
distance
velocity
acceleration
d
ve
A
b
c
General functions
Good Bad
drawPicture
squareRoot
computePrice
computeImage (*)
sqrt (+)
total

(*) computeImage is worse than drawPicture under the assumption that the function actually draws the image to the screen. The key reason here is that "draw" is more specific than "compute" in this context. In general, you should be as specific as possible while not making the name too long.

(+) sqrt is a complicated case, and depends entirely on the people who are reading your code. If you're writing code for someone that's been programming for a long time (and you yourself have been), it's faster to type sqrt so I'd prefer that. This particular naming convention is similar to user interface design - noone uses 3.5" floppy disks anymore but we're all used to that for a save icon. Because we're all used to knowing what it means, it doesn't matter that the analogy isn't appropriate anymore.

Special notes on function names

Realistically, the "human meaning" of a function isn't derived from the function alone. The meaning of a function is a combination of several things, illustrated with examples.

The simple meaning of a function can also be a part of a comment above the function. While such comments are crucial in good code, they are often not seen or known by the programmer writing a function call.

Special variable names

Just like the example, there are a lot of variable names that have traditional meanings, and it's fine to use them.

Naming conventions

Here are some conventions for naming variables, constants, and functions:

Indentation

Code should be indented with spaces or tabs so that every statement in the same block of code is lined up. A trick to having good indentation is to press Tab at the beginning of a line in emacs. This is an example of good indentation:
int main(void)
     {
     printf("Hello\n");
     printf("Hello again\n");
     return 0;
     }

This is an example of bad indentation:
int main(void)
     {
  printf("Hello\n");
     printf("Hello again\n");
   return 0;
     }

There are a few styles of placing curly braces { } in code. I've listed three styles below, but there are probably others; just make sure you stick with one.

  1. Style 1 (Java style)
    int main(void)
         {
         printf("Hello\n");
         printf("Hello again\n");
         return 0;
         }
  2. Style 2 (Traditional C/C++ style)
    int main(void) {
         printf("Hello\n");
         printf("Hello again\n");
         return 0;
    }
  3. Style 3
    int main(void)
    {
         printf("Hello\n");
         printf("Hello again\n");
         return 0;
    }

Spacing in equations

When you're typing an equation, make sure to put spaces here and there if it gets long. Here are some examples:
x = x0 + v0 * t + .5 * a * pow(t, 2); Good
x = x0 + v0*t + .5*a* pow(t, 2); Good - can see the precedence grouping easily
x=x0+v0*t+.5*a*pow(t, 2); Bad - tough to read

Program header

You should put a description of your program at the top. That description should include your name and a brief description of what your program does, like so:
/*
Keith Trnka (idont@wantmorespam.com)
CISC220 Lab 2

This program reads some velocities, times, and accelerations from DISTANCE.DAT
and writes out a table of information to DISTANCE.OUT.
*/

Comment alignment

Comments within a section of code should be indented with the code if they are on their own line, as in the following example:
int main(void)
     {
     /* shows "Hello" on the screen */
     printf("Hello\n");
     /* shows "Hello again" on the screen */
     printf("Hello again\n");
     /* exits the program normally */
     return 0;
     }

Alternatively, comments may be placed on the same line if they are aligned horizontally, like this:
int main(void)
     {
     printf("Hello\n");       /* shows "Hello" on the screen */
     printf("Hello again\n"); /* shows "Hello again" on the screen */
     return 0;                /* exits the program normally */
     }

Vertical spacing

If a bunch of lines of code go together but don't really go with anything else, put a blank line before and after this chunk of code. In the following example, vertical spacing is used in conjunction with comments to section off parts of related code. It isn't really important that you understand the code, just that you see how related chunks of code can be grouped.
     /* Initialize balance */
     balance = 0;

     /* Initialize server address */
     memset(&server_address, 0, sizeof(server_address));
     server_address.sin_family = AF_INET;
     server_address.sin_addr.s_addr = htonl(INADDR_ANY);
     server_address.sin_port = htons(SERVER_TCP_PORT);
     
     /* Open the listening socket */
     if ((sock_server = socket(AF_INET, SOCK_STREAM, 0)) < 0)
          {
          perror("Server: Couldn't open listening socket");
          exit(1);
          }

Functions

It's much easier to understand a program that has a lot of little functions than a program that has a few big functions. One of the reasons that function increases readability is that you can usually tell what a function does by its name, even if you don't understand how it's implemented. Consider the following example:
...
printFirstRow(initialVelocity, time, acceleration, computeDistance(initialVelocity, time, acceleration))
...
It should be clear what this code does, even though you don't have the code for the functions printFirstRow and computeDistance.

Now look at another way to write it:
...
distance = initialVelocity * time + .5 * acceleration * pow(time, 2);
printf("%10d%10d%10d%10.1lf", initialVelocity, time, acceleration, distance);
...

Coincidentally, this method of programming helps both debugging and extensibility. To see how, we need to look at a larger portion of each program. I've used to comment /* read from file */ as a placeholder for scanf statements.
Using your own functions Without your own functions
...
/* read from file */
printFirstRow(initialVelocity, time, acceleration,
     computeDistance(initialVelocity, time, acceleration))
/* read from file */
printOtherRow(acceleration,
     computeDistance(initialVelocity, time, acceleration))
/* read from file */
printOtherRow(acceleration,
     computeDistance(initialVelocity, time, acceleration))
...
...
/* read from file */
distance = initialVelocity * time
     + .5 * acceleration * pow(time, 2);
printf("%10d%10d%10d%10.1lf",
     initialVelocity, time, acceleration, distance);
/* read from file */
distance = initialVelocity * time
     + .5 * acceleration * pow(time, 2);
printf("%30d%10.1lf", acceleration, distance);
/* read from file */
distance = initialVelocity * time
     + .5 * acceleration * pow(time, 2);
printf("%30d%10.1lf", acceleration, distance);
...

Now, suppose that I tell you that your columns are incorrect. Maybe each column should be 15 characters wide instead of 10 characters wide. If you use your own functions, all you need to do is fix those functions. If you did the other way, now you must change every line. This demonstrates how functions can lead to extensible programs. But it's also important for debugging: without functions, you might forget to fix one row or make one typo, but with functions as shown above, you can't make those mistakes.

Putting a statement across multiple lines

Sometimes, one "line" (a sequence of characters that ends with \n) is too long to fit on one line on the screen or printer (usually 80 characters). If that's the case, you can continue it on the next line, indented.

Here's some code with a long line:
double meters1, meters2, meters3, meters4, kilometers1, kilometers2, kilometers3, kilometers4, yards1, yards2, yards3, yards4;

You can group things together so that they fit within the page:
double meters1, meters2, meters3, meters4,
     kilometers1, kilometers2, kilometers3, kilometers4,
     yards1, yards2, yards3, yards4;

Sometimes you might do this with a printf too:
printf("My %d buttons were exploring the Amazon when they discovered that they couldn't eat %d chickens.",
     buttons, chickens);

Commenting

You can comment too much and you can comment too little. It's common practice to write a comment before each function, describing the arguments to the function, the return value (if any), and the general idea of the code in the function. However, there are some functions that are self-explanatory and don't require commenting. If you have a large segment of code that does some complicated things, you should comment it just like a function: describe what the variables are like before the code is executed and after the code is executed, and describe your algorithm.

You can also comment too much. If you find that you place a comment along with every line of code, you're probably commenting too much. Think about it like this: if you showed your code to someone in the class who doesn't really know you, you should only have comments on the things that they'll have trouble figuring out.

Extensibility

Extensibility is more important when you're more familiar with programming. For the beginning of the class, don't worry about this.

Modular design

It sounds big and all, but it really just means that in bigger assignments, you should be able to "plug in" a new function instead of an old one, and it should take care of most of the work of updating your program.

Debugging

Develop and test your program in steps

Look at the programming assignment and "build" up to the complete assignment. For instance, one lab involved reading values from a file, computing some numbers based on those values, and then saving the results to a file. Take it one piece at a time. As a first step, hard-code values. In other words, say things like acceleration = 10. Have your program output the results using printf. As a second step, output to a file instead of to the screen. As a third step, read values from a file instead of hard-coding them.

Another way to approach this is to write functions that do the different parts of your program. Test each function separately before putting it all together.

The example I gave on the first day of class is something that helps with debugging: if you're used to writing things like if (0 == i) rather than if (i == 0) you won't be as confused if you don't press the '=' key hard enough one time.