Programming Fundamentals

Course Resources

Administrivia
Resources
Lab/Test/Assignment
Other
Revision

Week-by-Week

Tutorial
Laboratory
Tuesday June 01 2021 Lecture Topics
Thursday June 03 2021 Lecture Topics
Tutorial
Laboratory
Tuesday June 08 2021 Lecture Topics
Tuesday June 10 2021 Lecture Topics
Tutorial
Laboratory
Weekly Test
Tuesday June 15 2021 Lecture Topics
Thursday June 17 2021 Lecture Topics
Tutorial
Laboratory
Weekly Test
Tuesday June 22 2021 Lecture Topics
Tuesday June 24 2021 Lecture Topics
Tutorial
Laboratory
Weekly Test
Tuesday June 29 2021 Lecture Topics
Thursday July 01 2021 Lecture Topics
Tutorial
Laboratory
Weekly Test
Tuesday July 13 2021 Lecture Topics
Thursday July 15 2021 Lecture Topics
Tutorial
Laboratory
Weekly Test
Tuesday July 20 2021 Lecture Topics
Thursday July 22 2021 Lecture Topics
Tutorial
Laboratory
Weekly Test
Tuesday July 27 2021 Lecture Topics
Thursday July 29 2021 Lecture Topics
Tutorial
Laboratory
Weekly Test
Tuesday August 02 2021 Lecture Topics
Thursday August 04 2021 Lecture Topics

Topic-by-Topic

Course Intro
Introduction To C

Example Program showing output in C
#include <stdio.h>

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

Download hello.c

C Basics


Convert a temperature in fahrenheit to celsius
#include <stdio.h>

int main(void) {
    double fahrenheit, celsius;

    printf("Enter Fahrenheit temperature: ");
    scanf("%lf", &fahrenheit);
    celsius = 5.0 / 9.0 * (fahrenheit - 32);
    printf("%lf Fahrenheit = %lf Celsius\n", fahrenheit, celsius);
    return 0;
}

Download celsius2fahrenheit.c

If

Marc Chee, 2021
This small example will ask the user to input the result of rolling two dice.
It will then check the sum of them against its secret number.
It will report back: success (higher or equal) or failure (lower)
#include <stdio.h>

#define SECRET_TARGET 7

int main(void) {
    int die_one;
    int die_two;

    // we start by asking the user for their dice rolls
    printf("Please enter your first die roll: ");
    // then read in a number they type in the terminal
    scanf("%d", &die_one);

    // repeat for the second die
    printf("Please enter your second die roll: ");
    scanf("%d", &die_two);

    // calculate the total and report it
    int total = die_one + die_two;
    printf("Your total roll is: %d\n ", total);

    // Now test against the secret number
    if (total >= SECRET_TARGET) {
        // success
        printf("Skill roll succeeded!\n");
    } else {
        // the same as total < SECRET TARGET
        // but we don't have to test it because
        // we've already checked all other
        // possibilities
        printf("Skill roll failed!\n");
    }

    return 0;
}

Download dice_check.c


Marc Chee, 2021
This small example will ask the user to input the result of rolling two dice.
It will then check the sum of them against its secret number.
It will report back: success (higher or equal) or failure (lower)
#include <stdio.h>

#define MIN_VALUE 1
#define MAX_VALUE 6

#define SECRET_TARGET 7

int main(void) {
    int die_one;
    int die_two;

    // we start by asking the user for their dice rolls
    printf("Please enter your first die roll: ");
    // then read in a number they type in the terminal
    scanf("%d", &die_one);

    if (die_one < MIN_VALUE || die_one > MAX_VALUE) { // die_one is invalid
        printf("%d is not a valid roll for a D%d.\n", die_one, MAX_VALUE);
        die_one = die_one % MAX_VALUE;
        // make any value < MIN_VALUE into MAX_VALUE
        if (die_one < MIN_VALUE) {
            die_one = MAX_VALUE;
        }
    }

    // repeat for the second die
    printf("Please enter your second die roll: ");
    scanf("%d", &die_two);

    if (die_two < MIN_VALUE || die_two > MAX_VALUE) { // die_two is invalid
        printf("%d is not a valid roll for a D%d.\n", die_two, MAX_VALUE);
        die_two = die_two % MAX_VALUE;
        // make any value < MIN_VALUE into MAX_VALUE
        if (die_two < MIN_VALUE) {
            die_two = MAX_VALUE;
        }
    }

    // calculate the total and report it
    int total = die_one + die_two;
    printf("Your total roll is: %d\n ", total);

    // Now test against the secret number
    if (total >= SECRET_TARGET) {
        // success
        printf("Skill roll succeeded!\n");
    } else {
        // the same as total < SECRET TARGET
        // but we don't have to test it because
        // we've already checked all other
        // possibilities
        printf("Skill roll failed!\n");
    }

    return 0;
}

Download dice_check_v2.c

While

A simple program demonstrating the use of a while loop


#include <stdio.h>

int main(void) {
    // read an integer n
    // print n asterisks
    int n;
    printf("How many asterisks? ");
    scanf("%d", &n);

    int loop_counter = 0;
    while (loop_counter < n) {
        printf("*");
        loop_counter = loop_counter + 1;
    }
    printf("\n");

    return 0;
}

Download asterisks.c



Read numbers printing whether they are even or odd illustrates use of a sentinel variable (stop_loop)

Note for simplicity we are assuming scanf succeeds in reading an integer.
A robust program would check that scanf returns 1 to indicate an integer read.

#include <stdio.h>

int main(void) {
    int stop_loop, number;

    printf("Enter numbers, 0 to stop\n");

    stop_loop = 0;
    while (stop_loop != 1) {
        scanf("%d", &number);
        if (number == 0) {
            stop_loop = 1;
        } else if (number % 2 == 1) {
            printf("%d is odd.\n", number);
        } else {
            printf("%d is even.\n", number);
        }
    }

    return 0;
}

Download even_odd.c



A simple program which prints a square

#include <stdio.h>

int main(void) {
    int size;
    printf("Enter size: ");
    scanf("%d", &size);

    // print `size` lines
    int row = 0;
    while (row < size) {

        // print a row of `size` asterisks
        int column = 0;
        while (column < size) {
            printf("*");
            column = column + 1;
        }

        // the row is finished, start the next line
        printf("\n");

        row = row + 1;
    }

    return 0;
}

Download square.c

print all possible dice rolls
#include <stdio.h>

#define SIDE_LENGTH 13

int main(void) {
    int die_one_size;
    int die_two_size;
    // User decides the two dice sizes
    printf("Please enter the size of the first die: ");
    scanf("%d", &die_one_size);
    printf("Please enter the size of the second die: ");
    scanf("%d", &die_two_size);

    // Then loop through both dice
    int die1 = 1;
    while (die1 <= die_one_size) { // seen die1 - 1 values
        int die2 = 1;
        while (die2 <= die_two_size) { // seen die2 - 1 values
            int total = die1 + die2;
            printf("%d , %d total: %d\n", die1, die2, total);
            die2++;
        }
        die1++;
    }

    return 0;
}

Download all_die_rolls.c

print all possible dice rolls
#include <stdio.h>

#define SIDE_LENGTH 13

int main(void) {
    int die_one_size;
    int die_two_size;
    int target_value;

    // User decides the two dice sizes and target
    printf("Please enter the size of the first die: ");
    scanf("%d", &die_one_size);
    printf("Please enter the size of the second die: ");
    scanf("%d", &die_two_size);
    printf("Please enter the target value: ");
    scanf("%d", &target_value);

    // Then loop through both dice
    int die1 = 1;
    while (die1 <= die_one_size) { // seen die1 - 1 values
        int die2 = 1;
        while (die2 <= die_two_size) { // seen die2 - 1 values
            int total = die1 + die2;
            if (total == target_value) {
                printf("%d , %d total: %d\n", die1, die2, total);
            }
            die2++;
        }
        die1++;
    }

    return 0;
}

Download matching_die_rolls.c

print all possible dice rolls
#include <stdio.h>

#define SIDE_LENGTH 13

int main(void) {
    int die_one_size;
    int die_two_size;
    int target_value;
    // User decides the two dice sizes and target
    printf("Please enter the size of the first die: ");
    scanf("%d", &die_one_size);
    printf("Please enter the size of the second die: ");
    scanf("%d", &die_two_size);
    printf("Please enter the target value: ");
    scanf("%d", &target_value);

    int num_successes = 0;
    int num_rolls = 0;

    // Then loop through both dice
    int die1 = 1;
    while (die1 <= die_one_size) { // seen die1 - 1 values
        int die2 = 1;
        while (die2 <= die_two_size) { // seen die2 - 1 values
            num_rolls++;
            int total = die1 + die2;
            if (total == target_value) {
                num_successes++;
                printf("%d , %d total: %d\n", die1, die2, total);
            }
            die2++;
        }
        die1++;
    }

    int percentage = (100 * num_successes) / num_rolls;
    printf("Percentage chance of getting your target number is: %d%%\n ",
           percentage);
    return 0;
}

