Week 01 Tutorial Questions

Objectives

  1. Class introduction (for everyone, starting with the tutor):

    • What is your preferred name (what should we call you?) - feel free to share pronouns if you wish!
    • What is your degree and year of study?
    • Share a fun fact about yourself!
  2. The following snippet of C code declares two variables, of the variables s1 and s2 with some subtle differences between them.
    #include <stdio.h>
    
    char *s1 = "abc";
    
    int main(void) {
      char *s2 = "def";
      // ...
    }
      
    

    • What does the memory layout of a typical program look like?
    • What is a global variable?
    • How do they differ from local variables? Where are they each located in memory?
    • What is a string literal? Where are they located in memory?
  3. What is wrong with the following code?

    #include <stdio.h>
    
    int *get_num_ptr(void);
    
    int main(void) {
        int *num = get_num_ptr();
        printf("%d\n", *num);
    }
    
    int *get_num_ptr(void) {
        int x = 42;
        return &x;
    }
    

    Assuming we still want get_num_ptr to return a pointer, how can we fix this code?

    How does fixing this code affect each variable's location in memory?

  4. Consider the following C program:
    #include <stdio.h>
    
    int main(void) {
        char str[10];
        str[0] = 'H';
        str[1] = 'i';
        printf("%s", str);
        return 0;
    }
    

    What will happen when the above program is compiled and executed?

    In particular, what does this look like in memory?

    How could we fix this program?

  5. Consider the following while loop:
    #include <stdio.h>
    
    int main(void) {
      int i = 0;
      while (i < 10) {
        printf("%d\n", i);
        i++;
      }
      return 0;
    }
    
    How could we rewrite the above program using a for loop? What subtle difference would there be between the two programs?
  6. In the following program, what are argc and argv? The following program prints number of command-line arguments and each command-line argument on a separate line.
    #include <stdio.h>
    
    int main(int argc, char *argv[]) {
        printf("argc=%d\n", argc);
        for (int i = 0; i < argc; i++) {
            printf("argv[%d]=%s\n", i, argv[i]);
        }
        return 0;
    }
    
    What will be the output of the following commands?
    dcc -o print_arguments print_arguments.c
    print_arguments I love MIPS
    
  7. The following program sums up command-line arguments.

    Why do we need the function atoi in the following program?

    The program assumes that command-line arguments are integers. What if they are not integer values?

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[]) {
        int sum = 0;
        for (int i = 0; i < argc; i++) {
            sum += atoi(argv[i]);
        }
        printf("sum of command-line arguments = %d\n", sum);
        return 0;
    }
    
  8. For each of the following commands, describe what kind of output would be produced:

    1. clang -E x.c
    2. clang -S x.c
    3. clang -c x.c
    4. clang x.c

    In particular, how do these commands relate to what we will be studying in COMP1521?

    You can use the following simple C code as an example:

    #include <stdio.h>
    #define N 10
    
    int main(void) {
        char str[N] = { 'H', 'i', '\0' };
        printf("%s\n", str);
        return 0;
    }
    

Extra questions

The following questions are extra content not necessarily needed to cover this week's content.
Your tutor may still choose to cover some of these questions, time permitting.

  1. What is mipsy (and mipsy_web), and what do we use them for in COMP1521?

  2. Without diving into too much detail, translate the following C program into MIPS assembler and run it with 1521 mipsy.
    // Prints the square of a number
    
    #include <stdio.h>
    
    int main(void) {
        int x, y;
    
        printf("Enter a number: ");
        scanf("%d", &x);
    
        y = x * x;
    
        printf("%d\n", y);
    
        return 0;
    }
    
    Store variable x in register $t0 and store variable y in register $t1.

Revision questions

