COMP1511 19T2
COMP1511 19T2
  1. Well done everyone for such a great effort in the first assignment.

    Take a moment to think about how you found the assignment, and what you learned in the process of working on it.

    If you could go back in time to the very start of the assignment, and give one piece of advice to your past self, what would you say?

  2. You tutor will talk about how you will get feedback from the marking of assignment 1.
  3. 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!

  4. What is a pointer? How do pointers relate to other variables?
    Pointers are variables that store a memory address and a variable type.

    They are usually "aimed" at a variable of the particular type.

    They're able to read the value of that variable and also are handy because they can be passed into functions, giving the function access to a variable.

  5. What is a struct? What are the differences between structs and arrays,
    Arrays and struct are both compound data types, formed from other data types.

    Array are homogeneous - formed from a single data type.

    Structs can be heterogeneous - formed from a multiple data types.

    Array element are accessed with integer array indexes.

    Structs fields are accessed by name.

  6. Define a struct that might store information about a pet.

    The information should include the pet's name, type of animal, age and weight.

    Create a variable of this type and assign information to it to represent an axolotl named "Fluffy" of age 7 that weighs 300grams.

    #include <stdio.h>
    #include <string.h>
    
    #define MAX_NAME_LENGTH 256
    #define MAX_BREED_LENGTH 64
    
    struct pet {
        char name[MAX_NAME_LENGTH];
        char breed[MAX_BREED_LENGTH];
        int age;
        int weight;
    };
    
    int main(void) {
        struct pet my_pet;
    
        strcpy(my_pet.name, "Fluffy");
        strcpy(my_pet.breed, "axolotl");
        my_pet.age = 7;
        my_pet.weight = 300;
    
        return 0;
    }
    
  7. Write a function that increases the age of fluffy by one and then increases its weight by the fraction of its age that has increased. The function is defined like this:

    void age_fluffy(struct pet *my_pet);

    eg: If fluffy goes from age 7 to age 8, it should end up weighing 8/7 times the amount it weighed before. You can store the weight as an int and ignore any fractions.

    Show how this function can be called by passing the address of a struct variable to the function.

    #include <stdio.h>
    #include <string.h>
    
    #define MAX_NAME_LENGTH 256
    #define MAX_BREED_LENGTH 64
    
    struct pet {
        char name[MAX_NAME_LENGTH];
        char breed[MAX_BREED_LENGTH];
        int age;
        int weight;
    };
    
    void age_fluffy(struct pet *my_pet);
    
    int main(void) {
        struct pet my_pet;
    
        strcpy(my_pet.name, "Fluffy");
        strcpy(my_pet.breed, "axolotl");
        my_pet.age = 7;
        my_pet.weight = 300;
        
        age_fluffy(&my_pet);
        printf("%d, %d\n", my_pet.age, my_pet.weight);
    
        return 0;
    }
    
    void age_fluffy(struct pet *my_pet) {
        double old_age = my_pet->age;
        my_pet->age = my_pet->age + 1;    
        double weightMult = my_pet->age / old_age;
        my_pet->weight = my_pet->weight * weightMult;
    }
    
  8. Discuss this code which you are given for the lab exercises and how you have to change it for the lab exercises.
        //
    // Starting code for COMP1511 lab exercises
    //
    
    #include <stdio.h>
    #include <string.h>
    
    #define MAX_SPECIES_NAME_LENGTH 128
    #define MAX_SIGHTINGS 10000
    
    // a struct to represent the date
    // a whale pod sighting was made
    
    struct date {
        int year;
        int month;
        int day;
    };
    
    // a struct to represent a sighting
    // of a pod (group) of whales
    
    struct pod {
        struct date when;
        int         how_many;
        char        species[MAX_SPECIES_NAME_LENGTH];
    };
    
    
    int read_sightings_file(char filename[], int len, struct pod sightings[len]);
    int read_sighting(FILE *f, struct pod *w);
    int read_date(FILE *f, struct date *d);
    
    int count_orca_sightings(int n_sightings, struct pod sightings[n_sightings]);
    
    int main(int argc, char *argv[]) {
        if (argc != 2) {
            fprintf(stderr, "Usage: %s <file>\n", argv[0]);
            return 1;
        }
    
        struct pod whale_sightings[MAX_SIGHTINGS];
        int n_sightings = read_sightings_file(argv[1], MAX_SIGHTINGS, whale_sightings);
    
        if (n_sightings > 0) {
            int n_orca_pods = count_orca_sightings(n_sightings, whale_sightings);
            printf("%d Orca sightings in %s\n", n_orca_pods, argv[1]);
        }
        return 0;
    }
    
    // return the number of sightings of Orca
    
    int count_orca_sightings(int n_sightings, struct pod sightings[n_sightings]) {
        // REPLACE THIS COMMENT WITH YOUR CODE
        // THIS FUNCTION SHOULD NOT CALL SCANF OR PRINTF
        // IT SHOULD JUST RETURN A VALUE
        return 42; // CHANGE ME
    }
    
    
    //
    // DO NOT CHANGE THE FUNCTIONS BELOW HERE
    //
    
    // return number of sightings read from filename
    // -1 is returned if there is an error
    
    int read_sightings_file(char filename[], int len, struct pod sightings[len]) {
        FILE *f = fopen(filename, "r");
        if (f == NULL) {
            fprintf(stderr,"error: file '%s' can not open\n", filename);
            return -1;
        }
    
        int n_sightings = 0;
        while (read_sighting(f, &sightings[n_sightings]) == 1 && n_sightings < len) {
            n_sightings = n_sightings + 1;
        }
        fclose(f);
        return n_sightings;
    }
    
    
    // return 1 if a sighting can be read, 0 otherwise
    
    int read_sighting(FILE *f, struct pod *s) {
        if (read_date(f, &(s->when)) != 1) {
            return 0;
        }
        if (fscanf(f, "%d", &(s->how_many)) != 1) {
            return 0;
        }
        fgetc(f);
        if (fgets(s->species, MAX_SPECIES_NAME_LENGTH, f) == NULL) {
            return 0;
        }
    
        // finish string at '\n' if there is one
        char *newline_ptr = strchr(s->species, '\n');
        if (newline_ptr != NULL) {
            *newline_ptr = '\0';
        }
    
        // also finish string at '\r' if there is one - files from Windows  will
        newline_ptr = strchr(s->species, '\r');
        if (newline_ptr != NULL) {
            *newline_ptr = '\0';
        }
        return 1;
    }
    
    
    
    // return 1 if a date can be read, 0 otherwise
    
    int read_date(FILE *f, struct date *d) {
        int n_scanned = fscanf(f, "%d/%d/%d", &(d->year), &(d->month), &(d->day));
        return n_scanned == 3;
    }
    
    
    
    Note the C library functions like fopen, fscanf, fclose which are used to read and write files haven't been covered lectures.

    We'll squeeze them if we have time but they definitely won't be needed for the exam.

    Your tutor will explain basically what they do in the supplied code for this week's lab exercise but you don't need to change the code that calls these functions.

    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.

    #include <stdio.h>
    
    int main(void) {
        char str[10];
        str[0] = 'H';
        str[1] = 'i';
        printf("%s", str);
        return 0;
    }
    
    1. What will happen when the above program is compiled and executed?
      The above program will compile without errors . printf, like many C library functions expects strings to be null-terminated.

      In other words printf, expects the array str to contain an element with value '\0' which marks the end of the sequence of characters to be printed.

      printf will print str[0] ('H'), str[1] then examine str[2].

      Code produced by dcc --valgrind will then stop with an error because str[2] is uninitialized.

      The code with gcc will keep executing and printing element from str until it encounters one containing '\0'. Often str[2] will by chance contain '\0' and the program will work correctly.

      Another common behaviour will be that the program prints some extra "random" characters.

      It is also possible the program will index outside the array which would result in it stopping with an error if it was compiled with dcc.

      If the program was compiled with gcc and uses indexes well outside the array it may be terminated by the the operating system because of an illegal memory access.

    2. How do you correct the program.
      #include <stdio.h>
      
      int main(void) {
          char str[10];
          str[0] = 'H';
          str[1] = 'i';
          str[2] = '\0';
          printf("%s", str);
          return 0;
      }
      
  9. Give a struct that might hold marks for a COMP1511 student.
    #define MAX_STUDENT_NAME_LENGTH 128
    #define N_LABS 12
    #define N_TESTS 10
    #define N_ASSIGNMENTS 3
    
    struct student {
        int    zid;
        char   name[MAX_STUDENT_NAME_LENGTH];
        double lab_marks[N_LABS];
        double test_marks[N_TESTS];
        double assignment_marks[N_ASSIGNMENTS];
    
    };
    
  10. Given these declarations
    int     n;
    int    *p, *q;
    
    What will happen when each of the following statements is executed (in order)?
    p = &n;
    *p =  5;
    *q = 17;
    q =  p;
    *q =  8;
    
    p = &n;   // p will point to n
    *p =  5;  // 5 will be stored into n
    *q = 17;  // the program will attempt to store 17 into the variable pointed to by q,
              // which could cause an error because q has not been initialized.
              // dcc will give us a warning about this
    q =  p;   // q will point to the same variable that p does, ie n
    *q =  8;  // 8 will be stored into n
    
  11. What is wrong with this program:
    #include <stdio.h>
    
    #define MAX_PLATE 10000
    
    struct parking_fine {
        double   amount;
        char     number_plate[MAX_PLATE];
    };
    
    
    int read_parking_fine(struct parking_fine);
    
    int main(void) {
        struct parking_fine f;
    
        if (read_parking_fine(f)) {
            printf("%lf %s\n", f.amount, f.number_plate);
        }
    
        return 0;
    }
    
    // return 1 if a parking fine is successfully was read, 0 otherwise
    
    int read_parking_fine(struct parking_fine fine) {
        if (scanf("%lf", &(fine.amount)) != 1) {
            return 0;
        }
        return fgets(fine.number_plate, MAX_PLATE, stdin) != NULL;
    }
    
    
    Fix it.
    structs are passed by value, so a copy of the struct has been passed into the function.

    Any changes made to it do not affect the variable in the main function.

    We can instead pass a pointer to the struct:

    #include <stdio.h>
    
    #define MAX_PLATE 10000
    
    struct parking_fine {
        double   amount;
        char     number_plate[MAX_PLATE];
    };
    
    
    int read_parking_fine(struct parking_fine *fine);
    
    int main(void) {
        struct parking_fine f;
    
        if (read_parking_fine(&f)) {
            printf("%lf %s\n", f.amount, f.number_plate);
        }
    
        return 0;
    }
    
    // return 1 if a parking fine was successfully read, 0 otherwise
    
    int read_parking_fine(struct parking_fine *fine) {
        if (scanf("%lf", &(fine->amount)) != 1) {
            return 0;
        }
        return fgets(fine->number_plate, MAX_PLATE, stdin) != NULL;
    }
    
    
  12. Write a program strip_comments.c which reads lines from its input and prints them after removing any C // style comments. In another words if the line contains // it does not print the // or anything after it.

    The only functions you can use are fgets and printf.

    You can assume lines contain at most 4096 characters.

    For example:

    ./strip_comments
         x = x + 1;  // This means add one to the variable x
         x = x + 1;
    
    Also - is that a good comment to add to a C program?
    Sample solution for strip_comments.c
    #include <stdio.h>
    
    #define MAX_LINE 4096
    
    int main(void) {
        char line[MAX_LINE];
        int  i;
    
        // reads lines and print them after
        // removing // style comments
    
        while (fgets(line, MAX_LINE, stdin) != NULL) {
    
    
            // iterate through line and see if we encounter a '/' then '/'
            // everything after is a comment, so stop the string there
    
            i = 0;
            while (line[i] != '\n' && line[i] != '\0') {
    
                // safe to look at line[i+1] because
                // we know here line[i] != '\0'
    
                if (line[i] == '/' && line[i + 1] == '/') {
    
                    // replace // with a newline and null-terminator
                    line[i] = '\n';
                    line[i + 1] = '\0';
    
                    // could break here but loop will stop anyway
                    // because after i = i + 1, line[i] == '\0'
                }
    
                i = i + 1;
            }
    
            // write possibly-modified line
    
            printf("%s", line);
        }
        return 0;
    }
    
    
  13. Write a program filter_empty_lines.c which reads lines from its input and prints them only if they contain a non-white-space-character.

    In another words remove lines are empty or contain only white-space.

    The only functions you can use are fgets and printf.

    You can assume lines contain at most 4096 characters.

    You can assume there are only 3 white space characters, space, tab & new-line.

    For example:

    ./filter_empty_lines
    full line
    full line
             
    another no-empty line
    another no-empty line
    
    Sample solution for filter_empty_lines.c
    #include <stdio.h>
    #include <stdlib.h>
    
    #define MAX 4096
    
    int main(void) {
    
        char line[MAX];
    
        while (fgets(line, MAX, stdin) != NULL) {
    
            // count non-whitespace characaters on this line
            int non_whitespace_count = 0;
            int i = 0;
            while (line[i] != '\n' && line[i] != '\0') {
    
                // test for non white space
                // !isspace(line[i]) would be better (from ctype.h)
    
                if ((line[i] != ' ') && (line[i] != '\t') && (line[i] != '\n')) {
                    // this is a non-whitespace character
                    non_whitespace_count = non_whitespace_count + 1;
                    // could break here
                }
    
                i = i + 1;
            }
    
            // print the line if it has non-whitespace character
            if (non_whitespace_count > 0) {
                printf("%s", line);
            }
    
        }
    
        return 0;
    }
    
    
  14. Write a C program reverse.c which reads lines and writes them out with the characters of each line in reverse order. It should stop when it reaches the end of input.

    For example:

    ./reverse
    The quick brown fox jumped over the lazy dog.
    .god yzal eht revo depmuj xof nworb kciuq ehT
    It was the best of times. It was the worst of times.
    .semit fo tsrow eht saw tI .semit fo tseb eht saw tI
    This is the last line.
    .enil tsal eht si sihT
    
    
    Sample solution for reverse.c
    #include <stdio.h>
    
    #define MAX_LINE 4096
    
    int main(void) {
        char line[MAX_LINE];
    
        while (fgets(line, MAX_LINE, stdin) != NULL) {
    
            // find the length of this line
            int i = 0;
            while (line[i] != '\n' && line[i] != '\0') {
                i = i + 1;
            }
    
            // when we exit, i is the index of either
            // newline character or null terminator
            // so go back one position
    
            i = i - 1;
    
            // now print line in reverse
    
            while (i >= 0) {
                printf("%c", line[i]);
                i = i - 1;
            }
            printf("\n");
    
        }
        return 0;
    }
    
    
  15. Strlen is a function that returns the length of a string. Write your own C function to do the same.
            int myStrlen(char *string);
        
    The key part to the question is knowing that a string has a null terminator character "\0" to note the end of the string. You simply loop until you come across that character.
  16. Write a C function that is given two strings and returns 1 if they are both equal and 0 otherwise. This is a simplified version of strcmp, provided in the C library. Try writing one yourself.
            int myStrCmp(char *string1, char *string2);
        
    Remember that strings are just arrays of characters. The end of the string has the special null terminator. The tricky part with this question is dealing with strings of uneven lengths.
  17. Write a C function that is given two strings and returns 1 if the first begins with the second (and 0 otherwise). For example, "APPLE" begins with "APP".
            int beginsWith(char *string1, char *string2);
        
    This is similar to doing the string compare, but you need to be careful of the uneven lengths as well. Eg: What happens if the first string is longer than the second?
  18. Write a C function that is given two strings and returns 1 if the first is a substring of the second (and 0 otherwise). For example, "APP", "E", "PL" are all substrings of "APPLE".
            int isSubstring(char *substring, char *string);
        
    This is a bit harder, because unlike beginsWith, you don't know where the substring can start. The simplest method would be to go through the string, and attempt to do something like beginsWith from each character. Be very careful about going beyond the bounds of the array.