Download die_roll_chance.c



Read 43 numbers and then print the sum of the numbers

A simple program demonstrating while & scanf

Note for simplicity we are assuming scanf succeeds in reading an integer.
A robust program would check that scanf returns 1 to indicate an integer read.


#define N_NUMBERS 42

#include <stdio.h>

int main(void) {
    int sum = 0;
    printf("Enter %d numbers:\n", N_NUMBERS);

    int n = 0;
    while (n < N_NUMBERS) {
        int x;
        scanf("%d", &x);
        sum = sum + x;
        n = n + 1;
    }

    printf("Sum of the numbers is %d\n", sum);
    return 0;
}

Download sum_42_numbers.c



Read n numbers and then print the sum of the numbers

A simple program demonstrating while & scanf

Note for simplicity we are assuming scanf succeeds in reading an integer.
A robust program would check that scanf returns 1 to indicate an integer read.


#include <stdio.h>

int main(void) {

    int n_numbers;
    printf("How many numbers do you wish to sum: ");
    scanf("%d", &n_numbers);

    printf("Enter %d numbers:\n", n_numbers);
    int n = 0;
    int sum = 0;
    while (n < n_numbers) {
        int x;
        scanf("%d", &x);
        sum = sum + x;
        n = n + 1;
    }

    printf("Sum of the numbers is %d\n", sum);

    return 0;
}

Download sum_n_numbers.c



A simple program which prints a triangle

#include <stdio.h>

#define SIDE_LENGTH 13

int main(void) {
    int size;
    printf("Enter size: ");
    scanf("%d", &size);

    // print `size` lines
    int row = 0;
    while (row < size) {
        // print a row of `row + 1` asterisks
        int column = 0;
        while (column <= row) {
            printf("*");
            column = column + 1;
        }

        // the row is finished, start the next line
        printf("\n");

        row = row + 1;
    }

    return 0;
}

Download triangle.c


A simple program demonstrating nested while loops
It prints all prime numbers < 1000
#define MAX 1000

#include <stdio.h>

int main(void) {
    // loop through numbers 1..MAX
    int n = 1;
    while (n < MAX) {

        // loop through numbers 1..n counting factors
        int possible_factor = 1;
        int n_factors = 0;
        while (possible_factor <= n) {

            if (n % possible_factor == 0) {
                n_factors = n_factors + 1;
            }

            possible_factor = possible_factor + 1;
        }

        if (n_factors <= 2) {
            printf("%d is prime\n", n);
        }

        n = n + 1;
    }

    return 0;
}

Download primes1000.c

Style

IMPORTANT NOTE:
Before reading this code, bear in mind that it's an example of how poor coding style can affect our ability to understand or work with code

#include <stdio.h>
#define constant 7
int main(void) {
int x;int a;   int b;int y;
   printf("Please enter how many sides are on your dice: ");scanf("%d", &x);
      printf("Please enter the value of the first die: ");scanf("%d", &a);
if (a<1) {    printf("Die roll value: %d is outside of the range 1 - %d.\n", a, x);
    // this bit does the dice thing
    a = a % x; if (a == 0) a = x;}
 if(  a > x) {
    printf("Die roll value: %d is outside of the range 1 - %d.\n", a, x);
    a = a %x; if (a== 0) a = x;}
printf ("Your roll is: %d\n", a);
   printf("Please enter the value of the second die: ");scanf("%d", &b);
      if (a < 1 || a > x) {
printf("Die roll value: %d is outside of the range 1 - %d.\n", b, x);b = b % x;
    if (b ==0) b = x;
}printf ("Your roll is: %d\n",b);
  y = a + b;
      printf("Total roll is: %d\n", y);
  // can't remember why but don't delete this next line
if (y>constant) {printf("Dice Check Succeeded!\n");} else if(y==constant) {
  printf("Dice Check Tied.\n");} else {printf("Dice Check Failed!\n");}}

Download codestylebad.c

Functions

Simple lecture example of using functions
#include <stdio.h>

double cube(double x);

int main(void) {
    printf("42 cubed is %lf\n", cube(42.03));
    double a = 2;
    double b = cube(a);
    printf("2 cubed is %lf\n", b);
    return 0;
}

// calculate x to the power of 3
double cube(double x) {
    double result;
    result = x * x * x;
    return result;
}

Download cube.c


Lecture example illustrating simple function
#include <stdio.h>

double evaluate_quadratic(double a, double b,  double c, double x);

int main(int argc, char *argv[]) {

    double x;
    printf("Enter x: ");
    scanf("%lf", &x);

    double value = evaluate_quadratic(2.0, -3.0, 4.0, x);

    printf("2x^2 - 3x + 4 = %lf\n", value);

    return 0;
}

// given x calculate value of polynomial with coefficients a, b, c
double evaluate_quadratic(double a, double b,  double c, double x) {
    return (a * x * x) + (b * x) + c;
}

Download quadratic.c


Lecture example illustrating simple function
#include <stdio.h>
#include <math.h>

double quadratic_root(double a, double b,  double c);
double evaluate_quadratic(double a, double b, double c, double x);

int main(int argc, char *argv[]) {
    double a, b, c;
    printf("Enter a: ");
    scanf("%lf", &a);
    printf("Enter b: ");
    scanf("%lf", &b);
    printf("Enter c: ");
    scanf("%lf", &c);

    double root = quadratic_root(a, b, c);

    printf("Calculated root is %lf\n", root);

    double value = evaluate_quadratic(a, b, c, root);

    printf("The value of the quadratic for  %lf is %lf\n", root, value);

    return 0;
}


double quadratic_root(double a, double b,  double c) {
    return (-b + sqrt(b * b - 4 * a * c)) / (2 * a);
}

// given x calculate value of polynomial with coefficients a, b, c
double evaluate_quadratic(double a, double b, double c, double x) {
    return (a * x * x) + (b * x) + c;
}

Download quadratic_root.c

Arrays
#include <stdio.h>

#define NUM_PLAYERS 4

int main(void) {
    int scores[NUM_PLAYERS] = { 0 };

    // assigning values directly to indexes
    scores[0] = 55;
    scores[1] = 84;
    scores[2] = 32;
    scores[3] = 61;

    // loop through and display all scores
    int i = 0;
    while (i < NUM_PLAYERS) {
        printf(
            "Player %d has scored %d points.\n",
            i,
            scores[i]
        );

        i++;
    }

    // finding the highest score
    int highest = 0;
    int index_highest = -1;
    i = 0;
    while (i < NUM_PLAYERS) {
        if (scores[i] > highest) {
            highest = scores[i];
            index_highest = i;
        }

        i++;
    }

    printf(
        "Player %d has the highest score of %d.\n",
        index_highest, highest
    );

    // finding the total
    int total = 0;
    while (i < NUM_PLAYERS) {
        total += scores[i];
        i++;
    }

    printf("Total points scored across the players is %d\n", total);

    return 0;
}

Download players.v0.c

#include <stdio.h>

#define NUM_PLAYERS 4

int main(void) {
    int scores[NUM_PLAYERS] = { 0 };

    // assigning scores using user input
    int i = 0;
    while (i < NUM_PLAYERS) {
        printf("Please enter Player %d's score: ", i);
        scanf("%d", &scores[i]);
        i++;
    }

    // loop through and display all scores
    i = 0;
    while (i < NUM_PLAYERS) {
        printf("Player %d has scored %d points.\n", i, scores[i]);

        i++;
    }

    // finding the highest score
    int highest = 0;
    int index_highest = -1;
    i = 0;
    while (i < NUM_PLAYERS) {
        if (scores[i] > highest) {
            highest = scores[i];
            index_highest = i;
        }

        i++;
    }

    printf("Player %d has the highest score of %d.\n", index_highest, highest);

    // finding the total
    int total = 0;
    while (i < NUM_PLAYERS) {
        total += scores[i];
        i++;
    }

    printf("Total points scored across the players is %d\n", total);

    return 0;
}

Download players.v1.c


Read 5 numbers and print them in reverse order - the hard way
#include <stdio.h>

int main(void) {
    // This code is inflexible and it won't handle e.g. 10000 numbers
    // a much better approach is to use an array
    int x0, x1, x2, x3, x4;
    printf("Enter 5 numbers: ");
    scanf("%d", &x0);
    scanf("%d", &x1);
    scanf("%d", &x2);
    scanf("%d", &x3);
    scanf("%d", &x4);
    printf("Numbers reversed are:\n");
    printf("%d\n", x4);
    printf("%d\n", x3);
    printf("%d\n", x2);
    printf("%d\n", x1);
    printf("%d\n", x0);
    return 0;
}

Download reverse5_bad.c


Read 5 numbers and print them in reverse order

Note for simplicity we are assuming scanf succeeds in reading an integer.
A robust program would check that scanf returns 1 to indicate an integer read.
#include <stdio.h>

int main(void) {
    // Flexible code using an array
    int x[5];  // array size should be a #define not `5`
    printf("Enter 5 numbers: ");

    int i = 0;
    while (i < 5) {
        scanf("%d", &x[i]);
        i = i + 1;
    }

    printf("Numbers reversed are:\n");
    int j = 4;
    while (j >= 0) {
        printf("%d\n", x[j]);
        j = j - 1;
    }

    return 0;
}

