This is for Fall 2015 (10 years ago)
Code quality standards
In this class, you will learn to write good code. That takes a different kind of skill and effort than just making a small homework assignment work. However, good code is easier to read, debug, and maintain. It also helps you avoid creating bugs in the first places.
The rules in this document are adapted from a variety of sources. While some might disagree on a few small points, most of these reflect prevailing views on C code style.
Summary
1. |
global constants |
all caps with underscores |
2. |
comments |
always explain complex code; never state the obvious (e.g., // main function) |
3. |
struct names |
camel case with first letter capitalized (e.g., PersonNode) |
4. |
internal helper functions |
name starts with underscore (e.g., _sort_helper(…)); do not declare .h file |
5. |
curly braces for if/for/… |
always |
6. |
indent body of function/if/for/… |
always |
7. |
multiple statements on one |
line never |
8. |
dead code |
never |
9. |
useless variables |
never |
10. |
vim modeline |
always; must match the tab settings you have been using |
11. |
constants |
declare with const not #define (usually) |
12. |
variable names |
descriptive (usually) |
13. |
NULL vs. 0 vs. '\0' |
NULL for addresses, 0 for numeric values, '\0' for characters |
14. |
declarations |
always initialize in declaration (usually) |
15. |
boolean values |
use true/false from stdbool.h; never use 1/0 or true/false |
16. |
array initializers |
use where possible/appropriate (usually) |
17. |
struct initializers |
use where possible/appropriate (usually) |
18. |
data type sizes |
use sizeof; never make assumptions |
19. |
printf |
use only for "official" output |
20. |
variableNames vs. variable_name |
pick one of these two and be consistent |
21. |
curly braces for if blocks |
be consistent; use one of the two styles (see below) |
22. |
curly braces for functions |
be consistent; use one of the two styles (see below) |
23. |
indent type |
be consistent; tabs or spaces; any tab size; declare tab size in vim modeline |
24. |
spacing between functions |
be consistent; one or two spaces between functions |
25. |
sizeof |
argument must be a variable name when used in an initialization or malloc(..)
This was announced 10/15 and added here 10/20. It is optional for hw06 but required thereafter. |
In case of any inconsistency, the descriptions below take precedent over this table.
Required
Global constants
Rule: Use all caps with words separated by underscores
Reason: This makes code more readable by making it clear which variables might change.
Good: const char* s = COURSE_NAME;
Comments
Rule: Comment code that is complex, or where the purpose of a block of code would be unclear at first glance. Do not include comments for things that are extremely obvious.
Struct names
Rule: Use camel case with the first letter capitalized
Reason: This makes types look very different from instances of those types.
Good: struct ListNode{ … };
Internal helper functions or global constants
Rule: Begin with an underscore.
Reason: This warns readers of your code (including you) that the meaning may be hard to understand. It also lets them know that it may change, so it's best not to refer to it from elsewhere.
Good: void _do_something_mysterious() {
}
Good: const int _MAX_BUFFER_SIZE = 128;
Note: "Internal" means things that should not be called from outside the current file or context. This rule is more important for larger projects, and is pointless for projects that have only a single file. However, real projects tend to grow, so it is a good habit to build on.
Braces for if, for, while, switch, do, …
Rule: Always required. (Do not use inside each case of a switch, but use for the switch itself.)
Reason: Even though C will let you omit the braces, if the body is just one line, it
Good: if(a < 1) {
foo();
}
Note: Apple had a major security bug due to someone omitting the braces (
article).
indent body of function/if/for/while/do/switch
Rule: Body of a function definition or if/for/while/do/switch statement must be indented, relative to the lines that contain the enclosing curly braces.
Exception: In rare cases, you might want a loop or function with an empty body. In those cases, you may have both braces on the same line as the beginning of the loop. Also, note that this rule does not apply to struct or array initializers.
Good: for(int i=0; i<5; i++) {
printf("i == %d\n", i);
}
Good: for(int i=0; i<5; i++)
{
printf("i == %d\n", i);
}
Good: (or at least acceptable)
if(a == 0) {
foo();
} else {
bar();
}
Bad: for(int i=0; i<5; i++) {
printf("i == %d\n", i);
}
Bad: for(int i=0; i<5; i++) {
{
printf("i == %d\n", i);
}
Bad: for(int i=0; i<5; i++) {
{
printf("i == %d\n", i);
}
Bad: for(int i=0; i<5; i++)
{ printf("i == %d\n", i); }
Bad: for(int i=0; i<5; i++) { printf("i == %d\n", i); }
Good: (ugly, but marginally acceptable, at least in ECE 264)
for(; s != '\0'; s++) { }
Multiple statements on one line
Rule: Not allowed
Reason: It makes code unnecessarily hard to read.
Good #1: int a = 1;
int b = 2;
Good #2: int a = 1;
char c = 'c';
Good #3: (ugly, but marginally acceptable, at least in ECE 264)
for(; *s != '\0'; s++) {}
Bad #1: int a = 1; char c = 'c';
Bad #2: for(int i=0; i < 5; i++) { printf("i == %d"); }
Dead code
Rule: Not allowed
Reason: This makes code harder to debug by concealing potential problems.
Good: int main() {
// ...
}
void _foo() {
// ...
}
Good: if(1 == 0) {
doSomething(); // will never get here!!!
}
Note: "Dead code" is code that is never called.
Useless variables
Rule: Not allowed
Reason: This makes code harder to debug by concealing potential problems.
Bad: int foo(int a) {
int b = 1; // never used!!!
return a * 10;
}
Useless assignments
Rule: Not allowed
Reason: This makes code harder to debug by concealing potential problems.
Bad:
int foo() {
int a = 0;
a = a + 10; // no effect!!!
a = 20;
return a * 10;
}
Note: Useless assignments are assignments that cannot, under any circumstances, have any effect on the output.
Exception: This does not apply variables that are initialized to 0 or NULL (or similar).
Vim modeline:
Rule: Required
Reason: This ensures that when course staff in ECE 264 view your code, they will have the same indent type that you had. It's also a good habit when working on projects where others might be using Vim (outside of this class).
Good: (for those who prefer tabs)
/* vim: set tabstop=4 shiftwidth=4 fileencoding=utf-8 noexpandtab: */
Good: (for those who prefer spaces)
/* vim: set tabstop=4 shiftwidth=4 fileencoding=utf-8 expandtab: */
Constants
Rule: Declare with const, not #define
Reason: const enforces types. #define just does a simple text substitution.
Good: const int NUM_ASSIGNMENTS = 15;
Bad: #define NUM_ASSIGNMENTS 15
Exception: It's okay to use #define for constants if you have a very specific reason. For ECE 264, you must document the reason in a comment.
Variable names
Rule: Must describe contents
Reason: This makes code more readable, especially when you come back to it later or show it to someone who didn't read it (e.g., when asking for help).
Exception: For array indices or other variables that are used only in a small portion of the code where the meaning is completely obvious at first glance, it is okay to use one or two letters (e.g., i for an index, s or a string, ch for a string, n for some other number). Also, short names are okay for small examples to illustrate programming concepts where brevity is a goal in itself.
NULL vs. 0 vs. '\0'
Rule: Use NULL for addresses (i.e., null pointers), 0 for numeric values, and '\0' for characters (e.g., string terminator).
Reason: This makes code more readable and avoids bugs.
Note: Internally, NULL, 0, and '\0' are all stored as 0.
Declarations
Rule: Declarations initialize
Good: int i = 0;
Bad:
int i;
i = 0;
Boolean values
Rule: Do not use 1 and 0 for true and false. Instead, use the true and false values and the bool type from the stdbool.h library. We are using the C99 version of the C language standard in this class, which includes stdbool.
Note: We are using the C99 version of the C language standard in this class, which includes stdbool.
Good: bool is_positive = (a > 0);
Bad: int is_positive = (a > 0);
Reason: The constants true and false make your code more readable because express what you mean. Since stdbool.h is part of the standard library, they will be readable by others and interoperable with other code.
Array initializers
Rule: Use where possible (and appropriate)
Good: int numbers[] = {1, 2, 3};
Bad: int numbers[3];
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
Reason: Array initializers are clearer to read, and avoid certain types of bugs (e.g., numbers[3] = 4 for an array of size 3.).
Struct initializers
Rule: Use where possible (and appropriate)
Good: struct Point point1 = {.x = 1, .y = 2};
Bad #1: struct Point point1;
point1.x = 1;
point1.y = 2;
Bad #2: struct Point point1 = {1, 2};
Reason: Struct initializers are clearer to read. They also prevent mistakes where programmers sometimes forget to initialize one field, or perhaps edit the code later and inadvertently delete part of the initialization. Bad #2 is actually fine in earlier versions of c (e.g. c89), but the new syntax shown as Good prevents errors where you get the order wrong.
Note: This was added 10/1/2015.
Data type sizes
Rule: Do not make assumptions about the size of any data type. Use the sizeof(…) operator instead.
Good: char* s = malloc(n * sizeof(*s));
Bad: int* array = malloc(n * 4);
printf
Rule: Use printf only for "official" output. For debugging, use #define to create a log macro that you can turn off with a flag to the compiler (i.e., conditional compilation).
Good: #ifdef DBG_LOG
#define dbg_log(...) printf(__VA_ARGS__)
#else
#define dbg_log
#endif
int main() {
dbg_log("Starting...")
}
Bad: int main() {
printf("Starting...")
}
assert
Rule: Use assert only to catch bugs in your code. Do not use assert to catch anything that the user could control, external issues with the system, or anything else outside of your code.
Your choice - pick one and be consistent within a given project
Multi-word variable and function names
Option A: int list_idx = 0;
Option B: lowercase camel case
int listIdx = 0;
Braces for if blocks
Braces for functions
Indent type
Option A: tabs
Option B: spaces
Note: This must be reflected in your vim modeline (see above). Also, you are welcome to choose a different tab size (i.e., instead of 4).
Reason: If you mix tabs and spaces, your file layout may be hard to read for others with a different tabstop setting.
Spacing
Option A: one space between functions
Option B: two spaces between functions
Updates
9/24: Added links to Google C++ Style Guide; added reason for using stdbool.h; no substantive changes to code standards
10/7: Added section on indent body of function/if/for/while/do/switch. Proper indenting was previously discussed in lecture and practiced on HW01.