COMP1511 18s1 (webcms)
COMP1511 Style Guide
COMP1511 18s1 (flask)

Contents

Recommended Code Layout Variables Libraries Warnings, Errors and Undefined Behaviour Functions Avoid These C Features

 

You should take time to read this file carefully whenever new syntax is introduced in lectures.

You should come back and read this as you learn new syntax and concepts in lectures, so don't worry if you don't understand everything here yet.

Permitted Language Features

Language Features

If you do not have much coding experience, it is strongly recommended you only use C language features that have been described in lectures. You will find it much easier to follow the same collective path as the rest of the class.

If you have significant programming experience you are in general free to use language features you have learned outside this course, unless it is explicitly forbidden below. But please bear in mind you may not be able to get assistance from tutors, or in the course forum with your code.

Recommended Code Layout

Braces and Indentation

Functions, loops, and ifs should always use braces and should use compact braces.

Braces and Indentation

Functions, loops, and ifs should always use braces and should use compact braces.

For functions, the opening brace ({) should be on the same line as the function prototype. The closing brace (}) should be on its own line and not indented.

For if, the opening brace ({) should be in the same line as if and the loop condition. The closing brace should be on its own line and should line up with the i in the matching if.

For else and else if, the else should be on the same line as the closing brace (}) before it. The opening brace ({) should be on the same line as the else, or the else if and the condition. The closing brace (}) should be on its own line and should line up with the closing brace (}) before it.

For loops, the opening brace ({) should be on the same line as while and the loop condition. The closing brace (}) should be on its own line and should line up with the w in the matching while.

Braces and Indentation

Functions, loops, and ifs should always use braces and should use compact braces.

