Main Contents Page
CTEC1401 Programming in C
Quick Reference
Week 22: Consolidation
Week 23: Data Structures
Week 24: Practice and Consolidation
  1. Before the lab session read CFTTP Chapter 14 and study the solved exercises very carefully.

  2. malloc1

    Copy the following program into malloc1.c. Study the program carefully before you compile and run it. Then run the program and make sure you can see why each line of output is generated.

    #include <stdio.h>
    #include <stdlib.h> /* needed to use malloc and free */
    main()
    {
         int *p1;  /* address of 1st integer */
         int *p2;  /* address of 2nd integer */
         
         p1 = (int *) malloc(sizeof(int));      /* allocate space for 1st integer */
         printf("p1 allocated memory at address %p\n",p1);
         p2 = (int *) malloc(sizeof(int));      /* allocate space for 2nd integer */
         printf("p2 allocated memory at address %p\n",p2);
         printf("1st no? ");
         scanf(" %i",p1);                 /* store input as 'value at address p1' */
         printf("2nd no? ");
         scanf(" %i",p2);                 /* store input as 'value at address p2' */
         printf("sum of %d and %d is %d\n",*p1,*p2,*p1+*p2);
    
         /* print 'value at address p1', 'value at address p2' and
          * 'value at address p1' plus 'value at address p2'
          */
         free(p1);                        /* free up dynamic memory at address p1 */
         free(p2);                        /* free up dynamic memory at address p1 */
    }
    

    Look closely at the scanf statements. Why is it correct to write p1 and not &p1 (and similar argument for p2)?

  3. sumInts

    Copy the following program into sumInts.c. The program calculates the sum of a set of integers, where the number of integers is not known in advance, and demonstrates that the data at the address pointed to by p can be treated as an array of integers - notice the use of index p[i].

    #include <stdio.h>
    #include <stdlib.h>
    
    main()
    {
         int *p;
         int howMany;
         int i, sum=0;
         
         printf("how many numbers? "); 
         scanf(" %i", &howMany);
         p = (int *) malloc(howMany*sizeof(int));
         if (p == 0)
         {
            printf("no memory allocated\n"); return -1;
         }
         for (i=0;i<howMany;i++)
         {
               printf("number %d ",i);
               scanf(" %i", &p[i]);
         }
         for (i=0;i<howMany;i++)
         {
               sum=sum+p[i];
         }     
         printf("average of the %d numbers is %d\n",howMany,sum/howMany);
         free(p);
    }
    
    Add comments to explain what each line does. Compile the program and run it.

  4. malloc2

    Write a program malloc2.c that

    • asks the user how many numbers they want to enter;
    • mallocs enough space to store these numbers;
    • uses a loop to read in these numbers and store them;
    • prints out the numbers and the address at which they are stored;
    • releases the memory allocated.

  5. malloc3

    Write a program malloc3.c that

    • asks the user to specify how many letters (chars) they want to enter
    • dynamically allocates just enough memory for these;
    • uses a while loop to read that many characters from the keyboard and store them;
    • prints the characters entered by the user in the reverse order to which they were entered;
    • frees up the memory allocated.

  6. copy4Strings

    Copy the following program into copy4Strings.c. The program reads in four strings from the user, copies each string into a memory allocation just big enough to hold it and the prints all of the strings entered. Complete the program so that it does this.

    #include <stdio.h>
    #include <stdlib.h>
    
    int strlength(char * s)
    {
         int max = 0;
         while (s[max] != '\0')
         {
              max++;
         }
         return max;
    }
    
    int strcopy(char * s, char * t)
    {
         int i=0;
         while (s[i] != '\0')
         {
              t[i]=s[i];
              i++;
         }
         t[i]='\0';
    }
    
    main()
    {
         char string[256];  /* to store the input from user */
         char *p[4];       /* space for 4 string addresses */
         int i;
         int length;
         
         for (i=0; i < 4 ; i++)
         {
              printf("string? ");
              scanf("%[^\n]", string);
              
              /* calculate length of the input string
              *  length = 
              */
              
              /* allocate memory to hold a string of this length
              *      (remembering space for the null at the end)
              *  p[i] = 
              */
              
              /* copy the input string to this newly allocated
              *  address
              */
         }
         for (i=0; i < 4 ; i++)
         {
             /* print the ith copy                              */
             printf("%d: %s\n",i,p[i]);
             /* free the space for the ith copy                 */
         }
     }
    
  7. words

      • Create a program words.c that defines a function called words with the following function header:
               int words(char * str, char ** wds, int max);
               
        The function copies each word from the input string (str) into the array of words (wds) provided up to a maximum of max words. Thus the first word would be stored in wds[0], and so on. You will need to allocate sufficient memory to store each word as you go along. The function returns the number of words stored.
      • Define a main program that will test the words function is working properly.
      • A variation on this problem requires the words function to count the number of words initially and then allocate an array of words that is just the right length for the number of words.
               int words(char * str, char *** wds);
               
        Once again, the function returns the number of words stored. Modify your program to incorporate this new version of the words function and test it.

  8. malloc4

    Create a program malloc4.c that

    • defines a struct type called idType which contains an int field called number and a char *     field called name;
    • declares a variable of this type
    • gets a value for the number field from the user;
    • gets a length for the name field from the user;
    • allocates space for this name dynamically and stores the address in the name field;
    • gets the name string from the user and stores it;
    • prints the name and number;
    • frees the memory allocated.

  9. Supplementary Exercises

  10. dynRev1

    The following program reads in a series of characters and then prints them out in reverse order. Paste the code into dynRev1.c, compile it and run it several times to demonstrate that it works.

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct node { char           item;
                          struct node * next;
                        } Node;
    main()
    {
        char c;
        Node * top = NULL;
        Node * np;
    
        printf("Input character: end with a dot (.)\n");
        printf("next "); scanf(" %c", &c);
        while (c != '.')
        {
            np = malloc (sizeof(Node));
            np->item = c;
            np->next = top;
            top = np;
            printf("next "); scanf(" %c", &c);
        }
    
        while (top != NULL)
        {
            printf("%c", top->item);
            np = top;
            top = top->next;
            free(np);
        }
        printf("\n");
    }
    
    Add comments to the program to explain what the statements do.

  11. dynRev2

    Copy dynRev1 to dynRev2.c and modify it so that after each character is added it prints out the addresses of all the items on the stack. For example

    $ ./dynRev2
    
    Input character: end with a dot (.)
    next > a
    a010b68(a) --->
    next > b
    a010b78(b) ---> a010b68(a) --->
    next > c
    a010b88(c) ---> a010b78(b) ---> a010b68(a) --->
    
    next > .
    cba
    
    Compile and test your program.

  12. intstack

    Copy the following code into a header definition file called intstack.h

    #ifndef INTSTACK
    #define INTSTACK
    
    typedef struct node { int           item;
                          struct node * next;
                        } Node;
    typedef struct      { Node * top;
                          int   size;
                        } Stack;
    
    void initialise(Stack * s);
    int  isEmptyStack(Stack * s);
    int  push(Stack * s, int x);
    int  pop(Stack * s, int * x);
    void printStack(Stack * s);
    
    #endif
    

    Note that this header file contains all the type definitions and the headers of the functions in intStack.c but not the bodies of these functions.

    Copy the following code into a file called intStack.c.

    #include <stdio.h>
    #include <stdlib.h>
    #include "intstack.h"
    
    void initialise(Stack * s)
    {
        s->top = NULL;
        s->size = 0;
    }
    
    int isEmptyStack(Stack * s)
    {
        return s->top == NULL;
    }
    
    int push(Stack * s, int x)
    {
        Node * np;
        np = malloc(sizeof(Node));
        if (np == NULL)
            return 0;
        np->item = x;
        np->next = s->top;
        s->top   = np;
        (s->size)++;
        return 1;
    }
    
    int pop(Stack * s, int * x)
    {
        Node * np;
        if (isEmptyStack(s))
           return 0;
        else
        { 
             np     = s->top;
             *x     = s->top->item;
             s->top = s->top->next;
             free(np);
             (s->size)--;
             return 1;
        }
    }
    
    void printStack(Stack * s)
    {
        Node * tmp;
        
        tmp = s->top;
        while (tmp != NULL)
        {
            printf("%d\n",tmp->item);
            tmp = tmp->next;
        }       
    }
    

    Compile intstack.c to produce an object file intstack.o

    gcc -c intstack.c
    

    Using the operations declared in intstack.c, write a program called stack1.c which reads in characters one by one until a dot is entered and then prints them out in reverse order. Remember that you will need to

    #include "intstack.h"
    
    in your program.

    Produce an executable progam:

    gcc intstack.o stack1.c -o stack1
    
  13. queue1

    Copy the following code into a header definition file called intQueue.h

    #ifndef _INT_QUEUE
    #define _INT_QUEUE
    
    typedef struct node { int           item;
                          struct node * next;
                        } Node;
    
    typedef struct      { Node * front;
                          Node * back;
                          int   size;
                        } Queue;
    
    void initialiseQueue(Queue * q);
    int  isEmptyQueue(Queue * q);
    void join(Queue * q, int x);
    void leave(Queue * q, int * x);
    int  length(Queue * q);
    void printQueue(Queue * q);
    
    #endif
    

    Copy the following code into a file called intQueue.c.

    #include <stdio.h>
    #include <stdlib.h>
    #include "intQueue.h"
    
    void initialiseQueue(Queue * q)
    {
        q->front = NULL;
        q->back  = NULL;
        q->size = 0;
    }
    
    int isEmptyQueue(Queue * q)
    {
        return q->front == NULL;
    }
    
    void join(Queue * q, int x)
    {
        Node * np;
        np = malloc(sizeof(Node));
        np->item = x;
        np->next = NULL;    /* ie at back of the queue */
        if (isEmptyQueue(q))
           { q->front = np; }
        else
           { q->back->next = np; }
        q->back = np;
        (q->size)++;
    }
    
    void leave(Queue * q, int * x)
    {
        Node * np;
        if (isEmptyQueue(q))
           { printf("Remove error: empty queue\n");
             exit(1);
           }
        else
           { *x = q->front->item;
             np = q->front;
             q->front = q->front->next;
             free(np);
             (q->size)--;
           }
    }
    
    int length(Queue * q)
    {
        return q->size;
    }
    
    void printQueue(Queue * q)
    {
        Node * np = q->front;
        printf("< ");
        while (np != NULL)
        {
           printf("%d ",np->item);
           np = np->next;
        }
        printf("<");
    }
    
    Compile intQueue.c to produce an object file intQueue.o

    Using the operations declared in intQueue.c, write a program called queue1.c that

    1. reads in numbers one by one until -1 is entered and joins them onto a queue;
    2. prints out all the entries in the current queue after each number is entered;
    3. empties the queue after -1 has been entered.

    Compile queue1.c with intQueue.o to produce an executable program.

    gcc queue1.c intQueue.o -o queue1
    
  14. Supplementary Exercises

  15. queue2

    Given the type definitions

    typedef struct      { char jobName[20];
                          int cpuTime;
                          int priority;
                        } ProcessType;
    
    typedef struct node { ProcessType   process;
                          struct node * next;
                        } Node;
    
    

    1. Write a file processQueue.c which contains the implementation of the queue functions for items of this type

    2. Write a corresponding header file processQueue.h

    3. Compile processQueue.c into an object file processQueue.o

    4. Write a program queue2.c that

      1. reads in jobName and cpuTime and priority values for a process and joins the process onto a queue; it repeats this until the user enters a null jobName and then
      2. takes each process off the queue in turn, prints its name and subtracts one from cpuTime; if the cpuTime is greater than zero, it joins the item back onto the queue
      3. repeats step (b) above until there are no items left on the queue

    5. Compile and test queue2

    This is called Round Robin Scheduling

  16. queue3

    Using the same processQueue.o object file from the queue2 exercise , write a program queue3.c that

    1. Initialises 3 queues;

    2. Asks the user to enter values for jobName, cpuTime and priority

      1. if priority is between 0 and 49, the process is added to queue 0;
      2. if priority is between 50 and 99, the process is added to queue 1;
      3. otherwise the process is added to queue 2.
      It repeats this until the user enters a null jobName and then

    3. Services each queue in turn (starting with 0) by

      • taking each process off the queue in turn, printing its name and subtracting one from cpuTime
      • if the cpuTime is greater than zero, joining the item back onto the queue

    4. The program does not move on to the next queue until the current one is empty.

    5. Compile and test queue2.c

    This is called Priority Queue Scheduling

  17. rpCalc

    Calculator programs typically use a notation called Reverse Polish to evaluate the calculation entered. For example

    calculation would be represented as answer
    1 + 2 + 3 1 2 3 + + 6
    1 + 2 * 3 1 2 3 * + 7
    (1 + 2) * 3 1 2 + 3 * 9
    (((1 + 2)*(3 + 4)) - 5)/4 1 2 + 3 4 + * 5 - 4 / 4

    Write a program rpCalc.c which

    1. Reads the characters of a Reverse Polish string one by one from the keyboard until the character = is entered.

    2. If the character is a digit (i.e. an ascii value between 48 and 57), adds the number represented by that digit to the top of a stack.
      The number that a digit c represents can be calculated as c-48.

    3. If the character is +, -, * or /

      1. removes the last two entries from the stack
      2. either adds them (+), subtracts them (-), multiplies them (*) or divides them (/), being sure to do this the right way round.
      3. adds the result of this calculation to the top of the stack

    4. If the character input is not a digit or one of + - * /, it should be ignored.

    5. when an = is enterered, remove the top entry from the stack (it should be the only one) and print it out as the result of the calculation.

    Compile and test your calculator program. This is not an easy program but is not too difficult if you follow the above steps.