Programming Fundamentals

Contents

You should take the time to read this guide carefully.

You should refer back to this as you come across new syntax and concepts in your lectures, so do not worry if you do not understand everything here yet.

Please note that:
  • The code presented during live coding in lectures and tutorials may not always adhere perfectly to this guide.
  • The 1511 style checker is simply a tool that can assist you in finding some smaller style issues in your code.
It is your responsibility to manually check that your code adheres to the guidelines prescribed in the COMP1511 style guide.


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 generally free to use language features you have learned outside this course, unless explicitly forbidden below, labelled as an "illegal element". However, please bear in mind you may not be able to get assistance from tutors, or in the course forum with such code.

Recommended Code Layout

Header Comment

Every program 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.

The preferred style for a header comment is:

// Name of Exercise
// name.c
//
// This program was written by YOUR-NAME-HERE (zXXXXXXX)
// on INSERT-DATE-HERE
//
// One line summary of what this exercise 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 university.

Header Comment

Every program 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.

The preferred style for a header comment is:

// Name of Exercise
// name.c
//
// This program was written by YOUR-NAME-HERE (zXXXXXXX)
// on INSERT-DATE-HERE
//
// One line summary of what this exercise 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 university.

//
// COMP1511 Lab 10 Exercise - Turing's test
// turings.c
//
// Pretend to be a human in text-based conversation.
// https://en.wikipedia.org/wiki/Turing_test
//
// Authors:
// Grace Hopper (zXXXXXXX@unsw.edu.au)
// Ada Lovelace (zYYYYYYY@unsw.edu.au)
//
// Limitations:
// Sometimes purposefully fails test as to not arouse suspicion
//
// Written: 19/03/2034
//

#include <stdio.h>
#include <string.h>
#include <math.h>

#define MEANING_OF_LIFE 42


// function prototypes would go here


int main(void) {
    printf("Hello!\n");
    return 0;
}


// function definitions would go here

Program Comments

Where your code may be unclear to a reader, or require further explanation, you should leave brief comments describing the purpose of the code you have written.

Make sure that these comments are descriptive of why your code is doing what it does - rather than simply stating what the code does.

These comments should be left on the line before the code they are describing - rather than on the same line ("inline comments"). This helps to improve readability.

Program Comments

Where your code may be unclear to a reader, or require further explanation, you should leave brief comments describing the purpose of the code you have written.

Make sure that these comments are descriptive of why your code is doing what it does - rather than simply stating what the code does.

These comments should be left on the line before the code they are describing - rather than on the same line ("inline comments"). This helps to improve readability.

// A small program showing the use of some comments!

#include <stdio.h>

int main(void) {
    ///////////////////////////////////////////////////////////////////////////
    // An example of a not-useful comment - it states what the code
    // is doing - rather than why it is necessary.

    // This is a printf that prints hello
    printf("Hello!\n");
    ///////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////
    // An example of an inline comment -
    // we do not use inline comments in COMP1511

    int num;
    scanf("%d", &num); // Reads the number of lines the user wishes to type
    ///////////////////////////////////////////////////////////////////////////

    return 0;
}
Braces and Indentation

Functions, loops, and ifs should consistently use braces and should use compact braces (or, consistently use another common bracing scheme).

Braces and Indentation

Functions, loops, and ifs should consistently use braces and should use compact braces (or, consistently use another common bracing scheme).

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 consistently use braces and should use compact braces (or, consistently use another common bracing scheme).

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 consistently use braces and should use compact braces (or, consistently use another common bracing scheme).

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

Nesting Depth

Your code should not equal or exceed 5 levels of nesting (layers of curly brackets). If code is exceeding this level of nesting, then it is probably a sign that you should look to simplify your approach, or try to move some of the logic into a function!

Nesting Depth

Your code should not equal or exceed 5 levels of nesting (layers of curly brackets). If code is exceeding this level of nesting, then it is probably a sign that you should look to simplify your approach, or try to move some of the logic into a function!

// This is an example of code that is nested okay (3 levels of nesting)
void do_something_complicated(int i) {
    int j = 0;
    while (j < i) {
        if (j % 2 == 0) {
            printf("Hello!\n");
        }
    }
}