The following questions are primarily intended for revision, either this week or later in session.
Your tutor may still choose to cover some of these questions, time permitting.

  1. Consider the following C program:

    #include <stdio.h>
    
    void print_array(int nums[], int len) {
        for (int i = 0; i < len; i++) {
            printf("%d\n", nums[i]);
        }
    }
    
    int main(void)
    {
        int nums[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
        print_array(nums, 10);
    
        return 0;
    }
    

    This program uses a for loop to print out each element in the array

    Rewrite this program using a recursive function

  2. Consider the following C program skeleton:
    int  a;
    char b[100];
    
    int fun1() {
        int c, d;
        ...
    }
    
    double e;
    
    int fun2() {
        int f;
        static int ff;
        ...
        fun1();
        ...
    }
    
    int g;
    
    int main(void) {
        char h[10];
        int i;
        ...
        fun2()
        ...
    }
    

    Now consider what happens during the execution of this program and answer the following:

    1. Which variables are accessible from within main()?

    2. Which variables are accessible from within fun2()?

    3. Which variables are accessible from within fun1()?

    4. Which variables are removed when fun1() returns?

    5. Which variables are removed when fun2() returns?

    6. How long does the variable f exist during program execution?

    7. How long does the variable g exist during program execution?

  3. C's sizeof operator is a prefix unary operator (precedes its 1 operand) - what are examples of other C unary operators?
  4. Why is C's sizeof operator different to other C unary & binary operators?
  5. Discuss errors in this code:
        struct node *a = NULL:
        struct node *b = malloc(sizeof b);
        struct node *c = malloc(sizeof struct node);
        struct node *d = malloc(8);
        c = a;
        d.data = 42;
        c->data = 42;
    
  6. What is a pointer? How do pointers relate to other variables?
  7. Consider the following small C program:

    #include <stdio.h>
    
    int main(void) {
        int n[4] = { 42, 23, 11, 7 };
        int *p;
    
        p = &n[0];
        printf("%p\n", p); // prints 0x7fff00000000
        printf("%lu\n", sizeof (int)); // prints 4
    
        // what do these statements print ?
        n[0]++;
        printf("%d\n", *p);
        p++;
        printf("%p\n", p);
        printf("%d\n", *p);
    
        return 0;
    }
    

    Assume the variable n has address 0x7fff00000000.

    Assume sizeof (int) == 4.

    What does the program print?

  8. Consider the following pair of variables

    int  x;  // a variable located at address 1000 with initial value 0
    int *p;  // a variable located at address 2000 with initial value 0
    

    If each of the following statements is executed in turn, starting from the above state, show the value of both variables after each statement:

    1. p = &x;

    2. x = 5;

    3. *p = 3;

    4. x = (int)p;

    5. x = (int)&p;

    6. p = NULL;

    7. *p = 1;

    If any of the statements would trigger an error, state what the error would be.

  9. What is a struct? What are the differences between structs and arrays?
  10. 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.

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

    e.g.: 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.

  12. Write a main function that takes command line input that fills out the fields of the pet struct. Remember that command line arguments are given to our main function as an array of strings, which means we'll need something to convert strings to numbers.
  13. Consider the following (working) C code to trim whitespace from both ends of a string:

    // COMP1521 21T2 GDB debugging example
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    #include <assert.h>
    
    void trim(char *str);
    char **tokenise(char *str, char *sep);
    void freeTokens(char **toks);
    
    int main(int argc, char *argv[])
    {
    	if (argc != 2) exit(1);
    
    	char *string = strdup(argv[1]);
    	printf("Input: \"%s\"\n", string);
    	trim(string);
    	printf("Trimmed: \"%s\"\n", string);
    	char **tokens = tokenise(string, " ");
    	for (int i = 0; tokens[i] != NULL; i++)
    		printf("tok[%d] = \"%s\"\n", i, tokens[i]);
    	freeTokens(tokens);
    	return 0;
    }
    
    // trim: remove leading/trailing spaces from a string
    void trim(char *str)
    {
    	int first, last;
    	first = 0;
    	while (isspace(str[first])) first++;
    	last  = strlen(str)-1;
    	while (isspace(str[last])) last--;
    	int i, j = 0;
    	for (i = first; i <= last; i++) str[j++] = str[i];
    	str[j] = '\0';
    }
    
    // tokenise: split a string around a set of separators
    // create an array of separate strings
    // final array element contains NULL
    char **tokenise(char *str, char *sep)
    {
    	// temp copy of string, because strtok() mangles it
    	char *tmp;
    	// count tokens
    	tmp = strdup(str);
    	int n = 0;
    	strtok(tmp, sep); n++;
    	while (strtok(NULL, sep) != NULL) n++;
    	free(tmp);
    	// allocate array for argv strings
    	char **strings = malloc((n+1)*sizeof(char *));
    	assert(strings != NULL);
    	// now tokenise and fill array
    	tmp = strdup(str);
    	char *next; int i = 0;
    	next = strtok(tmp, sep);
    	strings[i++] = strdup(next);
    	while ((next = strtok(NULL,sep)) != NULL) strings[i++] = strdup(next);
    	strings[i] = NULL;
    	free(tmp);
    	return strings;
    }
    
    // freeTokens: free memory associated with array of tokens
    void freeTokens(char **toks)
    {
    	for (int i = 0; toks[i] != NULL; i++) free(toks[i]);
    	free(toks);
    }
    

    You can grab a copy of this code as trim.c.

    The part that you are required to write (i.e., would not be part of the supplied code) is highlighted in the code.

    Change the code to make it incorrect. Run the code, to see what errors it produces, using this command:

    gcc -std=gnu99 -Wall -Werror -g -o trim trim.c
    ./trim  "   a string  "
    

    Then use GDB to identify the location where the code "goes wrong".