Download reverse5_good.c


Read 5 numbers and print them in reverse order
#include <stdio.h>

// change this #define to change how many numbers read
#define N_NUMBERS 5

int main(void) {
    int x[N_NUMBERS];

    printf("Enter %d numbers: ", N_NUMBERS);


    int i = 0;
    while (i < N_NUMBERS) {
        scanf("%d", &x[i]);
        i = i + 1;
    }
    printf("Numbers reversed are:\n");
    int j = N_NUMBERS - 1;
    while (j >= 0) {
        printf("%d\n", x[j]);
        j = j - 1;
    }
    return 0;
}

Download reverse5_better.c


Read 5 numbers and print them in reverse order
This version checks that the scanf was able to read the number
#include <stdio.h>

#define N_NUMBERS 5

int main(void) {
    int x[N_NUMBERS];

    printf("Enter %d numbers: ", N_NUMBERS);
    int i = 0;
    int keep_looping = 1;
    while (i < N_NUMBERS && keep_looping) {

        // stop loop if scanf doesn't read a number
        if (scanf("%d", &x[i]) != 1) {
             keep_looping = 0;
        } else {
            i = i + 1;
        }
    }

    printf("Numbers reversed are:\n");
    int j = i - 1;
    while (j >= 0) {
        printf("%d\n", x[j]);
        j = j - 1;
    }

    return 0;
}

Download reverse5_best.c


A Tourist Game
Marc Chee (cs1511@cse.unsw.edu.au)

#include <stdio.h>

// The dimensions of the map
#define MAP_ROWS 10
#define MAP_COLUMNS 10

#define EXPLORED 1
#define UNEXPLORED 0

// Helper Function: Print out the map as a 2D grid
void print_map(int map[MAP_ROWS][MAP_COLUMNS], int t_row, int t_column);

int main(void) {
    int map[MAP_ROWS][MAP_COLUMNS] = {0};

    // Set up tourist roughly in the middle of the map
    int tourist_row = MAP_ROWS/2;
    int tourist_column = MAP_COLUMNS/2;

    print_map(map, tourist_row, tourist_column);

    // Looping for multiple "turns"
    int keep_looping = 1;
    while (keep_looping) {
        // Track where we've been
        map[tourist_row][tourist_column] = EXPLORED;

        printf("Type in 2,4,6 or 8 to move in that num-pad direction: ");
        int input = 0;
        int input_result = scanf("%d", &input);
        if (input_result != 1) {
            keep_looping = 0;
        } else {
            // input was a number
            // check which direction to go
            if (input == 8) {
                // up
                tourist_row--;
            } else if (input == 6) {
                // right
                tourist_column++;
            } else if (input == 4) {
                // left
                tourist_column--;
            } else if (input == 2) {
                // down
                tourist_row++;
            } else {
                // invalid or any other number
                keep_looping = 0;
            }
        }

        // Check if we've gone off the map
        // Clamp to legal map positions
        if (tourist_row < 0) {
            // top
            tourist_row = 0;
        } else if (tourist_row >= MAP_ROWS) {
            // bottom
            tourist_row = MAP_ROWS - 1;
        } else if (tourist_column < 0) {
            // left
            tourist_column = 0;
        } else if (tourist_column >= MAP_COLUMNS) {
            // right
            tourist_column = MAP_COLUMNS - 1;
        }

        print_map(map, tourist_row, tourist_column);

        // check to see if current location was already explored
        if (map[tourist_row][tourist_column] == EXPLORED) {
            printf("I have already been here, HOW BORING!\n");
            keep_looping = 0;
        }
    }
    return 0;
}


// Prints the map, by printing the integer value stored in
// each element of the 2-dimensional map array.
// Prints a T instead at the position t_row, t_col
void print_map(int map[MAP_ROWS][MAP_COLUMNS], int t_row, int t_column) {
    int row = 0;
    while (row < MAP_ROWS) {
        int column = 0;
        while (column < MAP_COLUMNS) {
            if(t_row == row && t_column == column) {
                printf("T ");
            } else {
                printf("%d ", map[row][column]);
            }
            column++;
        }
        row++;
        printf("\n");
    }
}

Download tourist.c

Pointers
a memory address as hexadecimal
#include <stdio.h>

int main(void) {
    int i = 100;
    // create a pointer called ip
    int *ip ;
    // make it point at i
    ip = &i;
    // prints something like this:
    // address of x in hexadecimal is 0x7fff51155000
    printf("address of x in hexadecimal is %p\n", (void *)ip);

    return 0;
}

Download memory_address.c


Some examples of using pointers
#include <stdio.h>

void increment_int(int n);
void increment_ptr(int *n);

int main(void) {
    int i = 100;
    // create a pointer called ip that points at i
    // it stores the address of i
    int *ip = &i;
    increment_int(i);
    printf("i, %d, hasn't changed.\n", i);
    increment_ptr(ip);
    printf("i, %d, has now changed.\n", i);

    return 0;
}


// This function will have no effect!
// It only modifies it's own variable `n`,
void increment_int(int n) {
    n = n + 1;
}


// This function will affect whatever n is pointing at
void increment_ptr(int *n) {
    *n = *n + 1;
}

Download increment.c


Simple example of using pointer to pass reference to variables
#include <stdio.h>

void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int main(int argc, char *argv[]) {
    int x = 42;
    int y = 13;

    printf("x=%d y=%d\n", x, y);

    swap(&x, &y);

    printf("x=%d y=%d\n", x, y);

    return 0;
}

Download swap.c


A program that scans input of a series of numbers and stores them in an array.
It then shuffles the order of those numbers and prints them back out.

A demo on using functions and pointers

#include <stdio.h>

#define MAX_CARDS 1024

int read_input(int deck[MAX_CARDS]);
void print_deck(int deck[MAX_CARDS], int length);
void swap_nums(int *num1, int *num2);
void shuffle(int deck[MAX_CARDS], int length);


int main(void) {
    int deck[MAX_CARDS] = {0};
    int num_cards = read_input(deck);
    shuffle(deck, num_cards);
    print_deck(deck, num_cards);
}


// Asks the user how many cards are in their deck
// Then reads that many cards (or MAX_CARDS if the user says too many)
// Returns how many cards were read in
int read_input(int deck[MAX_CARDS]) {
    // scan in the number of cards
    int num_cards;
    printf("How many cards are in your deck?\n");
    scanf("%d", &num_cards);

    // loop through the deck, writing in the numbers that user types in
    int i = 0;
    while (i < num_cards && i < MAX_CARDS) { // have read i cards
        scanf("%d", &deck[i]);
        i++;
    } // have read i cards

    return i;
}


// Prints out the first 'length' elements from the deck array
void print_deck(int deck[MAX_CARDS], int length) {
    int i = 0;
    while (i < length && i < MAX_CARDS) {
        printf("%d, ", deck[i]);
        i++;
    }
    printf("\n");
}


// Swaps the integers at the destinations of the pointers num1 and num2
void swap_nums(int *num1, int *num2) {
    int temp = *num1;
    *num1 = *num2;
    *num2 = temp;
}


// call swap multiple times on different pairs to shuffle the deck
void shuffle(int deck[MAX_CARDS], int length) {
    int i = 0;
    while (i < length) {
        // find another index to swap with
        int j = (i * 3) % length;
        swap_nums(&deck[i], &deck[j]);
        i++;
    }
}

Download shuffler.c


Simple example illustrating use of pointers to pass 3 values back from a function
#include <stdio.h>

void powers(double x, double *square, double *cube, double *fourth_power);
int main(void) {
    double s, c, f;

    powers(42, &s, &c, &f);

    printf("42^2 = %lf\n", s);
    printf("42^3 = %lf\n", c);
    printf("42^4 = %lf\n", f);
    return 0;
}

// given a number calculate sis 2nd third and 4th powers
void powers(double x, double *square, double *cube, double *fourth_power) {
    *square = x * x;
    *cube = x * x * x;
    *fourth_power = x * x * x * x;
}

Download powers.c


Some examples of using pointers
#include <stdio.h>

#define LENGTH 6

void print_fixed_size_array(int array[LENGTH]);
void print_any_size_array(int size, int array[size]);
void print_array_until_marker(int array[]);

int main(void) {
    int a[LENGTH] = { 1, 2, 4, 8, 16, 32 };

    print_fixed_size_array(a);
    print_any_size_array(LENGTH, a);

    int b[LENGTH + 1] = { 1, 2, 4, 8, 16, 32, -1 };
    print_array_until_marker(b);
    return 0;
}


// print an array containing LENGTH elements
void print_fixed_size_array(int array[LENGTH]) {
    int i = 0;
    while (i < LENGTH) {
        printf("%d\n", array[i]);
        i++;
    }
}


// print size elements of array
void print_any_size_array(int size, int array[size]) {
    int i = 0;
    while (i < size) {
        printf("%d\n", array[i]);
        i++;
    }
}


