EECS 211: Style Manual

  1. Compiler Warnings

    1. Your code compiles cleanly, without the compiler issuing any warnings (and this is not acheived by disabling the warnings we turn on for you in the provided Makefile).
  2. Names

    1. Field (member variable) names are appropriately descriptive of what they stand for; names shouldn’t just reiterate the type.
    2. Variable, constant, and function names are in lower_snake_case.
    3. “Small” structs and classes have lower_snake_case names, whereas “large” structs and classes have Upper_snake_case names.
    4. Preprocessor #defines are in SCREAMING_SNAKE_CASE.
  3. Variables

    1. It’s better to define a variable with an initializer later than to define it uninitialized earlier and assign it later.
      BadBetter
      some_type* x;
      
      if (some_condition) {
          x = malloc(sizeof *x);
          if (x == NULL) exit(1);
          // use x in here
      }
      
      
      
      if (some_condition) {
          some_type* x = malloc(sizeof *x);
          if (x == NULL) exit(1);
          // use x in here
      }
    2. It’s better to define a variable uninitialized than to initialize it twice.
      BadBetter
      int x = 0;
      if (scanf("%d", &x) == 1) {
          // use x in here
      }
      int x;
      if (scanf("%d", &x) == 1) {
          // use x in here
      }
    3. However, if the logic of what is and is not initialized in your program because too onerous to reason about, then it’s better to be safe than sorry with respect to undefined behavior.
  4. Booleans

    1. Don’t compare a Boolean to another Boolean, because it already is a Boolean!
      BadBetter
      if (is_good(doggo) == true) if (is_good(doggo))
      if (is_good(doggo) == false) if (!is_good(doggo))
    2. Don’t use an if just to return a Boolean you already have.
      BadBetter
      if (a < b)
          return true;
      else
          return false;
      return a < b;
    3. When you need a Boolean, use bool literals true and false, not int literals 1 and 0. C automatically converts between the two types as needed, but bool makes your code clearer because it gives the reader a more precise idea what it’s for.
  5. Formatting

    1. Lines are no longer than 80 characters.

      You can use the grep(1) command to print out the lines in a file that exceed the limit. For example, to check every .c and .h file in the src directory, run

          $ grep -nE '.{81,}' src/*.[ch]
    2. Indentation is consistent and properly reflects code structure. (Emacs can help.)
    3. Indentation is by four spaces; tabs are unacceptable because they do not display the same everywhere. (Note that this does not mean that you should avoid the Tab key—pressing Tab in Emacs will correctly align the current line using spaces, not tabs.)
    4. Long blocks of code are separated into “paragraphs” using blank lines.
    5. Infix operators generally have space on both sides.
    6. Commas and semicolons have whitespace (a space or end-of-line) after but no space before.
  6. Comments

    1. Each non-local declared entity (function, struct, class, member) has a header comment succinctly stating its purpose. (If an entity is declared multiple times, you should put this comment only on the first occurrence, preferably in the header file.)
    2. Excessive comments that inhibit readability are avoided. Assume your reader understands the language; only explain the non-obvious.
  7. In C++, Prefer C++ Ways over C Ways

    The old C way The better C++ way
    a. NULL

    nullptr

    b. malloc and free

    new and delete

    c. T stuff[N];

    (raw array)

    std::vector<T> stuff;

    (STL vector)

    d. char s[N] = {0};

    ('\0'-delimited char array)

    std::string s;

    (STL string)

    e. T* const p = &target;

    (non-null pointer with fixed referent)

    T& r = target;

    (reference)

    f. T* p = malloc(sizeof *p);

    (raw pointer)

    std::unique_ptr<T> p = std::make_unique<T>(); std::shared_ptr<T> p = std::make_shared<T>();

    (smart pointer)

    g. printf("%d␣or␣%s", z, s);

    std::cout << z << "␣or␣" << s;

    h. scanf("%d%lf", &z, &f);

    std::cin >> z >> f;

  8. C++ Class Design

    1. Constructors ensure that every object is properly initialized.
    2. Every member is protected by the strictest level of privacy that will work for it.
    3. Getters and setters are only provided where necessary, and not where they break abstraction.