// this is also correct with 2 levels of nesting
int main(void) {
    int i = 0;
    if (i < 3 && i > 0) {
        do_something_complicated(i);
    }
}

// This is incorrect, it has 5 levels of nesting
int main(void) {
    int i = 0;
    if (i < 3) {
        if (i > 0) {
            int j = 0;
            while (j < i) {
                if (j % 2 == 0) {
                    printf("Hello!\n");
                }
            }
        }
    }
}

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;
}
Vertical Whitespace

Use vertical whitespace (blank lines between code) occasionally to indicate where the related parts of your code are.

  • Using no blank lines between lines of code indicates the lines should be understood together, or are closely related.
  • Using exactly one blank line indicates two sections of code are distinct -- they split your code into "paragraphs".
  • Using exactly two blank line indicates two sections of code are entirely unrelated.
  • Using more than two blank lines is (almost) never appropriate.

The exact use of vertical whitespace is subjective, and your tutor will be able to give you feedback on your style throughout the term. Vertical whitespace is like chocolate -- some of it significantly improves your life, but too much can also be it's own problem; and used in the wrong place it can be very confusing.

Vertical Whitespace

Use vertical whitespace (blank lines between code) occasionally to indicate where the related parts of your code are.

  • Using no blank lines between lines of code indicates the lines should be understood together, or are closely related.
  • Using exactly one blank line indicates two sections of code are distinct -- they split your code into "paragraphs".
  • Using exactly two blank line indicates two sections of code are entirely unrelated.
  • Using more than two blank lines is (almost) never appropriate.

The exact use of vertical whitespace is subjective, and your tutor will be able to give you feedback on your style throughout the term. Vertical whitespace is like chocolate -- some of it significantly improves your life, but too much can also be it's own problem; and used in the wrong place it can be very confusing.


// The two variables here are closely related, and should not be seperated.
int x_position = 0;
int y_position = 0;


// The two if statements here are seperate ideas, and could be seperated.
if (x_position > 0) {
    printf("X position is positive.")
}

if (instruction == INCREASE_X_POSITION) {
    x_position += 1;
}


// We have used two empty lines here to indicate the the topic has changed.
printf("Please choose an item to use...\n")

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.

Statements

Only one executable statement should be used per line of code.

// Good - one statement per line
scanf("%d", &x);
printf("%d\n", x);
Statements

Only one executable statement should be used per line of code.