// print array elements until element containing -1 reached
void print_array_until_marker(int array[]) {
    int i = 0;
    while (array[i] != -1) {
        printf("%d\n", array[i]);
        i++;
    }
}

Download print_array.c

#include <stdio.h>

void count_big(int length, int nums[length], int *big_positive, int *big_negative);

#define SIZE 7

int main(void) {
    int array[SIZE] = {3, -4004, 110, 500, -6, 111, -3};
    int big_p;
    int big_n;
    count_big(SIZE, array, &big_p, &big_n);
    printf("big negative=%d big positive=%d\n", big_n, big_p);
    return 0;
}


// Count the big positive and big negative values in the array nums.
// A value is big is < -100 or > 1000
// Counts are stored in the variables
// that big_positive & big_negative point to
void count_big(int length, int nums[length],
               int *big_positive, int *big_negative) {
    int big_pos = 0;
    int big_neg = 0;

    int i = 0;
    while (i < length) {
        if (nums[i] < -100) {
            big_neg++;
        } else if (nums[i] > 100) {
            big_pos++;
        }

        i++;
    }

    // store counts in the variables that
    // big_positive & big_negative point to
    *big_positive = big_pos;
    *big_negative = big_neg;
}

Download count_big.c

Characters And Strings


Print the printable ASCII characters

#include <stdio.h>

int main(void) {
    int ascii = 33;
    while (ascii < 127) {
        printf("In ASCII %d prints as %c\n", ascii, ascii);
        ascii = ascii + 1;
    }
    return 0;
}

Download ascii.c


Read characters until eof
#include <stdio.h>

int main(void) {
    // getchar returns an int which will contain either
    // the ASCII code of the character read or EOF

    int ch = getchar();
    while (ch != EOF) {
        printf("'%c' read, ASCII code is %d\n", ch, ch);
        ch = getchar();
    }
    return 0;
}

Download getchar_eof.c


Read characters until eof, printing them with lower case letters convert to upper case

#include <stdio.h>

int make_upper_case(int character);

int main(void) {
    // getchar returns an int which will contain either
    // the ASCII code of the character read or EOF

    int character = getchar();
    while (character != EOF) {

        int new_character = make_upper_case(character);
        putchar(new_character);

        character = getchar();
    }
    return 0;
}

// return upper case letter corresponding to lower case letter
// e.g. given 'f' return 'F'
// other characters returned unchanged
//
// library function toupper() in <ctype.h> does this task

int make_upper_case(int character) {
    if (character >= 'a' && character <= 'z') {
        int alphabetPosition = character - 'a';
        return 'A' + alphabetPosition;
    } else {
        return character;
    }
}

Download upper_case.c



Print "Andrew Rocks!" - using ASCII codes for the characters

Compare the 8 andrew_rocks?.c programs which are all equivalent to get a better understanding of how & why C encodes character sequences

#include <stdio.h>

int main(void) {
    // DO NOT PUT ASCII CODES IN YOUR PROGRAM LIKE THIS 
    putchar(65);     // printf("%c", 65) is equivalent
    putchar(110);
    putchar(100);
    putchar(114);
    putchar(101);
    putchar(119);
    putchar(32);
    putchar(82);
    putchar(111);
    putchar(99);
    putchar(107);
    putchar(115);
    putchar(33);
    putchar(10);
    return 0;
}

Download andrew_rocks0.c



Print "Andrew Rocks!" - using character constants to get the ASCII codes for the characters
'A' is the C shorthand for the ASCII code for the character A (65)

#include <stdio.h>

int main(void) {
    putchar('A');   // equivalent to putchar(65) but readable!
    putchar('n');
    putchar('d');
    putchar('r');
    putchar('e');
    putchar('w');
    putchar(' ');
    putchar('R');
    putchar('o');
    putchar('c');
    putchar('k');
    putchar('s');
    putchar('\n');
    return 0;
}

Download andrew_rocks1.c



Print "Andrew Rocks!" - using character constants to get the ASCII codes for the characters, store them in array, and print the array. Note we have to track the array length.

#include <stdio.h>

int main(void) {
    int asciiCodes[14];

    asciiCodes[0] = 'A';
    asciiCodes[1] = 'n';
    asciiCodes[2] = 'd';
    asciiCodes[3] = 'r';
    asciiCodes[4] = 'e';
    asciiCodes[5] = 'w';
    asciiCodes[6] = ' ';
    asciiCodes[7] = 'R';
    asciiCodes[8] = 'o';
    asciiCodes[9] = 'c';
    asciiCodes[10] = 'k';
    asciiCodes[11] = 's';
    asciiCodes[12] = '!';
    asciiCodes[13] = '\n';

    int i = 0;
    while (i < 14) {
        putchar(asciiCodes[i]);
        i = i + 1;
    }

    return 0;
}

Download andrew_rocks2.c



Print "Andrew Rocks!" - using character constants to get the ASCII codes for the characters, initialize an array to the values , and print the array.
Note we have to track the array length.

#include <stdio.h>

int main(void) {
    // we can put all the ASCII codes in array and
    // then print every array element
    // we need to track the array size (should be a #define)
    int ascii_codes[14] = { 'A', 'n', 'd', 'r', 'e', 'w', ' ',
                           'R', 'o', 'c', 'k', 's', '!', '\n' };

    int i = 0;
    while (i < 14) {
        putchar(ascii_codes[i]);
        i = i + 1;
    }

    return 0;
}

Download andrew_rocks3.c



Print "Andrew Rocks!" - using character constants to get the ASCII codes for the characters, and initialize the array to the vales array using , and print the array.

This version has a extra special value in the array (0) to indicate the end of the sequence. This means we no longer have to keep track of how many characters in the array (note the while loop condition)

#include <stdio.h>

int main(void) {
    // we can put all the ASCII codes in array
    // if we put a special value (0) as last array element

    // We don't need to track the array size which is more convenient.

    // We don't specify the size of an array being initialized so
    // C makes it big enough to hold all the v (15 in this case)

    int ascii_codes[] = { 'A', 'n', 'd', 'r', 'e', 'w',  ' ', 'R',
                          'o', 'c', 'k', 's', '!', '\n', 0 };

    int i = 0;
    while (ascii_codes[i] != 0) {
        putchar(ascii_codes[i]);
        i = i + 1;
    }

    return 0;
}

Download andrew_rocks4.c



Print "Andrew Rocks!" - using character constants to get the ASCII codes for the characters, and initialize the array to the vales array using , and print the array.

This version has switched to using the numeric type char. This type is almost always 8 bits and shouldn't be used for arithmetic. It is commonly used to hold ASCII encodings.


#include <stdio.h>

int main(void) {

    // same as last version except use a char array

    char ascii_codes[] = {'A', 'n', 'd', 'r', 'e', 'w', ' ',
                         'R', 'o', 'c', 'k', 's', '!', '\n', 0};

    int i = 0;
    while (ascii_codes[i] != 0) {
        putchar(ascii_codes[i]);
        i = i + 1;
    }

    return 0;
}

Download andrew_rocks5.c



Print "Andrew Rocks!" - using character constants to get the ASCII codes for the characters, and initialize the array to the vales array using , and print the array.

C has a convenient shorthand for char arrays containing a sequence of
ASCII codes with an extra 0 value marking the end of the sequence.
Its "Andrew Rocks!";


Compare the 8 andrew_rocks?.c programs which are all equivalent to get a better understand of how & why C encodes character sequences

#include <stdio.h>

int main(void) {
    // C has a shorthand  for char arrays containing a sequence of
    // ASCII codes with an extra 0 value marking the end 
    // Its "Andrew Rocks!";
    
    char ascii_codes[] = "Andrew Rocks!\n";
    
    int i = 0;
    while (ascii_codes[i] != 0) {
        putchar(ascii_codes[i]);
        i = i + 1;
    }

    return 0;
}

Download andrew_rocks6.c



Print "Andrew Rocks!" - using character constants to get the ASCII codes for the characters, and initialize the array to the vales array using , and print the array.

C has a convenient shorthand for char arrays containing a sequence of
ASCII codes with an extra 0 value marking the end of the sequence.
Its "Andrew Rocks!";

A number of C library functions accept zero-terminated char arrays
For example printf with the "%s" specification (below)

#include <stdio.h>

int main(void) {
    char asciiCodes[] = "Andrew Rocks!\n";

    // Many C library functions work with zero-terminated char arrays

    // puts prints a zero-terminated char array
    fputs(asciiCodes, stdout);

    // printf "%s" also prints a zero-terminated char array
    printf("%s", asciiCodes);
    return 0;
}

Download andrew_rocks7.c

print command line argument
#include <stdio.h>

int main(int argc, char *argv[]) {

    printf("argc = %d\n", argc);

    printf("My name is %s\n", argv[0]);

    // print command line arguments
    int i = 1;
    while (i < argc) {
        printf("Argument %d is: %s\n", i, argv[i]);
        i = i + 1;
    }

    return 0;
}

Download print_arguments.c


