-
Compiler Warnings
- 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).
-
Names
- Field (member variable) names are appropriately descriptive of what they stand for; names shouldn’t just reiterate the type.
- Variable, constant, and function names are in lower_snake_case.
- “Small” structs and classes have lower_snake_case names, whereas “large” structs and classes have Upper_snake_case names.
- Preprocessor
#define
s are in SCREAMING_SNAKE_CASE.
-
Variables
-
It’s better to define a variable with an initializer later
than to define it uninitialized earlier and assign it later.
Bad Better 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 }
-
It’s better to define a variable uninitialized than to
initialize it twice.
Bad Better int x = 0; if (scanf("%d", &x) == 1) { // use x in here }
int x; if (scanf("%d", &x) == 1) { // use x in here }
- 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.
Booleans
- Don’t compare a Boolean to another Boolean, because it
already is a Boolean!
Bad Better if (is_good(doggo) == true)
if (is_good(doggo))
if (is_good(doggo) == false)
if (!is_good(doggo))
- Don’t use an
if
just to return a Boolean you already have.Bad Better if (a < b) return true; else return false;
return a < b;
- When you need a Boolean, use
bool
literalstrue
andfalse
, notint
literals1
and0
. C automatically converts between the two types as needed, butbool
makes your code clearer because it gives the reader a more precise idea what it’s for.
Formatting
- 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]
- Indentation is consistent and properly reflects code structure. (Emacs can help.)
- 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.)
- Long blocks of code are separated into “paragraphs” using blank lines.
- Infix operators generally have space on both sides.
- Commas and semicolons have whitespace (a space or end-of-line) after but no space before.
Comments
- 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.)
- Excessive comments that inhibit readability are avoided. Assume your reader understands the language; only explain the non-obvious.
In C++, Prefer C++ Ways over C Ways
The old C way The better C++ way a. NULL
nullptr
b. malloc
andfree
new
anddelete
c. T stuff[N];
(raw array)
std::vector<T> stuff;
(STL vector)
d. char s[N] = {0};
(
'\0'
-delimitedchar
array)std::string s;
(STL string)
e. T* const p = ⌖
(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;
C++ Class Design
- Constructors ensure that every object is properly initialized.
- Every member is protected by the strictest level of privacy that will work for it.
- Getters and setters are only provided where necessary, and not where they break abstraction.
-
It’s better to define a variable with an initializer later
than to define it uninitialized earlier and assign it later.