Program Style Guidelines for CS 1440


In general, the goal in programming is to write clear, readable programs that are as efficient as possible and solve a given problem. A program is not only a means of communicating with the computer, but is also a means of communicating with other people, including yourself. A program should be written so that a person who has not seen the program before, but knows the language, can read and understand the code.

The following guidelines will be used in grading programs for CS 1440 and will help you in achieving the goal of writing readable programs. Other courses that you take later in your career will indubitably tell you to adhere to some style guidelines. Those guidelines will probably be very similar to these, but they may not be the same.

Visual Clarity

Program Outline

The file containing the main function should have the following basic outline:
  1. Program heading comments
  2. #include statements
  3. Global constants
  4. Function prototypes
  5. Main function implementation
  6. Function heading comment and function implementation for each function

Indentation

Use a uniform indentation scheme (always indent the same way) to indicate statements within control structures, nesting of control structures, and statement continuation (if a statement won't fit on one line). Indentation of spaces is usual; much less is not enough and much more doesn't leave room for deep nesting. Avoid putting more than one statement on a line.

Spacing

Use spacing within a line to make it more readable. Use blanks around operators and other delimiters, after each comma in a list of items, and anywhere else that makes a program more readable. Do not put line breaks in the middle of quoted strings. Do not use very long lines in your program. There are still many display devices on which lines longer than about 80 characters are difficult to read. SOme examples,
    int    next_index       = 3;
    int    currentIndex     = 2;
    char   do_again         = 'y';
    char   currentCharacter = 'A';
    Widget myWidget         = new Widget();

Braces

All braces should line up with the current level of indention. Braces may follow a control structure or appear on the next line.
    i = 0;
    sum = 0;
    while (i < 10)
    {
        while (j < 10)
        {
            sum = sum  +  i * j;
            ++j;       
        }
        ++i;
    }
Or
    i = 0;
    sum = 0;
    while (i < 10)  {
        while (j < 10)  {
            sum = sum  +  i * j;
            ++j;
        }
        ++i;
    }

NOT
    i = 0;
    sum = 0;
    while (i < 10)
        {
        while (j < 10)
            {
            sum = sum  +  i * j;
            ++j;
            }
        ++i;
        }
For some kinds of conditional code, avoiding braces and making things line up vertically can improve readability. For example,
       if (weight <= 1.0)       price = 0.37;
  else if (weight <= 2.0)       price = 0.60;
  else if (weigth <= 3.0)       price = 0.83;
  else if (weight <= 4.0)       price = 1.06;
  else                          price = 2.99;
as opposed to
       if (weight <= 1.0)
       {
              price = 0.37;
       }
       else if (weight <= 2.0)
       {
              price = 0.60;
       }
       else if (weigth <= 3.0)
       {
              price = 0.83;
       }
       else if (weight <= 4.0)
       {
              price = 1.06;
       }
       else                      
       {
              price = 2.99;
       }

Blank lines

Use blank lines for vertical grouping of statements that logically belong together. Blank lines are useful in the following situations:
  1. around the body of compound statements,
  2. to separate sequential statements into logical groups,
  3. to separate a control structure (if, while, for) from complex surroundings.
An excess of blank lines is not helpful. For example, do not double space your entire program.

Program and function heading comments

Every program file, include file, and function within the program should be prefaced by a heading comment.
  1. Main program heading
     // Program:     <Program Name>
     // Author:      <Your Name>
     // Date:        <Date program was started>
     // Assignment:  <Which Program>
     // Purpose:     <A brief description of what the program does and,
     //               at a very basic level, how it does it>
     // Input:       <Describe the input expected by the program>
     // Output:      <Describe the output produced by the program>
     // Related
     // Files:       <Other files needed - header files, source files>
     // Functions:   <A list of the functions used in the program
     //               with a brief description of each function>
     // History:     <Optional - list of modifications following
     //               the initial implementation>
    
  2. Heading for files containing other functions and for include files
     // Filename:   <Filename>
     // Author:     <Your Name>
     // Date:       <Date code was started>
     // Assignment: <Which Program>
     // Purpose:    <A brief description of what type of functions are in
     //              the file and how they relate to other program files>
     // Related
     // Files:      <Other files needed - header files, source files>
     // Functions:  <A list of the functions defined in this file
     //              with a brief description each function>
     // History:    <Optional - list of modifications following
     //              the initial implementation>
    
  3. Function heading
     // Function:    <Function Name>
     // Purpose:     <A brief description of the function's behavior>
     // Author:      <Your name or whoever wrote the function>
     // Date:        <Date the function was written>
     // Parameters:  <List of the parameters expected by the function and
     //               a description of what each one means logically>
     // Returns:     <Value, if any, the function returns>
     // Pre-
     // Conditions:  <A list of conditions that must be true before
     //               the function is invoked>
     // Post-
     // Conditions:  <A list of conditions that will be true after the
     //               the function returns>
    

Inline comments

You should spend some time thinking about what should be commented. Don't add comments that do not add any new information. Too many comments can be just as distracting as too few comments. A simple rule: if when you read the statement and the comment, you hear the same thing twice, discard the comment.

Inline comments are useful in the following situations:

  1. Each constant and variable should be placed on a separate line. It should be followed by a comment which describes how it is used in the program, unless the name makes the content of the comment obvious. Recall that formal parameters are documented in the function header, although it is okay to include inline documentation of them as well.
  2. The ending of a control structure in a complex section of code should be marked by a comment indented at the same level as the beginning of the control structure. e.g., the end of the body of a nested if statement or the else part of nested ifs.
  3. Unclear program statements should be documented. These comments should describe the logical effect of the statements or help the programmer remember obscure facts. A comment such as
             // a gets the value of b
    
    for the statement "a = b;" is useless.

Upper/lower case

Normally we shall use lower case exclusively, except for names of constants which should be all uppercase.

Understandability

Names

The names of variables, functions, types, etc. should be descriptive of their purpose. Don't be afraid to use longer names to avoid names that are short and cryptic. Some general rules:
  1. Function names can be long and descriptive since they usually appear infrequently and describe an action rather than an object.
  2. Type names can be long and descriptive since they usually appear only in variable or parameter declarations.
  3. Variable names up to 10 characters usually provide a sufficient description without cluttering up expressions.
  4. Variables used as array subscripts should be shorter if possible.
  5. Variables used only as counters, loop controls, etc., can be as short as 1 to 3 characters in length.
  6. Use the underscore character to separate words within a name.

Complexity

Make algorithms as simple and straightforward as possible. This usually doesn't happen with the first implementation of an algorithm. It requires a clear understanding of the algorithm through multiple attempts/refinements. In cases where efficiency and clarity are at odds, clarity should usually win out. There are two rules for trying to optimize code (especially when doing so affects clarity):
  1. Don't.
  2. (For experts only) Don't do it yet.
(Jon Bentley, author of Programming Pearls attributes the rules to Michael Jackson of Michael Jackson Systems Ltd.)

Modularity

A program is easier to design/write/debug/maintain when it is broken into a group of smaller units that solve particular parts of the whole problem. All functions should be designed to do one task only. Thus, functions should be relatively small (say 50 lines or less). Any task that is repeated at several points in the program should be put in a separate function. Even tasks that are done only once, like initialization, can be put in a separate function to simplify the code. Your main function will normally be a few function calls and perhaps some simple control structures.

Global variables

No global variables will be used. Any communication between a function and its caller will be done through parameters and/or the function's return value. The use of global variables often introduces hard-to-find logic errors and indicates a poorly organized program. There are circumstances where global variables may be used, but we won't see those circumstances in this course.

Runtime behavior

Robustness

A program that does not terminate abnormally when given erroneous input data is robust. Input validation can require extensive programming and some errors may still not be detected. The amount of input validation necessary depends on the reliability of the input source.

Error handling

When an error is detected at runtime, usually an input error, the program should report the error in an informative manner and continue useful processing. If the program cannot continue useful processing, it should terminate as gracefully as possible.

Output formatting

A program's usefulness is generally measured by it's output. Output should be readable by the program user. Any output data should be accompanied by a description or put into tabular form with appropriate headings.

Generality

Programs are not static creatures. They should be designed to accommodate change. Avoid using hard-coded literal constants; use named constants instead. A general function is easier to use in another program. Related functions should be grouped together in separate source files and related declarations should be grouped in include files. If you are smart about the way you write programs, you will rarely have to start from scratch when starting a new program. Reuse code when possible.

---
File time-stamp: Tuesday, 29-Oct-2002 17:33:24 EST