Convert command-line arguments to integers and print their sum.

No check is made that the command-line arguments are actually integers.
See strtol for a more powerful library function which would allow checking.

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {

    int sum = 0;
    int argument = 1;
    while (argument < argc) {

        // convert string to integer
        int n = atoi(argv[argument]);

        // you could use `strtol` instead of `atoi` like this
        // int n = strtol(argv[argument], NULL, 10);

        sum = sum + n;
        argument= argument + 1;
    }
    printf("Total is %d\n", sum);

    return 0;
}

Download sum_arguments.c


Reads lines of input until end-of-input
Print snap! if two consecutive lines are identical

See snap_line1.c for how to use functions to produce simpler code

#include <stdio.h>

#define MAX_LINE 4096

int main(int argc, char *argv[]) {
    char line[MAX_LINE];
    char last_line[MAX_LINE];

    printf("Enter line: ");
    fgets(last_line, MAX_LINE, stdin);

    printf("Enter line: ");
    while (fgets(line, MAX_LINE, stdin) != NULL) {
        // DOES NOT WORK - arrays can not be compared
        if (line == last_line) {
            // lines are identical
            printf("Snap!\n");
        }

        // DOES NOT WORK - arrays can not be assigned
        last_line = line;

        printf("Enter line: ");
    }

    return 0;
}

Download snap_line_broken.c


Reads lines of input until end-of-input
Print snap! if two consecutive lines are identical

See snap_line2.c to see how to replace compare_arrays & copy_array calls to with (strcmp & strcpy) from <string.h>

#include <stdio.h>

#define MAX_LINE 4096

int compare_arrays(char array1[], char array2[]);
void copy_array(char destination_array[], char source_array[]);

int main(int argc, char *argv[]) {
    char line[MAX_LINE];
    char last_line[MAX_LINE];

    // read first line into array last_line
    printf("Enter line: ");
    fgets(last_line, MAX_LINE, stdin);

    printf("Enter line: ");
    while (fgets(line, MAX_LINE, stdin) != NULL) {
        if (compare_arrays(line, last_line) == 0) {
            // lines are identical
            printf("Snap!\n");
        }

        copy_array(last_line, line);

        printf("Enter line: ");
    }

    return 0;
}

// return 1 if array1 & array2 differ in any element, 0 otherwise
// array1 & array2 must be null-terminated char arrays
// strcmp from  <string.h> performs similar function
int compare_arrays(char array1[], char array2[]) {
    int i = 0;
    while (array1[i] != '\0') {
        if (array1[i] != array2[i]) {
            return 1;
        }
        i = i + 1;
    }
    if (array2[i] == '\0') {
        return 0;
    } else {
        return 1;
    }
}

// copy elements in source_array to destination_array
// source_array must be a null-terminated char array
// destination_array must be large enough to hold string
// strcpy from  <string.h> performs the same function
void copy_array(char destination_array[], char source_array[]) {
    int i = 0;
    while (source_array[i] != '\0') {
        destination_array[i] = source_array[i];
        i = i + 1;
    }
    destination_array[i] = '\0';
}

Download snap_line_ok.c


Reads lines of input until end-of-input
Print snap! if two consecutive lines are identical

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

#define MAX_LINE 4096

int main(int argc, char *argv[]) {
    char line[MAX_LINE];
    char lastLine[MAX_LINE];

    // read first line into array lastLine
    printf("Enter line: ");
    fgets(lastLine, MAX_LINE, stdin);

    printf("Enter line: ");
    while (fgets(line, MAX_LINE, stdin) != NULL) {

        if (strcmp(line, lastLine) == 0) {
            // lines are identical
            printf("Snap!\n");
        }

        strncpy(lastLine, line, MAX_LINE);

        printf("Enter line: ");
    }

    return 0;
}

Download snap_line_good.c

Malloc
simple example of using malloc to create an array containing n odd numbers
#include <stdio.h>
#include <stdlib.h>

int *create_odd_array(int n);

int main(void) {
    int n_odd_numbers = 10;

    int *ptr = create_odd_array(n_odd_numbers);

    int j = 0;
    while (j < n_odd_numbers) {
        printf("%d\n", ptr[j]);
        j = j + 1;
    }

    // free the allocated memory
    free(ptr);

    return 0;
}

// create an array containing first n odd numbers
int *create_odd_array(int n) {
    int *odd_ptr = malloc(n * sizeof (int));

    int i = 0;
    while (i < n) {
        odd_ptr[i] = 2 * i + 1;
        i = i  + 1;
    }

    return odd_ptr;
}

Download malloc_array.c

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

#define MAX_LENGTH 100

struct bender {
    char name[MAX_LENGTH];
    char element[MAX_LENGTH];
    int power;
};

int main(void) {
    // create a struct variable
    struct bender avatar;
    // use . to access the field of a struct
    avatar.power = 10;
    strcpy(avatar.name, "Aang");
    strcpy(avatar.element, "Air");
    printf("%s's element is: %s.\n", avatar.name, avatar.element);

    return 0;
}

Download simple_struct_example.c

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

#define MAX_LENGTH 100

struct bender {
    char name[MAX_LENGTH];
    char element[MAX_LENGTH];
    int power;
};

void display_person(struct bender *person);

int main(void) {
    // create a struct variable
    struct bender avatar;

    // create a pointer to the struct variable
    struct bender *andrew = &avatar;

    // given a pointer to a struct
    // use can use . and * to access
    (*andrew).power = 100;

    // but there is a better way
    // -> dereferences the pointer and
    // accesses a field inside the struct:
    andrew->power = 100;

    strcpy(andrew->name, "Andrew");
    strcpy(andrew->element, "Code");
    display_person(andrew);

    return 0;
}

void display_person(struct bender *person) {
    printf("Name: %s\n", person->name);
    printf("Element: %s\n", person->element);
    printf("Power: %d\n", person->power);
}

Download simple_struct_pointer_example.c


Bender Team
A demonstration of structs and memory allocation using Element Benders as an example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LENGTH 100
#define TEAM_SIZE 4

// Structures
struct bender {
    char name[MAX_LENGTH];
    char element[MAX_LENGTH];
    int power;
};

struct team {
    char name[MAX_LENGTH];
    int team_size;
    struct bender *team_members[TEAM_SIZE];
};

// Functions
struct bender *create_bender(char *b_name, char *b_element, int b_power);
void print_team(struct team *print_team);
void free_team(struct team *f_team);

int main(void) {
    struct team *companions = malloc(sizeof(struct team));
    strcpy(companions->name, "My Cabbages!");
    companions->team_size = 0;
    companions->team_members[companions->team_size] =
        create_bender("Aang", "Air", 10);
    companions->team_size++;
    companions->team_members[companions->team_size] =
        create_bender("Katara", "Water", 6);
    companions->team_size++;

    print_team(companions);

    free_team(companions);
}

// Allocate memory for a bender, populate it with
// the given information, then return a pointer
// to the allocated memory location
struct bender *create_bender(char *b_name, char *b_element, int b_power) {
    struct bender *new_bender = malloc(sizeof(struct bender));

    strcpy(new_bender->name, b_name);
    strcpy(new_bender->element, b_element);
    new_bender->power = b_power;

    return new_bender;
}

// print_team will print out the details of the team members
// to the terminal. It will not change the team.
void print_team(struct team *print_team) {
    printf("Team name is %s\n", print_team->name);
    int i = 0;
    while (i < print_team->team_size) {
        printf("Team member %s uses the element: %s\n",
               print_team->team_members[i]->name,
               print_team->team_members[i]->element);
        i++;
    }
}

// Free all structs (from the team level)
void free_team(struct team *f_team) {
    // free all the allocations (bender structs) the team points at
    int i = 0;
    while (i < f_team->team_size) {
        free(f_team->team_members[i]);
        i++;
    }
    // then free the team struct itself
    free(f_team);
}

Download bender.c

Lists

A demo of a basic linked list

#include <stdlib.h>
#include <stdio.h>

struct node {
    int data;
    struct node *next;
};

struct node *create_node(int data, struct node *next);
void print_data(struct node *n);

int main(void) {
    // head will always point to the first element of our list
    struct node *head = create_node(5, NULL);
    head = create_node(4, head);
    head = create_node(3, head);
    head = create_node(2, head);
    head = create_node(1, head);

    print_data(head);

    return 0;
}

// Create a node using the data and next pointer provided
// Return a pointer to this node
struct node *create_node(int data, struct node *next) {
    struct node *n;
    n = malloc(sizeof(struct node));
    n->data = data;
    n->next = next;
    return n;
}

// Loop through a list of nodes, printing out their data
void print_data(struct node *n) {
    while (n != NULL) { // have printed from list's head up to just before n
        printf("%d\n", n->data);
        n = n->next;
    }
}

Download linked_list.c

  Battle Royale
    A linked list example that tracks players in a game.
    We want to be able to maintain the list
    as well as remove players as they are knocked out.
    We also want to be able to display who is currently
    still in the game.

Marc Chee (cs1511@cse.unsw.edu.au) April 1st 2021

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

