Style Guide
Code Layout
Code Layout
Use the following order for laying out your code:
- Header comment
- Inclusion of system header files
- Inclusion of local header files
- Definition of constants (
#define
s andenum
s)- Definition of data types (
struct
s andtypedef
s)- Global variables (try to avoid these altogether)
- Function prototypes
main()
function- Function definitions
Code Layout
Use the following order for laying out your code:
- Header comment
- Inclusion of system header files
- Inclusion of local header files
- Definition of constants (
#define
s andenum
s)- Definition of data types (
struct
s andtypedef
s)- Global variables (try to avoid these altogether)
- Function prototypes
main()
function- Function definitions
// COMP2521 Lab 10 Exercise - Turing's test // // Pretend to be a human in text-based conversation. // https://en.wikipedia.org/wiki/Turing_test // // Authors: // Grace Hopper (z1234567@unsw.edu.au) // Ada Lovelace (z5000000@unsw.edu.au) // // Written: 19/03/2019 #include <stdio.h> #include <string.h> #include <math.h> #include "language.h" #define MEANING_OF_LIFE 42 // struct definitions would go here // function prototypes would go here int main(void) { printf("Hello!\n"); return 0; } // function definitions would go here
Header Comment
All programs should have a header comment.
For small lab exercises the header comment should contain at least:
- The names of the author(s) - include your UNSW zID with any code you submit at UNSW
- The date it was written
- A description of what the program does
For larger programs such as assignments, also include:
- An overview of how the program works, including any bugs or limitations
- Any other information that might help a reader understand the code
- References to resources used in constructing the code
It is not necessary to include licensing information, but it is commonly present in header comments of code outside uni.
Header Comment
All programs should have a header comment.
For small lab exercises the header comment should contain at least:
- The names of the author(s) - include your UNSW zID with any code you submit at UNSW
- The date it was written
- A description of what the program does
For larger programs such as assignments, also include:
- An overview of how the program works, including any bugs or limitations
- Any other information that might help a reader understand the code
- References to resources used in constructing the code
It is not necessary to include licensing information, but it is commonly present in header comments of code outside uni.
// COMP2521 Lab 10 Exercise - Turing's test // // Pretend to be a human in text-based conversation. // https://en.wikipedia.org/wiki/Turing_test // // Authors: // Grace Hopper (z1234567@unsw.edu.au) // Ada Lovelace (z5000000@unsw.edu.au) // // Written: 19/03/2019
Braces
Braces must be used with
if
,else
,while
,for
,do ... while
andswitch
statements. It is acceptable to omit braces if the body is short and on the same line as the condition.Compact braces should be used, which means:
- No line break before the opening brace.
- Line break after the opening brace.
- Line break before the closing brace.
- Line break after the closing brace, except when it is followed by
else
or when it is followed by thewhile
keyword in ado ... while
statement.- There is an exception for the previous point - for long conditions (of
if
,while
,for
anddo ... while
statements) and functions that have long parameter lists (which span multiple lines), the opening brace may appear on its own line.
Braces
Braces must be used with
if
,else
,while
,for
,do ... while
andswitch
statements. It is acceptable to omit braces if the body is short and on the same line as the condition.Compact braces should be used, which means:
- No line break before the opening brace.
- Line break after the opening brace.
- Line break before the closing brace.
- Line break after the closing brace, except when it is followed by
else
or when it is followed by thewhile
keyword in ado ... while
statement.- There is an exception for the previous point - for long conditions (of
if
,while
,for
anddo ... while
statements) and functions that have long parameter lists (which span multiple lines), the opening brace may appear on its own line.
For functions, the opening brace should be on the same line as the function signature (unless the function has a long parameter list which spans multiple lines, in which case the opening brace may appear on its own line.) The closing brace should be on its own line and not indented.
For if
statements, the opening brace should be on the same line as the if
and the condition. The closing brace should be on the same line as the following else
, if there is one, or on its own line otherwise, and should be indented to the same level as the matching if
.
For while
and for
loops, the opening brace should be on the same line as the loop condition (unless the condition is long and spans multiple lines, in which case the opening brace may appear on its own line). The closing brace should be on its own line and should be indented to the same level as the matching while
/for
.
For do ... while
loops, the opening brace should be on the same line as the do
. The closing brace should on the same line as the while
, and should be indented to the same level as the do
.
Braces
Braces must be used with
if
,else
,while
,for
,do ... while
andswitch
statements. It is acceptable to omit braces if the body is short and on the same line as the condition.Compact braces should be used, which means:
- No line break before the opening brace.
- Line break after the opening brace.
- Line break before the closing brace.
- Line break after the closing brace, except when it is followed by
else
or when it is followed by thewhile
keyword in ado ... while
statement.- There is an exception for the previous point - for long conditions (of
if
,while
,for
anddo ... while
statements) and functions that have long parameter lists (which span multiple lines), the opening brace may appear on its own line.
int someFunction(int a, int b) { if (a > b) { // do something } else if (a < b) { // do something } else { // do something } int i = 0; while (i <= 10) { // do something i++; } for (int j = 10; j >= 0; j--) { // do something } int k = 0; do { // do something k++; } while (k <= 10); if (a == 0) return 0; } void functionWithManyParameters(int a, int b, int c, int d, int e, int f, int g, int h) { // do something }
Braces
Braces must be used with
if
,else
,while
,for
,do ... while
andswitch
statements. It is acceptable to omit braces if the body is short and on the same line as the condition.Compact braces should be used, which means:
- No line break before the opening brace.
- Line break after the opening brace.
- Line break before the closing brace.
- Line break after the closing brace, except when it is followed by
else
or when it is followed by thewhile
keyword in ado ... while
statement.- There is an exception for the previous point - for long conditions (of
if
,while
,for
anddo ... while
statements) and functions that have long parameter lists (which span multiple lines), the opening brace may appear on its own line.
// BAD - the code between the {} should be indented if (i % 2 == 0) { printf("even"); } else { printf("odd"); } // BAD - put newlines after { and before and after } while (i < 0) { i++; } // BAD - always use braces on if statements and while loops // (unless the body is short and on the same line as the condition) while (i < 0) i++; // BAD - closing braces don't line up if (a > b) { // do something } else if (b < a) { // do something } else { // do something }
Indentation
Between any pair of braces, the indentation level should increase by one stop. You may use either tabs or spaces for indentation, but do not use both approaches in the same program.
For long expressions that span multiple lines, the indentation level of each line (except the first) should increase by two stops or by an amount that improves readability (e.g., aligning the arguments of long function calls).
Indentation
Between any pair of braces, the indentation level should increase by one stop. You may use either tabs or spaces for indentation, but do not use both approaches in the same program.
For long expressions that span multiple lines, the indentation level of each line (except the first) should increase by two stops or by an amount that improves readability (e.g., aligning the arguments of long function calls).
if (something && somethingElse && somethingElseAgain && anotherThing && evenMoreThings && somethingElseEntirely && thereAreTooManyConditions && thisLineIsTooLong) { ... } functionWithManyArguments(something, somethingElse, somethingElseAgain, anotherThing, evenMoreThings);
Spacing
Include a space after keywords such as:
if, else, while, for, do, returnInclude a space on each side of binary operators such as:
= + - < > * / % <= >= == !=Do not include a space after prefix unary operators such as:
& * !Do not include a space before postfix unary operators such as:
++ --Do not include a space on each side of member access operators such as:
. ->When declaring pointers, the
*
should go after the space, not before. For example:int *ptr; int **ptr;
Spacing
Include a space after keywords such as:
if, else, while, for, do, returnInclude a space on each side of binary operators such as:
= + - < > * / % <= >= == !=Do not include a space after prefix unary operators such as:
& * !Do not include a space before postfix unary operators such as:
++ --Do not include a space on each side of member access operators such as:
. ->When declaring pointers, the
*
should go after the space, not before. For example:int *ptr; int **ptr;
// This is correct: if (a + b > c) { i++; curr = curr->next; } // This is *not* correct: if(a+b>c) { i ++; curr = curr -> next; }
Statements
Only one executable statement should be used per line of code.
Statements
Only one executable statement should be used per line of code.
Don't put multiple statements on a single line - it makes code very hard to read.
Line Width
Keep lines under 80 characters.
Line Width
Keep lines under 80 characters.
Break long lines up to keep them under 80 characters, unless keeping it as a longer line is significantly more readable.
Line Width
Keep lines under 80 characters.
// This is WAY too long: if (something && somethingElse && somethingElseAgain && anotherThing && evenMoreThings && somethingElseEntirely && thereAreTooManyConditions && thisLineIsTooLong) { // This is better (but consider using a function instead): if (something && somethingElse && somethingElseAgain && anotherThing && evenMoreThings && somethingElseEntirely && thereAreTooManyConditions && thisLineIsTooLong) { // This is best: if (thoseThingsAreTrue()) {
Constants
Constants
Use
#define
s to give constants names.
enum
s may be used to give groups of related constants names.
#define
andenum
names must be written inALL_CAPS_WITH_UNDERSCORES
.
Constants
Use
#define
s to give constants names.
enum
s may be used to give groups of related constants names.
#define
andenum
names must be written inALL_CAPS_WITH_UNDERSCORES
.
Unexplained numbers, often called magic numbers, can be hard to understand.
If a number appears multiple times in the code, bugs are created when the is code changed but not all occurrences of the number are changed.
A similar problem is that a number may appear in the code for multiple reasons, e.g., a commonly used number like 10, and if the code needs to be changed it can be hard to determine which occurrences of the number should be changed.
enum
is also used by programmers to give constants names in C.
Constants
Use
#define
s to give constants names.
enum
s may be used to give groups of related constants names.
#define
andenum
names must be written inALL_CAPS_WITH_UNDERSCORES
.
#define DAYS_OF_WEEK 7 typedef enum { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY, } Weekday; // .... int array[DAYS_OF_WEEK]; int i = 0; while (i < DAYS_OF_WEEK) { a[i] = i; i++; }
Constants
Use
#define
s to give constants names.
enum
s may be used to give groups of related constants names.
#define
andenum
names must be written inALL_CAPS_WITH_UNDERSCORES
.
// BAD - the constant 7 should be given a name int array[7]; int i = 0; while (i < 7) { a[i] = i; i++; }
Variables
Variable Names
Descriptive variable names should always be used where possible.
Short variable names such as
x
ori
are acceptable if there is no appropriate long descriptive name (which is often the case for variables used as loop counters), the meaning of the variable is obvious, or if the variable is used in a very small scope. Examples of acceptable short variable names are:
s
for stringl
for listt
for treeg
for graphn
for nodev
for vertex or valuev
andw
for a pair of verticesi
,j
andk
for loop countersVariable names must begin with a lower case letter.
Multi-word variable names be in either snake_case or camelCase. Use only one of these approaches in your program. This should be consistent with your style for function names.
Declaring Variables
Variables should be declared when (or close to when) they are first assigned a value, and close to when they are first used.
Declaring Variables
Variables should be declared when (or close to when) they are first assigned a value, and close to when they are first used.
int i = 0; int j; if (scanf("%d", &j) == 1) { // do something }
Declaring Variables
Variables should be declared when (or close to when) they are first assigned a value, and close to when they are first used.
int i; // a lot of other code not involving i i = 0;
Numeric Variables
Use only the types
bool
,char
,int
anddouble
for numeric variables.You should not need to use other types such as
short
andfloat
in this course.
Numeric Variables
Use only the types
bool
,char
,int
anddouble
for numeric variables.You should not need to use other types such as
short
andfloat
in this course.
char c = 'A'; int age = 18; double height = 1.65;
Numeric Variables
Use only the types
bool
,char
,int
anddouble
for numeric variables.You should not need to use other types such as
short
andfloat
in this course.
short age = 18; float height = 1.65; // common C programming bug - getchar returns an int char c = getchar();
Loop Variables
It is recommended variables controlling
while
loops be declared before the loop and used only to control that loop.It is recommended variables controlling
for
loops be declared as part of the for loop.
Loop Variables
It is recommended variables controlling
while
loops be declared before the loop and used only to control that loop.It is recommended variables controlling
for
loops be declared as part of the for loop.
int i = 0; while (i < MAXIMUM) { // do something i++; } for (int i = 0; i < MAXIMUM; i++) { // do something }
Global and Static Variables
Global variables and static variables should not be used unless you are explicitly permitted.
Global and Static Variables
Global variables and static variables should not be used unless you are explicitly permitted.
Global variables are variables that are declared outside of a function.
Global variables make code hard to read, maintain and parallelize.
There are important uses for global variables but not in this course.
The only acceptable use likely in this course would be a global variable to control debug output.
All the above also applies to variables inside functions which have the static
qualifier.
The values in static
variables persist across multiple calls to the function creating similar problems.
There is likely to be a major style deduction if you use global variables or static variables without permission.
Variable-Length Arrays
Variable-length arrays should not be used in this course.
Variable-Length Arrays
Variable-length arrays should not be used in this course.
Variable-length arrays are arrays whose size is not known when the program compiles.
Arrays should only be declared with a constant size, such as with a literal integer, e.g. int array[12]
, or with a constant, e.g. int array[MAX_LENGTH]
. Arrays should not be declared with a size using a variable, such as int array[x]
.
Many C implementations poorly handle local variables consuming more than a small amount of memory, often terminating with a cryptic message if the storage used for local variables exceeds a few megabytes.
This applies to both arrays with a fixed size at compile-time and variable-length arrays.
For this reason, programmers often use malloc for larger arrays (10000+ elements). malloc gives the programmer complete control over storage lifetime, but as a consequence introduces other classes of bugs.
Libraries
Libraries
Unless a task specifies otherwise, you are only permitted to call your own functions and functions from the standard C library.
The standard C library includes functions declared in
assert.h
,ctype.h
,math.h
,stdbool.h
,stdio.h
,stdlib.h
andstring.h
.You are not permitted to use functions such as
read
,write
andopen
defined inunistd.h
. They are not part of the standard C library.Note: the
math.h
library is part of the standard C library and are you are allowed to use it. gcc and clang require you to compile with the-lm
option to use the math library.
External Programs
Unless a task specifies otherwise, you are not permitted to run other programs from your C program, using the function
system
or otherwise.
Warnings, Errors & Undefined Behaviour
Compile-time Warnings
Your code should not generate any warnings or errors when compiled with
gcc -Wall -Werror
.You should assume warnings from gcc are errors that need to be fixed.
Invalid C, Run-time errors & Undefined Behaviour
Your code should not perform any operation which results in undefined behaviour or a runtime error (when given any input that is valid for the exercise).
For example, you must ensure your code does not access uninitialized variables, overflow numeric variables, access non-existent illegal array elements or dereference NULL pointers.
All of these will heavily penalized in marking.
Invalid C, Run-time errors & Undefined Behaviour
Your code should not perform any operation which results in undefined behaviour or a runtime error (when given any input that is valid for the exercise).
For example, you must ensure your code does not access uninitialized variables, overflow numeric variables, access non-existent illegal array elements or dereference NULL pointers.
All of these will heavily penalized in marking.
Novice programmers need to be very careful that their code is not invalid.
For example, they need to carefully consider whether an expression used to index an array will always result in a valid array index.
Invalid C is a frequent source of serious security exploits - for example attackers often exploit invalid array indices and integer overflows.
Functions
Repeated Code
Do not cut-and-paste code. The same code should not appear twice in your program. Avoid repeating code by defining a function and calling it twice (or more).
Repeated Code
Do not cut-and-paste code. The same code should not appear twice in your program. Avoid repeating code by defining a function and calling it twice (or more).
Repeated code makes your program hard to read and hard to modify.
Function Purpose
Functions should have one clearly defined purpose and should be short.
"Functions should do one thing, and one thing only."
Function Purpose
Functions should have one clearly defined purpose and should be short.
"Functions should do one thing, and one thing only."
If you have a function that performs two smaller tasks in sequence, make a function for each of those smaller tasks and call them from the more abstract function. Aim for your functions to be 30 lines or less (not including comments). If they are long, think of how you can break them up into smaller functions.
Function Prototypes / Function Signatures
Function prototypes must be used for all functions (except
main
) and must come before all functions, appearing in the same order as the function implementations.Function signatures must have the return type, function name, and state the argument list.
Function Prototypes / Function Signatures
Function prototypes must be used for all functions (except
main
) and must come before all functions, appearing in the same order as the function implementations.Function signatures must have the return type, function name, and state the argument list.
Function prototypes describe functions by telling the compiler (and anyone reading the program):
- The type of value the function returns
- The name of the function
- The type and name of any arguments to the function
Function Comments
Every function must have a comment placed before the function implementation describing the purpose of the function and any side-effects the function has. Note: You do not need to do this for functions that have already been provided to you.
Function Comments
Every function must have a comment placed before the function implementation describing the purpose of the function and any side-effects the function has. Note: You do not need to do this for functions that have already been provided to you.
// return pointer to last node in list // NULL is returned if list is empty struct node *last(struct node *head) {
Function Names
Function names should be descriptive, typically containing multiple words.
Names for most functions (exceptions below) should be in either snake_case or camelCase. Use only one of these approaches in your program. This should be consistent with your style for variable names.
When writing functions for ADTs, you should keep the naming consistent by including the name of the ADT at the beginning. Names of ADT interface functions should be in PascalCase. For example,
SetNew
,SetFree
,SetAdd
,SetContains
,SetRemove
.
Function Arguments
Argument names in a function prototype should match the names used in the function implementation and should always be used in the prototype.
Long function argument lists may be broken over multiple lines.
Functions that do not take any arguments should use
(void)
instead of an empty argument list.
Function Arguments
Argument names in a function prototype should match the names used in the function implementation and should always be used in the prototype.
Long function argument lists may be broken over multiple lines.
Functions that do not take any arguments should use
(void)
instead of an empty argument list.
A space may optionally be used between the function name and argument list.
The return type, function name, and start of the argument list should all be on the same line.
If the argument list is too long to fit on a single line with the function name and return type, it may be split across multiple lines, with the additional lines being indented by 2 stops (8 spaces) or aligned with the first argument in the first line.
Function and argument naming is discussed in the functions section of this document.
Function Arguments
Argument names in a function prototype should match the names used in the function implementation and should always be used in the prototype.
Long function argument lists may be broken over multiple lines.
Functions that do not take any arguments should use
(void)
instead of an empty argument list.
// A short function int aFunctionWithNoArguments(void); // A short function int aSimpleFunction(int argumentA, char *argumentB); // A function with a lot of arguments int aLongFunction(int argumentA, int argumentB, int argumentC, int argumentD, int argumentE, int argumentF); // Another function with a lot of arguments int anotherLongFunction(int argumentA, int argumentB, int argumentC, int argumentD, int argumentE, int argumentF); // Another function with a lot of arguments. // Note that the closing ');' is on a line by itself to make it clearer where // the prototype ends. int anotherLongFunction( int argumentA, int argumentB, int argumentC, int argumentD, int argumentE, int argumentF );
Return
Do not overuse return statements.
Use multiple returns only if this makes your function less complex or more readable.
For example, it is acceptable to have an early
return
statement(s) to handle error conditions or special cases, as well as another return statement for the computation of the function's main result.
Be Careful With These C Features
break
Use
break
statements sensibly.
break
Use
break
statements sensibly.
Novice programmers often over-use break
statements, producing confusing and difficult to debug code.
Use break
only when you are sure that it makes your code simpler and more readable.
continue
Use
continue
statements sensibly.
continue
Use
continue
statements sensibly.
Novice programmers often misuse and over-use continue
statements, producing confusing and difficult to debug code.
It is best not to use continue
are until you are a more experienced programmer - and then use it carefully ensuring it makes your code more readable.
switch
Use
switch
statements sensibly.
switch
Use
switch
statements sensibly.
The C switch
statement can have subtle and confusing behaviour.
When using the switch
statement, use break
statements to avoid fall-through behaviour, unless fall-through is intended.
Ternary if
Use the ternary if construct sensibly.
Ternary if
Use the ternary if construct sensibly.
Unlike some other languages, C is a statement-oriented language; this means, for example, an if
statement doesn't have an inherent value and can't be used inside an expression.
C does have an operator similar to the if statement, the ternary if, which can be used in expressions. The ternary if construct is:
(condition)
? true_expression
: false_expression
Novice programmers attempting to use the ternary if typically construct unreadable code and confuse themselves.
Most experienced programmers either avoid the ternary if entirely, or use it in very limited circumstances.
do
... while
Use the
do { ... } while (condition);
construct sensibly.
do
... while
Use the
do { ... } while (condition);
construct sensibly.
The do { ... } while (condition);
construct is similar to a normal while loop, except the body will always run once before the condition is checked.
Novice programmers often confuse themselves and construct hard-to-debug code using do...while
loops.
Many experienced programmers avoid do...while
entirely, but they are commonly used in some contexts such as kernel programming by experienced programmers.
Avoid These C Features
Macros
Avoid
#define
statements with parameters (usually called macros) unless you are experienced with them.Define a function instead.
Macros
Avoid
#define
statements with parameters (usually called macros) unless you are experienced with them.Define a function instead.
Macros introduce a nasty class of bugs due to their implementation as textual pre-processing.
Macros were primarily used for efficiency in the past. This is no longer needed.
Modern C compilers will produce the same code for functions as for macros, without the risk of nasty bugs.
Pointer Arithmetic
Avoid pointer arithmetic. Use array indices instead.
Pointer Arithmetic
Avoid pointer arithmetic. Use array indices instead.
Experienced programmers use pointer arithmetic to produce succinct idiomatic code.
Novice programmers often confuse themselves by trying to use pointer arithmetic. Any code using pointer arithmetic can also be written using array indices. Use array indices until you are an experienced programmer.
Type Cast
Avoid type casts, except when converting between numeric types such as from
int
todouble
or when using standard library functions such asbsearch
orqsort
.
Type Cast
Avoid type casts, except when converting between numeric types such as from
int
todouble
or when using standard library functions such asbsearch
orqsort
.
Safe use of type casts, other than between numeric types, involves a deep knowledge of C semantics.
A need for other uses for type casts should not arise in this course, and other uses for type casts should be avoided.
Type Cast
Avoid type casts, except when converting between numeric types such as from
int
todouble
or when using standard library functions such asbsearch
orqsort
.
int x = 42; int y = 11; double f; f = x / ((double) y); // convert y to a double
Goto
Do not use
goto
.There will be a major style deduction if you use
goto
.
Goto
Do not use
goto
.There will be a major style deduction if you use
goto
.
Most programmers completely avoid use of the goto
statement because it can produce incomprehensible code.
Introductory programming courses always ban use of goto
to help beginner programmers learn better coding techniques.
There are very limited circumstances, e.g., writing device drivers or emulators, where goto
's use is appropriate, but such circumstances will not arise in this course.
Comma Operator
Avoid using the comma operator to combine multiple expressions.
Comma Operator
Avoid using the comma operator to combine multiple expressions.
Novice programmers attempting to use the comma operator typically construct unreadable code and confuse themselves.
union
Do not use
union
s; usestruct
s instead.
union
Do not use
union
s; usestruct
s instead.
Unions allow a polymorphic datatype to occupy less space than if a struct
was used. They are sometimes useful for embedded programming and for building language implementations.
No need for unions should arise in this course.
Unions introduce a nasty class of bugs which are hard to debug and are best avoided by novice programmers.
Comments
Comment Use
Comment Use
Comment Use
Comment Agreement
Trailing Comments