COMP1511 19T2
COMP1511 19T2
  1. The tutorial will start with a code review.

    Your tutor has asked a lab pair to present their week 7 work.

    Discuss the good, the bad and the ugly aspects of their code.

    Please be gentle in any criticism - we are all learning!

  2. New lab partners this week! Your tutor will assign new lab pairs for the final three weeks of the term.
  3. Assignment 2 has been released - Castle Defense. What is Castle Defense? What are the different features of the Castle Defense game?

    Try out the reference Castle Defense implementation: 1511 castle_defense.

    Castle Defense is a program that mimics the game genre "Tower Defense". It will store a series of locations, always starting in a Lair and ending in a Castle. Some of these locations can be Towers. Castle Defense also manages Enemies, who will move from the Lair to the Castle. Towers and Enemies will interact, with Towers spending their uses to reduce Enemy health, trying to stop them from reaching the Castle.
  4. What is a Realm and a Location? How are they used and how do they relate to each other?

    Both the Realm and Locations are structs.

    The Locations are a linked list, but instead of simple ints that we've seen so far, they contain more information.

    The Realm is a special struct that contains pointers to the start (Lair) and end (Castle) of the linked list of locations.

    Both of these structs will need to be edited and worked with in the assignment.

  5. Have you learnt anything you think would be useful about Castle Defense to share with the tutorial?

    Do you have questions that others in the tutorial may be able to answer?

  6. Given a linked list with the following structures:
    
    struct overall {
        struct node *start;
        struct node *end;
    };
    
    
    struct node {
        int data;
        struct node *next;
    };
    
    where start points at the first element of the list and end points at the last element of the list:

    1. What does the overall struct look like in memory? What is its purpose?
    2. What does the node struct look like in memory? What is its purpose?
    3. What does the malloc function do? How could you use it to allocate memory for a single struct node?
    4. Write a function called new_node that creates a new node with the specified data value:
      
      struct node *new_node(int data) {
      }
      
      
      struct node *new_node(int data) {
          struct node *new = malloc(sizeof(struct node));
          node->data = data;
          node->next = NULL;
          return node;
      }
      
    5. Write a function called insert_at_head that uses your new_node function to create a node, and then inserts that node at the front of the linked list.
      
      void insert_at_front(struct overall *o, int data) {
      }
      
      
      void insert_at_front(struct overall *o, int data) {
          struct node *new = new_node(data);
          new->next = o->head;
          o->head = new;
      }
      
    6. Write a function called insert_before_end that inserts an element just before the end element.
      
      void insert_before_end(struct overall *o, int data) {
      }
      
      
      void insert_before_end(struct overall *o, int data) {
          // loop until just before the end
          struct node *n = o->start;
          while (n != o->end) {
              n = n->next;
          }
      
          // create a new node and insert it after n
          struct node *newNode = malloc(sizeof (struct node));
          newNode->data = data;
          newNode->next = n->next;
          n->next = newNode;
      }
      
    1. My program works when I run it, autotest says I have an uninitialized variable. What is an uninitialised variable and and how can it affect my program?
      An uninitialised variable is one that has not been given a value.

      Anything can happen when using an uninitialized variable including your program terminating immediately.

      With common C implementations you'll often find a value formed from the bytes of a previous variable. This will often be zero because past values will often be part or all zeros.

      As commonly zero is the value that the variable should have been initialized with, the code will seem to work.

      But a slightest change to platform, compiler, the code, perhaps even just time of day, will result in the bytes being different and the program breaking.

      Small simple programs, like COMP1511 lab exercises, are particularly vulnerable to this because the value of an uninitialized variable will often be formed from bytes that haven't been previously used for anything and will be hence zero.

    2. How does dcc help you find errors?
      dcc actually runs a C compiler called clang.

      dcc enables all the compile-time error checking options that clang offers.

      dcc adds extra explanation to help you understand these error messages.

      dcc also enables run-time checking of your code using a tool called Address Sanitizer. This checks, for example, that array indices are valid.

      Address Sanitizer error messages are hard to understand so dcc adds code to your program to explain the error. This code uses Python and a debugging tool named gdb to give you an explanation of the error with some hopefully helpful information about the state of your program when the error occurred.

    3. What is dcc --leakcheck?
      dcc --leakcheck uses a variant of valgrind which helps you check memory allocated with malloc is freed before the program finishes. It will be used for the challenge exercise this week and for the assignment.
  7. Last week's lab used an array of this struct to store a whale sighting:
    #define MAX_SPECIES_NAME_LENGTH 128
    struct pod {
        struct date when;
        int         how_many;
        char        species[MAX_SPECIES_NAME_LENGTH];
    };
    
    What if we use a linked list of this struct to store a whale sighting?
    struct pod {
        struct pod  *next;
        struct date *when;
        int         how_many;
        char        *species;
    };
    
    And a date is still represented by the same struct:
    struct date {
        int year;
        int month;
        int day;
    };
    
    Sketch out how these two data structures would be laid out in memory.

    Discuss the differences and how that will affect the use of this data

    Arrays will be laid out adjacent in memory.

    Linked Lists are potentially scattered through memory.

    Arrays will be easier to loop through and sometimes to access individual elements because they can be accessed by index.

    Linked Lists can be easier to modify because elements can be inserted and removed from any part of the list easily.

    In addition to this, the date struct in the linked list will actually be separate in memory, linked only via a pointer.

    Revision questions

    The remaining tutorial questions are primarily intended for revision - either this week or later in session.

    Your tutor may still choose to cover some of the questions time permitting.

    • Write a C function split_string which takes a string read by fgets containing substrings separated by commas, places the substrings in an char[][] array and returns the number of strings.

      You can assume there are at most 128 words on the line and no word is more than 32 characters long.

    • Write a C function print_substrings which prints one per line the substrings in the char[][] array created in the previous question.
    • Write a C function substrings_sorted which given the char[][] array in the previous question returns 1 if the substrings are increasing order and 0 otherwise.
    • Write a C function search_substrings which given the char[][] array in the previous question returns 1 if the array contains a specified string and 0 otherwise.
    • Write a program substring.c which reads lines using fgets and calls the above functions.
    Sample solution for substring.c
    // Written by Costa Paraskevopoulos as a
    // COMP1511 tutorial solution
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX_NUM_WORDS 128
    #define MAX_WORD_LEN 33 // including null char
    
    // accounts for 128 * 32 "word" characters + 127 commas + 1 null char
    #define MAX_LINE_LEN (MAX_NUM_WORDS * MAX_WORD_LEN)
    
    int split_string(char *src,
            char dest[MAX_NUM_WORDS][MAX_WORD_LEN]);
    void print_substrings(int num_substrings,
            char strings[MAX_NUM_WORDS][MAX_WORD_LEN]);
    int substrings_sorted(int num_substrings,
            char strings[MAX_NUM_WORDS][MAX_WORD_LEN]);
    int search_substrings(int num_substrings, char *search_str,
            char strings[MAX_NUM_WORDS][MAX_WORD_LEN]);
    
    int main(int argc, char *argv[]) {
    
        char line[MAX_LINE_LEN];
        int line_no = 1;
    
        while (fgets(line, MAX_LINE_LEN, stdin) != NULL) {
            char substrings[MAX_NUM_WORDS][MAX_WORD_LEN];
            int num_words = split_string(line, substrings);
            printf("%d words on line %d:\n", num_words, line_no);
            print_substrings(num_words, substrings);
    
            if (substrings_sorted(num_words, substrings) != 0) {
                printf("Substrings are in increasing order\n");
            } else {
                printf("Substrings are not in increasing order\n");
            }
    
            // search for some common test strings
            char *searches[4] = {"hello", "world", "test", "qwerty"};
            for (int i = 0; i < 4; i = i + 1) {
                if (search_substrings(num_words, searches[i], substrings)) {
                    printf("Found \"%s\" on line %d\n", searches[i], line_no);
                } else {
                    printf("\"%s\" not found on line %d\n", searches[i], line_no);
                }
            }
    
            printf("\n");
            line_no = line_no + 1;
        }
    
        return 0;
    }
    
    // Extracts words separated by commas and places them in dest
    int split_string(char *src, char dest[MAX_NUM_WORDS][MAX_WORD_LEN]) {
        int num_words = 0;
        int i, j;
        for (i = 0; i < MAX_LINE_LEN && num_words != MAX_NUM_WORDS; i = i + 1) {
            // reached end of src
            if (src[i] == '\n' || src[i] == '\0') {
                break;
            }
    
            // copy current word over to dest
            for (j = i; j - i < MAX_WORD_LEN - 1; j = j +1) {
                if ((src[j] == ',' || src[j] == '\n' || src[j] == '\0') && i != j) {
                    dest[num_words][j - i] = '\0';
                    num_words = num_words + 1;
                    i = j; // start next word
                    break;
                } else if (src[j] == ',') {
                    break; // skip empty words
                }
                dest[num_words][j - i] = src[j];
            }
    
            // terminate long word
            if (i != j) {
                dest[num_words][j - i] = '\0';
                num_words = num_words + 1;
                i = j;
                // skip rest of word
                while (src[i] != '\0' && src[i] != '\n' && src[i] != ',') {
                    i = i + 1;
                }
            }
        }
        return num_words;
    }
    
    // Prints all words in strings on separate lines
    void print_substrings(int num_substrings,
            char strings[MAX_NUM_WORDS][MAX_WORD_LEN]) {
    
        for (int i = 0; i < num_substrings; i = i + 1) {
            printf("%s\n", strings[i]);
        }
    
    }
    
    // Returns 1 if strings is sorted; 0 otherwise
    int substrings_sorted(int num_substrings,
            char strings[MAX_NUM_WORDS][MAX_WORD_LEN]) {
    
        for (int i = 1; i < num_substrings; i = i + 1) {
            if (strcmp(strings[i], strings[i - 1]) < 0) {
                return 0;
            }
        }
    
        return 1;
    }
    
    // Returns 1 if search_str is in strings; 0 otherwise
    int search_substrings(int num_substrings, char *search_str,
            char strings[MAX_NUM_WORDS][MAX_WORD_LEN]) {
    
        for (int i = 0; i < num_substrings; i = i + 1) {
            if (strcmp(strings[i], search_str) == 0) {
                return 1;
            }
        }
    
        return 0;
    }
    
    
  8. Write a function that is given two struct date pointers and compares returns 0 if they are equal, -1 if the first is less than the second and 1 if the first is larger than the second.
    int compare_date(struct date *d1, struct date *d2);