int someFunction(int a, int b) { if (a > b) { // do something } else if (b < a) { // do something } else { // do something } int i = 0; while (i < 0) { // do something i++; } }

Braces and Indentation

Functions, loops, and ifs should always use braces and should use compact braces.

// BAD - the code between the {} should be indented if (i % 2 == 0) { printf("even"); } else { printf("odd"); } // BAD - put newlines after { and after } while (i < 0) { i++; } // BAD - always use braces on ifs and whiles 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 4 spaces. Spaces should be used for all indentation.

Spaces

Use a space after keywords such as:

if, while, for, return

Use a space on each side of binary operators such as

=  +  -  <  >  *  /  %   <=  >=  ==  !=

Do not use space after prefix unary operators such as

& * !

Do not use space before postfix unary operators such as

++ --
Do not use space on each side of member access operators such as
. ->
Spaces

Use a space after keywords such as:

if, while, for, return

Use a space on each side of binary operators such as

=  +  -  <  >  *  /  %   <=  >=  ==  !=

Do not use space after prefix unary operators such as

& * !

Do not use space before postfix unary operators such as

++ --
Do not use space on each side of member access operators such as
. ->

// 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 to give constants names.

#defines must be written in ALL_CAPS_WITH_UNDERSCORES.

Constants

Use #define to give constants names.

#defines must be written in ALL_CAPS_WITH_UNDERSCORES.

Unexplained numbers,often called magic numbers, appearing in code make it hard to understand.

If a number appears multiple times in the code, bugs are created when the 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 be programmers to give constants names in C.

Constants

Use #define to give constants names.

#defines must be written in ALL_CAPS_WITH_UNDERSCORES.

#define DAYS_OF_WEEK 7 // .... int array[DAYS_OF_WEEK]; int i = 0; while (i < DAYS_OF_WEEK) { a[i] = i; i++; }

Constants

Use #define to give constants names.

#defines must be written in ALL_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 or i are acceptable if there is no appropriate long descriptive name. This is often the case for variables used as loop counters.

Variable 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.

#define names must be in ALL_CAPS_WITH_UNDERSCORES.

Declaring Variables

Variables can be declared when they are first assigned a value, as part of assigning the value.

Declaring Variables

Variables can be declared when they are first assigned a value, as part of assigning the value.

// This is fine: int i = 0; // Probably avoid this: int i; [insert a lot of other code here] i = 0;

Numeric Variables

Use only the types int and double for numeric variables.

You should not need to use other types such as short and float in this course.

You will need to use the type char for arrays, but do not use the type char for simple variables.

Numeric Variables

Use only the types int and double for numeric variables.

You should not need to use other types such as short and float in this course.

You will need to use the type char for arrays, but do not use the type char for simple variables.

// DO NOT do this short age = 18; float height = 1.65; char c = 'A'; // Instead do this: int age = 18; double height = 1.65; int c = 'A'; // 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 should not be used. Static variables should not be used.

Global and Static Variables

Global variables should not be used. Static variables should not be used.

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.

There is likely to be a major style deduction if you use global variables.

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 exactly similar problems.

There is likely to be a major style deduction if you use static variables.

Libraries

Libraries

Unless a task specifies otherwise, you are only permitted to call functions from the standard C library.

The standard C library includes functions declared in stdio.h, stdlib.h, string.h, math.h and ctype.h.

You are not permitted to use functions such as read, write and open defined in unistd.h. They are not part of the standard C library.

You are not permitted to use the -l option to dcc to include other libraries.

Note: the math.h library is part of the standard C library and are you are allowed to use it. dcc includes it by default, but gcc and clang require you to compile with a -lm option to use the maths 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 dcc.

You should assume warnings from dcc 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 permitted 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 permitted 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. A 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. A 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 understand and hard to change.

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 less than 20 lines or less. 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 be 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.

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.

// 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. Function names containing multiple words should be in either snake_case or camelCase. Do not use both approaches in the one program.

Function Names

Function names should be descriptive, typically containing multiple words. Function names containing multiple words should be in either snake_case or camelCase. Do not use both approaches in the one program.

When writing functions for ADTs, you may want to keep your naming consistent and the abstract type name will usually appear in the function name, usually at the end.

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 and the function name.

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);

Return

Minimise the number of return statements in your functions.

Have multiple 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.

Avoid These C Features

break

Use of break statements should be minimized.

break

Use of break statements should be minimized.

Novice programmers often over-use break statements. producing confusing and difficult to debug code.

Use break only when you are sure it makes your code simpler and more readable.

continue

The continue statement should be avoided.

continue

The continue statement should be avoided.

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.

Macros

Use of macros, #define statements with parameters, should be avoided.

Define a function instead.

Macros

Use of macros, #define statements with parameters, should be avoided.

Define a function instead.

Macros due to their implementation at textual pre-processing introduce a nasty class of bugs.

Macros were primarily used for efficiency in the past. Modern C compilers can produce just as efficient code for functions, without the risk of nasty bugs.

Pointer Arithmetic

Pointer Arithmetic should be avoided.

Use array indices instead.

Pointer Arithmetic

Pointer Arithmetic should be avoided.

Use array indices instead.

Experienced programmers use pointer arithmetic to produce succinct idiomatic code.

Novice programmers 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

Do not use explicit type casts, apart from converting int to double or vice-versa.

Type Cast

Do not use explicit type casts, apart from converting int to double or vice-versa.

Safe use of type casts, other than between numeric types, involves a deep knowledge of C semantics.

Other uses for type casts should not arise in this course.

An exception would be implementing your own collections data types, for an advanced exercises,or the challenge part of the assignment.

Type Cast

Do not use explicit type casts, apart from converting int to double or vice-versa.

int x = 42; int y = 11; double f; f = x/((double)y); // convert y to a double

Switch

The switch statement should be avoided.

Use if...else if...else instead.

Switch

The switch statement should be avoided.

Use if...else if...else instead.

The C switch statement can have subtle and confusing behaviour.

While there are some circumstances, e.g. writing emulators where its use is desirable, leave its use until you are a more experienced programmer.

Use if...else if...else instead.

Goto

goto should not be used.

Goto

goto should not be used.

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 its use is appropriate

These circumstances will not arise in this course.

There will be a major style deduction if you use goto.

Comma Operator

Do note use the comma operator to combine multiple expressions.

Comma Operator

Do note use the comma operator to combine multiple expressions.

Novice programmers attempting to use the comma operator if typically construct unreadable code and confuse themselves.

Ternary If

Ternary syntax, (condition) ? true_expression : false_expression should not be used.

Ternary If

Ternary syntax, (condition) ? true_expression : false_expression should not be used.

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.

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 very limited circumstances which should not arise in this course.

do...while

do {} while (condition); should not be used; use while (condition) {} instead.

do...while

do {} while (condition); should not be used; use while (condition) {} instead.

The do { ... } while (condition); construct is similar to a normal while (condition) { ... }, except the body will always run before a 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.