#define MAX_NAME_LENGTH 100

// Structs
struct player {
    struct player *next;
    char name[MAX_NAME_LENGTH];
};

// Functions
struct player *create_player(char new_name[MAX_NAME_LENGTH], struct player *new_next);
void print_players(struct player *head);

int main(void) {
    struct player *head = create_player("Marc", NULL);
    head = create_player("Chicken", head);
    head = create_player("Shrey", head);
    head = create_player("Anusha", head);

    print_players(head);
}

// Allocate memory for a player node
// Populate that node with new_name and new_next
// Return a pointer to the allocated memory
struct player *create_player(char new_name[MAX_NAME_LENGTH], struct player *new_next) {
    struct player *new_player = malloc(sizeof (struct player));
    new_player->next = new_next;
    strcpy(new_player->name, new_name);
    return new_player;
}

// Takes the head of the list
// Prints out everyone who is in the list
void print_players(struct player *head) {
    struct player *current = head;
    while (current != NULL) { // I'm looking at a valid player
        printf("%s\n", current->name);
        current = current->next;
    }
}

Download battle_royale_v0/battle_royale.c

/*  Battle Royale - Header File
    A linked list example that tracks players in a game.
    We want to be able to maintain the list
    as well as remove players as they are knocked out.
    We also want to be able to display who is currently
    still in the game.
    
    Marc Chee (cs1511@cse.unsw.edu.au) April 1st 2021
*/

#define MAX_NAME_LENGTH 100

// Create a player by allocating memory,
// populating their data then returning a pointer to them
struct player *create_player(char new_name[MAX_NAME_LENGTH], struct player *new_next);

// Loop through and display the players
// One per line in the terminal
void print_players(struct player *head);

// Insert into a list alphabetically
// Returns the head of the list (even if it has changed)
struct player *insert_alpha(struct player *head, char new_name[MAX_NAME_LENGTH]);

Download battle_royale_v1/battle_royale.h

  Battle Royale - Main File
    A linked list example that tracks players in a game.
    We want to be able to maintain the list
    as well as remove players as they are knocked out.
    We also want to be able to display who is currently
    still in the game.

Marc Chee (cs1511@cse.unsw.edu.au) April 1st 2021

#include <stdlib.h>
#include <stdio.h>
#include "battle_royale.h"

int main(void) {
    struct player *head = NULL;
    head = insert_alpha(head, "Marc");
    print_players(head);
    printf("-----\n");
    head = insert_alpha(head, "Chicken");
    print_players(head);
    printf("-----\n");
    head = insert_alpha(head, "Goku");

    print_players(head);
}

Download battle_royale_v1/main.c

  Battle Royale - Implementation File
    A linked list example that tracks players in a game.
    We want to be able to maintain the list
    as well as remove players as they are knocked out.
    We also want to be able to display who is currently
    still in the game.

Marc Chee (cs1511@cse.unsw.edu.au) April 1st 2021

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "battle_royale.h"

// Structs
struct player {
    struct player *next;
    char name[MAX_NAME_LENGTH];
};

// Allocate memory for a player node
// Populate that node with new_name and new_next
// Return a pointer to the allocated memory
struct player *create_player(char new_name[MAX_NAME_LENGTH], struct player *new_next) {
    struct player *new_player = malloc(sizeof (struct player));
    new_player->next = new_next;
    strcpy(new_player->name, new_name);
    return new_player;
}

// Takes the head of the list
// Prints out everyone who is in the list
void print_players(struct player *head) {
    struct player *current = head;
    while (current != NULL) { // I'm looking at a valid player
        printf("%s\n", current->name);
        current = current->next;
    }
}

// Inserts into a list (head) alphabetically.
// Assumes the list is already in alphabetical order
// Returns the head of the list (it may have changed)
struct player *insert_alpha(struct player *head, char new_name[MAX_NAME_LENGTH]) {
    // loop through and find the insertion point
    struct player *current = head;
    struct player *previous = NULL;
    while (current != NULL && strcmp(current->name, new_name) < 0) {
        // go past any names that are earlier than us alphabetically
        previous = current;
        current = current->next;
    } // current is now the first player in the list with a name "higher or equal"
      // than new_name
      // previous is now the last player in the list with a name "lower"
      // than new_name

    if (previous == NULL) {
        // Inserting at the head of a list
        // also works for an empty list (head == NULL)
        head = create_player(new_name, head);
    } else {
        // Inserting further down the list
        previous->next = create_player(new_name, previous->next);
    }

    return head;
}

Download battle_royale_v1/battle_royale.c

/*  Battle Royale - Header File
    A linked list example that tracks players in a game.
    We want to be able to maintain the list
    as well as remove players as they are knocked out.
    We also want to be able to display who is currently
    still in the game.
    
    Marc Chee (cs1511@cse.unsw.edu.au) April 1st 2021
*/

#define MAX_NAME_LENGTH 100

// Create a player by allocating memory,
// populating their data then returning a pointer to them
struct player *create_player(char new_name[MAX_NAME_LENGTH], struct player *new_next);

// Loop through and display the players
// One per line in the terminal
// Returns the number of players that it printed out
int print_players(struct player *head);

// Insert into a list alphabetically
// Returns the head of the list (even if it has changed)
struct player *insert_alpha(struct player *head, char new_name[MAX_NAME_LENGTH]);

// Find the player that matches rem_name in the list starting at head
// Remove and free that player, return the head (it may have changed)
struct player *remove_player(struct player *head, char rem_name[MAX_NAME_LENGTH]);

// Ask for input from the user to remove a player.
// Return the head of the list (it may have changed)
struct player *remove_by_name(struct player *head);

// Free all the players in the given list
void free_list(struct player *head);

Download battle_royale_v2/battle_royale.h

  Battle Royale - Main File
    A linked list example that tracks players in a game.
    We want to be able to maintain the list
    as well as remove players as they are knocked out.
    We also want to be able to display who is currently
    still in the game.

Marc Chee (cs1511@cse.unsw.edu.au) April 1st 2021

#include <stdlib.h>
#include <stdio.h>
#include "battle_royale.h"

int main(void) {
    struct player *head = NULL;
    head = insert_alpha(head, "Shrey");
    head = insert_alpha(head, "Anosua");
    head = insert_alpha(head, "Zoe");
    head = insert_alpha(head, "Alvin");
    head = insert_alpha(head, "Tom");
    head = insert_alpha(head, "Andrew");

    while (print_players(head) > 1) {
        head = remove_by_name(head);
        printf("----------\n");
    }
    printf("is the winner!\n");
    free_list(head);
}

Download battle_royale_v2/main.c

  Battle Royale - Implementation File
    A linked list example that tracks players in a game.
    We want to be able to maintain the list
    as well as remove players as they are knocked out.
    We also want to be able to display who is currently
    still in the game.

Marc Chee (cs1511@cse.unsw.edu.au) April 1st 2021

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "battle_royale.h"

// Structs
struct player {
    struct player *next;
    char name[MAX_NAME_LENGTH];
};

// Allocate memory for a player node
// Populate that node with new_name and new_next
// Return a pointer to the allocated memory
struct player *create_player(char new_name[MAX_NAME_LENGTH], struct player *new_next) {
    struct player *new_player = malloc(sizeof (struct player));
    new_player->next = new_next;
    strcpy(new_player->name, new_name);
    return new_player;
}

// Takes the head of the list
// Prints out everyone who is in the list
int print_players(struct player *head) {
    struct player *current = head;
    int num_players = 0;
    while (current != NULL) { // I'm looking at a valid player
        printf("%s\n", current->name);
        num_players++;
        current = current->next;
    }
    return num_players;
}

// Inserts into a list (head) alphabetically.
// Assumes the list is already in alphabetical order
// Returns the head of the list (it may have changed)
struct player *insert_alpha(struct player *head, char new_name[MAX_NAME_LENGTH]) {
    // loop through and find the insertion point
    struct player *current = head;
    struct player *previous = NULL;
    while (current != NULL && strcmp(current->name, new_name) < 0) {
        // go past any names that are earlier than us alphabetically
        previous = current;
        current = current->next;
    } // current is now the first player in the list with a name "higher or equal"
      // than new_name
      // previous is now the last player in the list with a name "lower"
      // than new_name

    if (previous == NULL) {
        // Inserting at the head of a list
        // also works for an empty list (head == NULL)
        head = create_player(new_name, head);
    } else {
        // Inserting further down the list
        previous->next = create_player(new_name, previous->next);
    }

    return head;
}

// Loop through list looking for a match of
// player's name to rem_name
// Remove and free that node (if it exists)
// Return the possibly-changed head of the list
// Note: head only changes if we remove the first player
struct player *remove_player(struct player *head,
                             char rem_name[MAX_NAME_LENGTH]) {
    // Loop through and stop with curr->name == rem_name
    struct player *curr = head;
    struct player *prev = NULL;
    while (curr != NULL && strcmp(rem_name, curr->name) != 0) {
        prev = curr;
        curr = curr->next;
    }

    if (curr != NULL) {
        // curr now points at a player whose name matches rem_name
        if (prev == NULL) { // (head == curr) also works
            // removing the head of the list
            head = curr->next;
        } else {
            // removing an element somewhere other than the head
            prev->next = curr->next;
        }
        free(curr);
    }
    // or curr is NULL if it has reached the end of the list

    return head;
}

