Programming Style Guide



It has been said that `programs must be written for people to read, and only incidentally for machines to execute' (ref. H. Abelson and G. J. Sussman, Structure and Interpretation of Computer Programs, MIT Press, preface to the first edition). Therefore, as discussed in class, it is important to follow some basic guidelines for writing source code. Follow the guidelines below for all programming assignments. Note: We will evolve this set of guidelines as we learn new programming paradigms, languages, and constructs.

Remember, assignments provide you with an opportunity to show us that you care enough to submit a professionally-prepared submission. Practice good programming habits early and you will be rewarded with effective and efficient programs. Following this guide will improve the readability, writeabiliy, and maintainability of your programs and therefore reduce the likelihood of costly errors which will save you time in debugging. A portion of your grade for all work will be evaluated for style.


  • Begin each source file with the following header filled-in appropriately.

      C:
      /*******************************************************************************
      /
      /      filename:  wc.c
      /
      /   description:  Implements the word count command.
      /
      /        author:  Linus Lowgator
      /      login id:  cps444-n1.19
      /
      /         class:  CPS 444
      /    instructor:  Perugini
      /    assignment:  Homework #1
      /
      /      assigned:  August 23, 2005
      /           due:  August 30, 2005
      /
      /******************************************************************************/
      
      Shell Programming:
      #*******************************************************************************
      #
      #      filename:  crossref
      #
      #   description:  A Korn shell script which cross-references C/C++ source files
      #		  with their include files.
      #
      #        author:  Linus Lowgator
      #      login id:  cps444-n1.19
      #
      #         class:  CPS 444
      #    instructor:  Perugini
      #    assignment:  Homework #2
      #
      #      assigned:  September 22, 2005
      #           due:  October 13, 2005
      #
      #*******************************************************************************
      
      Lex and Yacc:

      Note: Put your flowerbox in the definitions section of your Lex and Yacc specification files, i.e., before the first %%.
      /*******************************************************************************
      /
      /      filename:  calc.y
      /
      /   description:  Yacc specification for an arithmetic calculator.
      /
      /        author:  Linus Lowgator
      /      login id:  cps444-n1.19
      /
      /         class:  CPS 444
      /    instructor:  Perugini
      /    assignment:  Homework #4
      /
      /      assigned:  October 25, 2005
      /           due:  November 8, 2005
      /
      /******************************************************************************/
      
      ML:
      (*******************************************************************************
      (
      (      filename:  hw5.sml
      (
      (   description:  A collection of functions solving problems from Homework #5.
      (
      (        author:  Linus Lowgator
      (      login id:  cps444-n1.19
      (
      (         class:  CPS 444
      (    instructor:  Perugini
      (    assignment:  Homework #5
      (
      (      assigned:  November 8, 2005
      (           due:  November 15, 2005
      (
       ******************************************************************************)
      
  • Do not allow any line of code to exceed 80 characters in length. Most text editors have an option to give you column position. Find an appropriate place to break long program statements to continue them on the following line. Break long character strings using string concatenation.

  • Indent all code within a block.

  • Do not use tabs anywhere in your code. For each level of indentation, use three spaces. Tabs cause different amounts of horizontal spacing on different systems. By using spaces (and a fixed-width font), you guarantee your code will be properly indented for every system, editor, and printout.

  • Align corresponding opening and closing braces, begin or ends, or any other program unit delimiters. My preference for curly braces { } (or similar delimiters) is to always place the opening brace on the same line as the block it opens. This makes it easy to see where blocks of code, such as loops, begin and end, and does not waste a line of code. An alternate style is to place each brace on line by itself. You may use either of these styles, but do not mix them. Always be consistent.

  • Use descriptive (variable, constant, procedure, function) identifiers and use appropriate naming conventions for variables (total_sold) and constants (OUNCES_PER_TON). Remember, syntax should imply semantics.
      Descriptive: int dollars, average, weight
      Cryptic: int x, y, z
  • Initialize variables (to a value of the appropriate type) before you use them to avoid garbage. This can be done when you declare the variable or with an assignment statement before the variable is used.
      Incorrect: double radius = 3;
      Correct: double radius = 3.0;
  • Avoid type mismatches.

  • Do not assign a variable or literal of one type to a variable of another, even if our compilers/interpreters permit it. Following this guideline will make your programs portable.
      Consider: double avg_score = 76.7; int exam1 = 86;

      Incorrect: avg_score = exam1;
      Correct: avg_score = static_cast (exam1);

      Consider: double average = 0.0; int total = 967, num_students = 10;

      Incorrect: average = total/num_students;
      Correct: average = static_cast (total)/num_students;
  • Do not use goto.

  • Avoid the use of global variables.

  • Use comments to explain critical subsections or any ambiguous parts of your programs (e.g., a tricky expression). If supported, do not use multi-line comments where they are likely to make your program less readable.

  • Use named constants rather than "magic" numbers. This gives you a single point of modification which will save you time and reduce bugs.

      Examples:
      #define SIZE 76
      
      const int NUMBER_OF_RECORDS = 101;
      
      const char A = 'a';
      const char E = 'e';
      const char I = 'i';
      const char O = 'o';
      const char U = 'u';
      
      switch (character) {
          case A:
          case E:
          case I:
          case O:
          case U:
      }
      
  • Always use enumerated types where they make your code more readable.

      Example:
      typedef enum { JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC } months;
      
         main() {
            months my_months;
      
            switch (my_months) {
               case JAN:
                  ...
                  break;
               case FEB:
                  ...
                  break;
      
               ...
      
               case NOV:
                  ...
                  break;
               case DEC:
                  ...
                  break;
            }
         }
      
  • Enforce the principle of least privilege.

  • Do not use local variables with same name in different scopes (they are different variables).

  • Always exit from main with a 0 exit status to indicate success and a non-zero status to indicate failure. Use exit rather than return to make your program more uniform. Use an int as a return type for main.

      Example:
      int main() {
         FILE* fp = NULL;
         char* filename = "input.txt";
      
         if ((fp = fopen (filename, "r")) == NULL) {
            fprintf (stderr, "cannot open %s\n", filename);
            exit (1);
         } else {
              ...
              exit (0);
           }
      
  • Always initialize pointer variables.

      Examples:
      Student* student_ptr = NULL;
      
      char* filename = "input.txt";
      
      FILE* myinstream = fopen (filename, "r");
      
  • When allocating memory, always verify that the memory was allocated successfully.

      Example:
      if ((student_ptr = malloc (sizeof (Student))) == NULL) {
         fprintf (stderr, "out of memory!");
         exit (1);
      } else {
           ...
           exit (0);
         }
      
  • Once finished, always free memory that you explicitly allocated.

      Example:
      if ((student_ptr = malloc (sizeof (Student))) == NULL) {
         fprintf (stderr, "out of memory!");
         exit (1);
      } else {
           ...
           free (student_ptr);
           exit (0);
         }
      
  • When opening a file, always verify that the file was opened successfully.

      Example:
      if ((fp = fopen (filename, "r")) == NULL) {
         fprintf (stderr, "cannot open %s\n", filename);
         exit (1);
      } else {
           ...
           exit (0);
         }
      
  • Always close files that you explicitly opened.

      Example:
      if ((fp = fopen (filename, "r")) == NULL) {
         fprintf (stderr, "cannot open %s\n", filename);
         exit (1);
      } else {
           ...
           fclose (fp);
           exit (0);
        }
      
  • Always print error messages to stderr.

      Example:
      if ((fp = fopen (filename, "r")) == NULL) {
         fprintf (stderr, "cannot open %s\n", filename);
         exit (1);
      } else {
           ...
        }
      
  • Routines/subprograms:

    • Always use a procedure/function prototype.
    • Use parameter names in procedure/function prototypes.
    • Use different identifiers for formal parameters and actual parameters to reinforce that they are different variables.
    • Precede every procedure/function with the following header explaining its purpose, the meaning of each parameter, precondition, postcondition, and the general strategy of its implementation, if applicable.

        C:
        /*******************************************************************************
        /
        / purpose: To compute the factorial of a non-negative integer.
        /
        /******************************************************************************/
        int factorial (int n) {
           if (n == 0) then
              return 1;
           else
              return n*factorial (n-1);
        }
        
        ML:
        (*******************************************************************************
        (
        ( purpose: To compute the factorial of a non-negative integer.
        (
         ******************************************************************************)
        fun factorial (int n): integer; begin
           if (n = 0) then
              factorial := 1
           else
              factorial := n* factorial (n-1);
        end;
        
    • No routine/subprogram, block, procedure, function, or method (or message) should exceed 50 lines of code.

  • Be consistent in your application of the above guidelines.

  • Overall, write your programs such that they are self-documenting. In other words, structure your code such that the program itself provides its own documentation. Self-documentation means using descriptive identifiers and a consistent, aligned format.

  • Specific for Shell Programming:

    • Always begin your script with a proper "bang" line.

        Example:
        #!/bin/ksh
        
    • Always return from your script with a 0 exit status to indicate success and a non-zero status to indicate failure.


  • Specific for Functional Programming in ML:

    • Utilize recursion, higher-order functions, pattern matching, and dynamic dispatch to the fullest extent.

    • Avoid the of use imperative features other than the language's facilities for input and output.

    • Avoid statement blocks. Use let's instead as they do not run contrary to the spirit of functional programming. For more information, see pp. 106--107 of [EMPL] J. D. Ullman. Elements of ML Programming. Prentice Hall, ML97 edition, 1998.

    • Avoid the use of iteration.

    • Avoid side-effects, again, with the exception of input and output.

    • When possible, always use the cons operator (::) rather than the concatenation (@) operator. "It may not be obvious, but while it takes time proportional to the length of the first list to concatenate lists, we can cons a head and tail in constant time" [EMLP]. For the details, see pp. 84--88 of [EMPL].

    • Never open structures.

    • Verify, with your instructor, the permission to use any language feature not discussed in class by your instructor.


Return Home