// BAD - multiple statements on one line scanf("%d", &x); printf("%d\n", x);

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 && something_else && something_else_again && another_thing && even_more_things && something_else_entirely && there_are_too_many_conditions && this_line_is_too_long) {

// This is better (but consider using a function instead):
if (
    something && something_else && something_else_again && 
    another_thing && even_more_things && something_else_entirely && 
    there_are_too_many_conditions && this_line_is_too_long
) {

// This is best:
if (those_things_are_true()) {

// If your function signature, implementation, or call is too long:

struct node *do_something_complicated(struct node *thing1, struct node *thing2, struct node something_different, int thingo, int thingi);

// Try putting it on multiple lines, like this:

struct node *do_something_complicated(
    struct node *thing1,
    struct node *thing2,
    struct node something_different,
    int thingo,
    int thingi
);

// and calling it like:

result = do_something_complicated(
    my_thing1,
    my_thing2,
    my_something_different,
    my_thingo,
    my_thingi
);

// If your print statement is too long, you can break it up in the same way:
// You can put strings on new lines without a comma to seperate them.

printf(
    "According to all known laws of aviation, "
    "there is no way that a %c should be able to fly.\n"
    "Its wings are too small to get its fat little body off the ground.\n"
    "The bee, of course, flies anyways. "
    "Because bees don't care what humans think is impossible.\n",
    'B'
);

Constants

Constants and Enumerations

Use #define or enum to give constants names.

You are only allowed to use #define's for literals (i.e. numbers, strings, characters) only.

#defines must be written in ALL_CAPS_WITH_UNDERSCORES. enum names must be written in lower_snake_case, and fields must be written in UPPER_SNAKE_CASE. You should never rely on the value of an enum -- in other words, do not use an enum to represent a specific numerical value.

Constants and Enumerations

Use #define or enum to give constants names.

You are only allowed to use #define's for literals (i.e. numbers, strings, characters) only.

#defines must be written in ALL_CAPS_WITH_UNDERSCORES. enum names must be written in lower_snake_case, and fields must be written in UPPER_SNAKE_CASE. You should never rely on the value of an enum -- in other words, do not use an enum to represent a specific numerical value.

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

Additionally unexplained characters might cause similar problems. For example if 'w' ,'a', 's', 'd' are used as command movements in a game, it is not clear what they represent and if they were changed it would be hard to determine which occurrences should be changed.

Constants and Enumerations

Use #define or enum to give constants names.

You are only allowed to use #define's for literals (i.e. numbers, strings, characters) only.

#defines must be written in ALL_CAPS_WITH_UNDERSCORES. enum names must be written in lower_snake_case, and fields must be written in UPPER_SNAKE_CASE. You should never rely on the value of an enum -- in other words, do not use an enum to represent a specific numerical value.

#define DAYS_OF_WEEK 7

enum days {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, WEEKEND};

// ....

int array[DAYS_OF_WEEK];
int i = 0;
while (i < DAYS_OF_WEEK) {
    a[i] = i;
    i++;
}

// ....

#define MOVE_UP 'w'
#define MOVE_DOWN 's'
#define MOVE_LEFT 'a'
#define MOVE_RIGHT 'd'

// ....
if (command == MOVE_UP) {
    // move up
}

// ....

// This is okay as we are using 'a' to literally represent 'a'.
if (character >= 'a' && character <= 'z') {
    character = character - 'a' + 'A';
}
Constants and Enumerations

Use #define or enum to give constants names.

You are only allowed to use #define's for literals (i.e. numbers, strings, characters) only.

#defines must be written in ALL_CAPS_WITH_UNDERSCORES. enum names must be written in lower_snake_case, and fields must be written in UPPER_SNAKE_CASE. You should never rely on the value of an enum -- in other words, do not use an enum to represent a specific numerical value.

// BAD - enum fields are not captialized enum colours {red, green, blue}; // BAD - a #define has been used for a non-literal value of curr->next #define next curr->next // BAD - the constant 7 should be given a name int array[7]; int i = 0; while (i < 7) { a[i] = i; i++; } // BAD - Should #define 'w' as MOVE_UP if (command == 'w') { // move up }

Variables

Variable Names

Descriptive variable names should always be used where possible.

Short variable names such as x, i, j or k 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 should be in snake_case.

#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. Declare variables close to where they are used for the first time. Do not use the same variable name for multiple purposes. Avoid declaring more than one variable on the same line.

Declaring Variables

Variables can be declared when they are first assigned a value, as part of assigning the value. Declare variables close to where they are used for the first time. Do not use the same variable name for multiple purposes. Avoid declaring more than one variable on the same line.

Assigning a value to a variable when it is used prevents confusion. Having it close to the place it is first used makes it easy to find and understand; and difficult to accidentally reassign elsewhere. If you need a new variable, give it a different name -- reusing variable names leads to subtle and hard to find bugs. Declaring multiple variables on the same line can have subtle and unexpected behaviour when declaring pointer variables, and can result in code that is harder to read.

Declaring Variables

Variables can be declared when they are first assigned a value, as part of assigning the value. Declare variables close to where they are used for the first time. Do not use the same variable name for multiple purposes. Avoid declaring more than one variable on the same line.

// ...
// Declaring multiple variables of the same type on one line is okay, but is best to avoid.
int x, y, z;

// Initialising multiple variables on the same line is not allowed
int x = 4, y = 5, z = 2;

// Do not do this (it does not do what you think)
int *a = &i, b = 5;
// ... 
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.

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.

// DO NOT do this
short age = 18;
float height = 1.65;

// Instead do this:
int age = 18;
double height = 1.65;
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
}

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, stdbool.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.

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. 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 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 50 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 snake_case.

Function Names

Function names should be descriptive, typically containing multiple words. Function names containing multiple words should be in snake_case.

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). Do not use 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). Do not use 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). Do not use an empty argument list.

// A short function
int function_with_no_arguments(void);

// A short function
int simple_function(int argument_a, char *argument_b);

