SI 204 Spring 2017 / Resources


This is the archived website of SI 204 from the Spring 2017 semester. Feel free to browse around; you may also find more recent offerings at my teaching page.

C Programming Style Guide

1 General

This style guide is mandatory for all submitted work for grading (homework, projects, labs, exams). The purpose of the guide is not to restrict your programming but rather to establish a consistent style format for your programs. This will help you debug and maintain your programs, and help others (including your instructor) to understand them. As your programs grow in length and complexity, it is critical to use a consistent style. In the long run this will help you since one focus of this course is to build a foundation for later courses. Points will be deducted from submitted work that does not conform to this style guide.

2 Visual Layout

"Whitespace" refers to newlines, space characters and tabs. In source code we do not generally use tabs — in fact, your editor should be replacing tab characters with an appropriate number of space characters. In many circumstances whitespace is not required in order for compilers to correctly compile source code, even though the result is unreadable to us. For example, the following is a perfectly valid program:

Bad Formatting Good Formatting
#include "si204.h"
int main(){int
x; int y=
  1; x =readnum
  (stdin); while
(x>0){y=2*y;--


             x;}
writenum
           (  y
, stdout); fputs(
"\n",stdout);return 0
    ;      }
#include "si204.h"

int main() {
  int x;
  int y = 1;

  x = readnum(stdin);

  while(x > 0) {
    y = 2*y;
    --x;
  }

  writenum(y, stdout);
  fputs("\n", stdout);

  return 0;
}
... but pretty hard to read. there are three major ways we use whitespace to format code for maximum clarity:
  • whitespace separating elements of code: consistent use of whitespace transcends blank lines and indentation, it also looks at how spaces are used to separate elements of a line of code. the style guide recommends whitespace between operators and operands, except where precedence is highlighted by leaving out spaces.
    good y = 2*y + x/y + 5; if (y >= 2*y - 5 && x*y <= 24)
    bad y=2*y+x/y+5; if (y>=2* y-5&&x * y<=24)
  • blank lines: in source code, use blank lines to separate chunks of code that accomplish distinct tasks. while to some extent this is taste and art, you should expect distinct functions to be separated by blank lines, and a long function body should usually be broken into chunks by blank lines as well.
  • indentation: indentation highlights the block structure of programs, much as it does in formatting outlines. each level of block nesting must be indented by a consistent number of spaces (two in the emacs settings we provide you). new levels of indenting are introduced by function bodies, for, while and do-while bodies, the then and else blocks of an if statement, and struct definitions.

3 Curly braces, i.e. { }'s

Curly braces, i.e. { }'s in C are used to create a "block" from one or more statements. the delineate the bodies of functions, loops and switch statements, struct definitions, and the then and else blocks of if statments. generally, one should either always put the opening curly brace at the end of the line, or on a new line by itself. (The exception is that a comment may follow an opening curly brace.)

Once again consistency is key. If the opening brace goes on a new line, the preference for this course is to keep the previous line's indentation — i.e. do not indent the curly brace. The closing curly brace is usually the only thing on its line, and should always be indented at the same level as the opening of the block.

GoodGood
if (x < 3) {
  fputs("x is tiny\n", stdout);
  ++x;
} else if (x < 10) {
  fputs("just right\n", stdout);
} else {
  fputs("x is big\n", stdout);
  --x;
}
fputs("all done\n", stdout);
if (x < 3)
{
  fputs("x is tiny\n", stdout);
  ++x;
}
else if (x < 10)
{
  fputs("just right\n", stdout);
}
else
{
  fputs("x is big\n", stdout);
  --x;
}
fputs("all done\n", stdout);
BadBad
if (x < 3)
{ fputs("x is tiny\n", stdout);
  ++x; }
else if (x < 10)
{ fputs("just right\n", stdout); }
else
{ fputs("x is big\n", stdout);
  --x; }
fputs("all done\n", stdout);
if (x < 3) { fputs("x is tiny\n", stdout);
  ++x;
} else if (x < 10) {
  fputs("just right\n", stdout); }
else {
fputs("x is big\n", stdout);
--x; } fputs("all done\n", stdout);

4 Comments

