Week 01 Tutorial Questions

Objectives

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

    • What is your preferred name (what should we call you?)
    • What other courses are you doing this term
    • What parts of C from DPST1091 were the hardest to understand?
    • Do you know any good resources to help students who have forgotten their C? For example:
  2. 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
    
  3. 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;
    }
    
  4. 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

  5. By default both stdout and stderr streams print to the screen. In linux we can choose to direct our different output streams to files when we run our executable program.

    > redirects stdout to a file
    2> redirects stderr to a file
    &> redirects both stdout and stderr to a file

    Consider the following C program:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char * argv[]) {
        if(argc < 3){
            fprintf(stderr, "Usage: %s num1 num2\n",argv[0]);
            return 1;
        }
        int result = atoi(argv[1])  + atoi(argv[2]);
        printf("Result: %d\n",result);
        return 0;
    }
    

    What gets printed on the screen and what gets saved in the file named "someFile" if we run the program in the following way:

    1. ./add
    2. ./add 3 4
    3. ./add 1 > someFile
    4. ./add 1 2 > someFile
    5. ./add 99 2> someFile
    6. ./add 3 2 2> someFile
    7. ./add 1 &> someFile
    8. ./add 10 15 &> someFile
  6. Explain the differences between the properties of the variables s1 and s2 in the following program fragment:
    #include <stdio.h>
    
    char *s1 = "abc";
    
    int main(void) {
        char *s2 = "def";
        // ...
    }
    

    Where is each variable located in memory? Where are the strings themselves located?

  7. 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 effect each variable's location in memory?

  8. 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?

  9. How could you correct the above program?
  10. What is a pointer? How do pointers relate to other variables?
  11. 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?

  12. C's sizeof operator is a prefix unary operator (precedes its 1 operand) - what are examples of other C unary operators?
  13. Why is C's sizeof operator different to other C unary & binary operators?
  14. 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;
    
  15. 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 DPST1092?

    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;
    }
    
  16. Rewrite the following code without using break or continue:
    #include <stdio.h>
    #define N 10
    
    int main(void){
       int a[] = {4, 3, 9, -8, 5, -4, 3, 1, 0, 4};
       int sum = 0;
       int i;
       for (i = 0; i < N; i++) {
           if (a[i] == 0) break;
           if (a[i] < 0) continue;
           sum += a[i];
       }
       printf("The sum is %d\n",sum);
       return 0;
    }             
    

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. Think about how the following code might behave for each of the inputs below (i.e. the final value for each variable):

    int n, a, b, c;
    n = scanf("%d %d %d", &a, &b, &c);
    

    Inputs:

    1. 42 64 999
    2. 42 64.4 999
    3. 42 64 hello
    4. 42 hello there
    5. hello there
    6. Ctrl + d
  2. What is the difference in meaning between the following pairs (a/b and c/d) of groups of C statements:

    1. if (x == 0) {
          printf("zero\n");
      }
      
    2. if (x == 0)
          printf("zero\n");
      
    3. if (x == 0) {
          printf("zero\n");
          printf("after\n");
      }
      
    4. if (x == 0)
          printf("zero\n");
          printf("after\n");
      
  3. 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?

  4. Consider the following small C program:

    int a;          // global int variable
    
    int main(void) {
       int  b;      // local int variable
       char c;      // local char variable
       char d[10];  // local char array
       ...
    }
    
    int e;          // global? int variable
    
    int f(int g) {  // function + parameter
       double h;    // local double variable
       double *j;   // pointer to double object
       ...
       j = malloc(sizeof(double));
       ...
    }
    

    Describe the following properties for each of the objects: a, b, j, and *j.

    • region (which part of the memory is the object located in)
    • size (how large is the object, in bytes)
    • scope (which part of the program can "see" the object)
    • lifetime (when is the object created, and when removed)
  5. 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.

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

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

  9. 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.
  10. 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".