// Ask the user for a player's name to remove.
// Remove that node and return the head of the list (may have changed)
struct player *remove_by_name(struct player *head) {
    printf("Who's just been knocked out?\n");
    char input[MAX_NAME_LENGTH];
    fgets(input, MAX_NAME_LENGTH, stdin);
    // fgets will store the \n that we type in in the input string
    // if we find a \n at the end of input, remove it from the string
    if (input[strlen(input) - 1] == '\n') {
        input[strlen(input) - 1] = '\0';
    }
    head = remove_player(head, input);
    return head;
}

// Free all elements of the list starting at head
void free_list(struct player *head) {
    struct player *curr = head;
    while (curr != NULL) {
        struct player *free_node = curr;
        curr = curr->next;
        free(free_node);
    }
}

Download battle_royale_v2/battle_royale.c

Multiple File C
// .h files sometimes include other .h files
// Sometimes because they need something in that filects
// Sometimes just for convenience
#include <stdio.h>

// prototypes of functions called from a different .c file 
// should have a prototype in a .h file 
// This .h file should be included in the file the function is defined
// and in every file where the function is called
int answer(double x);

// often #defines are used in multiple files
// if so they should be placed in a .h file 
#define ANSWER 42

// often types are used in multiple files
// if so the typedefs should be placed in a .h file 
typedef int answer_t;

// function definitions (code) should never be placed
// in a .h file, always a .c file

Download answer.h

#include "answer.h"

answer_t answer(double x) {
    return x * ANSWER;
}

Download answer.c


Simple example of a multi-file program, compile like this: dcc print_answer.c answer.c -o answer
#include "answer.h"

int main(void) {
    int i = answer(2);
    printf("%d\n", i);
    return 0;
}

Download print_answer.c

// First look at multi-file projects
//
// Marc Chee (cs1511@cse.unsw.edu.au), October 2020
//
// A header file like this contains information about a some code.
// It's usually enough to read this to be able to use the code
// without needing to actually delve into the C file.

#define MAX_NAME_LENGTH 50
#define MAX_POWER_LENGTH 50
#define MAX_NUM_POWERS 5 

// This typedef declares that the type "Person" is the same as the type
// "pointer to struct person".

// The actual definition of "struct person" is not in person.h (this
// file), it's in person.c

// This means that you cannot directly access any of the fields of the
// person struct -- e.g. "person->name" is invalid.
typedef struct person *Person;

// Create a person and return a pointer to them
Person create_person(char name[MAX_NAME_LENGTH]);

// free_person frees any memory used for a person
void free_person(Person hero);

// give_power will add a power to a person
// It will add the string to the array of strings
// called powers if the person still has
// space to add powers
void give_power(char power[MAX_POWER_LENGTH], Person hero);

// Displays a person's information
void display_person(Person hero);

Download person.h


First look at multi-file projects
Marc Chee (cs1511@cse.unsw.edu.au)
This demo shows how we include a header file so that we can use its functionality in our main here compile like this: dcc batman.c person.c -o batman

#include "person.h"

int main(void) {
    Person batman = create_person("Batman");
    display_person(batman);

    give_power("Ninjitsu", batman);
    give_power("Tech gadgets", batman);
    give_power("Brooding", batman);
    give_power("Gravelly Christian Bale Voice", batman);

    display_person(batman);

    // Free our memory now that we're not going to use it
    free_person(batman);
}

Download batman.c


First look at multi-file projects
Implementation of the person that is declared in person.h
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "person.h"


struct person {
    char name[MAX_NAME_LENGTH];
    char powers[MAX_NUM_POWERS][MAX_POWER_LENGTH];
    int num_powers;
};

// allocate memory for a struct person,
// initialise information
// and return the pointer to the memory
Person create_person(char name[MAX_NAME_LENGTH]) {
    Person hero = malloc(sizeof (struct person));
    strcpy(hero->name, name);
    hero->num_powers = 0;
    return hero;
}

void free_person(Person hero) {
    free(hero);
}

// Add a power.
// Do nothing if the person already has MAX_NUM_POWERS powers
void give_power(char power[MAX_POWER_LENGTH], struct person *hero) {
    if (hero->num_powers < MAX_NUM_POWERS) {
        strcpy(hero->powers[hero->num_powers], power);
        hero->num_powers++;
    }
}

// print out information about the person
void display_person(struct person *hero) {
    printf("Name: %s\n", hero->name);
    printf("Powers:\n");
    int i = 0;
    while(i < hero->num_powers) {
        fputs(hero->powers[i], stdout);
        putchar('\n');
        i++;
    }
    printf("----------\n");
}

Download person.c

Abstract Data Types
// Abstract Data Type Stack demo
// Marc Chee, April 2021

// This file describes the interface to a stack of ints
// It must be included in other code files to be of use

// ======================================================
// The type "Stack" refers to a stack_internals pointer.
// In other files, programs can use Stack, but they will
// not get direct access to the stack's internal workings
// nor will they know exactly what they are

// ======================================================
// These functions are the only way external code can
// manipulate a stack.

// Stack type hides the struct that it is implemented as
typedef struct stack_internals *Stack;

// functions to create and destroy stacks
Stack stack_create();
void stack_free(Stack s);

// Add and remove items from stacks
// Removing the item returns the item for use
void stack_push(Stack s, int item);
int stack_pop(Stack s);

// Check on the size of the stack
int stack_size(Stack s);

Download stack.h


Abstract Data Types Demo
Using a stack
#include <stdio.h>

#include "stack.h"

int main(void) {
    Stack deck = stack_create();
    int card = 42;
    printf("Pushing %d on top of the deck.\n", card);
    stack_push(deck, card);
    card = 37;
    printf("Pushing %d on top of the deck.\n", card);
    stack_push(deck, card);
    card = 13;
    printf("Pushing %d on top of the deck.\n", card);
    stack_push(deck, card);

    printf("Taking %d off the top of the deck.\n", stack_pop(deck));
    printf("Taking %d off the top of the deck.\n", stack_pop(deck));
    printf("Taking %d off the top of the deck.\n", stack_pop(deck));
}

Download stack_demo.c


Linked List implementation of a Stack
#include "stack.h"
#include <stdlib.h>
#include <stdio.h>

struct stack_internals {
    struct stack_node *head;
};

struct stack_node {
    struct stack_node *next;
    int data;
};

// Create the stack, intially empty
Stack stack_create() {
    Stack new_stack = malloc(sizeof (struct stack_internals));
    new_stack->head = NULL;
    return new_stack;
}

// Add an element to the top of the stack
// The top of the stack is the head of the linked list
void stack_push(Stack s, int item) {
    struct stack_node *new_node = malloc(sizeof (struct stack_node));
    new_node->next = s->head;
    new_node->data = item;

    s->head = new_node;
}

// Remove the head (top of the stack) of the list
// return the value that was stored there
int stack_pop(Stack s) {
    // Is the stack empty?
    if (s->head == NULL) {
        printf("Cannot pop from an empty stack.\n");
        exit(1);
    }

    // retrieve data from the top of the stack
    int data = s->head->data;

    // remove the head of the stack
    struct stack_node *rem_node = s->head;
    s->head = s->head->next;
    free(rem_node);

    return data;
}

Download stack_linked_list.c


Array Implementation of a Stack
#include "stack.h"
#include <stdlib.h>
#include <stdio.h>

#define MAX_STACK_SIZE 1024

struct stack_internals {
    int stack_data[MAX_STACK_SIZE];
    int top;
};

// Create the stack, starting empty
Stack stack_create() {
    struct stack_internals *new_stack = malloc(sizeof (struct stack_internals));
    new_stack->top = 0;
    return new_stack;
}

// Add an item to the top of the stack (at the index top)
void stack_push(Stack s, int item) {
    if (s->top >= MAX_STACK_SIZE) {
        // we have run out of space for our stack
        printf("Maximum stack has overflowed, cannot push.\n");
        exit(2);
    }
    s->stack_data[s->top] = item;
    s->top++;
}

// Pop an item by reading the item in index top-1
// Move the top down one.
int stack_pop(Stack s) {
    if (s->top <= 0) {
        // No elements in the stack
        printf("Cannot pop from an empty stack.\n");
        exit(1);
    }
    s->top--;
    return s->stack_data[s->top];
}

Download stack_array.c

Recursion

A list of 10 items is created with create_list summed with sum_list and then freed
#include <stdio.h>
#include <stdlib.h>

struct node {
    struct node *next;
    int         data;
};

struct node *create_list(int start, int finish);
int sum_list(struct node *head);
void free_list(struct node *head);

int main(int argc, char *argv[]) {
    // creates a list containing 10 integers 11..20
    struct node *head = create_list(11, 20);
    int sum = sum_list(head);
    printf("%d\n", sum); // prints 155
    free_list(head);

    return 0;
}