Comments should be indented consistently with the block of code they describe. Every file you write should start with a comment saying what the program/file is for and stating your name.

Comments can be divided into two general categories, strategic and tactical:

  • Strategic comments are used to give the reader a general overview of what is going on. These comments appear at the beginning of files, before important functions, and above important blocks of code. Strategic comments tend to be written in sentences with an emphasis on explaining the big picture of what the block of code is designed to do.
  • Tactical comments are designed to explain tricky areas of code, what parameters do, and hints about the control flow of the program. These comments should be intermixed in the code when needed. They should be shorter than strategic comments, in bullet format, and may be in inline format. One should, for instance, place a tactical comment next to the statement that assigns a fixed, non-obvious value to a variable.
The following example shows good commenting. Note the tactical comment about why "9.0" is used instead of "9".
/* Fahrenheit to Celsius Conversion
 * Dr. Roche, January 2017
 */
#include "si204.h"

int main() {
  // Read temperature in Fahrenheit
  double Tf;
  fputs("Enter temperature in Fahrenheit: ", stdout);
  Tf = readnum(stdin);

  // Compute temperature in Celsius
  double Tc;
  Tc = (Tf - 32)*(5/9.0); // .0 forces division as double

  // Write temperature in Celsius
  fputs("That is ", stdout);
  writenum(Tc, stdout);
  fputs(" degrees Celsius.\n", stdout);

  return 0;
}

5 Naming

Variables should have meaningful names. For example, consider a program that uses a variable to track the yards per carry of a football game. Such a variable should be called yardsPerCarry vice ypc for program readability. This must be balanced against using names which are too long, which can obscure the code. (Fifteen or so characters approaches the “too long” limit.) It should be mentioned that this is most important for variables that have large scope — i.e. they are visible and need to be used either from different files or from widely separated lines in the same file. The code example above seems to violate this rule, but variables Tf and Tc only a appear, and are only in scope, within less than a dozen lines of code, and in the context of those few lines they are descriptive enough. Also, because this is an example embedded in a document, compactness of the code is very important.

Single letter variables or constants should generally not be used. An exception to this rule is when it is common practice to identify something with a single letter. An example of this is the coordinate system (x, y, and z). A second exception occurs in the use of loop counter variables where it is common practice to use variables like i and j as counters in for loops.

Function names and variable names should begin with a lower case letter. An identifier consisting of multiple names SHALL have each name distinguished by making the first letter of each name part (after the first) upper case (e.g. yardsPerCarry) or by using underscores (yards_per_carry).

Constants should be named in all upper case letters. Example: const int THISYEAR = 2017;

It's generally good to avoid lower case letter ‘L’, or the letter ‘O’ in names unless they are part of normal words. This is to avoid confusion with the numbers 1 and 0. For example, is "cell" c- e - 1- ONE or c- e- 1-1?

Avoid names that differ only in case, look similar, or differ only slightly. For example, InputData, InData and DataInput will certainly be confusing if used in the same program.

Names of functions should reflect what they do (printArray), or what they return (getAge). Boolean (true/false) variable names should sound like Yes/No things — "isEmpty" and "isFinished" are possible examples.

6 Miscellaneous but Important Stuff

No line of code should go past 80 characters (sticking below 72 is even better). If a line of code becomes too long, break it up into pieces. Remember the compiler largely ignores extra whitespace in your programs that aren't in strings.

For breaking up strings, the compiler will automatically combine string literals that are on separate lines. For example, the following line of code:

fputs("This is my very long string.\nIt is so long that it will go past 80 characters and sadness will ensue.\n", stdout);
could be written instead as
fputs("This is my very long string.\n"
      "It is so long that it will go past "
      "80 characters and sadness will ensue.\n",
      stdout);

Always use the most appropriate operator or construct for the work you want it to do. Good design and clarity take precedence over optimization. Try not to declare or use more variables than are necessary.

Numerical constants ("magic numbers") should not be coded directly. The only allowable exceptions are for 0, 1 or -1, and those which are not likely to change; for example code determining if a number is even can use the number 2 since it is not likely to change. Numerical constants must have a comment explaining the value if it is not evident from the name.