C/C++ Style Guide

Programming style is one of the holy wars of computer programming. However, regardless of your tastes one thing is certain: when in Rome, do as the Romans do. That is, learning the accepted style of a programming language is just as important as learning the language itself.

It seems that every company or group has its own style guidelines (e.g., the linux kernel C guide, the Google C++ style guide, or the Mozilla coding style guide). Regardless, for consistency's sake, we'll stick to a set of common rules for our code. You won't lose points for a few small style violations; it's only if your files flagrantly violate style that you will lose points.

(This guide borrows heavily from the OCaml style guide for old iterations of CIS 500.)

Indentation and Spacing

  1. 80 Column Limit: No line of code can have more than 80 columns. Using more than 80 columns causes your code to wrap around to the next line which is devastating to the readability of your code.
  2. No Tab Characters: Do not use the tab character (0x09). Instead, use spaces to control indenting. Ensure that your editor of choice uses spaces when you insert a tab (i.e., soft tabs).
  3. Indent Two or Four Spaces: Most lines that indent code should only indent by two or four spaces more than the previous line of code. Pick one and be consistent.
  4. Line Breaks: Obviously the best way to stay within the 80 character limit imposed by the rule above is pressing the enter key every once in a while. Including empty lines should only be done between declarations (e.g., class definitions or field/method definitions within classes) and should be restricted to a single empty line at most.

    If a declaration or expression extends over 80 characters, indent to the opening delimeter of the declaration or expression. For example

    bool LargeMethod(FirstVeryLargeParamterType t1,
                     SecondVeryLargeParameterType t2,
                     ShortThirdParameterType t3)
    {
        return (SomeBooleanFunction(t1, t2)    ||
                AnotherBooleanFunction(t2, t3) ||
                SomeBooleanFunction(t1, t3));
    }
    
    uses line breaks and indention to break up long parameter lists and boolean expressions.
  5. Use K&R or Allman style braces: K&R looks like this:
    namespace N {
        class C {
            void f() {
                int x;
            }
        };
    }

    Allman style braces push the opening brace onto its own line:

    namespace N
    {
        class C
        {
            void f()
            {
                int x;
            }
        };
    }
    

    Pick a style of braces and consistently use that style on any given homework or the project.

Comments

  1. Comments go above the code they reference: For the sake of consistency (and to avoid issues with the 80 column limit), place comments above code. (If the comment is super-short, you can get away with placing it off to the side, but don't do it too often.) For example,
    /* This is a well-placed comment. */
    #define AN_IMPORTANT_NUMBER 42
    
    // So is this.
    #define A_SOMEWHAT_IMPORTANT_NUMBER 42
    
    versus other places, e.g.,
    #define AN_IMPORTANT_NUMBER 42      // This is a badly placed comment off to the side.
    /* ...or a comment badly placed below. */
    
  2. Avoid useless comments: Comments that merely repeat the code they reference or state the obvious are a travesty to programmers. Comments should state the invariants, the non-obvious, or any references that have more information about the code.
  3. Avoid over-commenting: Incredibly long comments are not very useful. Long comments should only appear at the top of a file --- here you should explain the overall design of the code and reference any sources that have more information about the algorithms or data structures. All other comments in the file should be as short as possible. After all, brevity is the soul of wit. Most often the best place for any comment is just before a function declaration. You should only need to comment inside the body of a function if it is extremely long or complicated --- variable naming should normally be enough.
  4. Take credit for your code: At the top of each file, include a brief comment that includes the file name, your name and Penn ID, and a short description of what the file contains/does. See hello_world.c from Lecture 1 for an example.

Readability

  1. No "Magic Numbers": Using "magic numbers" (numbers directly in code) is a bad programming practice. It can make code more difficult to parse, lead to inadvertant errors, and is a pain if every instance of the number needs to be changed. Instead, use #define to give the "magic number" a meaningful name, and use that in your code instead. (As a rule of thumb, "magic numbers" are any number other than 0 or 1, except for where #define wouldn't work (like in printf formatting)).
  2. Aesthetics: This goes hand in hand with limiting columns to 80 characters -- strive to make your code aesthetically pleasing, and it will also be more readable. A good example is below; although both the left and right sides are the same exact variable declarations, the right side is much easier to read with a quick glance.
    int choice;                                  int   choice;
    int i = 0;                                   int   i       = 0;
    float pi = 3.14159;                          float pi      = 3.14159;
    float e = 2.71828;                           float e       = 2.71828;
    char *str1 = "hello world";                  char  *str1   = "hello world";
    char str2[] = "testing123";                  char   str2[] = "testing123";
    
  3. Spacing: Try to use the same rules as those that are used in CIS 110. For example, use a single space on either side of mathematical operators, so that it looks like x + y and not x+y, and only put spacing outside parentheses, not inside, so it should look like a + (b + c) and not a+(b + c) or a + ( b + c ).

Naming

  1. Use meaningful names: Variable names should describe what they are for. Distinguishing what a variable references is best done by following a particular naming convention (see suggestion below). Variable names should be words or combinations of words except in special cases where single letter variables are understood, e.g., i and j for loop variables.
  2. Case and naming conventions: The following table outlines the capitalization conventions we will use for names:
    Identifier Case Example
    Class Pascal Case AppDomain
    Method Pascal Case ToString
    Parameters, Locals, and Fields Camel Case myVariable
    Namespace Lower-case-underscores std::my_namespace
    Macros Angry MY_AWESOME_MACRO

    Definitions: Pascal case capitalizes the first letter of each word in the name. Camel case is the exact same as Pascal, except it leaves the first letter uncapitalized. Angry case is all-caps with words separate by underscores.

  3. Don't abuse typedef, #include, #define or using namespace in header files: Only typedef types that would be useful to someone who #includes the header file, because whatever you typedef in a header file carries over to any file that includes it. When it comes to using namespace, you usually shouldn't put this line in the header file for the same reason --- it automatically carries over to any file that #includes it, and could cause some name clashes.

Verbosity

  1. Don't rewrite existing code: The C and C++ standard libraries have a number of functions and data structures --- use them (unless otherwise stated)!
  2. Boolean Zen: Remember that the type of the condition in an if-statement is bool. Avoid redundancy in your condition if you already have a value of type bool. For example
    if (foo == true) { /* ... */ }
    
    is redundant as foo must be a boolean variable. Therefore, we should rewrite this as
    if (foo) { /* ... */ }