// return sum of data fields of list
int sum_list(struct node *head) {
    struct node *n = head;
    int total = 0;
    while (n != NULL) {
        total += n->data;
        n = n->next;
    }
    return total;
}

// free all the node in a list
void free_list(struct node *head) {
    struct node *p = head;
    while (p != NULL) {
        struct node *next_node = p->next;
        free(p);
        p = next_node;
    }
}

// create a list of the integers start..finish
// return pointer to head
struct node *create_list(int start, int finish) {
    struct node *head = NULL;
    // create list starting from last node
    int i = finish;
    while (i >= start) {
        struct node *new_node = malloc(sizeof (struct node));
        new_node->data = i;
        new_node->next = head;
        head = new_node;
        i--;
    }
    return head;
}

Download sum_list.c


A list of 10 items is created with create_list summed with sum_list and then freed
recursive version of sum_list, free_list and create_list
#include <stdio.h>
#include <stdlib.h>

struct node {
    struct node *next;
    int         data;
};

struct node *create_list(int start, int finish);
int sum_list(struct node *head);
void free_list(struct node *head);

int main(int argc, char *argv[]) {
    // creates a list containing 10 integers 11..20
    struct node *head = create_list(11, 20);
    int sum = sum_list(head);
    printf("%d\n", sum); // prints 155
    free_list(head);

    return 0;
}

// return sum of data fields of list
int sum_list(struct node *head) {
    if (head == NULL) {
        return 0;
    } else {
        return head->data + sum_list(head->next);
    }
}

// free all the node in a list
void free_list(struct node *head) {
    if (head != NULL) {
        free_list(head->next);
        free(head);
    }
}

// create a list of the integers start..finish
// return pointer to head
struct node *create_list(int start, int finish) {
    if (start > finish) {
        return NULL;
    } else {
        struct node *new_node = malloc(sizeof (struct node));
        new_node->data = start;
        new_node->next = create_list(start + 1, finish);
        return new_node;
    }
}

Download sum_list_recursive.c

/*  Battle Royale - Header File
    A linked list example that tracks players in a game.
    We want to be able to maintain the list
    as well as remove players as they are knocked out.
    We also want to be able to display who is currently
    still in the game.
    
    Marc Chee (cs1511@cse.unsw.edu.au) April 1st 2021
*/

#define MAX_NAME_LENGTH 100

// Create a player by allocating memory,
// populating their data then returning a pointer to them
struct player *create_player(char new_name[MAX_NAME_LENGTH], struct player *new_next);

// Loop through and display the players
// One per line in the terminal
// Returns the number of players that it printed out
int print_players(struct player *head);

// Print out the names stored in the list in reverse order
// This is a procedural programming implementation
void reverse_print(struct player *player_list);

// Print out the names stored in the list in reverse order
// This is a recursive programming implementation
void rev_print_rec(struct player *player_list);

// Insert into a list alphabetically
// Returns the head of the list (even if it has changed)
struct player *insert_alpha(struct player *head, char new_name[MAX_NAME_LENGTH]);

// Find the player that matches rem_name in the list starting at head
// Remove and free that player, return the head (it may have changed)
struct player *remove_player(struct player *head, char rem_name[MAX_NAME_LENGTH]);

// Ask for input from the user to remove a player.
// Return the head of the list (it may have changed)
struct player *remove_by_name(struct player *head);

// Free all the players in the given list
void free_list(struct player *head);

Download battle_royale.h

  Battle Royale - Implementation File
    A linked list example that tracks players in a game.
    We want to be able to maintain the list
    as well as remove players as they are knocked out.
    We also want to be able to display who is currently
    still in the game.

Marc Chee (cs1511@cse.unsw.edu.au) April 1st 2021

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "battle_royale.h"

// Structs
struct player {
    struct player *next;
    char name[MAX_NAME_LENGTH];
};

// Allocate memory for a player node
// Populate that node with new_name and new_next
// Return a pointer to the allocated memory
struct player *create_player(char new_name[MAX_NAME_LENGTH], struct player *new_next) {
    struct player *new_player = malloc(sizeof (struct player));
    new_player->next = new_next;
    strcpy(new_player->name, new_name);
    return new_player;
}

// Takes the head of the list
// Prints out everyone who is in the list
int print_players(struct player *head) {
    struct player *current = head;
    int num_players = 0;
    while (current != NULL) { // I'm looking at a valid player
        printf("%s\n", current->name);
        num_players++;
        current = current->next;
    }
    return num_players;
}

struct player *print_before(struct player *player_list,
                            struct player *after) {
    // loop until you see the after pointer
    struct player *curr = player_list;
    struct player *prev = NULL;
    while (curr != after) {
        prev = curr;
        curr = curr->next;
    }
    if (prev != NULL) {
        // element exists, print its name
        printf("%s\n", prev->name);
    }
    return prev;
}

// Print out the names stored in the list in reverse order
// This is a procedural programming implementation
void reverse_print(struct player *player_list) {
    struct player *end = NULL;
    int finished = 0;
    // Loop once for each name in the list
    while (!finished) {
        end = print_before(player_list, end);
        if (end == NULL) {
            finished = 1;
        }
    }
}

// Print out the names stored in the list in reverse order
// This is a recursive programming implementation
void rev_print_rec(struct player *player_list) {
    if (player_list != NULL) {
        // there are element(s)
        rev_print_rec(player_list->next);
        printf("%s\n", player_list->name);
    }
}


// Inserts into a list (head) alphabetically.
// Assumes the list is already in alphabetical order
// Returns the head of the list (it may have changed)
struct player *insert_alpha(struct player *head, char new_name[MAX_NAME_LENGTH]) {
    // loop through and find the insertion point
    struct player *current = head;
    struct player *previous = NULL;
    while (current != NULL && strcmp(current->name, new_name) < 0) {
        // go past any names that are earlier than us alphabetically
        previous = current;
        current = current->next;
    } // current is now the first player in the list with a name "higher or equal"
      // than new_name
      // previous is now the last player in the list with a name "lower"
      // than new_name

    if (previous == NULL) {
        // Inserting at the head of a list
        // also works for an empty list (head == NULL)
        head = create_player(new_name, head);
    } else {
        // Inserting further down the list
        previous->next = create_player(new_name, previous->next);
    }

    return head;
}

// Loop through list looking for a match of
// player's name to rem_name
// Remove and free that node (if it exists)
// Return the possibly-changed head of the list
// Note: head only changes if we remove the first player
struct player *remove_player(struct player *head,
                             char rem_name[MAX_NAME_LENGTH]) {
    // Loop through and stop with curr->name == rem_name
    struct player *curr = head;
    struct player *prev = NULL;
    while (curr != NULL && strcmp(rem_name, curr->name) != 0) {
        prev = curr;
        curr = curr->next;
    }

    if (curr != NULL) {
        // curr now points at a player whose name matches rem_name
        if (prev == NULL) { // (head == curr) also works
            // removing the head of the list
            head = curr->next;
        } else {
            // removing an element somewhere other than the head
            prev->next = curr->next;
        }
        free(curr);
    }
    // or curr is NULL if it has reached the end of the list

    return head;
}

// Ask the user for a player's name to remove.
// Remove that node and return the head of the list (may have changed)
struct player *remove_by_name(struct player *head) {
    printf("Who's just been knocked out?\n");
    char input[MAX_NAME_LENGTH];
    fgets(input, MAX_NAME_LENGTH, stdin);
    // fgets will store the \n that we type in in the input string
    // if we find a \n at the end of input, remove it from the string
    if (input[strlen(input) - 1] == '\n') {
        input[strlen(input) - 1] = '\0';
    }
    head = remove_player(head, input);
    return head;
}

// Free all elements of the list starting at head
void free_list(struct player *head) {
    struct player *curr = head;
    while (curr != NULL) {
        struct player *free_node = curr;
        curr = curr->next;
        free(free_node);
    }
}

Download battle_royale.c


Battle Royale a main function to test rev_print_rec
#include <stdlib.h>
#include <stdio.h>
#include "battle_royale.h"

int main(void) {
    struct player *head = NULL;
    head = insert_alpha(head, "Shrey");
    head = insert_alpha(head, "Anosua");
    head = insert_alpha(head, "Zoe");
    head = insert_alpha(head, "Alvin");
    head = insert_alpha(head, "Tom");
    head = insert_alpha(head, "Andrew");

    rev_print_rec(head);
    free_list(head);
}

Download test_rev_print_rec.c


Battle Royale a main function to test reverse_print
#include <stdlib.h>
#include <stdio.h>
#include "battle_royale.h"

int main(void) {
    struct player *head = NULL;
    head = insert_alpha(head, "Shrey");
    head = insert_alpha(head, "Anosua");
    head = insert_alpha(head, "Zoe");
    head = insert_alpha(head, "Alvin");
    head = insert_alpha(head, "Tom");
    head = insert_alpha(head, "Andrew");

    reverse_print(head);
    free_list(head);
}

Download test_reverse_print.c

Revision