// A function with a lot of arguments
int long_function(int argument_a, int argument_b, int argument_c, int argument_d,
        int argument_e, int argument_f);

// Another function with a lot of arguments
int another_long_function(int argument_a, int argument_b, int argument_c,
                        int argument_d, int argument_e, int argument_f);

// 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 another_long_function(
    int argument_a, int argument_b, int argument_c,
    int argument_d, int argument_e, int argument_f
);
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

Minimize use of break statements.

break

Minimize use of break statements.

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

Avoid using the continue statement.

continue

Avoid using the continue statement.

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.

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 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 for non-numericals. Casting between int and double is allowed.

Type Cast

Avoid type casts for non-numericals. Casting between int and double is allowed.

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.

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

Type Cast

Avoid type casts for non-numericals. Casting between int and double is allowed.

int x = 42;
int y = 11;
double f;
// This is allowed as it is casting between numeric types
f = x/((double)y);       // convert y to a double
Type Cast

Avoid type casts for non-numericals. Casting between int and double is allowed.

char *p = malloc(100); int *q = (int *)p; // BAD - casting between pointer types

Switch

Avoid the switch statement.

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

Switch

Avoid the switch statement.

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; they will not arise in COMP1511. If you think you need to use a switch statement, talk to your tutor.

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

Ternary If

Avoid use the ternary if, i.e.: (condition) ? true_expression : false_expression.

Ternary If

Avoid use the ternary if, i.e.: (condition) ? true_expression : false_expression.

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

Avoid the use of do {} while (condition); try use while (condition) {} instead.

do...while

Avoid the use of do {} while (condition); try 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.

Illegal elements

union

Do not use unions use structs instead.

union

Do not use unions use structs instead.

Unions allow a polymorphic datatype to occupy less space than if a struct was used.

No need for unions will arise in this course. If you think you need to use them, talk to your tutor.

Union introduce a nasty class of bugs which are hard to debug and best avoided by novice programmers.

Unions are also sometimes useful for embedded programming and for building language implementations.

Comma Operator

Do not use the comma operator to combine multiple expressions.

Comma Operator

Do not use the comma operator to combine multiple expressions.

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

Using it to declare multiple variables on the same line is allowed, as long as they are of the same type and variables are not initialised.

Comma Operator

Do not use the comma operator to combine multiple expressions.

// do not use the comma as seperators to combine multiple expressions
int a = 1, b = 2, c = 3;

// or like this
int i = (a , b);
Goto

Do not use goto.

Goto

Do not 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

These circumstances will not arise in this course.

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

Macros

Do not use #define statements with parameters (usually called macros).

Define a function instead.

Macros

Do not use #define statements with parameters (usually called macros).

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.

There will be a style deduction if you use macros.

Macros

Do not use #define statements with parameters (usually called macros).

Define a function instead.

// Good use of a #define
#define MAX_LENGTH 100

// BAD - any arithmetic in a #define is counted as a macro in this course
#define BIG_LENGTH (MAX_LENGTH + 100)

// BAD - use a function instead
#define MAX(a, b) ((a) > (b) ? (a) : (b))
    
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.

Global and Static Variables

You are not permitted to use global or static.

Global and Static Variables

You are not permitted to use global or static.

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

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

Scanning Strings

You are not permitted to use the %s format specifier in the scanf or sscanf functions.

Scanning Strings

You are not permitted to use the %s format specifier in the scanf or sscanf functions.

The %s format specifier in scanf and the sscanf function are unsafe and can lead to buffer overflows.

You should use fgets to read strings from the user.

Scanning Strings

You are not permitted to use the %s format specifier in the scanf or sscanf functions.

// BAD - using %s in scanf
char name[MAX_LEN];
scanf("%s", name);

// GOOD - using fgets
char name[MAX_LEN];
fgets(name, MAX_LEN, stdin);
Variable-Length Arrays

Variable-length arrays must not be used in this course.

Variable-Length Arrays

Variable-length arrays must 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 handle poorly 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 both to arrays with size fixed 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.

Variable-Length Arrays

Variable-length arrays must not be used in this course.

    // BAD 
    int length;
    scanf("%d", &length);
    char name[length];
    
    // GOOD 
    #define MAX_LEN 100
    char name[MAX_LEN];